SNode.C
Loading...
Searching...
No Matches
Fields.cpp
Go to the documentation of this file.
1/*
2 * snode.c - a slim toolkit for network communication
3 * Copyright (C) Volker Christian <me@vchrist.at>
4 * 2020, 2021, 2022, 2023, 2024, 2025
5 *
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU Lesser General Public License as published
8 * by the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20#include "web/http/decoder/Fields.h"
21
22#include "core/socket/stream/SocketContext.h"
23
24#ifndef DOXYGEN_SHOULD_SKIP_THIS
25
26#include "web/http/http_utils.h"
27
28#include <cctype>
29#include <utility>
30
31#endif // DOXYGEN_SHOULD_SKIP_THIS
32
33#define MAX_LINE_LENGTH 8192
34// #define MAX_LINE_LENGTH 1
35
36namespace web::http::decoder {
37
38 Fields::Fields(core::socket::stream::SocketContext* socketContext, std::set<std::string> fieldsExpected)
39 : socketContext(socketContext)
42 }
43
44 void Fields::setFieldsExpected(std::set<std::string> fieldsExpected) {
45 this->fieldsExpected = std::move(fieldsExpected);
46 }
47
48 std::size_t Fields::read() {
49 std::size_t consumed = 0;
50
51 if (completed || errorCode != 0) {
52 completed = false;
53 fields.clear();
54 errorCode = 0;
55 errorReason = "";
56 }
57
58 std::size_t ret = 0;
59 do {
60 char ch = '\0';
61
63 consumed += ret;
64
65 if (ret > 0) {
66 if (!line.empty() || ch != ' ') {
67 line += ch;
68
69 if (maxLineLength == 0 || line.size() <= maxLineLength) {
71 last = ch;
72
73 if (lastButOne == '\r' && last == '\n') {
74 line.pop_back(); // Remove \n
75 line.pop_back(); // Remove \r
76
77 completed = line.empty();
78 if (!completed) {
80
81 if (!fieldsExpected.empty() && fields.size() > fieldsExpected.size()) {
82 errorCode = 400;
83 errorReason = "Too many fields";
84 }
85 } else if (!fieldsExpected.empty() && fields.size() < fieldsExpected.size()) {
86 errorCode = 400;
87 errorReason = "Too view fields";
88
89 completed = false;
90 }
91 line.clear();
92 lastButOne = '\0';
93 last = '\0';
94 }
95 } else {
96 errorCode = 431;
97 errorReason = "Line too long: " + line;
98 }
99 } else {
100 errorCode = 400;
101 errorReason = "Header Folding";
102 }
103 }
104 } while (ret > 0 && !completed && errorCode == 0);
105
106 return consumed;
107 }
108
109 void Fields::splitLine(const std::string& line) {
110 auto [headerFieldName, value] = httputils::str_split(line, ':');
111
112 if (headerFieldName.empty()) {
113 errorCode = 400;
114 errorReason = "Header field empty";
115 } else if ((std::isblank(headerFieldName.back()) != 0) || (std::isblank(headerFieldName.front()) != 0)) {
116 errorCode = 400;
117 errorReason = "White space before or after field";
118 } else if (value.empty()) {
119 errorCode = 400;
120 errorReason = "Value of field \"" + headerFieldName + "\" empty";
121 } else {
122 if (fieldsExpected.empty() || fieldsExpected.contains(headerFieldName)) {
123 httputils::str_trimm(value);
124
125 if (!fields.contains(headerFieldName)) {
126 fields.emplace(headerFieldName, value);
127 } else {
128 fields[headerFieldName] += "," + value;
129 }
130 } else if (!fieldsExpected.empty() && !fieldsExpected.contains(headerFieldName)) {
131 errorCode = 400;
132 errorReason = "Field '" + headerFieldName + "' not in expected fields";
133 }
134 }
135 }
136
137 web::http::CiStringMap<std::string>&& Fields::getHeader() {
138 return std::move(fields);
139 }
140
141 bool Fields::isComplete() const {
142 return completed;
143 }
144
145 bool Fields::isError() const {
146 return errorCode != 0;
147 }
148
149 int Fields::getErrorCode() const {
150 return errorCode;
151 }
152
153 std::string Fields::getErrorReason() {
154 return errorReason;
155 }
156
157} // namespace web::http::decoder
#define MAX_LINE_LENGTH
Definition Fields.cpp:33
std::size_t readFromPeer(char *chunk, std::size_t chunklen) const final
std::string getErrorReason()
Definition Fields.cpp:153
Fields(core::socket::stream::SocketContext *socketContext, std::set< std::string > fieldsExpected={})
Definition Fields.cpp:38
core::socket::stream::SocketContext * socketContext
Definition Fields.h:63
void splitLine(const std::string &line)
Definition Fields.cpp:109
std::size_t maxLineLength
Definition Fields.h:69
void setFieldsExpected(std::set< std::string > fieldsExpected)
Definition Fields.cpp:44
web::http::CiStringMap< std::string > && getHeader()
Definition Fields.cpp:137
std::string errorReason
Definition Fields.h:73