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#include "web/http/client/ResponseParser.h"
21
22#include "web/http/ContentDecoder.h"
23#include "web/http/StatusCodes.h"
24
25#ifndef DOXYGEN_SHOULD_SKIP_THIS
26
27#include "web/http/http_utils.h"
28
29#include <regex>
30#include <tuple>
31#include <utility>
32
33#endif /* DOXYGEN_SHOULD_SKIP_THIS */
34
35namespace web::http::client {
36
37 ResponseParser::ResponseParser(core::socket::stream::SocketContext* socketContext,
38 const std::function<void()>& onResponseStart,
39 const std::function<void(Response&&)>& onResponseParsed,
40 const std::function<void(int, const std::string&)>& onResponseParseError)
41 : Parser(socketContext)
42 , onResponseStart(onResponseStart)
43 , onResponseParsed(onResponseParsed)
44 , onResponseParseError(onResponseParseError) {
45 }
46
48 if (!decoderQueue.empty()) {
49 response.body = decoderQueue.back()->getContent();
50 }
51
52 return std::move(response);
53 }
54
56 onResponseStart();
57 }
58
59 void ResponseParser::parseStartLine(const std::string& line) {
61
62 if (!line.empty()) {
63 std::string remaining;
64
65 std::tie(response.httpVersion, remaining) = httputils::str_split(line, ' ');
66
67 std::smatch httpVersionMatch;
68 if (!std::regex_match(response.httpVersion, httpVersionMatch, httpVersionRegex)) {
69 parseError(400, "Wrong protocol version: " + response.httpVersion);
70 } else {
71 httpMajor = std::stoi(httpVersionMatch.str(1));
72 httpMinor = std::stoi(httpVersionMatch.str(2));
73
74 std::tie(response.statusCode, response.reason) = httputils::str_split(remaining, ' ');
75 if (StatusCode::contains(std::stoi(response.statusCode))) {
76 if (response.reason.empty()) {
77 parseError(400, "No reason phrase");
78 }
79 } else {
80 parseError(400, "Unknown status code");
81 }
82 }
83 } else {
84 parseError(400, "Response line empty");
85 }
86 }
87
90
91 if (headers.contains("Connection")) {
92 const std::string& connection = headers["Connection"];
93 if (web::http::ciContains(connection, "keep-alive")) {
94 response.connectionState = ConnectionState::Keep;
95 } else if (web::http::ciContains(connection, "close")) {
96 response.connectionState = ConnectionState::Close;
97 }
98 }
99 if (headers.contains("Set-Cookie")) {
100 std::string cookiesLine = headers["Set-Cookie"];
101
102 while (!cookiesLine.empty()) {
103 std::string cookieLine;
104 std::tie(cookieLine, cookiesLine) = httputils::str_split(cookiesLine, ',');
105
106 std::string cookieOptions;
107 std::string cookie;
108 std::tie(cookie, cookieOptions) = httputils::str_split(cookieLine, ';');
109
110 std::string cookieName;
111 std::string cookieValue;
112 std::tie(cookieName, cookieValue) = httputils::str_split(cookie, '=');
113
114 httputils::str_trimm(cookieName);
115 httputils::str_trimm(cookieValue);
116
117 std::map<std::string, CookieOptions>::iterator cookieElement;
118 bool inserted = false;
119 std::tie(cookieElement, inserted) = response.cookies.insert({cookieName, CookieOptions(cookieValue)});
120
121 while (!cookieOptions.empty()) {
122 std::string option;
123 std::tie(option, cookieOptions) = httputils::str_split(cookieOptions, ';');
124
125 std::string optionName;
126 std::string optionValue;
127 std::tie(optionName, optionValue) = httputils::str_split(option, '=');
128
129 httputils::str_trimm(optionName);
130 httputils::str_trimm(optionValue);
131
132 cookieElement->second.setOption(optionName, optionValue);
133 }
134 }
135
136 Parser::headers.erase("Set-Cookie");
137 }
138
139 if (transferEncoding == TransferEncoding::Identity && contentLength == 0) {
141 } else {
143 }
144 }
145
147 response.httpMajor = httpMajor;
148 response.httpMinor = httpMinor;
149 response.headers = std::move(headers);
150 response.body = std::move(content);
151
152 onResponseParsed(std::move(response));
153
154 reset();
155
157 }
158
159 void ResponseParser::parseError(int code, const std::string& reason) {
160 onResponseParseError(code, reason);
161
162 reset();
163
165 }
166
167} // namespace web::http::client
ParserState parserState
Definition Parser.h:78
virtual void analyzeHeader()
Definition Parser.cpp:138
void parseError(int code, const std::string &reason) override
void parseStartLine(const std::string &line) override