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