Line data Source code
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 10 : reserve_bytes(cap);
212 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
|