-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathHttpHeader.hpp
255 lines (226 loc) · 6.8 KB
/
HttpHeader.hpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
#pragma once
#include <string>
#include <vector>
#include <optional>
#include <ranges>
#include "ws_client/utils/string.hpp"
namespace ws_client
{
using std::string;
using std::optional;
using std::vector;
using std::pair;
/**
* HTTP status code, protocol version and optional reason text.
* Example: "HTTP/1.1 200 OK"
*/
struct HttpStatusLine
{
/**
* Protocol version, e.g. "HTTP/1.1"
*/
string protocol_version;
/**
* Status code, e.g. 200, 404, 500, etc.
*/
int status_code{0};
/**
* Reason text, e.g. "OK", "Not Found", "Internal Server Error", etc.
* This field is optional and may be empty.
*/
string reason;
};
/**
* HTTP request line with method, request target and protocol version.
* Example: "GET /index.html HTTP/1.1"
*/
struct HttpRequestLine
{
/**
* HTTP method, e.g. "GET", "POST", "PUT", "DELETE", etc.
*/
string method;
/**
* Request target, e.g. "/", "/index.html", "/api/v1/resource", etc.
*/
string request_target;
/**
* Protocol version, e.g. "HTTP/1.1"
*/
string http_version;
};
struct HttpHeaderFields
{
/**
* List of key-value header pairs.
*
* Using vector of pairs instead of map-like structure to allow
* for multiple headers with the same key and to preserve insertion order.
* E.g. "Set-Cookie" or "WWW-Authenticate" etc. may appear multiple times.
*/
vector<pair<string, string>> fields;
HttpHeaderFields() noexcept = default;
explicit HttpHeaderFields(vector<pair<string, string>>&& fields) noexcept : fields(std::move(fields))
{
}
/**
* Adds a key-value header pair to the list of headers.
* Existing headers with the same key are not replaced or altered,
* the new header is simply added as separate entry.
* Key-comparison is case-insensitive.
*/
void add(const string& key, const string& value) noexcept
{
fields.emplace_back(key, value);
}
/**
* Adds a key-value header pair to the list of headers
* if no header with the same key exists, otherwise does nothing.
*/
void add_if_missing(const string& key, const string& value) noexcept
{
if (!this->contains_key(key))
this->add(key, value);
}
/**
* Adds a key-value header pair to the list of headers.
* If a header with the same key already exists, it is replaced.
* Key-comparison is case-insensitive.
*/
void set(const string& key, const string& value) noexcept
{
// remove all existing headers with the same key
fields.erase(
std::remove_if(
fields.begin(),
fields.end(),
[&key](const auto& header) { return equals_ci(header.first, key); }
),
fields.end()
);
// add the new header
fields.emplace_back(key, value);
}
/**
* Retrievs all values headers that match the given key.
* Key-comparison is case-insensitive.
*/
vector<string> get(const string& key) const noexcept
{
vector<string> result;
for (const auto& header : fields)
{
if (equals_ci(header.first, key))
result.push_back(header.second);
}
return result;
}
/**
* Retrievs the first value of the header with the given key.
* Note that the HTTP standard allows for multiple headers with the same key,
* though it is uncommon.
* Key-comparison is case-insensitive.
*/
optional<string> get_first(const string& key) const noexcept
{
auto result = std::ranges::find_if(
fields,
[&key](const auto& header)
{
// case-insensitive
return equals_ci(header.first, key);
}
);
if (result != fields.end())
return result->second;
return {};
}
/**
* Removes all headers that match the given key.
* Key-comparison is case-insensitive.
*/
void remove_key(const string& key) noexcept
{
fields.erase(
std::remove_if(
fields.begin(),
fields.end(),
[&key](const auto& header) { return equals_ci(header.first, key); }
),
fields.end()
);
}
/**
* Checks if a header with the given key exists.
* Key-comparison is case-insensitive.
*/
bool contains_key(const string& key) const noexcept
{
return std::ranges::any_of(
fields, [&key](const auto& header) { return equals_ci(header.first, key); }
);
}
/**
* Counts the number of headers with the given key.
* Key-comparison is case-insensitive.
*/
size_t count_key(const string& key) const noexcept
{
return std::ranges::count_if(
fields, [&key](const auto& header) { return equals_ci(header.first, key); }
);
}
};
/**
* Holds the HTTP request line and header fields of an HTTP request.
*/
struct HttpRequestHeader
{
HttpRequestLine request_line;
HttpHeaderFields fields;
HttpRequestHeader() noexcept = default;
explicit HttpRequestHeader(HttpRequestLine request_line) noexcept : request_line(std::move(request_line))
{
}
explicit HttpRequestHeader(HttpRequestLine request_line, HttpHeaderFields fields) noexcept
: request_line(std::move(request_line)), fields(std::move(fields))
{
}
explicit HttpRequestHeader(HttpRequestLine request_line, vector<pair<string, string>>&& fields) noexcept
: request_line(std::move(request_line)), fields(HttpHeaderFields(std::move(fields)))
{
}
};
inline std::ostream& operator<<(std::ostream& stream, const HttpRequestHeader& request_header)
{
stream << request_header.request_line.method << " "
<< request_header.request_line.request_target << " "
<< request_header.request_line.http_version << "\r\n";
for (const auto& [key, value] : request_header.fields.fields)
{
stream << key << ": " << value << "\r\n";
}
stream << "\r\n";
return stream;
}
/**
* Holds the HTTP status line and header fields of an HTTP response.
*/
struct HttpResponseHeader
{
HttpStatusLine status_line;
HttpHeaderFields fields;
HttpResponseHeader() noexcept = default;
HttpResponseHeader(HttpStatusLine status_line) noexcept : status_line(std::move(status_line))
{
}
HttpResponseHeader(HttpStatusLine status_line, HttpHeaderFields fields) noexcept
: status_line(std::move(status_line)), fields(std::move(fields))
{
}
HttpResponseHeader(HttpStatusLine status_line, vector<pair<string, string>>&& fields) noexcept
: status_line(std::move(status_line)), fields(HttpHeaderFields(std::move(fields)))
{
}
};
} // namespace ws_client