Line data Source code
1 : //
2 : // Copyright (c) 2021 Vinnie Falco (vinnie.falco@gmail.com)
3 : // Copyright (c) 2025 Mohammad Nejati
4 : //
5 : // Distributed under the Boost Software License, Version 1.0. (See accompanying
6 : // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
7 : //
8 : // Official repository: https://github.com/cppalliance/http_proto
9 : //
10 :
11 : #ifndef BOOST_HTTP_PROTO_REQUEST_HPP
12 : #define BOOST_HTTP_PROTO_REQUEST_HPP
13 :
14 : #include <boost/http_proto/detail/config.hpp>
15 : #include <boost/http_proto/message_base.hpp>
16 :
17 : namespace boost {
18 : namespace http_proto {
19 :
20 : /** A container for HTTP requests.
21 :
22 : This container owns a request, represented by
23 : a buffer which is managed by performing
24 : dynamic memory allocations as needed. The
25 : contents may be inspected and modified, and
26 : the implementation maintains a useful
27 : invariant: changes to the request always leave
28 : it in a valid state.
29 :
30 : @par Example
31 : @code
32 : request req(method::get, "/");
33 :
34 : req.set(field::host, "example.com");
35 : req.set(field::accept_encoding, "gzip, deflate, br");
36 : req.set(field::cache_control, "no-cache");
37 :
38 : assert(req.buffer() ==
39 : "GET / HTTP/1.1\r\n"
40 : "Host: example.com\r\n"
41 : "Accept-Encoding: gzip, deflate, br\r\n"
42 : "Cache-Control: no-cache\r\n"
43 : "\r\n");
44 : @endcode
45 :
46 : @see
47 : @ref request_parser,
48 : @ref response.
49 : */
50 : class request
51 : : public message_base
52 : {
53 : friend class request_parser;
54 :
55 315 : request(
56 : view_tag_t,
57 : detail::header const& h) noexcept
58 315 : : message_base(view_tag, h)
59 : {
60 315 : }
61 :
62 : public:
63 : //--------------------------------------------
64 : //
65 : // Special Members
66 : //
67 : //--------------------------------------------
68 :
69 : /** Constructor.
70 :
71 : A default-constructed request contains
72 : a valid HTTP `GET` request with no headers.
73 :
74 : @par Example
75 : @code
76 : request req;
77 : @endcode
78 :
79 : @par Postconditions
80 : @code
81 : this->buffer() == "GET / HTTP/1.1\r\n\r\n"
82 : @endcode
83 :
84 : @par Complexity
85 : Constant.
86 : */
87 28 : request() noexcept
88 28 : : message_base(detail::kind::request)
89 : {
90 28 : }
91 :
92 : /** Constructor.
93 :
94 : Constructs a request from the string `s`,
95 : which must contain valid HTTP request
96 : or else an exception is thrown.
97 : The new request retains ownership by
98 : making a copy of the passed string.
99 :
100 : @par Example
101 : @code
102 : request req(
103 : "GET / HTTP/1.1\r\n"
104 : "Accept-Encoding: gzip, deflate, br\r\n"
105 : "Cache-Control: no-cache\r\n"
106 : "\r\n");
107 : @endcode
108 :
109 : @par Postconditions
110 : @code
111 : this->buffer.data() != s.data()
112 : @endcode
113 :
114 : @par Complexity
115 : Linear in `s.size()`.
116 :
117 : @par Exception Safety
118 : Calls to allocate may throw.
119 : Exception thrown on invalid input.
120 :
121 : @throw system_error
122 : The input does not contain a valid request.
123 :
124 : @param s The string to parse.
125 : */
126 : explicit
127 208 : request(
128 : core::string_view s)
129 208 : : message_base(detail::kind::request, s)
130 : {
131 207 : }
132 :
133 : /** Constructor.
134 :
135 : The start-line of the request will
136 : contain the standard text for the
137 : supplied method, target and HTTP version.
138 :
139 : @par Example
140 : @code
141 : request req(method::get, "/index.html", version::http_1_0);
142 : @endcode
143 :
144 : @par Complexity
145 : Linear in `to_string(m).size() + t.size()`.
146 :
147 : @par Exception Safety
148 : Calls to allocate may throw.
149 :
150 : @param m The method to set.
151 :
152 : @param t The string representing a target.
153 :
154 : @param v The version to set.
155 : */
156 : request(
157 : http_proto::method m,
158 : core::string_view t,
159 : http_proto::version v) noexcept
160 : : message_base(detail::kind::request)
161 : {
162 : set_start_line(m, t, v);
163 : }
164 :
165 : /** Constructor.
166 :
167 : The start-line of the request will
168 : contain the standard text for the
169 : supplied method and target with the HTTP
170 : version defaulted to `HTTP/1.1`.
171 :
172 : @par Example
173 : @code
174 : request req(method::get, "/index.html");
175 : @endcode
176 :
177 : @par Complexity
178 : Linear in `to_string(s).size()`.
179 :
180 : @par Exception Safety
181 : Calls to allocate may throw.
182 :
183 : @param m The method to set.
184 :
185 : @param t The string representing a target.
186 : */
187 : request(
188 : http_proto::method m,
189 : core::string_view t)
190 : : request(
191 : m, t, http_proto::version::http_1_1)
192 : {
193 : }
194 :
195 : /** Constructor.
196 :
197 : Allocates `cap` bytes initially, with an
198 : upper limit of `max_cap`. Growing beyond
199 : `max_cap` will throw an exception.
200 :
201 : Useful when an estimated initial size is
202 : known, but further growth up to a maximum
203 : is allowed.
204 :
205 : When `cap == max_cap`, the container
206 : guarantees to never allocate.
207 :
208 : @par Preconditions
209 : @code
210 : max_cap >= cap
211 : @endcode
212 :
213 : @par Exception Safety
214 : Calls to allocate may throw.
215 :
216 : @param cap Initial capacity in bytes (may be `0`).
217 :
218 : @param max_cap Maximum allowed capacity in bytes.
219 : */
220 10 : request(
221 : std::size_t cap,
222 : std::size_t max_cap = std::size_t(-1))
223 10 : : message_base(detail::kind::request)
224 : {
225 10 : reserve_bytes(cap);
226 10 : set_max_capacity_in_bytes(max_cap);
227 10 : }
228 :
229 : /** Constructor.
230 :
231 : The contents of `r` are transferred
232 : to the newly constructed object,
233 : which includes the underlying
234 : character buffer.
235 : After construction, the moved-from
236 : object is as if default-constructed.
237 :
238 : @par Postconditions
239 : @code
240 : r.buffer() == "GET / HTTP/1.1\r\n\r\n"
241 : @endcode
242 :
243 : @par Complexity
244 : Constant.
245 :
246 : @param r The request to move from.
247 : */
248 25 : request(
249 : request&& other) noexcept
250 25 : : message_base(detail::kind::request)
251 : {
252 25 : swap(other);
253 25 : }
254 :
255 : /** Constructor.
256 :
257 : The newly constructed object contains
258 : a copy of `r`.
259 :
260 : @par Postconditions
261 : @code
262 : this->buffer() == r.buffer() && this->buffer.data() != r.buffer().data()
263 : @endcode
264 :
265 : @par Complexity
266 : Linear in `r.size()`.
267 :
268 : @par Exception Safety
269 : Calls to allocate may throw.
270 :
271 : @param r The request to copy.
272 : */
273 4 : request(
274 : request const& r) = default;
275 :
276 : /** Assignment
277 :
278 : The contents of `r` are transferred to
279 : `this`, including the underlying
280 : character buffer. The previous contents
281 : of `this` are destroyed.
282 : After assignment, the moved-from
283 : object is as if default-constructed.
284 :
285 : @par Postconditions
286 : @code
287 : r.buffer() == "GET / HTTP/1.1\r\n\r\n"
288 : @endcode
289 :
290 : @par Complexity
291 : Constant.
292 :
293 : @param r The request to assign from.
294 :
295 : @return A reference to this object.
296 : */
297 : request&
298 23 : operator=(request&& r) noexcept
299 : {
300 23 : request temp(std::move(r));
301 23 : temp.swap(*this);
302 46 : return *this;
303 23 : }
304 :
305 : /** Assignment.
306 :
307 : The contents of `r` are copied and
308 : the previous contents of `this` are
309 : discarded.
310 :
311 : @par Postconditions
312 : @code
313 : this->buffer() == r.buffer() && this->buffer().data() != r.buffer().data()
314 : @endcode
315 :
316 : @par Complexity
317 : Linear in `r.size()`.
318 :
319 : @par Exception Safety
320 : Strong guarantee.
321 : Calls to allocate may throw.
322 : Exception thrown if max capacity exceeded.
323 :
324 : @throw std::length_error
325 : Max capacity would be exceeded.
326 :
327 : @param r The request to copy.
328 :
329 : @return A reference to this object.
330 : */
331 : request&
332 3 : operator=(
333 : request const& r)
334 : {
335 3 : copy_impl(r.h_);
336 3 : return *this;
337 : }
338 :
339 : //--------------------------------------------
340 :
341 : /** Swap.
342 :
343 : Exchanges the contents of this request
344 : with another request. All views,
345 : iterators and references remain valid.
346 :
347 : If `this == &other`, this function call has no effect.
348 :
349 : @par Example
350 : @code
351 : request r1(method::get, "/");
352 : request r2(method::delete_, "/item/42");
353 : r1.swap(r2);
354 : assert(r1.buffer() == "DELETE /item/42 HTTP/1.1\r\n\r\n" );
355 : assert(r2.buffer() == "GET / HTTP/1.1\r\n\r\n" );
356 : @endcode
357 :
358 : @par Complexity
359 : Constant
360 :
361 : @param other The object to swap with
362 : */
363 : void
364 48 : swap(request& other) noexcept
365 : {
366 48 : h_.swap(other.h_);
367 48 : std::swap(max_cap_, other.max_cap_);
368 48 : std::swap(view_, other.view_);
369 48 : }
370 :
371 : /** Swap.
372 :
373 : Exchanges the contents of `v0` with
374 : another `v1`. All views, iterators and
375 : references remain valid.
376 :
377 : If `&v0 == &v1`, this function call has no effect.
378 :
379 : @par Example
380 : @code
381 : request r1(method::get, "/");
382 : request r2(method::delete_, "/item/42");
383 : std::swap(r1, r2);
384 : assert(r1.buffer() == "DELETE /item/42 HTTP/1.1\r\n\r\n" );
385 : assert(r2.buffer() == "GET / HTTP/1.1\r\n\r\n" );
386 : @endcode
387 :
388 : @par Effects
389 : @code
390 : v0.swap(v1);
391 : @endcode
392 :
393 : @par Complexity
394 : Constant.
395 :
396 : @param v0 The first object to swap.
397 : @param v1 The second object to swap.
398 :
399 : @see
400 : @ref request::swap
401 : */
402 : friend
403 : void
404 : swap(
405 : request& v0,
406 : request& v1) noexcept
407 : {
408 : v0.swap(v1);
409 : }
410 :
411 : //--------------------------------------------
412 : //
413 : // Observers
414 : //
415 : //--------------------------------------------
416 :
417 : /** Return the method as a name constant.
418 :
419 : If the method returned is equal to
420 : @ref method::unknown, the method may
421 : be obtained as a string instead, by
422 : calling @ref method_text.
423 : */
424 : http_proto::method
425 76 : method() const noexcept
426 : {
427 76 : return h_.req.method;
428 : }
429 :
430 : /** Return the method as a string.
431 : */
432 : core::string_view
433 83 : method_text() const noexcept
434 : {
435 166 : return core::string_view(
436 83 : h_.cbuf,
437 83 : h_.req.method_len);
438 : }
439 :
440 : /** Return the request-target string.
441 : */
442 : core::string_view
443 73 : target() const noexcept
444 : {
445 146 : return core::string_view(
446 73 : h_.cbuf +
447 73 : h_.req.method_len + 1,
448 73 : h_.req.target_len);
449 : }
450 :
451 : //--------------------------------------------
452 : //
453 : // Modifiers
454 : //
455 : //--------------------------------------------
456 :
457 : /** Set the method of the request to the enum.
458 :
459 : @par Exception Safety
460 : Strong guarantee.
461 : Calls to allocate may throw.
462 : Exception thrown if max capacity exceeded.
463 :
464 : @throw std::length_error
465 : Max capacity would be exceeded.
466 :
467 : @param m The method to set.
468 : */
469 : void
470 2 : set_method(
471 : http_proto::method m)
472 : {
473 2 : set_start_line_impl(
474 : m,
475 : to_string(m),
476 : target(),
477 : version());
478 2 : }
479 :
480 : /** Set the method of the request to the string.
481 :
482 : @par Exception Safety
483 : Strong guarantee.
484 : Calls to allocate may throw.
485 : Exception thrown on invalid input.
486 : Exception thrown if max capacity exceeded.
487 :
488 : @throw system_error
489 : Input is invalid.
490 :
491 : @throw std::length_error
492 : Max capacity would be exceeded.
493 :
494 : @param s A string view representing the
495 : method to set.
496 : */
497 : void
498 5 : set_method(
499 : core::string_view s)
500 : {
501 5 : set_start_line_impl(
502 : string_to_method(s),
503 : s,
504 : target(),
505 : version());
506 5 : }
507 :
508 : /** Set the target string of the request.
509 :
510 : This function sets the request-target.
511 : The caller is responsible for ensuring
512 : that the string passed is syntactically
513 : valid.
514 :
515 : @par Exception Safety
516 : Strong guarantee.
517 : Calls to allocate may throw.
518 : Exception thrown on invalid input.
519 : Exception thrown if max capacity exceeded.
520 :
521 : @throw system_error
522 : Input is invalid.
523 :
524 : @throw std::length_error
525 : Max capacity would be exceeded.
526 :
527 : @param s A string view representing the
528 : target to set.
529 : */
530 : void
531 5 : set_target(
532 : core::string_view s)
533 : {
534 5 : set_start_line_impl(
535 : h_.req.method,
536 : method_text(),
537 : s,
538 : version());
539 5 : }
540 :
541 : /** Set the HTTP version of the request.
542 :
543 : @par Exception Safety
544 : Strong guarantee.
545 : Calls to allocate may throw.
546 : Exception thrown if max capacity exceeded.
547 :
548 : @throw std::length_error
549 : Max capacity would be exceeded.
550 :
551 : @param v The version to set.
552 : */
553 : void
554 2 : set_version(
555 : http_proto::version v)
556 : {
557 2 : set_start_line_impl(
558 : h_.req.method,
559 : method_text(),
560 : target(),
561 : v);
562 2 : }
563 :
564 : /** Set the method, target, and version of the request.
565 :
566 : This is more efficient than setting the
567 : properties individually.
568 :
569 : @par Exception Safety
570 : Strong guarantee.
571 : Calls to allocate may throw.
572 : Exception thrown on invalid input.
573 : Exception thrown if max capacity exceeded.
574 :
575 : @throw system_error
576 : Input is invalid.
577 :
578 : @throw std::length_error
579 : Max capacity would be exceeded.
580 :
581 : @param m The method to set.
582 :
583 : @param t A string view representing the
584 : target to set.
585 :
586 : @param v The version to set.
587 : */
588 : void
589 2 : set_start_line(
590 : http_proto::method m,
591 : core::string_view t,
592 : http_proto::version v =
593 : http_proto::version::http_1_1)
594 : {
595 2 : set_start_line_impl(m, to_string(m), t, v);
596 1 : }
597 :
598 : /** Set the method, target, and version of the request.
599 :
600 : This is more efficient than setting the
601 : properties individually.
602 :
603 : @par Exception Safety
604 : Strong guarantee.
605 : Calls to allocate may throw.
606 : Exception thrown on invalid input.
607 : Exception thrown if max capacity exceeded.
608 :
609 : @throw system_error
610 : Input is invalid.
611 :
612 : @throw std::length_error
613 : Max capacity would be exceeded.
614 :
615 : @param m A string view representing the
616 : method to set.
617 :
618 : @param t A string view representing the
619 : target to set.
620 :
621 : @param v The version to set.
622 : */
623 : void
624 : set_start_line(
625 : core::string_view m,
626 : core::string_view t,
627 : http_proto::version v =
628 : http_proto::version::http_1_1)
629 : {
630 : set_start_line_impl(string_to_method(m), m, t, v);
631 : }
632 :
633 : /** Set the `Expect: 100-continue` header.
634 :
635 : @par Exception Safety
636 : Strong guarantee.
637 : Calls to allocate may throw.
638 : Exception thrown if max capacity exceeded.
639 :
640 : @throw std::length_error
641 : Max capacity would be exceeded.
642 :
643 : @param b If `true` sets `Expect: 100-continue`
644 : header otherwise erase it.
645 : */
646 : BOOST_HTTP_PROTO_DECL
647 : void
648 : set_expect_100_continue(bool b);
649 :
650 : private:
651 : BOOST_HTTP_PROTO_DECL
652 : void
653 : set_start_line_impl(
654 : http_proto::method m,
655 : core::string_view ms,
656 : core::string_view t,
657 : http_proto::version v);
658 : };
659 :
660 : } // http_proto
661 : } // boost
662 :
663 : #endif
|