/src/pistache/include/pistache/http.h
Line | Count | Source |
1 | | /* |
2 | | * SPDX-FileCopyrightText: 2015 Mathieu Stefani |
3 | | * |
4 | | * SPDX-License-Identifier: Apache-2.0 |
5 | | */ |
6 | | |
7 | | /* http.h |
8 | | Mathieu Stefani, 13 August 2015 |
9 | | |
10 | | Http Layer |
11 | | */ |
12 | | |
13 | | #pragma once |
14 | | |
15 | | #include <algorithm> |
16 | | #include <array> |
17 | | #include <chrono> |
18 | | #include <memory> |
19 | | #include <sstream> |
20 | | #include <stdexcept> |
21 | | #include <string> |
22 | | #include <type_traits> |
23 | | #include <vector> |
24 | | |
25 | | #include <pistache/eventmeth.h> |
26 | | |
27 | | #ifndef _USE_LIBEVENT_LIKE_APPLE |
28 | | // Note: sys/timerfd.h is linux-only (and certainly POSIX only) |
29 | | #include <sys/timerfd.h> |
30 | | #endif |
31 | | |
32 | | #include <pistache/async.h> |
33 | | #include <pistache/cookie.h> |
34 | | #include <pistache/http_defs.h> |
35 | | #include <pistache/http_headers.h> |
36 | | #include <pistache/meta.h> |
37 | | #include <pistache/mime.h> |
38 | | #include <pistache/net.h> |
39 | | #include <pistache/stream.h> |
40 | | #include <pistache/tcp.h> |
41 | | #include <pistache/transport.h> |
42 | | |
43 | | #ifdef PISTACHE_USE_CONTENT_ENCODING_BROTLI |
44 | | #include <brotli/encode.h> |
45 | | #endif |
46 | | |
47 | | #ifdef PISTACHE_USE_CONTENT_ENCODING_DEFLATE |
48 | | #include <zlib.h> |
49 | | #endif |
50 | | |
51 | | #ifdef PISTACHE_USE_CONTENT_ENCODING_ZSTD |
52 | | #include <zstd.h> |
53 | | #endif |
54 | | |
55 | | namespace Pistache |
56 | | { |
57 | | namespace Tcp |
58 | | { |
59 | | class Peer; |
60 | | } |
61 | | namespace Http |
62 | | { |
63 | | |
64 | | namespace details |
65 | | { |
66 | | struct prototype_tag |
67 | | { }; |
68 | | |
69 | | template <typename P> |
70 | | struct IsHttpPrototype |
71 | | { |
72 | | template <typename U> |
73 | | static auto test(U*) -> decltype(typename U::tag()); |
74 | | template <typename U> |
75 | | static auto test(...) -> std::false_type; |
76 | | |
77 | | static constexpr bool value = std::is_same<decltype(test<P>(nullptr)), prototype_tag>::value; |
78 | | }; |
79 | | } // namespace details |
80 | | |
81 | | #define HTTP_PROTOTYPE(Class) \ |
82 | | PROTOTYPE_OF(Pistache::Tcp::Handler, Class) \ |
83 | | typedef Pistache::Http::details::prototype_tag tag; |
84 | | |
85 | | namespace Private |
86 | | { |
87 | | class RequestLineStep; |
88 | | class ResponseLineStep; |
89 | | class HeadersStep; |
90 | | class BodyStep; |
91 | | } // namespace Private |
92 | | |
93 | | template <class CharT, class Traits> |
94 | | std::basic_ostream<CharT, Traits>& crlf(std::basic_ostream<CharT, Traits>& os) |
95 | 0 | { |
96 | 0 | static constexpr char CRLF[] = { 0xD, 0xA }; |
97 | 0 | os.write(CRLF, 2); |
98 | |
|
99 | 0 | return os; |
100 | 0 | } |
101 | | |
102 | | // 4. HTTP Message |
103 | | class Message |
104 | | { |
105 | | public: |
106 | | friend class Private::HeadersStep; |
107 | | friend class Private::BodyStep; |
108 | | friend class ResponseWriter; |
109 | | |
110 | 2.68k | Message() = default; |
111 | | explicit Message(Version version); |
112 | | |
113 | 0 | Message(const Message& other) = default; |
114 | | Message& operator=(const Message& other) = default; |
115 | | |
116 | 0 | Message(Message&& other) = default; |
117 | 0 | Message& operator=(Message&& other) = default; |
118 | | |
119 | | Version version() const; |
120 | | Code code() const; |
121 | | |
122 | | const std::string& body() const; |
123 | | std::string body(); |
124 | | |
125 | | const CookieJar& cookies() const; |
126 | | CookieJar& cookies(); |
127 | | const Header::Collection& headers() const; |
128 | | Header::Collection& headers(); |
129 | | |
130 | | protected: |
131 | | Version version_ = Version::Http11; |
132 | | Code code_; |
133 | | |
134 | | std::string body_; |
135 | | |
136 | | CookieJar cookies_; |
137 | | Header::Collection headers_; |
138 | | }; |
139 | | |
140 | | namespace Uri |
141 | | { |
142 | | |
143 | | class Query |
144 | | { |
145 | | public: |
146 | | Query(); |
147 | | explicit Query( |
148 | | std::initializer_list<std::pair<const std::string, std::string>> params); |
149 | | |
150 | | void add(std::string name, std::string value); |
151 | | std::optional<std::string> get(const std::string& name) const; |
152 | | bool has(const std::string& name) const; |
153 | | // Return empty string or "?key1=value1&key2=value2" if query exist |
154 | | std::string as_str() const; |
155 | | |
156 | 0 | void clear() { params.clear(); } |
157 | | |
158 | | // \brief Return iterator to the beginning of the parameters map |
159 | | std::unordered_map<std::string, std::string>::const_iterator |
160 | | parameters_begin() const |
161 | 0 | { |
162 | 0 | return params.begin(); |
163 | 0 | } |
164 | | |
165 | | // \brief Return iterator to the end of the parameters map |
166 | | std::unordered_map<std::string, std::string>::const_iterator |
167 | | parameters_end() const |
168 | 0 | { |
169 | 0 | return params.end(); |
170 | 0 | } |
171 | | |
172 | | // \brief returns all parameters given in the query |
173 | | std::vector<std::string> parameters() const |
174 | 0 | { |
175 | 0 | std::vector<std::string> keys; |
176 | 0 | std::transform( |
177 | 0 | params.begin(), params.end(), std::back_inserter(keys), |
178 | 0 | [](const std::unordered_map<std::string, std::string>::value_type& pair) { return pair.first; }); |
179 | 0 | return keys; |
180 | 0 | } |
181 | | |
182 | | private: |
183 | | // first is key second is value |
184 | | std::unordered_map<std::string, std::string> params; |
185 | | }; |
186 | | } // namespace Uri |
187 | | |
188 | | // Remove when RequestBuilder will be out of namespace Experimental |
189 | | namespace Experimental |
190 | | { |
191 | | class RequestBuilder; |
192 | | } |
193 | | |
194 | | // 5. Request |
195 | | class Request : public Message |
196 | | { |
197 | | public: |
198 | | friend class Private::RequestLineStep; |
199 | | |
200 | | friend class Experimental::RequestBuilder; |
201 | | |
202 | 2.40k | Request() = default; |
203 | | |
204 | 0 | Request(const Request& other) = default; |
205 | | Request& operator=(const Request& other) = default; |
206 | | |
207 | 0 | Request(Request&& other) = default; |
208 | 0 | Request& operator=(Request&& other) = default; |
209 | | |
210 | | Method method() const; |
211 | | const std::string& resource() const; |
212 | | |
213 | | const Uri::Query& query() const; |
214 | | |
215 | | /* @Investigate: this is disabled because of a lock in the shared_ptr / |
216 | | weak_ptr implementation of libstdc++. Under contention, we experience a |
217 | | performance drop of 5x with that lock |
218 | | |
219 | | If this turns out to be a problem, we might be able to replace the |
220 | | weak_ptr trick to detect peer disconnection by a plain old "observer" |
221 | | pointer to a tcp connection with a "stale" state |
222 | | */ |
223 | | #ifdef LIBSTDCPP_SMARTPTR_LOCK_FIXME |
224 | | std::shared_ptr<Tcp::Peer> peer() const; |
225 | | #endif |
226 | | |
227 | | const Address& address() const; |
228 | | |
229 | 0 | void copyAddress(const Address& address) { address_ = address; } |
230 | | |
231 | | std::chrono::milliseconds timeout() const; |
232 | | |
233 | | /* |
234 | | * Returns the "best" encoding to use to encode (typically compress) |
235 | | * a response to the current request. The "best" encoding is the one |
236 | | * which is supported by both the server and client, and which has |
237 | | * the highest preference expressed by the client (i.e., the highest |
238 | | * quality value, as defined in |
239 | | * <https://www.rfc-editor.org/rfc/rfc9110.html#name-accept-encoding>) |
240 | | */ |
241 | | Header::Encoding getBestAcceptEncoding() const; |
242 | | |
243 | | private: |
244 | | #ifdef LIBSTDCPP_SMARTPTR_LOCK_FIXME |
245 | | void associatePeer(const std::shared_ptr<Tcp::Peer>& peer) |
246 | | { |
247 | | if (peer_.use_count() > 0) |
248 | | throw std::runtime_error("A peer was already associated to the response"); |
249 | | |
250 | | peer_ = peer; |
251 | | } |
252 | | #endif |
253 | | |
254 | | Method method_; |
255 | | std::string resource_; |
256 | | Uri::Query query_; |
257 | | |
258 | | #ifdef LIBSTDCPP_SMARTPTR_LOCK_FIXME |
259 | | std::weak_ptr<Tcp::Peer> peer_; |
260 | | #endif |
261 | | Address address_; |
262 | | std::chrono::milliseconds timeout_ = std::chrono::milliseconds(0); |
263 | | }; |
264 | | |
265 | | class Handler; |
266 | | class ResponseWriter; |
267 | | |
268 | | class Timeout |
269 | | { |
270 | | public: |
271 | | friend class ResponseWriter; |
272 | | |
273 | | explicit Timeout(Timeout&& other) |
274 | 0 | : handler(other.handler) |
275 | 0 | , transport(other.transport) |
276 | 0 | , armed(other.armed) |
277 | 0 | , timerFd(other.timerFd) |
278 | 0 | , peer(std::move(other.peer)) |
279 | 0 | { |
280 | | // cppcheck-suppress useInitializationList |
281 | 0 | other.timerFd = PS_FD_EMPTY; |
282 | | // For libevent, don't need to free, passed to this->timerFd |
283 | 0 | } |
284 | | |
285 | | Timeout& operator=(Timeout&& other) |
286 | 0 | { |
287 | 0 | handler = other.handler; |
288 | 0 | transport = other.transport; |
289 | 0 | version = other.version; |
290 | 0 | armed = other.armed; |
291 | 0 | timerFd = other.timerFd; |
292 | |
|
293 | 0 | other.timerFd = PS_FD_EMPTY; |
294 | | // For libevent, don't need to free, passed to this->timerFd |
295 | |
|
296 | 0 | peer = std::move(other.peer); |
297 | 0 | return *this; |
298 | 0 | } |
299 | | |
300 | | ~Timeout(); |
301 | | |
302 | | template <typename Duration> |
303 | | void arm(Duration duration) |
304 | | { |
305 | | Async::Promise<uint64_t> p([duration, this](Async::Deferred<uint64_t> deferred) { |
306 | | #ifdef _USE_LIBEVENT |
307 | | std::shared_ptr<EventMethEpollEquiv> |
308 | | event_meth_epoll_equiv( |
309 | | transport->getEventMethEpollEquiv()); |
310 | | if (!event_meth_epoll_equiv) |
311 | | throw std::runtime_error( |
312 | | "event_meth_epoll_equiv null"); |
313 | | |
314 | | timerFd = TRY_NULL_RET(EventMethFns::em_timer_new( |
315 | | PST_CLOCK_MONOTONIC, |
316 | | F_SETFDL_NOTHING, PST_O_NONBLOCK, |
317 | | event_meth_epoll_equiv.get())); |
318 | | #else |
319 | | timerFd = TRY_RET(timerfd_create(PST_CLOCK_MONOTONIC, TFD_NONBLOCK)); |
320 | | #endif |
321 | | transport->armTimer(timerFd, duration, std::move(deferred)); |
322 | | }); |
323 | | |
324 | | p.then( |
325 | | [this](uint64_t numWakeup) { |
326 | | this->armed = false; |
327 | | this->onTimeout(numWakeup); |
328 | | CLOSE_FD(timerFd); |
329 | | timerFd = PS_FD_EMPTY; |
330 | | }, |
331 | | [](std::exception_ptr exc) { std::rethrow_exception(exc); }); |
332 | | |
333 | | armed = true; |
334 | | } |
335 | | |
336 | | void disarm(); |
337 | | |
338 | | bool isArmed() const; |
339 | | |
340 | | private: |
341 | 0 | Timeout(const Timeout& other) = default; |
342 | | |
343 | | Timeout(Tcp::Transport* transport_, Http::Version version, Handler* handler_, |
344 | | std::weak_ptr<Tcp::Peer> peer_); |
345 | | |
346 | | void onTimeout(uint64_t numWakeup); |
347 | | |
348 | | Handler* handler; |
349 | | Http::Version version; |
350 | | Tcp::Transport* transport; |
351 | | bool armed; |
352 | | Fd timerFd; |
353 | | std::weak_ptr<Tcp::Peer> peer; |
354 | | }; |
355 | | |
356 | | class ResponseStream final |
357 | | { |
358 | | public: |
359 | | friend class ResponseWriter; |
360 | | |
361 | | ResponseStream(ResponseStream&& other); |
362 | | |
363 | | ResponseStream& operator=(ResponseStream&& other); |
364 | | |
365 | | template <typename T> |
366 | | friend ResponseStream& operator<<(ResponseStream& stream, const T& val); |
367 | | |
368 | | std::streamsize write(const char* data, std::streamsize sz); |
369 | | |
370 | | void flush(); |
371 | | void ends(); |
372 | | |
373 | | private: |
374 | | ResponseStream(Message&& other, std::weak_ptr<Tcp::Peer> peer, |
375 | | Tcp::Transport* transport, Timeout timeout, size_t streamSize, |
376 | | size_t maxResponseSize); |
377 | | |
378 | | std::shared_ptr<Tcp::Peer> peer() const; |
379 | | |
380 | | Message response_; |
381 | | std::weak_ptr<Tcp::Peer> peer_; |
382 | | DynamicStreamBuf buf_; |
383 | | Tcp::Transport* transport_; |
384 | | Timeout timeout_; |
385 | | }; |
386 | | |
387 | | inline ResponseStream& ends(ResponseStream& stream) |
388 | 0 | { |
389 | 0 | stream.ends(); |
390 | 0 | return stream; |
391 | 0 | } |
392 | | |
393 | | inline ResponseStream& flush(ResponseStream& stream) |
394 | 0 | { |
395 | 0 | stream.flush(); |
396 | 0 | return stream; |
397 | 0 | } |
398 | | |
399 | | template <typename T> |
400 | | ResponseStream& operator<<(ResponseStream& stream, const T& val) |
401 | | { |
402 | | Size<T> size; |
403 | | |
404 | | std::ostream os(&stream.buf_); |
405 | | os << std::hex << size(val) << crlf; |
406 | | os << val << crlf; |
407 | | |
408 | | return stream; |
409 | | } |
410 | | |
411 | | inline ResponseStream& operator<<(ResponseStream& stream, |
412 | | ResponseStream& (*func)(ResponseStream&)) |
413 | 0 | { |
414 | 0 | return (*func)(stream); |
415 | 0 | } |
416 | | |
417 | | // 6. Response |
418 | | // @Investigate public inheritence |
419 | | class Response : public Message |
420 | | { |
421 | | public: |
422 | | friend class Private::ResponseLineStep; |
423 | | |
424 | 277 | Response() = default; |
425 | | explicit Response(Version version); |
426 | | |
427 | 0 | Response(const Response& other) = default; |
428 | | Response& operator=(const Response& other) = default; |
429 | 0 | Response(Response&& other) = default; |
430 | | Response& operator=(Response&& other) = default; |
431 | | }; |
432 | | |
433 | | class ResponseWriter final |
434 | | { |
435 | | public: |
436 | | static constexpr size_t DefaultStreamSize = 512; |
437 | | |
438 | | friend Async::Promise<PST_SSIZE_T> |
439 | | serveFile(ResponseWriter&, const std::string&, const Mime::MediaType&); |
440 | | |
441 | | friend class Handler; |
442 | | friend class Timeout; |
443 | | |
444 | | ResponseWriter& operator=(const ResponseWriter& other) = delete; |
445 | | |
446 | | friend class Private::ResponseLineStep; |
447 | | |
448 | | ResponseWriter(Http::Version version, Tcp::Transport* transport, |
449 | | Handler* handler, std::weak_ptr<Tcp::Peer> peer); |
450 | | |
451 | | // |
452 | | // C++11: std::weak_ptr move constructor is C++14 only so the default |
453 | | // version of move constructor / assignement operator does not work and we |
454 | | // have to define it ourself |
455 | | // We're now using C++17, should this be removed? |
456 | | ResponseWriter(ResponseWriter&& other); |
457 | | |
458 | | ResponseWriter& operator=(ResponseWriter&& other) = default; |
459 | | |
460 | | void setMime(const Mime::MediaType& mime); |
461 | | |
462 | | /* @Feature: add helper functions for common http return code: |
463 | | * - halt() -> 404 |
464 | | * - movedPermantly -> 301 |
465 | | * - moved() -> 302 |
466 | | */ |
467 | | Async::Promise<PST_SSIZE_T> |
468 | | sendMethodNotAllowed(const std::vector<Http::Method>& supportedMethods); |
469 | | |
470 | | Async::Promise<PST_SSIZE_T> send(Code code, const std::string& body = "", |
471 | | const Mime::MediaType& mime = Mime::MediaType()); |
472 | | |
473 | | template <size_t N> |
474 | | Async::Promise<PST_SSIZE_T> |
475 | | send(Code code, const char (&arr)[N], |
476 | | const Mime::MediaType& mime = Mime::MediaType()) |
477 | 0 | { |
478 | 0 | return sendImpl(code, arr, N - 1, mime); |
479 | 0 | } |
480 | | |
481 | | Async::Promise<PST_SSIZE_T> send(Code code, const char* data, const size_t size, |
482 | | const Mime::MediaType& mime = Mime::MediaType()); |
483 | | |
484 | | ResponseStream stream(Code code, size_t streamSize = DefaultStreamSize); |
485 | | |
486 | | template <typename Duration> |
487 | | void timeoutAfter(Duration duration) |
488 | | { |
489 | | timeout_.arm(duration); |
490 | | } |
491 | | |
492 | | const CookieJar& cookies() const; |
493 | | CookieJar& cookies(); |
494 | | |
495 | | const Header::Collection& headers() const; |
496 | | Header::Collection& headers(); |
497 | | |
498 | | Timeout& timeout(); |
499 | | |
500 | | std::shared_ptr<Tcp::Peer> peer() const; |
501 | | |
502 | | // Returns total count of HTTP bytes (headers, cookies, body) written when |
503 | | // sending the response. Result valid AFTER ResponseWriter.send() is called. |
504 | 0 | PST_SSIZE_T getResponseSize() const { return sent_bytes_; } |
505 | | |
506 | | // Returns HTTP result code that was sent with the response. |
507 | 0 | Code getResponseCode() const { return response_.code(); } |
508 | | |
509 | | // Unsafe API |
510 | | |
511 | | DynamicStreamBuf* rdbuf(); |
512 | | |
513 | | DynamicStreamBuf* rdbuf(DynamicStreamBuf* other); |
514 | | |
515 | | ResponseWriter clone() const; |
516 | | |
517 | | std::shared_ptr<Tcp::Peer> getPeer() const |
518 | 0 | { |
519 | 0 | if (auto sp = peer_.lock()) |
520 | 0 | return sp; |
521 | 0 | return nullptr; |
522 | 0 | } |
523 | | |
524 | | // Compress using the requested content encoding, if supported, |
525 | | // before sending bits to client. Content-Encoding header will be |
526 | | // automatically set to the requested encoding, if supported... |
527 | | void setCompression(const Pistache::Http::Header::Encoding _contentEncoding); |
528 | | |
529 | | #ifdef PISTACHE_USE_CONTENT_ENCODING_BROTLI |
530 | | // Set the compression level for Brotli algorithm. Defaults to |
531 | | // BROTLI_DEFAULT_QUALITY... |
532 | | void setCompressionBrotliLevel(const int _contentEncodingBrotliLevel) |
533 | | { |
534 | | contentEncodingBrotliLevel_ = _contentEncodingBrotliLevel; |
535 | | } |
536 | | #endif |
537 | | #ifdef PISTACHE_USE_CONTENT_ENCODING_ZSTD |
538 | | // Set the compression level for zstandard algorithm. Defaults to |
539 | | // ZSTD_CLEVEL_DEFAULT = 3... |
540 | | void setCompressionZstdLevel(const int contentEncodingZstdLevel) |
541 | | { |
542 | | contentEncodingZstdLevel_ = contentEncodingZstdLevel; |
543 | | } |
544 | | |
545 | | #endif |
546 | | |
547 | | #ifdef PISTACHE_USE_CONTENT_ENCODING_DEFLATE |
548 | | // Set the compression level for deflate algorithm. Defaults to |
549 | | // Z_DEFAULT_COMPRESSION... |
550 | | void setCompressionDeflateLevel(const int _contentEncodingDeflateLevel) |
551 | | { |
552 | | contentEncodingDeflateLevel_ = _contentEncodingDeflateLevel; |
553 | | } |
554 | | #endif |
555 | | |
556 | | private: |
557 | | ResponseWriter(const ResponseWriter& other); |
558 | | |
559 | | Async::Promise<PST_SSIZE_T> sendImpl(Code code, const char* data, |
560 | | const size_t size, |
561 | | const Mime::MediaType& mime); |
562 | | |
563 | | Async::Promise<PST_SSIZE_T> putOnWire(const char* data, size_t len); |
564 | | |
565 | | Response response_; |
566 | | std::weak_ptr<Tcp::Peer> peer_; |
567 | | DynamicStreamBuf buf_; |
568 | | Tcp::Transport* transport_ = nullptr; |
569 | | Timeout timeout_; |
570 | | PST_SSIZE_T sent_bytes_ = 0; |
571 | | |
572 | | Http::Header::Encoding contentEncoding_ = Http::Header::Encoding::Identity; |
573 | | |
574 | | #ifdef PISTACHE_USE_CONTENT_ENCODING_BROTLI |
575 | | int contentEncodingBrotliLevel_ = BROTLI_DEFAULT_QUALITY; |
576 | | #endif |
577 | | |
578 | | #ifdef PISTACHE_USE_CONTENT_ENCODING_ZSTD |
579 | | |
580 | | // Value 0 means default, which is controlled by ZSTD_CLEVEL_DEFAULT = 3 |
581 | | int contentEncodingZstdLevel_ = 0; |
582 | | #endif |
583 | | |
584 | | #ifdef PISTACHE_USE_CONTENT_ENCODING_DEFLATE |
585 | | int contentEncodingDeflateLevel_ = Z_DEFAULT_COMPRESSION; |
586 | | #endif |
587 | | }; |
588 | | |
589 | | Async::Promise<PST_SSIZE_T> |
590 | | serveFile(ResponseWriter& writer, const std::string& fileName, |
591 | | const Mime::MediaType& contentType = Mime::MediaType()); |
592 | | |
593 | | namespace Private |
594 | | { |
595 | | |
596 | | enum class State { Again, |
597 | | Next, |
598 | | Done }; |
599 | | using StepId = uint64_t; |
600 | | |
601 | | struct Step |
602 | | { |
603 | | explicit Step(Message* request); |
604 | | |
605 | 8.04k | virtual ~Step() = default; |
606 | | |
607 | | virtual StepId id() const = 0; |
608 | | virtual State apply(StreamCursor& cursor) = 0; |
609 | | |
610 | | [[noreturn]] static void raise(const char* msg, Code code = Code::Bad_Request); |
611 | | |
612 | | protected: |
613 | | Message* message; |
614 | | }; |
615 | | |
616 | | class RequestLineStep : public Step |
617 | | { |
618 | | public: |
619 | | static constexpr StepId Id = Meta::Hash::fnv1a("RequestLine"); |
620 | | |
621 | | explicit RequestLineStep(Request* request) |
622 | 2.40k | : Step(request) |
623 | 2.40k | { } |
624 | | |
625 | 0 | StepId id() const override { return Id; } |
626 | | State apply(StreamCursor& cursor) override; |
627 | | }; |
628 | | |
629 | | class ResponseLineStep : public Step |
630 | | { |
631 | | public: |
632 | | static constexpr StepId Id = Meta::Hash::fnv1a("ResponseLine"); |
633 | | |
634 | | explicit ResponseLineStep(Response* response) |
635 | 277 | : Step(response) |
636 | 277 | { } |
637 | | |
638 | 0 | StepId id() const override { return Id; } |
639 | | State apply(StreamCursor& cursor) override; |
640 | | }; |
641 | | |
642 | | class HeadersStep : public Step |
643 | | { |
644 | | public: |
645 | | static constexpr StepId Id = Meta::Hash::fnv1a("Headers"); |
646 | | |
647 | | explicit HeadersStep(Message* request) |
648 | 2.68k | : Step(request) |
649 | 2.68k | { } |
650 | | |
651 | 0 | StepId id() const override { return Id; } |
652 | | State apply(StreamCursor& cursor) override; |
653 | | }; |
654 | | |
655 | | class BodyStep : public Step |
656 | | { |
657 | | public: |
658 | | static constexpr auto Id = Meta::Hash::fnv1a("Body"); |
659 | | |
660 | | explicit BodyStep(Message* message_) |
661 | 2.68k | : Step(message_) |
662 | 2.68k | , chunk(message_) |
663 | 2.68k | , bytesRead(0) |
664 | 2.68k | { } |
665 | | |
666 | 0 | StepId id() const override { return Id; } |
667 | | State apply(StreamCursor& cursor) override; |
668 | | |
669 | | private: |
670 | | struct Chunk |
671 | | { |
672 | | enum Result { Complete, |
673 | | Incomplete, |
674 | | Final }; |
675 | | |
676 | | explicit Chunk(Message* message_) |
677 | 2.68k | : message(message_) |
678 | 2.68k | , bytesRead(0) |
679 | 2.68k | , size(-1) |
680 | 2.68k | { } |
681 | | |
682 | | Result parse(StreamCursor& cursor); |
683 | | |
684 | | void reset() |
685 | 820 | { |
686 | 820 | bytesRead = 0; |
687 | 820 | size = -1; |
688 | 820 | } |
689 | | |
690 | | private: |
691 | | Message* message; |
692 | | size_t bytesRead; |
693 | | PST_SSIZE_T size; |
694 | | PST_SSIZE_T alreadyAppendedChunkBytes; |
695 | | }; |
696 | | |
697 | | State parseContentLength(StreamCursor& cursor, |
698 | | const std::shared_ptr<Header::ContentLength>& cl); |
699 | | State |
700 | | parseTransferEncoding(StreamCursor& cursor, |
701 | | const std::shared_ptr<Header::TransferEncoding>& te); |
702 | | |
703 | | Chunk chunk; |
704 | | size_t bytesRead; |
705 | | }; |
706 | | |
707 | | class ParserBase |
708 | | { |
709 | | public: |
710 | | static constexpr size_t StepsCount = 3; |
711 | | |
712 | | explicit ParserBase(size_t maxDataSize); |
713 | | |
714 | | ParserBase(const ParserBase&) = delete; |
715 | | ParserBase& operator=(const ParserBase&) = delete; |
716 | | ParserBase(ParserBase&&) = default; |
717 | | ParserBase& operator=(ParserBase&&) = default; |
718 | | |
719 | 2.68k | virtual ~ParserBase() = default; |
720 | | |
721 | | bool feed(const char* data, size_t len); |
722 | | virtual void reset(); |
723 | | State parse(); |
724 | | |
725 | | Step* step(); |
726 | | |
727 | | protected: |
728 | | std::array<std::unique_ptr<Step>, StepsCount> allSteps; |
729 | | size_t currentStep = 0; |
730 | | |
731 | | private: |
732 | | ArrayStreamBuf<char> buffer; |
733 | | StreamCursor cursor; |
734 | | }; |
735 | | |
736 | | template <typename Message> |
737 | | class ParserImpl; |
738 | | |
739 | | template <> |
740 | | class ParserImpl<Http::Request> : public ParserBase |
741 | | { |
742 | | public: |
743 | | explicit ParserImpl(size_t maxDataSize); |
744 | | |
745 | | void reset() override; |
746 | | |
747 | | std::chrono::steady_clock::time_point time() const |
748 | 0 | { |
749 | 0 | return time_; |
750 | 0 | } |
751 | | |
752 | | Request request; |
753 | | |
754 | | private: |
755 | | std::chrono::steady_clock::time_point time_; |
756 | | }; |
757 | | |
758 | | template <> |
759 | | class ParserImpl<Http::Response> : public ParserBase |
760 | | { |
761 | | public: |
762 | | explicit ParserImpl(size_t maxDataSize); |
763 | | |
764 | | Response response; |
765 | | }; |
766 | | |
767 | | } // namespace Private |
768 | | |
769 | | using Parser = Private::ParserBase; |
770 | | using RequestParser = Private::ParserImpl<Http::Request>; |
771 | | using ResponseParser = Private::ParserImpl<Http::Response>; |
772 | | |
773 | | class Handler : public Tcp::Handler |
774 | | { |
775 | | public: |
776 | | static constexpr const char* ParserData = "__Parser"; |
777 | | |
778 | | virtual void onRequest(const Request& request, ResponseWriter response) = 0; |
779 | | |
780 | | virtual void onTimeout(const Request& request, ResponseWriter response); |
781 | | |
782 | | void setMaxRequestSize(size_t value); |
783 | | size_t getMaxRequestSize() const; |
784 | | void setMaxResponseSize(size_t value); |
785 | | size_t getMaxResponseSize() const; |
786 | | |
787 | | template <typename Duration> |
788 | | void setHeaderTimeout(Duration timeout) |
789 | | { |
790 | | headerTimeout_ = std::chrono::duration_cast<std::chrono::milliseconds>(timeout); |
791 | | } |
792 | | |
793 | | template <typename Duration> |
794 | | void setBodyTimeout(Duration timeout) |
795 | | { |
796 | | bodyTimeout_ = std::chrono::duration_cast<std::chrono::milliseconds>(timeout); |
797 | | } |
798 | | |
799 | | std::chrono::milliseconds getHeaderTimeout() const |
800 | 0 | { |
801 | 0 | return headerTimeout_; |
802 | 0 | } |
803 | | |
804 | | std::chrono::milliseconds getBodyTimeout() const |
805 | 0 | { |
806 | 0 | return bodyTimeout_; |
807 | 0 | } |
808 | | |
809 | | static std::shared_ptr<RequestParser> getParser(const std::shared_ptr<Tcp::Peer>& peer); |
810 | | |
811 | | ~Handler() override = default; |
812 | | |
813 | | private: |
814 | | void onConnection(const std::shared_ptr<Tcp::Peer>& peer) override; |
815 | | void onInput(const char* buffer, size_t len, |
816 | | const std::shared_ptr<Tcp::Peer>& peer) override; |
817 | | |
818 | | private: |
819 | | size_t maxRequestSize_ = Const::DefaultMaxRequestSize; |
820 | | size_t maxResponseSize_ = Const::DefaultMaxResponseSize; |
821 | | |
822 | | std::chrono::milliseconds headerTimeout_ = Const::DefaultHeaderTimeout; |
823 | | std::chrono::milliseconds bodyTimeout_ = Const::DefaultBodyTimeout; |
824 | | }; |
825 | | |
826 | | template <typename H, typename... Args> |
827 | | std::shared_ptr<H> make_handler(Args&&... args) |
828 | | { |
829 | | static_assert(std::is_base_of<Handler, H>::value, |
830 | | "An http handler must inherit from the Http::Handler class"); |
831 | | static_assert(details::IsHttpPrototype<H>::value, |
832 | | "An http handler must be an http prototype, did you forget the " |
833 | | "HTTP_PROTOTYPE macro ?"); |
834 | | |
835 | | return std::make_shared<H>(std::forward<Args>(args)...); |
836 | | } |
837 | | |
838 | | } // namespace Http |
839 | | } // namespace Pistache |