SNode.C
Loading...
Searching...
No Matches
RequestParser.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/*
21 * MIT License
22 *
23 * Permission is hereby granted, free of charge, to any person obtaining a copy
24 * of this software and associated documentation files (the "Software"), to deal
25 * in the Software without restriction, including without limitation the rights
26 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
27 * copies of the Software, and to permit persons to whom the Software is
28 * furnished to do so, subject to the following conditions:
29 *
30 * The above copyright notice and this permission notice shall be included in
31 * all copies or substantial portions of the Software.
32 *
33 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
34 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
35 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
36 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
37 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
38 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
39 * THE SOFTWARE.
40 */
41
42#include "web/http/server/RequestParser.h"
43
44#ifndef DOXYGEN_SHOULD_SKIP_THIS
45
46#include "web/http/http_utils.h"
47
48#include <regex>
49#include <tuple>
50#include <utility>
51
52#endif /* DOXYGEN_SHOULD_SKIP_THIS */
53
54namespace web::http::server {
55
56 RequestParser::RequestParser(core::socket::stream::SocketContext* socketContext,
57 const std::function<void()>& onRequestStart,
58 const std::function<void(Request&&)>& onRequestParsed,
59 const std::function<void(int, const std::string&)>& onRequestParseError)
60 : Parser(socketContext)
61 , onRequestStart(onRequestStart)
62 , onRequestParsed(onRequestParsed)
63 , onRequestParseError(onRequestParseError) {
64 }
65
66 bool RequestParser::methodSupported(const std::string& method) const {
67 return supportedMethods.contains(method);
68 }
69
72 }
73
74 void RequestParser::parseStartLine(const std::string& line) {
76
77 if (!line.empty()) {
78 std::string remaining;
79
80 std::tie(request.method, remaining) = httputils::str_split(line, ' ');
82
83 std::string queriesLine;
84 std::tie(std::ignore, queriesLine) = httputils::str_split(request.url, '?');
85
87 parseError(400, "Bad request method: " + request.method);
88 } else if (request.url.empty() || request.url.front() != '/') {
89 parseError(400, "Malformed request");
90 } else {
91 std::smatch httpVersionMatch;
92 if (!std::regex_match(request.httpVersion, httpVersionMatch, httpVersionRegex)) {
93 parseError(400, "Wrong protocol-version: " + request.httpVersion);
94 } else {
95 httpMajor = std::stoi(httpVersionMatch.str(1));
96 httpMinor = std::stoi(httpVersionMatch.str(2));
97
98 while (!queriesLine.empty()) {
99 std::string query;
100
101 std::tie(query, queriesLine) = httputils::str_split(queriesLine, '&');
103 }
104 }
105 }
106 } else {
107 parseError(400, "Request-line empty");
108 }
109 }
110
113
114 if (headers.contains("Connection")) {
115 const std::string& connection = headers["Connection"];
116 if (web::http::ciContains(connection, "keep-alive")) {
118 } else if (web::http::ciContains(connection, "close")) {
120 }
121 }
122
123 if (headers.contains("Cookie")) {
124 std::string& cookiesLine = headers["Cookie"];
125
126 while (!cookiesLine.empty()) {
127 std::string cookieLine;
128 std::tie(cookieLine, cookiesLine) = httputils::str_split(cookiesLine, ',');
129
130 while (!cookieLine.empty()) {
131 std::string cookie;
132 std::tie(cookie, cookieLine) = httputils::str_split(cookieLine, ';');
133
134 std::string cookieName;
135 std::string cookieValue;
136 std::tie(cookieName, cookieValue) = httputils::str_split(cookie, '=');
137
138 httputils::str_trimm(cookieName);
139 httputils::str_trimm(cookieValue);
140
141 request.cookies.emplace(cookieName, cookieValue);
142 }
143 }
144
145 headers.erase("Cookie");
146 }
147
150 } else {
152 }
153 }
154
158 request.headers = std::move(Parser::headers);
159 request.body = std::move(content);
160
161 onRequestParsed(std::move(request));
162
163 reset();
164
166 }
167
168 void RequestParser::parseError(int code, const std::string& reason) {
169 onRequestParseError(code, reason);
170
171 reset();
172
174 }
175
176} // namespace web::http::server
ParserState parserState
Definition Parser.h:100
Parser(core::socket::stream::SocketContext *socketContext, const enum HTTPCompliance &compliance=HTTPCompliance::RFC2616|HTTPCompliance::RFC7230)
Definition Parser.cpp:63
static const std::regex httpVersionRegex
Definition Parser.h:104
std::size_t contentLength
Definition Parser.h:143
CiStringMap< std::string > headers
Definition Parser.h:119
TransferEncoding transferEncoding
Definition Parser.h:102
std::vector< char > content
Definition Parser.h:120
virtual void analyzeHeader()
Definition Parser.cpp:160
void parseStartLine(const std::string &line) override
void parseError(int code, const std::string &reason) override
std::function< void(int, const std::string &)> onRequestParseError
std::function< void(Request &&)> onRequestParsed
std::set< std::string > supportedMethods
virtual bool methodSupported(const std::string &method) const
std::function< void()> onRequestStart
RequestParser(core::socket::stream::SocketContext *socketContext, const std::function< void()> &onRequestStart, const std::function< void(Request &&)> &onRequestParsed, const std::function< void(int, const std::string &)> &onRequestParseError)
std::vector< char > body
Definition Request.h:88
CiStringMap< std::string > cookies
Definition Request.h:87
CiStringMap< std::string > headers
Definition Request.h:86
CiStringMap< std::string > queries
Definition Request.h:85
std::string httpVersion
Definition Request.h:81
ConnectionState connectionState
Definition Request.h:75
std::pair< std::string, std::string > str_split(const std::string &base, char c_middle)
std::string & str_trimm(std::string &text)
bool ciContains(const std::string &str1, const std::string &str2)