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