HTTP v1.13.1
Loading...
Searching...
No Matches
HTTPBuilder.cpp
Go to the documentation of this file.
1#include "HTTPBuilder.h"
2
3#include <array>
4#include <unordered_map>
5#include <unordered_set>
6#include <algorithm>
7#include <format>
8
9#include "HTTPParser.h"
10
11using namespace std;
12
13static const unordered_set<string_view> availableHTTPVersions =
14{
15 "HTTP/0.9",
16 "HTTP/1.0",
17 "HTTP/1.1",
18 "HTTP/2",
19 "HTTP/3"
20};
21
22namespace web
23{
24 string HTTPBuilder::getChunks(const vector<string>& chunks, bool partialChunks, bool preCalculateSize)
25 {
26 string result;
27
28 if (preCalculateSize)
29 {
30 size_t resultSize = 0;
31
32 for (const string& chunk : chunks)
33 {
34 resultSize += format("{:x}", chunk.size()).size() + HTTPParser::crlf.size() + chunk.size() + HTTPParser::crlf.size();
35 }
36
37 resultSize += 1 + HTTPParser::crlf.size();
38
39 result.reserve(resultSize);
40 }
41
42 for (const string& chunk : chunks)
43 {
45 }
46
47 if (!partialChunks)
48 {
49 result += HTTPBuilder::getChunk({});
50 }
51
52 return result;
53 }
54
55 string HTTPBuilder::getChunk(string_view chunk)
56 {
57 return format("{:x}{}{}{}", chunk.size(), HTTPParser::crlf, chunk, HTTPParser::crlf);
58 }
59
60 HTTPBuilder::HTTPBuilder(string_view fullHTTPVersion) :
61 _HTTPVersion(fullHTTPVersion),
62 _partialChunks(false)
63 {
64 if (availableHTTPVersions.find(fullHTTPVersion) == availableHTTPVersions.end())
65 {
66 throw runtime_error("Wrong HTTP version");
67 }
68 }
69
71 {
72 method = "GET";
73
74 return *this;
75 }
76
78 {
79 method = "POST";
80
81 return *this;
82 }
83
85 {
86 method = "PUT";
87
88 return *this;
89 }
90
92 {
93 method = "HEAD";
94
95 return *this;
96 }
97
99 {
100 method = "OPTIONS";
101
102 return *this;
103 }
104
106 {
107 method = "DELETE";
108
109 return *this;
110 }
111
113 {
114 method = "CONNECT";
115
116 return *this;
117 }
118
120 {
121 method = "TRACE";
122
123 return *this;
124 }
125
127 {
128 method = "PATCH";
129
130 return *this;
131 }
132
133 HTTPBuilder& HTTPBuilder::parameters(string_view parameters)
134 {
135 if (method != "CONNECT")
136 {
137 if (size_t queryStart = parameters.find('?'); queryStart != string::npos)
138 {
139 _parameters = format
140 (
141 "{}{}",
142 string_view(parameters.begin(), parameters.begin() + queryStart + 1),
143 string_view(parameters.begin() + queryStart + 1, parameters.end())
144 );
145 }
146 else
147 {
148 _parameters = parameters;
149 }
150
151 if (!_parameters.starts_with('/'))
152 {
153 _parameters.insert(_parameters.begin(), '/');
154 }
155 }
156 else
157 {
158 _parameters = parameters;
159 }
160
161 return *this;
162 }
163
165 {
166 _responseCode = format("{} {}", static_cast<int>(code), getMessageFromCode(code));
167
168 return *this;
169 }
170
171 HTTPBuilder& HTTPBuilder::responseCode(int code, string_view responseMessage)
172 {
173 _responseCode = format("{} {}", code, responseMessage);
174
175 return *this;
176 }
177
178 HTTPBuilder& HTTPBuilder::HTTPVersion(string_view HTTPVersion)
179 {
180 if (HTTPVersion.find("HTTP") == string::npos)
181 {
182 _HTTPVersion = HTTPVersion;
183 }
184 else
185 {
186 _HTTPVersion = format("HTTP/{}", HTTPVersion);
187 }
188
189 if (availableHTTPVersions.find(_HTTPVersion) == availableHTTPVersions.end())
190 {
191 throw runtime_error("Wrong HTTP version");
192 }
193
194 return *this;
195 }
196
197 HTTPBuilder& HTTPBuilder::chunks(const vector<string>& chunks)
198 {
199 _chunks.reserve(chunks.size());
200
201 copy(chunks.begin(), chunks.end(), back_inserter(_chunks));
202
203 return *this;
204 }
205
206 HTTPBuilder& HTTPBuilder::chunks(vector<string>&& chunks)
207 {
208 _chunks.reserve(chunks.size());
209
210 move(chunks.begin(), chunks.end(), back_inserter(_chunks));
211
212 return *this;
213 }
214
215 HTTPBuilder& HTTPBuilder::chunk(string_view chunk)
216 {
217 _chunks.emplace_back(chunk);
218
219 return *this;
220 }
221
222 string HTTPBuilder::build(string_view data, const unordered_map<string, string>& additionalHeaders) const
223 {
224 string result;
225 unordered_map<string, string> buildHeaders(additionalHeaders);
226
227 if (data.size())
228 {
229 buildHeaders["Content-Length"] = to_string(data.size());
230 }
231 else if (_chunks.size())
232 {
233 buildHeaders["Transfer-Encoding"] = "chunked";
234 }
235
236 if (method.empty())
237 {
238 result = format("{} {}{}{}", _HTTPVersion, _responseCode, HTTPParser::crlf, _headers);
239 }
240 else
241 {
242 result = method + ' ';
243
244 if (_parameters.empty() && method != "CONNECT")
245 {
246 result += "/";
247 }
248
249 result += format("{} {}{}{}", _parameters, _HTTPVersion, HTTPParser::crlf, _headers);
250 }
251
252 for (const auto& [header, value] : buildHeaders)
253 {
254 result += format("{}: {}{}", header, value, HTTPParser::crlf);
255 }
256
257 result += HTTPParser::crlf;
258
259 if (data.size())
260 {
261 result += data;
262 }
263 else if (_chunks.size())
264 {
265 result += HTTPBuilder::getChunks(_chunks, _partialChunks);
266 }
267
268 return result;
269 }
270
271 string HTTPBuilder::build(const json::JSONBuilder& builder, unordered_map<string, string> additionalHeaders) const
272 {
273 additionalHeaders["Content-Type"] = "application/json";
274
275 return this->build(builder.build(), additionalHeaders);
276 }
277
278 string HTTPBuilder::build(const unordered_map<string, string>& urlEncoded, unordered_map<string, string> additionalHeaders) const
279 {
280 string body;
281
282 for (const auto& [key, value] : urlEncoded)
283 {
284 body += format("{}={}&", web::encodeUrl(key), web::encodeUrl(value));
285 }
286
287 body.pop_back(); // remove last &
288
289 additionalHeaders["Content-Type"] = "application/x-www-form-urlencoded";
290
291 return this->build(body, additionalHeaders);
292 }
293
295 {
296 method.clear();
297 _parameters.clear();
298 _responseCode.clear();
299 _headers.clear();
300
301 return *this;
302 }
303
305 {
306 _partialChunks = true;
307
308 return *this;
309 }
310
311 ostream& operator << (ostream& outputStream, const HTTPBuilder& builder)
312 {
313 return outputStream << builder.build();
314 }
315}
HTTP builder.
Definition HTTPBuilder.h:16
HTTPBuilder & traceRequest()
Set TRACE request.
HTTPBuilder & clear()
HTTPBuilder & parameters(std::string_view parameters)
Set parameters.
static std::string getChunk(std::string_view chunk)
Make HTTP parsed chunk.
HTTPBuilder & getRequest()
Set GET request.
HTTPBuilder & partialChunks()
HTTPBuilder & connectRequest()
Set CONNECT request.
HTTPBuilder & optionsRequest()
Set OPTIONS request.
HTTPBuilder & postRequest()
Set POST request.
HTTPBuilder(std::string_view fullHTTPVersion="HTTP/1.1")
static std::string getChunks(const std::vector< std::string > &chunks, bool partialChunks, bool preCalculateSize=false)
Make HTTP parsed data with zero chunk.
HTTPBuilder & headRequest()
Set HEAD request.
HTTPBuilder & responseCode(ResponseCodes code)
HTTPBuilder & putRequest()
Set PUT request.
HTTPBuilder & chunk(std::string_view chunk)
HTTPBuilder & patchRequest()
Set PATCH request.
HTTPBuilder & HTTPVersion(std::string_view httpVersion)
std::string build(std::string_view data="", const std::unordered_map< std::string, std::string > &additionalHeaders={}) const
HTTPBuilder & deleteRequest()
Set DELETE request.
HTTPBuilder & chunks(const std::vector< std::string > &chunks)
static constexpr std::string_view crlf
Definition HTTPParser.h:31
ResponseCodes
Response codes.
Definition HTTPUtility.h:25
std::string getMessageFromCode(const T &code)
Get response message from response code.
ostream & operator<<(ostream &outputStream, const HTTPBuilder &builder)
string encodeUrl(string_view data)