SNode.C
Loading...
Searching...
No Matches
ResponseParser.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/client/ResponseParser.h"
43
44#include "web/http/StatusCodes.h"
45
46#ifndef DOXYGEN_SHOULD_SKIP_THIS
47
48#include "web/http/http_utils.h"
49
50#include <regex>
51#include <tuple>
52#include <utility>
53
54#endif /* DOXYGEN_SHOULD_SKIP_THIS */
55
56namespace web::http::client {
57
58 ResponseParser::ResponseParser(core::socket::stream::SocketContext* socketContext,
59 const std::function<void()>& onResponseStart,
60 const std::function<void(Response&)>& onResponseParsed,
61 const std::function<void(int, const std::string&)>& onResponseParseError)
62 : Parser(socketContext)
63 , onResponseStart(onResponseStart)
64 , onResponseParsed(onResponseParsed)
65 , onResponseParseError(onResponseParseError) {
66 }
67
70 }
71
72 void ResponseParser::parseStartLine(const std::string& line) {
74
75 if (!line.empty()) {
76 std::string remaining;
77
78 std::tie(response.httpVersion, remaining) = httputils::str_split(line, ' ');
79
80 std::smatch httpVersionMatch;
81 if (!std::regex_match(response.httpVersion, httpVersionMatch, httpVersionRegex)) {
82 parseError(400, "Wrong protocol version: " + response.httpVersion);
83 } else {
84 httpMajor = std::stoi(httpVersionMatch.str(1));
85 httpMinor = std::stoi(httpVersionMatch.str(2));
86
87 if (httpMinor == 0) {
89 } else if (httpMinor == 1) {
91 }
92
95 if (response.reason.empty()) {
96 parseError(400, "No reason phrase");
97 }
98 } else {
99 parseError(400, "Unknown status code");
100 }
101 }
102 } else {
103 parseError(400, "Response line empty");
104 }
105 }
106
109
110 if (headers.contains("Connection")) {
111 const std::string& connection = headers["Connection"];
112 if (web::http::ciContains(connection, "keep-alive")) {
114 } else if (web::http::ciContains(connection, "close")) {
116 }
117 }
118 if (headers.contains("Set-Cookie")) {
119 std::string cookiesLine = headers["Set-Cookie"];
120
121 while (!cookiesLine.empty()) {
122 std::string cookieLine;
123 std::tie(cookieLine, cookiesLine) = httputils::str_split(cookiesLine, ',');
124
125 std::string cookieOptions;
126 std::string cookie;
127 std::tie(cookie, cookieOptions) = httputils::str_split(cookieLine, ';');
128
129 std::string cookieName;
130 std::string cookieValue;
131 std::tie(cookieName, cookieValue) = httputils::str_split(cookie, '=');
132
133 httputils::str_trimm(cookieName);
134 httputils::str_trimm(cookieValue);
135
136 std::map<std::string, CookieOptions>::iterator cookieElement;
137 bool inserted = false;
138 std::tie(cookieElement, inserted) = response.cookies.insert({cookieName, CookieOptions(cookieValue)});
139
140 while (!cookieOptions.empty()) {
141 std::string option;
142 std::tie(option, cookieOptions) = httputils::str_split(cookieOptions, ';');
143
144 std::string optionName;
145 std::string optionValue;
146 std::tie(optionName, optionValue) = httputils::str_split(option, '=');
147
148 httputils::str_trimm(optionName);
149 httputils::str_trimm(optionValue);
150
151 cookieElement->second.setOption(optionName, optionValue);
152 }
153 }
154
155 Parser::headers.erase("Set-Cookie");
156 }
157
160 } else {
162 }
163 }
164
168 response.headers = std::move(headers);
169 response.body = std::move(content);
170
172
173 reset();
174
176 }
177
178 void ResponseParser::parseError(int code, const std::string& reason) {
179 onResponseParseError(code, reason);
180
181 reset();
182
184 }
185
186} // namespace web::http::client
void setOption(const std::string &optionName, const std::string &optionValue)
CookieOptions(const std::string &value)
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
static bool contains(int status)
void parseError(int code, const std::string &reason) override
void parseStartLine(const std::string &line) override
ResponseParser(core::socket::stream::SocketContext *socketContext, const std::function< void()> &onResponseStart, const std::function< void(Response &)> &onResponseParsed, const std::function< void(int, const std::string &)> &onResponseParseError)
std::function< void(Response &)> onResponseParsed
std::function< void()> onResponseStart
std::function< void(int, const std::string &)> onResponseParseError
CiStringMap< std::string > headers
Definition Response.h:83
CiStringMap< CookieOptions > cookies
Definition Response.h:84
ConnectionState connectionState
Definition Response.h:74
std::vector< char > body
Definition Response.h:85
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)