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