Line | Branch | Exec | Source |
---|---|---|---|
1 | // | ||
2 | // Copyright (c) 2025 Mohammad Nejati | ||
3 | // | ||
4 | // Distributed under the Boost Software License, Version 1.0. (See accompanying | ||
5 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) | ||
6 | // | ||
7 | // Official repository: https://github.com/cppalliance/http_proto | ||
8 | // | ||
9 | |||
10 | #ifndef BOOST_HTTP_PROTO_REQUEST_HPP | ||
11 | #define BOOST_HTTP_PROTO_REQUEST_HPP | ||
12 | |||
13 | #include <boost/http_proto/request_base.hpp> | ||
14 | |||
15 | namespace boost { | ||
16 | namespace http_proto { | ||
17 | |||
18 | /** A modifiable container for HTTP requests. | ||
19 | |||
20 | This container owns a request, represented by | ||
21 | a buffer which is managed by performing | ||
22 | dynamic memory allocations as needed. The | ||
23 | contents may be inspected and modified, and | ||
24 | the implementation maintains a useful | ||
25 | invariant: changes to the request always leave | ||
26 | it in a valid state. | ||
27 | |||
28 | @par Example | ||
29 | @code | ||
30 | request req(method::get, "/"); | ||
31 | |||
32 | req.set(field::host, "example.com"); | ||
33 | req.set(field::accept_encoding, "gzip, deflate, br"); | ||
34 | req.set(field::cache_control, "no-cache"); | ||
35 | |||
36 | assert(req.buffer() == | ||
37 | "GET / HTTP/1.1\r\n" | ||
38 | "Host: example.com\r\n" | ||
39 | "Accept-Encoding: gzip, deflate, br\r\n" | ||
40 | "Cache-Control: no-cache\r\n" | ||
41 | "\r\n"); | ||
42 | @endcode | ||
43 | |||
44 | @see | ||
45 | @ref static_request, | ||
46 | @ref request_base. | ||
47 | */ | ||
48 | class request | ||
49 | : public request_base | ||
50 | { | ||
51 | public: | ||
52 | //-------------------------------------------- | ||
53 | // | ||
54 | // Special Members | ||
55 | // | ||
56 | //-------------------------------------------- | ||
57 | |||
58 | /** Constructor. | ||
59 | |||
60 | A default-constructed request contains | ||
61 | a valid HTTP `GET` request with no headers. | ||
62 | |||
63 | @par Example | ||
64 | @code | ||
65 | request req; | ||
66 | @endcode | ||
67 | |||
68 | @par Postconditions | ||
69 | @code | ||
70 | this->buffer() == "GET / HTTP/1.1\r\n\r\n" | ||
71 | @endcode | ||
72 | |||
73 | @par Complexity | ||
74 | Constant. | ||
75 | */ | ||
76 | 64 | request() noexcept = default; | |
77 | |||
78 | /** Constructor. | ||
79 | |||
80 | Constructs a request from the string `s`, | ||
81 | which must contain valid HTTP request | ||
82 | or else an exception is thrown. | ||
83 | The new request retains ownership by | ||
84 | making a copy of the passed string. | ||
85 | |||
86 | @par Example | ||
87 | @code | ||
88 | request req( | ||
89 | "GET / HTTP/1.1\r\n" | ||
90 | "Accept-Encoding: gzip, deflate, br\r\n" | ||
91 | "Cache-Control: no-cache\r\n" | ||
92 | "\r\n"); | ||
93 | @endcode | ||
94 | |||
95 | @par Postconditions | ||
96 | @code | ||
97 | this->buffer.data() != s.data() | ||
98 | @endcode | ||
99 | |||
100 | @par Complexity | ||
101 | Linear in `s.size()`. | ||
102 | |||
103 | @par Exception Safety | ||
104 | Calls to allocate may throw. | ||
105 | Exception thrown on invalid input. | ||
106 | |||
107 | @throw system_error | ||
108 | The input does not contain a valid request. | ||
109 | |||
110 | @param s The string to parse. | ||
111 | */ | ||
112 | explicit | ||
113 | 211 | request( | |
114 | core::string_view s) | ||
115 | 211 | : request_base(s) | |
116 | { | ||
117 | 210 | } | |
118 | |||
119 | /** Constructor. | ||
120 | |||
121 | The start-line of the request will | ||
122 | contain the standard text for the | ||
123 | supplied method, target and HTTP version. | ||
124 | |||
125 | @par Example | ||
126 | @code | ||
127 | request req(method::get, "/index.html", version::http_1_0); | ||
128 | @endcode | ||
129 | |||
130 | @par Complexity | ||
131 | Linear in `to_string(m).size() + t.size()`. | ||
132 | |||
133 | @par Exception Safety | ||
134 | Calls to allocate may throw. | ||
135 | |||
136 | @param m The method to set. | ||
137 | |||
138 | @param t The string representing a target. | ||
139 | |||
140 | @param v The version to set. | ||
141 | */ | ||
142 | request( | ||
143 | http_proto::method m, | ||
144 | core::string_view t, | ||
145 | http_proto::version v) noexcept | ||
146 | : request() | ||
147 | { | ||
148 | set_start_line(m, t, v); | ||
149 | } | ||
150 | |||
151 | /** Constructor. | ||
152 | |||
153 | The start-line of the request will | ||
154 | contain the standard text for the | ||
155 | supplied method and target with the HTTP | ||
156 | version defaulted to `HTTP/1.1`. | ||
157 | |||
158 | @par Example | ||
159 | @code | ||
160 | request req(method::get, "/index.html"); | ||
161 | @endcode | ||
162 | |||
163 | @par Complexity | ||
164 | Linear in `obsolete_reason(s).size()`. | ||
165 | |||
166 | @par Exception Safety | ||
167 | Calls to allocate may throw. | ||
168 | |||
169 | @param m The method to set. | ||
170 | |||
171 | @param t The string representing a target. | ||
172 | */ | ||
173 | request( | ||
174 | http_proto::method m, | ||
175 | core::string_view t) | ||
176 | : request( | ||
177 | m, t, http_proto::version::http_1_1) | ||
178 | { | ||
179 | } | ||
180 | |||
181 | /** Constructor. | ||
182 | |||
183 | Allocates `cap` bytes initially, with an | ||
184 | upper limit of `max_cap`. Growing beyond | ||
185 | `max_cap` will throw an exception. | ||
186 | |||
187 | Useful when an estimated initial size is | ||
188 | known, but further growth up to a maximum | ||
189 | is allowed. | ||
190 | |||
191 | When `cap == max_cap`, the container | ||
192 | guarantees to never allocate. | ||
193 | |||
194 | @par Preconditions | ||
195 | @code | ||
196 | max_cap >= cap | ||
197 | @endcode | ||
198 | |||
199 | @par Exception Safety | ||
200 | Calls to allocate may throw. | ||
201 | |||
202 | @param cap Initial capacity in bytes (may be `0`). | ||
203 | |||
204 | @param max_cap Maximum allowed capacity in bytes. | ||
205 | */ | ||
206 | 10 | request( | |
207 | std::size_t cap, | ||
208 | std::size_t max_cap = std::size_t(-1)) | ||
209 | 10 | : request() | |
210 | { | ||
211 |
1/2✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
|
10 | reserve_bytes(cap); |
212 |
2/2✓ Branch 1 taken 8 times.
✓ Branch 2 taken 2 times.
|
10 | set_max_capacity_in_bytes(max_cap); |
213 | 10 | } | |
214 | |||
215 | /** Constructor. | ||
216 | |||
217 | The contents of `r` are transferred | ||
218 | to the newly constructed object, | ||
219 | which includes the underlying | ||
220 | character buffer. | ||
221 | After construction, the moved-from | ||
222 | object is as if default-constructed. | ||
223 | |||
224 | @par Postconditions | ||
225 | @code | ||
226 | r.buffer() == "GET / HTTP/1.1\r\n\r\n" | ||
227 | @endcode | ||
228 | |||
229 | @par Complexity | ||
230 | Constant. | ||
231 | |||
232 | @param r The request to move from. | ||
233 | */ | ||
234 | 26 | request( | |
235 | request&& other) noexcept | ||
236 | 26 | : request() | |
237 | { | ||
238 | 26 | swap(other); | |
239 | 26 | } | |
240 | |||
241 | /** Constructor. | ||
242 | |||
243 | The newly constructed object contains | ||
244 | a copy of `r`. | ||
245 | |||
246 | @par Postconditions | ||
247 | @code | ||
248 | this->buffer() == r.buffer() && this->buffer.data() != r.buffer().data() | ||
249 | @endcode | ||
250 | |||
251 | @par Complexity | ||
252 | Linear in `r.size()`. | ||
253 | |||
254 | @par Exception Safety | ||
255 | Calls to allocate may throw. | ||
256 | |||
257 | @param r The request to copy. | ||
258 | */ | ||
259 | 2 | request( | |
260 | request const& r) = default; | ||
261 | |||
262 | /** Constructor. | ||
263 | |||
264 | The newly constructed object contains | ||
265 | a copy of `r`. | ||
266 | |||
267 | @par Postconditions | ||
268 | @code | ||
269 | this->buffer() == r.buffer() && this->buffer.data() != r.buffer().data() | ||
270 | @endcode | ||
271 | |||
272 | @par Complexity | ||
273 | Linear in `r.size()`. | ||
274 | |||
275 | @par Exception Safety | ||
276 | Calls to allocate may throw. | ||
277 | |||
278 | @param r The request to copy. | ||
279 | */ | ||
280 | 2 | request( | |
281 | request_base const& r) | ||
282 | 2 | : request_base(r) | |
283 | { | ||
284 | 2 | } | |
285 | |||
286 | /** Assignment | ||
287 | |||
288 | The contents of `r` are transferred to | ||
289 | `this`, including the underlying | ||
290 | character buffer. The previous contents | ||
291 | of `this` are destroyed. | ||
292 | After assignment, the moved-from | ||
293 | object is as if default-constructed. | ||
294 | |||
295 | @par Postconditions | ||
296 | @code | ||
297 | r.buffer() == "GET / HTTP/1.1\r\n\r\n" | ||
298 | @endcode | ||
299 | |||
300 | @par Complexity | ||
301 | Constant. | ||
302 | |||
303 | @param r The request to assign from. | ||
304 | |||
305 | @return A reference to this object. | ||
306 | */ | ||
307 | request& | ||
308 | 24 | operator=(request&& r) noexcept | |
309 | { | ||
310 | 24 | request temp(std::move(r)); | |
311 | 24 | temp.swap(*this); | |
312 | 48 | return *this; | |
313 | 24 | } | |
314 | |||
315 | /** Assignment. | ||
316 | |||
317 | The contents of `r` are copied and | ||
318 | the previous contents of `this` are | ||
319 | discarded. | ||
320 | |||
321 | @par Postconditions | ||
322 | @code | ||
323 | this->buffer() == r.buffer() && this->buffer().data() != r.buffer().data() | ||
324 | @endcode | ||
325 | |||
326 | @par Complexity | ||
327 | Linear in `r.size()`. | ||
328 | |||
329 | @par Exception Safety | ||
330 | Strong guarantee. | ||
331 | Calls to allocate may throw. | ||
332 | Exception thrown if max capacity exceeded. | ||
333 | |||
334 | @throw std::length_error | ||
335 | Max capacity would be exceeded. | ||
336 | |||
337 | @param r The request to copy. | ||
338 | |||
339 | @return A reference to this object. | ||
340 | */ | ||
341 | request& | ||
342 | 3 | operator=( | |
343 | request const& r) | ||
344 | { | ||
345 | 3 | copy_impl(r.h_); | |
346 | 3 | return *this; | |
347 | } | ||
348 | |||
349 | /** Assignment. | ||
350 | |||
351 | The contents of `r` are copied and | ||
352 | the previous contents of `this` are | ||
353 | discarded. | ||
354 | |||
355 | @par Postconditions | ||
356 | @code | ||
357 | this->buffer() == r.buffer() && this->buffer().data() != r.buffer().data() | ||
358 | @endcode | ||
359 | |||
360 | @par Complexity | ||
361 | Linear in `r.size()`. | ||
362 | |||
363 | @par Exception Safety | ||
364 | Strong guarantee. | ||
365 | Calls to allocate may throw. | ||
366 | Exception thrown if max capacity exceeded. | ||
367 | |||
368 | @throw std::length_error | ||
369 | Max capacity would be exceeded. | ||
370 | |||
371 | @param r The request to copy. | ||
372 | |||
373 | @return A reference to this object. | ||
374 | */ | ||
375 | request& | ||
376 | 1 | operator=( | |
377 | request_base const& r) | ||
378 | { | ||
379 | 1 | copy_impl(r.h_); | |
380 | 1 | return *this; | |
381 | } | ||
382 | |||
383 | //-------------------------------------------- | ||
384 | |||
385 | /** Swap. | ||
386 | |||
387 | Exchanges the contents of this request | ||
388 | with another request. All views, | ||
389 | iterators and references remain valid. | ||
390 | |||
391 | If `this == &other`, this function call has no effect. | ||
392 | |||
393 | @par Example | ||
394 | @code | ||
395 | request r1(method::get, "/"); | ||
396 | request r2(method::delete_, "/item/42"); | ||
397 | r1.swap(r2); | ||
398 | assert(r1.buffer() == "DELETE /item/42 HTTP/1.1\r\n\r\n" ); | ||
399 | assert(r2.buffer() == "GET / HTTP/1.1\r\n\r\n" ); | ||
400 | @endcode | ||
401 | |||
402 | @par Complexity | ||
403 | Constant | ||
404 | |||
405 | @param other The object to swap with | ||
406 | */ | ||
407 | void | ||
408 | 50 | swap(request& other) noexcept | |
409 | { | ||
410 | 50 | h_.swap(other.h_); | |
411 | 50 | std::swap(max_cap_, other.max_cap_); | |
412 | 50 | } | |
413 | |||
414 | /** Swap. | ||
415 | |||
416 | Exchanges the contents of `v0` with | ||
417 | another `v1`. All views, iterators and | ||
418 | references remain valid. | ||
419 | |||
420 | If `&v0 == &v1`, this function call has no effect. | ||
421 | |||
422 | @par Example | ||
423 | @code | ||
424 | request r1(method::get, "/"); | ||
425 | request r2(method::delete_, "/item/42"); | ||
426 | std::swap(r1, r2); | ||
427 | assert(r1.buffer() == "DELETE /item/42 HTTP/1.1\r\n\r\n" ); | ||
428 | assert(r2.buffer() == "GET / HTTP/1.1\r\n\r\n" ); | ||
429 | @endcode | ||
430 | |||
431 | @par Effects | ||
432 | @code | ||
433 | v0.swap(v1); | ||
434 | @endcode | ||
435 | |||
436 | @par Complexity | ||
437 | Constant. | ||
438 | |||
439 | @param v0 The first object to swap. | ||
440 | @param v1 The second object to swap. | ||
441 | |||
442 | @see | ||
443 | @ref request::swap | ||
444 | */ | ||
445 | friend | ||
446 | void | ||
447 | swap( | ||
448 | request& v0, | ||
449 | request& v1) noexcept | ||
450 | { | ||
451 | v0.swap(v1); | ||
452 | } | ||
453 | }; | ||
454 | |||
455 | } // http_proto | ||
456 | } // boost | ||
457 | |||
458 | #endif | ||
459 |