Coverage Report

Created: 2026-04-12 07:00

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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