Coverage Report

Created: 2025-07-11 06:13

/src/pistache/src/common/http.cc
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * SPDX-FileCopyrightText: 2015 Mathieu Stefani
3
 *
4
 * SPDX-License-Identifier: Apache-2.0
5
 */
6
7
/* http.cc
8
   Mathieu Stefani, 13 August 2015
9
10
   Http layer implementation
11
*/
12
13
#include <pistache/winornix.h>
14
15
#include <pistache/config.h>
16
#include <pistache/eventmeth.h>
17
#include <pistache/http.h>
18
#include <pistache/http_header.h>
19
#include <pistache/net.h>
20
#include <pistache/peer.h>
21
#include <pistache/transport.h>
22
23
#include PST_STRERROR_R_HDR
24
25
#include <charconv>
26
#include <cstring>
27
#include <ctime>
28
#include <iomanip>
29
#include <iostream>
30
#include <memory>
31
#include <stdexcept>
32
#include <string>
33
#include <unordered_map>
34
35
#include <fcntl.h> // for file-constants (_O_RDONLY etc.) in Windows
36
#include PST_FCNTL_HDR // for function fcntl()
37
38
#include PST_MISC_IO_HDR // for _close (io.h / unistd.h)
39
#include PIST_FILEFNS_HDR // for "open"
40
41
#include <sys/stat.h>
42
#include <sys/types.h>
43
44
namespace Pistache::Http
45
{
46
47
    template <typename H, typename Stream, typename... Args>
48
    typename std::enable_if<Header::IsHeader<H>::value, Stream&>::type
49
    writeHeader(Stream& stream, Args&&... args)
50
0
    {
51
0
        H header(std::forward<Args>(args)...);
52
53
0
        stream << H::Name << ": ";
54
0
        header.write(stream);
55
56
0
        stream << crlf;
57
58
0
        return stream;
59
0
    }
Unexecuted instantiation: _ZN8Pistache4Http11writeHeaderINS0_6Header16TransferEncodingENSt3__113basic_ostreamIcNS4_11char_traitsIcEEEEJNS2_8EncodingEEEENS4_9enable_ifIXsr6Header8IsHeaderIT_EE5valueERT0_E4typeESD_DpOT1_
Unexecuted instantiation: _ZN8Pistache4Http11writeHeaderINS0_6Header13ContentLengthENSt3__113basic_ostreamIcNS4_11char_traitsIcEEEEJRmEEENS4_9enable_ifIXsr6Header8IsHeaderIT_EE5valueERT0_E4typeESD_DpOT1_
Unexecuted instantiation: _ZN8Pistache4Http11writeHeaderINS0_6Header13ContentLengthENSt3__113basic_ostreamIcNS4_11char_traitsIcEEEEJRKmEEENS4_9enable_ifIXsr6Header8IsHeaderIT_EE5valueERT0_E4typeESE_DpOT1_
60
61
    namespace
62
    {
63
        bool writeStatusLine(Version version, Code code, DynamicStreamBuf& buf)
64
0
        {
65
0
#define PST_OUT(...)      \
66
0
    do                    \
67
0
    {                     \
68
0
        __VA_ARGS__;      \
69
0
        if (!os)          \
70
0
            return false; \
71
0
    } while (0)
72
73
0
            std::ostream os(&buf);
74
75
0
            PST_OUT(os << version << " ");
76
0
            PST_OUT(os << static_cast<int>(code));
77
0
            PST_OUT(os << ' ');
78
0
            PST_OUT(os << code);
79
0
            PST_OUT(os << crlf);
80
81
0
            return true;
82
83
0
#undef PST_OUT
84
0
        }
85
86
        bool writeHeaders(const Header::Collection& headers, DynamicStreamBuf& buf)
87
0
        {
88
0
#define PST_OUT(...)      \
89
0
    do                    \
90
0
    {                     \
91
0
        __VA_ARGS__;      \
92
0
        if (!os)          \
93
0
            return false; \
94
0
    } while (0)
95
96
0
            std::ostream os(&buf);
97
98
0
            for (const auto& header : headers.list())
99
0
            {
100
0
                PST_OUT(os << header->name() << ": ");
101
0
                PST_OUT(header->write(os));
102
0
                PST_OUT(os << crlf);
103
0
            }
104
105
0
            return true;
106
107
0
#undef PST_OUT
108
0
        }
109
110
        bool writeCookies(const CookieJar& cookies, DynamicStreamBuf& buf)
111
0
        {
112
0
#define PST_OUT(...)      \
113
0
    do                    \
114
0
    {                     \
115
0
        __VA_ARGS__;      \
116
0
        if (!os)          \
117
0
            return false; \
118
0
    } while (0)
119
120
0
            std::ostream os(&buf);
121
0
            for (const auto& cookie : cookies)
122
0
            {
123
0
                PST_OUT(os << "Set-Cookie: ");
124
0
                PST_OUT(os << cookie);
125
0
                PST_OUT(os << crlf);
126
0
            }
127
128
0
            return true;
129
130
0
#undef PST_OUT
131
0
        }
132
133
        using HttpMethods = std::unordered_map<std::string, Method>;
134
135
        const HttpMethods httpMethods = {
136
#define METHOD(repr, str) { str, Method::repr },
137
            HTTP_METHODS
138
#undef METHOD
139
        };
140
141
    } // namespace
142
143
    namespace Private
144
    {
145
146
        Step::Step(Message* request)
147
7.66k
            : message(request)
148
7.66k
        { }
149
150
        void Step::raise(const char* msg, Code code /* = Code::Bad_Request */)
151
611
        {
152
611
            throw HttpError(code, msg);
153
611
        }
154
155
        State RequestLineStep::apply(StreamCursor& cursor)
156
2.57k
        {
157
2.57k
            StreamCursor::Revert revert(cursor);
158
159
2.57k
            auto* request = static_cast<Request*>(message);
160
161
2.57k
            StreamCursor::Token methodToken(cursor);
162
2.57k
            if (!match_until(' ', cursor))
163
26
                return State::Again;
164
165
2.54k
            auto it = httpMethods.find(methodToken.text());
166
2.54k
            if (it != httpMethods.end())
167
2.42k
            {
168
2.42k
                request->method_ = it->second;
169
2.42k
            }
170
126
            else
171
126
            {
172
126
                raise("Unknown HTTP request method");
173
126
            }
174
175
2.54k
            int n;
176
177
2.54k
            if (cursor.eof())
178
0
                return State::Again;
179
2.54k
            else if ((n = cursor.current()) != ' ')
180
0
                raise("Malformed HTTP request after Method, expected SP");
181
182
2.54k
            if (!cursor.advance(1))
183
0
                return State::Again;
184
185
2.54k
            StreamCursor::Token resToken(cursor);
186
24.0k
            while ((n = cursor.current()) != '?' && n != ' ')
187
21.5k
                if (!cursor.advance(1))
188
40
                    return State::Again;
189
190
2.50k
            request->resource_ = resToken.text();
191
192
            // Query parameters of the Uri
193
2.50k
            if (n == '?')
194
545
            {
195
545
                if (!cursor.advance(1))
196
0
                    return State::Again;
197
198
8.25k
                while ((n = cursor.current()) != ' ')
199
8.08k
                {
200
8.08k
                    StreamCursor::Token keyToken(cursor);
201
8.08k
                    if (!match_until({ '=', ' ', '&' }, cursor))
202
312
                        return State::Again;
203
204
7.76k
                    std::string key = keyToken.text();
205
206
7.76k
                    auto c = cursor.current();
207
7.76k
                    if (c == ' ')
208
87
                    {
209
87
                        request->query_.add(std::move(key), "");
210
87
                    }
211
7.68k
                    else if (c == '&')
212
5.00k
                    {
213
5.00k
                        request->query_.add(std::move(key), "");
214
5.00k
                        if (!cursor.advance(1))
215
0
                            return State::Again;
216
5.00k
                    }
217
2.68k
                    else if (c == '=')
218
2.68k
                    {
219
2.68k
                        if (!cursor.advance(1))
220
0
                            return State::Again;
221
222
2.68k
                        StreamCursor::Token valueToken(cursor);
223
2.68k
                        if (!match_until({ ' ', '&' }, cursor))
224
62
                            return State::Again;
225
226
2.61k
                        std::string value = valueToken.text();
227
2.61k
                        request->query_.add(std::move(key), std::move(value));
228
2.61k
                        if (cursor.current() == '&')
229
2.54k
                        {
230
2.54k
                            if (!cursor.advance(1))
231
0
                                return State::Again;
232
2.54k
                        }
233
2.61k
                    }
234
7.76k
                }
235
545
            }
236
237
            // @Todo: Fragment
238
239
            // SP
240
2.13k
            if (!cursor.advance(1))
241
0
                return State::Again;
242
243
            // HTTP-Version
244
2.13k
            StreamCursor::Token versionToken(cursor);
245
246
9.70k
            while (!cursor.eol())
247
7.75k
                if (!cursor.advance(1))
248
186
                    return State::Again;
249
250
1.94k
            const char* ver   = versionToken.rawText();
251
1.94k
            const size_t size = versionToken.size();
252
1.94k
            if (strncmp(ver, "HTTP/1.0", size) == 0)
253
1.24k
            {
254
1.24k
                request->version_ = Version::Http10;
255
1.24k
            }
256
703
            else if (strncmp(ver, "HTTP/1.1", size) == 0)
257
476
            {
258
476
                request->version_ = Version::Http11;
259
476
            }
260
227
            else
261
227
            {
262
227
                raise("Encountered invalid HTTP version");
263
227
            }
264
265
1.94k
            if (!cursor.advance(2))
266
0
                return State::Again;
267
268
1.94k
            revert.ignore();
269
1.94k
            return State::Next;
270
1.94k
        }
271
272
        State ResponseLineStep::apply(StreamCursor& cursor)
273
348
        {
274
348
            StreamCursor::Revert revert(cursor);
275
276
348
            auto* response = static_cast<Response*>(message);
277
278
348
            if (match_raw("HTTP/1.1", strlen("HTTP/1.1"), cursor))
279
175
            {
280
                // response->version = Version::Http11;
281
175
            }
282
173
            else if (match_raw("HTTP/1.0", strlen("HTTP/1.0"), cursor))
283
166
            {
284
166
            }
285
7
            else
286
7
            {
287
7
                raise("Encountered invalid HTTP version");
288
7
            }
289
290
348
            int n;
291
            // SP
292
348
            if ((n = cursor.current()) != StreamCursor::Eof && n != ' ')
293
9
                raise("Expected SPACE after http version");
294
348
            if (!cursor.advance(1))
295
4
                return State::Again;
296
297
344
            StreamCursor::Token codeToken(cursor);
298
344
            if (!match_until(' ', cursor))
299
2
                return State::Again;
300
301
342
            int code               = 0;
302
342
            const char* beg        = codeToken.rawText();
303
342
            const char* end        = codeToken.rawText() + codeToken.size();
304
342
            const auto parseResult = std::from_chars(beg, end, code);
305
306
342
            if (parseResult.ec != std::errc {} || *parseResult.ptr != ' ')
307
127
                raise("Failed to parse return code");
308
342
            response->code_ = static_cast<Http::Code>(code);
309
310
342
            if (!cursor.advance(1))
311
0
                return State::Again;
312
313
1.92k
            while (!cursor.eol() && !cursor.eof())
314
1.58k
            {
315
1.58k
                cursor.advance(1);
316
1.58k
            }
317
318
342
            if (!cursor.advance(2))
319
138
                return State::Again;
320
321
204
            revert.ignore();
322
204
            return State::Next;
323
342
        }
324
325
        State HeadersStep::apply(StreamCursor& cursor)
326
2.91k
        {
327
2.91k
            StreamCursor::Revert revert(cursor);
328
329
35.4k
            while (!cursor.eol())
330
34.8k
            {
331
34.8k
                StreamCursor::Revert headerRevert(cursor);
332
333
                // Read the header name
334
34.8k
                size_t start = cursor;
335
336
439k
                while (cursor.current() != ':')
337
406k
                    if (!cursor.advance(1))
338
1.67k
                        return State::Again;
339
340
                // Skip the ':'
341
33.1k
                if (!cursor.advance(1))
342
0
                    return State::Again;
343
344
33.1k
                std::string name = std::string(cursor.offset(start), cursor.diff(start) - 1);
345
346
                // Ignore spaces
347
43.5k
                while (cursor.current() == ' ')
348
10.4k
                    if (!cursor.advance(1))
349
0
                        return State::Again;
350
351
                // Read the header value
352
33.1k
                start = cursor;
353
751k
                while (!cursor.eol())
354
719k
                {
355
719k
                    if (!cursor.advance(1))
356
584
                        return State::Again;
357
719k
                }
358
359
32.5k
                if (Header::LowercaseEqualStatic(name, "cookie"))
360
2.98k
                {
361
2.98k
                    message->cookies_.removeAllCookies(); // removing existing cookies before
362
                                                          // re-adding them.
363
2.98k
                    message->cookies_.addFromRaw(cursor.offset(start), cursor.diff(start));
364
2.98k
                }
365
29.5k
                else if (Header::LowercaseEqualStatic(name, "set-cookie"))
366
0
                {
367
0
                    message->cookies_.add(
368
0
                        Cookie::fromRaw(cursor.offset(start), cursor.diff(start)));
369
0
                }
370
371
                // If the header is registered with the Registry, add its strongly
372
                //  typed form to the headers list...
373
29.5k
                else if (Header::Registry::instance().isRegistered(name))
374
16.5k
                {
375
16.5k
                    std::shared_ptr<Header::Header> header = Header::Registry::instance().makeHeader(name);
376
16.5k
                    header->parseRaw(cursor.offset(start), cursor.diff(start));
377
16.5k
                    message->headers_.add(header);
378
16.5k
                }
379
380
                // But also preserve a raw header version too, regardless of whether
381
                //  its type was known to the Registry...
382
32.5k
                std::string value(cursor.offset(start), cursor.diff(start));
383
32.5k
                message->headers_.addRaw(Header::Raw(std::move(name), std::move(value)));
384
385
                // CRLF
386
32.5k
                if (!cursor.advance(2))
387
0
                    return State::Again;
388
389
32.5k
                headerRevert.ignore();
390
32.5k
            }
391
392
652
            if (!cursor.advance(2))
393
0
                return State::Again;
394
395
652
            revert.ignore();
396
652
            return State::Next;
397
652
        }
398
399
        State BodyStep::apply(StreamCursor& cursor)
400
687
        {
401
687
            auto cl = message->headers_.tryGet<Header::ContentLength>();
402
687
            auto te = message->headers_.tryGet<Header::TransferEncoding>();
403
404
687
            if (cl && te)
405
2
                raise("Got mutually exclusive ContentLength and TransferEncoding header");
406
407
687
            if (cl)
408
181
                return parseContentLength(cursor, cl);
409
410
506
            if (te)
411
423
                return parseTransferEncoding(cursor, te);
412
413
83
            return State::Done;
414
506
        }
415
416
        State BodyStep::parseContentLength(
417
            StreamCursor& cursor, const std::shared_ptr<Header::ContentLength>& cl)
418
181
        {
419
181
            auto contentLength = cl->value();
420
421
181
            auto readBody = [&](size_t size) {
422
181
                StreamCursor::Token token(cursor);
423
181
                const size_t available = cursor.remaining();
424
425
                // We have an incomplete body, read what we can
426
181
                if (available < size)
427
164
                {
428
164
                    cursor.advance(available);
429
164
                    message->body_.append(token.rawText(), token.size());
430
431
164
                    bytesRead += available;
432
433
164
                    return false;
434
164
                }
435
436
17
                cursor.advance(size);
437
17
                message->body_.append(token.rawText(), token.size());
438
17
                return true;
439
181
            };
440
441
            // We already started to read some bytes but we got an incomplete payload
442
181
            if (bytesRead > 0)
443
74
            {
444
                // How many bytes do we still need to read ?
445
74
                const size_t remaining = static_cast<size_t>(
446
74
                    contentLength - bytesRead);
447
74
                if (!readBody(remaining))
448
74
                    return State::Again;
449
74
            }
450
            // This is the first time we are reading the payload
451
107
            else
452
107
            {
453
107
                message->body_.reserve(
454
107
                    static_cast<unsigned int>(contentLength));
455
107
                if (!readBody(static_cast<size_t>(contentLength)))
456
90
                    return State::Again;
457
107
            }
458
459
17
            bytesRead = 0;
460
17
            return State::Done;
461
181
        }
462
463
        BodyStep::Chunk::Result BodyStep::Chunk::parse(StreamCursor& cursor)
464
1.30k
        {
465
1.30k
            if (size == -1)
466
1.23k
            {
467
1.23k
                StreamCursor::Revert revert(cursor);
468
1.23k
                StreamCursor::Token chunkSize(cursor);
469
470
18.5k
                while (!cursor.eol())
471
17.3k
                    if (!cursor.advance(1))
472
41
                        return Incomplete;
473
474
1.19k
                const char* raw { chunkSize.rawText() };
475
1.19k
                const auto* end { chunkSize.rawText() + chunkSize.size() };
476
477
1.19k
                size_t sz              = 0;
478
1.19k
                const auto parseResult = std::from_chars(raw, end, sz, 16);
479
480
1.19k
                if (parseResult.ec != std::errc {} || *parseResult.ptr != '\r')
481
229
                    throw std::runtime_error("Invalid chunk size");
482
483
                // CRLF
484
965
                if (!cursor.advance(2))
485
0
                    return Incomplete;
486
487
965
                revert.ignore();
488
489
965
                size                      = sz;
490
965
                alreadyAppendedChunkBytes = 0;
491
965
            }
492
493
1.03k
            if (size == 0)
494
2
                return Final;
495
496
1.03k
            message->body_.reserve(size);
497
1.03k
            StreamCursor::Token chunkData(cursor);
498
1.03k
            const PST_SSIZE_T available = cursor.remaining();
499
500
1.03k
            if (available + alreadyAppendedChunkBytes < size + 2)
501
136
            {
502
136
                cursor.advance(available);
503
136
                message->body_.append(chunkData.rawText(), available);
504
136
                alreadyAppendedChunkBytes += available;
505
136
                return Incomplete;
506
136
            }
507
895
            cursor.advance(size - alreadyAppendedChunkBytes);
508
509
            // trailing EOL
510
895
            cursor.advance(2);
511
512
895
            message->body_.append(chunkData.rawText(), size - alreadyAppendedChunkBytes);
513
514
895
            return Complete;
515
1.03k
        }
516
517
        State BodyStep::parseTransferEncoding(
518
            StreamCursor& cursor, const std::shared_ptr<Header::TransferEncoding>& te)
519
423
        {
520
423
            auto encoding = te->encoding();
521
423
            if (encoding == Http::Header::Encoding::Chunked)
522
415
            {
523
415
                Chunk::Result result;
524
415
                try
525
415
                {
526
1.30k
                    while ((result = chunk.parse(cursor)) != Chunk::Final)
527
1.07k
                    {
528
1.07k
                        if (result == Chunk::Incomplete)
529
177
                            return State::Again;
530
531
893
                        chunk.reset();
532
893
                        if (cursor.eof())
533
5
                            return State::Again;
534
893
                    }
535
233
                    chunk.reset();
536
233
                }
537
415
                catch (const std::exception& e)
538
415
                {
539
                    // reset chunk incase signal handled & chunk eventually reused
540
231
                    chunk.reset();
541
231
                    raise(e.what());
542
231
                }
543
544
2
                return State::Done;
545
415
            }
546
8
            else
547
8
            {
548
8
                raise("Unsupported Transfer-Encoding", Code::Not_Implemented);
549
8
            }
550
            // raise defined with [[noreturn]], so compiler knows we cannot
551
            // reach here
552
423
        }
553
554
        ParserBase::ParserBase(size_t maxDataSize)
555
2.55k
            : buffer(maxDataSize)
556
2.55k
            , cursor(&buffer)
557
2.55k
        { }
558
559
        State ParserBase::parse()
560
4.22k
        {
561
4.22k
            State state;
562
4.22k
            do
563
6.51k
            {
564
6.51k
                Step* step = allSteps[currentStep].get();
565
6.51k
                state      = step->apply(cursor);
566
6.51k
                if (state == State::Next)
567
2.29k
                {
568
2.29k
                    ++currentStep;
569
2.29k
                }
570
6.51k
            } while (state == State::Next);
571
572
            // Should be either Again or Done
573
4.22k
            return state;
574
4.22k
        }
575
576
        bool ParserBase::feed(const char* data, size_t len)
577
2.55k
        {
578
2.55k
            return buffer.feed(data, len);
579
2.55k
        }
580
581
        void ParserBase::reset()
582
0
        {
583
0
            buffer.reset();
584
0
            cursor.reset();
585
586
0
            currentStep = 0;
587
0
        }
588
589
        Step* ParserBase::step()
590
0
        {
591
0
            return allSteps[currentStep].get();
592
0
        }
593
594
    } // namespace Private
595
596
    namespace Uri
597
    {
598
599
        Query::Query()
600
2.29k
            : params()
601
2.29k
        { }
602
603
        Query::Query(
604
            std::initializer_list<std::pair<const std::string, std::string>> params)
605
0
            : params(params)
606
0
        { }
607
608
        void Query::add(std::string name, std::string value)
609
7.72k
        {
610
7.72k
            params.insert(std::make_pair(std::move(name), std::move(value)));
611
7.72k
        }
612
613
        std::optional<std::string> Query::get(const std::string& name) const
614
0
        {
615
0
            auto it = params.find(name);
616
0
            if (it == std::end(params))
617
0
                return std::nullopt;
618
619
0
            return std::optional<std::string>(it->second);
620
0
        }
621
622
        std::string Query::as_str() const
623
0
        {
624
0
            std::string query_url;
625
0
            for (const auto& e : params)
626
0
            {
627
0
                query_url += "&" + e.first + "=" + e.second;
628
0
            }
629
0
            if (!query_url.empty())
630
0
            {
631
0
                query_url[0] = '?'; // replace first `&` with `?`
632
0
            }
633
0
            return query_url;
634
0
        }
635
636
        bool Query::has(const std::string& name) const
637
0
        {
638
0
            return params.find(name) != std::end(params);
639
0
        }
640
641
    } // namespace Uri
642
643
    Message::Message(Version version)
644
0
        : version_(version)
645
0
    { }
646
647
0
    Version Message::version() const { return version_; }
648
649
0
    Code Message::code() const { return code_; }
650
651
0
    const std::string& Message::body() const { return body_; }
652
653
0
    std::string Message::body() { return body_; }
654
655
0
    const Header::Collection& Message::headers() const { return headers_; }
656
657
0
    Header::Collection& Message::headers() { return headers_; }
658
659
0
    const CookieJar& Message::cookies() const { return cookies_; }
660
661
0
    CookieJar& Message::cookies() { return cookies_; }
662
663
0
    Method Request::method() const { return method_; }
664
665
0
    const std::string& Request::resource() const { return resource_; }
666
667
0
    const Uri::Query& Request::query() const { return query_; }
668
669
0
    const Address& Request::address() const { return address_; }
670
671
0
    std::chrono::milliseconds Request::timeout() const { return timeout_; }
672
673
    Header::Encoding Request::getBestAcceptEncoding() const
674
0
    {
675
0
        const auto& maybe_header = headers().tryGet<Header::AcceptEncoding>();
676
0
        if (maybe_header == nullptr)
677
0
        {
678
0
            return Header::Encoding::Identity;
679
0
        }
680
681
0
        const auto& header = *maybe_header;
682
683
0
        for (const auto& encoding : header.encodings())
684
0
        {
685
            // If the qvalue is 0, the encoding is not supported by the client
686
0
            if (encodingSupported(encoding.first) && encoding.second != 0)
687
0
            {
688
0
                return encoding.first;
689
0
            }
690
0
        }
691
692
0
        return Header::Encoding::Identity;
693
0
    }
694
695
    Response::Response(Version version)
696
0
        : Message(version)
697
0
    { }
698
699
#ifdef LIBSTDCPP_SMARTPTR_LOCK_FIXME
700
    std::shared_ptr<Tcp::Peer> Request::peer() const
701
    {
702
        auto p = peer_.lock();
703
704
        if (!p)
705
            throw std::runtime_error("Failed to retrieve peer: Broken pipe");
706
707
        return p;
708
    }
709
#endif
710
711
    ResponseStream::ResponseStream(ResponseStream&& other)
712
0
        : response_(std::move(other.response_))
713
0
        , peer_(std::move(other.peer_))
714
0
        , buf_(std::move(other.buf_))
715
0
        , transport_(other.transport_)
716
0
        , timeout_(std::move(other.timeout_))
717
0
    { }
718
719
    ResponseStream::ResponseStream(Message&& other, std::weak_ptr<Tcp::Peer> peer,
720
                                   Tcp::Transport* transport, Timeout timeout,
721
                                   size_t streamSize, size_t maxResponseSize)
722
0
        : response_(std::move(other))
723
0
        , peer_(std::move(peer))
724
0
        , buf_(streamSize, maxResponseSize)
725
0
        , transport_(transport)
726
0
        , timeout_(std::move(timeout))
727
0
    {
728
0
        if (!writeStatusLine(response_.version(), response_.code(), buf_))
729
0
            throw Error("Response exceeded buffer size");
730
731
0
        if (!writeCookies(response_.cookies(), buf_))
732
0
        {
733
0
            throw Error("Response exceeded buffer size");
734
0
        }
735
736
0
        if (writeHeaders(response_.headers(), buf_))
737
0
        {
738
0
            std::ostream os(&buf_);
739
            /* @Todo @Major:
740
             * Correctly handle non-keep alive requests
741
             * Do not put Keep-Alive if version == Http::11 and request.keepAlive ==
742
             * true
743
             */
744
            // writeHeader<Header::Connection>(os, ConnectionControl::KeepAlive);
745
            // if (!os) throw Error("Response exceeded buffer size");
746
0
            writeHeader<Header::TransferEncoding>(os, Header::Encoding::Chunked);
747
0
            if (!os)
748
0
                throw Error("Response exceeded buffer size");
749
0
            os << crlf;
750
0
        }
751
0
    }
752
753
    ResponseStream& ResponseStream::operator=(ResponseStream&& other)
754
0
    {
755
0
        response_  = std::move(other.response_);
756
0
        peer_      = std::move(other.peer_);
757
0
        buf_       = std::move(other.buf_);
758
0
        transport_ = other.transport_;
759
0
        timeout_   = std::move(other.timeout_);
760
761
0
        return *this;
762
0
    }
763
764
    std::streamsize ResponseStream::write(const char* data, std::streamsize sz)
765
0
    {
766
0
        std::ostream os(&buf_);
767
0
        os << std::hex << sz << crlf;
768
0
        os.write(data, sz);
769
0
        os << crlf;
770
0
        return sz;
771
0
    }
772
773
    std::shared_ptr<Tcp::Peer> ResponseStream::peer() const
774
0
    {
775
0
        if (peer_.expired())
776
0
        {
777
0
            throw std::runtime_error("Write failed: Broken pipe");
778
0
        }
779
780
0
        return peer_.lock();
781
0
    }
782
783
    void ResponseStream::flush()
784
0
    {
785
0
        timeout_.disarm();
786
0
        auto buf = buf_.buffer();
787
788
0
        auto fd = peer()->fd();
789
0
        transport_->asyncWrite(fd, buf);
790
791
        // Calling transport_->flush from here is unnecessary - we already
792
        // placed the write on the transport's writesQueue with the call to
793
        // asyncWrite directly above; the transport will send just as soon as
794
        // the fd becomes writable.
795
        //
796
        // Calling transport_->flush is also dangerous - writesQueue is
797
        // supposed to be single consumer queue, but calling flush here
798
        // initiates a pop from a different thread (i.e. from our thread
799
        // here). This can cause queue corruption if both our thread here and
800
        // the Pistache consumer thread are popping at the same moment; see
801
        // Issue #1290.
802
        //
803
        // transport_->flush();
804
805
0
        buf_.clear();
806
0
    }
807
808
    void ResponseStream::ends()
809
0
    {
810
0
        std::ostream os(&buf_);
811
0
        os << "0" << crlf;
812
0
        os << crlf;
813
814
0
        if (!os)
815
0
        {
816
0
            throw Error("Response exceeded buffer size");
817
0
        }
818
819
0
        flush();
820
0
    }
821
822
    ResponseWriter::ResponseWriter(ResponseWriter&& other)
823
0
        : response_(std::move(other.response_))
824
0
        , peer_(other.peer_)
825
0
        , buf_(std::move(other.buf_))
826
0
        , transport_(other.transport_)
827
0
        , timeout_(std::move(other.timeout_))
828
0
    { }
829
830
    ResponseWriter::ResponseWriter(Http::Version version, Tcp::Transport* transport,
831
                                   Handler* handler, std::weak_ptr<Tcp::Peer> peer)
832
0
        : response_(version)
833
0
        , peer_(peer)
834
0
        , buf_(DefaultStreamSize, handler->getMaxResponseSize())
835
0
        , transport_(transport)
836
0
        , timeout_(transport, version, handler, peer)
837
0
    { }
838
839
    ResponseWriter::ResponseWriter(const ResponseWriter& other)
840
0
        : response_(other.response_)
841
0
        , peer_(other.peer_)
842
0
        , buf_(DefaultStreamSize, other.buf_.maxSize())
843
0
        , transport_(other.transport_)
844
0
        , timeout_(other.timeout_)
845
0
    { }
846
847
    void ResponseWriter::setMime(const Mime::MediaType& mime)
848
0
    {
849
0
        auto ct = response_.headers().tryGet<Header::ContentType>();
850
0
        if (ct)
851
0
        {
852
0
            ct->setMime(mime);
853
0
        }
854
0
        else
855
0
        {
856
0
            response_.headers().add(std::make_shared<Header::ContentType>(mime));
857
0
        }
858
0
    }
859
860
    Async::Promise<PST_SSIZE_T> ResponseWriter::sendMethodNotAllowed(
861
        const std::vector<Http::Method>& supportedMethods)
862
0
    {
863
0
        response_.code_ = Http::Code::Method_Not_Allowed;
864
0
        response_.headers().add(
865
0
            std::make_shared<Http::Header::Allow>(supportedMethods));
866
0
        const std::string& body = codeString(Pistache::Http::Code::Method_Not_Allowed);
867
0
        return putOnWire(body.c_str(), body.size());
868
0
    }
869
870
    Async::Promise<PST_SSIZE_T> ResponseWriter::send(Code code, const std::string& body,
871
                                                     const Mime::MediaType& mime)
872
0
    {
873
0
        return sendImpl(code, body.c_str(), body.size(), mime);
874
0
    }
875
876
    Async::Promise<PST_SSIZE_T> ResponseWriter::send(Code code, const char* data,
877
                                                     const size_t size,
878
                                                     const Mime::MediaType& mime)
879
0
    {
880
0
        return sendImpl(code, data, size, mime);
881
0
    }
882
883
    Async::Promise<PST_SSIZE_T> ResponseWriter::sendImpl(Code code, const char* data,
884
                                                         const size_t size,
885
                                                         const Mime::MediaType& mime)
886
0
    {
887
0
        if (!peer_.expired())
888
0
        {
889
0
            auto curPeer = peer_.lock();
890
0
            curPeer->setIdle(true); // change peer state to idle
891
892
            // It will result in double free
893
            // Http::Handler::getParser(curPeer)->reset(); // reset the timeout time
894
0
        }
895
896
0
        response_.code_ = code;
897
898
0
        if (mime.isValid())
899
0
        {
900
0
            auto contentType = headers().tryGet<Header::ContentType>();
901
0
            if (contentType)
902
0
            {
903
0
                contentType->setMime(mime);
904
0
            }
905
0
            else
906
0
            {
907
0
                headers().add(std::make_shared<Header::ContentType>(mime));
908
0
            }
909
0
        }
910
911
        // Compress data, if necessary, before sending over wire to user...
912
0
        switch (contentEncoding_)
913
0
        {
914
915
#ifdef PISTACHE_USE_CONTENT_ENCODING_BROTLI
916
        // User requested Brotli compression...
917
        case Http::Header::Encoding::Br: {
918
919
            // Location for size of compressed buffer, initially set to upper
920
            //  bound on the data after its been compressed...
921
            size_t compressedSize = ::BrotliEncoderMaxCompressedSize(size);
922
923
            // Failed...
924
            if (compressedSize == 0)
925
                throw std::runtime_error("BrotliEncoderMaxCompressedSize() failed");
926
927
            // Allocate a smart buffer to contain compressed data...
928
            std::unique_ptr compressedData = std::make_unique<std::byte[]>(compressedSize);
929
930
            // Compress data. The encoder expects compressedSize to initially be
931
            //  the size of the output buffer. After it completes writing it
932
            //  will update its value to reflect actual size used...
933
            const auto compressionStatus = ::BrotliEncoderCompress(
934
                contentEncodingBrotliLevel_,
935
                BROTLI_DEFAULT_WINDOW,
936
                BROTLI_DEFAULT_MODE,
937
                size,
938
                reinterpret_cast<const uint8_t*>(data),
939
                &compressedSize,
940
                reinterpret_cast<uint8_t*>(compressedData.get()));
941
942
            // Failed...
943
            if (compressionStatus != BROTLI_TRUE)
944
                throw std::runtime_error("BrotliEncoderCompress() failed");
945
946
            // Notify client to expect Brotli compressed response...
947
            headers().add<Http::Header::ContentEncoding>(
948
                Http::Header::Encoding::Br);
949
950
            // Send compressed data back to client...
951
            return putOnWire(
952
                reinterpret_cast<const char*>(compressedData.get()),
953
                compressedSize);
954
        }
955
#endif
956
957
#ifdef PISTACHE_USE_CONTENT_ENCODING_ZSTD
958
959
        case Http::Header::Encoding::Zstd: {
960
            size_t estimated_size = ZSTD_compressBound(size);
961
            // Allocate a smart buffer to contain compressed data...
962
            std::unique_ptr compressedData = std::make_unique<std::byte[]>(estimated_size);
963
964
            auto compress_size = ZSTD_compress(reinterpret_cast<void*>(compressedData.get()), estimated_size,
965
                                               data, size, contentEncodingZstdLevel_);
966
            if (ZSTD_isError(compress_size))
967
            {
968
                throw std::runtime_error(
969
                    std::string("failed to compress data to ZSTD on ZSTD_compress(), returning: ") + std::to_string(compress_size));
970
            }
971
            headers().add<Http::Header::ContentEncoding>(
972
                Http::Header::Encoding::Zstd);
973
974
            // Send compressed data back to client...
975
            return putOnWire(
976
                reinterpret_cast<const char*>(compressedData.get()),
977
                compress_size);
978
        }
979
980
#endif
981
982
#ifdef PISTACHE_USE_CONTENT_ENCODING_DEFLATE
983
        // User requested deflate compression...
984
        case Http::Header::Encoding::Deflate: {
985
986
            // Compute upper bound on size of expected compressed data. This
987
            //  will be updated by compress2()...
988
            uLongf compressedSize = static_cast<uLongf>(::compressBound(static_cast<uLong>(size)));
989
990
            // Allocate a smart buffer to contain compressed data...
991
            std::unique_ptr compressedData = std::make_unique<std::byte[]>(compressedSize);
992
993
            // Compress user data at requested level...
994
            const auto compressionStatus = ::compress2(
995
                reinterpret_cast<unsigned char*>(compressedData.get()),
996
                &compressedSize,
997
                reinterpret_cast<const unsigned char*>(data),
998
                static_cast<uLong>(size),
999
                contentEncodingDeflateLevel_);
1000
1001
            // Failed...
1002
            if (compressionStatus != Z_OK)
1003
                throw std::runtime_error(
1004
                    std::string("compress2() failed, returning: ") + std::to_string(compressionStatus));
1005
1006
            // Notify client to expect deflate compressed response...
1007
            headers().add<Http::Header::ContentEncoding>(
1008
                Http::Header::Encoding::Deflate);
1009
1010
            // Send compressed data back to client...
1011
            return putOnWire(
1012
                reinterpret_cast<const char*>(compressedData.get()),
1013
                compressedSize);
1014
        }
1015
#endif
1016
        // No compression requested. Send uncompressed data to client...
1017
0
        case Http::Header::Encoding::Identity:
1018
0
            return putOnWire(data, size);
1019
1020
        // Unknown...
1021
0
        default:
1022
0
            throw std::runtime_error("User requested unknown content encoding.");
1023
0
        }
1024
0
    }
1025
1026
    ResponseStream ResponseWriter::stream(Code code, size_t streamSize)
1027
0
    {
1028
0
        response_.code_ = code;
1029
1030
0
        return ResponseStream(std::move(response_), peer_, transport_,
1031
0
                              std::move(timeout_), streamSize, buf_.maxSize());
1032
0
    }
1033
1034
0
    const CookieJar& ResponseWriter::cookies() const { return response_.cookies(); }
1035
1036
0
    CookieJar& ResponseWriter::cookies() { return response_.cookies(); }
1037
1038
    const Header::Collection& ResponseWriter::headers() const
1039
0
    {
1040
0
        return response_.headers();
1041
0
    }
1042
1043
0
    Header::Collection& ResponseWriter::headers() { return response_.headers(); }
1044
1045
0
    Timeout& ResponseWriter::timeout() { return timeout_; }
1046
1047
    std::shared_ptr<Tcp::Peer> ResponseWriter::peer() const
1048
0
    {
1049
0
        if (peer_.expired())
1050
0
        {
1051
0
            throw std::runtime_error("Write failed: Broken pipe");
1052
0
        }
1053
1054
0
        return peer_.lock();
1055
0
    }
1056
1057
0
    DynamicStreamBuf* ResponseWriter::rdbuf() { return &buf_; }
1058
1059
    DynamicStreamBuf* ResponseWriter::rdbuf(DynamicStreamBuf* /*other*/)
1060
0
    {
1061
0
        throw std::domain_error("Unimplemented");
1062
0
    }
1063
1064
0
    ResponseWriter ResponseWriter::clone() const { return ResponseWriter(*this); }
1065
1066
    Async::Promise<PST_SSIZE_T> ResponseWriter::putOnWire(const char* data,
1067
                                                          size_t len)
1068
0
    {
1069
0
        try
1070
0
        {
1071
0
            std::ostream os(&buf_);
1072
1073
0
#define PST_OUT(...)                                      \
1074
0
    do                                                    \
1075
0
    {                                                     \
1076
0
        __VA_ARGS__;                                      \
1077
0
        if (!os)                                          \
1078
0
        {                                                 \
1079
0
            return Async::Promise<PST_SSIZE_T>::rejected( \
1080
0
                Error("Response exceeded buffer size"));  \
1081
0
        }                                                 \
1082
0
    } while (0);
1083
1084
0
            PST_OUT(writeStatusLine(response_.version(), response_.code(), buf_));
1085
0
            PST_OUT(writeHeaders(response_.headers(), buf_));
1086
0
            PST_OUT(writeCookies(response_.cookies(), buf_));
1087
1088
            /* @Todo @Major:
1089
             * Correctly handle non-keep alive requests
1090
             * Do not put Keep-Alive if version == Http::11 and request.keepAlive ==
1091
             * true
1092
             */
1093
            // PST_OUT(writeHeader<Header::Connection>(os, ConnectionControl::KeepAlive));
1094
0
            PST_OUT(writeHeader<Header::ContentLength>(os, len));
1095
1096
0
            PST_OUT(os << crlf);
1097
1098
0
            if (len > 0)
1099
0
            {
1100
0
                PST_OUT(os.write(data, len));
1101
0
            }
1102
1103
0
            auto buffer = buf_.buffer();
1104
0
            sent_bytes_ += buffer.size();
1105
1106
0
            timeout_.disarm();
1107
1108
0
#undef PST_OUT
1109
1110
0
            auto fd = peer()->fd();
1111
1112
0
            return transport_->asyncWrite(fd, buffer)
1113
0
                .then<std::function<Async::Promise<PST_SSIZE_T>(PST_SSIZE_T)>,
1114
0
                      std::function<void(std::exception_ptr&)>>(
1115
0
                    [](PST_SSIZE_T data) {
1116
0
                        return Async::Promise<PST_SSIZE_T>::resolved(data);
1117
0
                    },
1118
1119
0
                    [](std::exception_ptr& eptr) {
1120
0
                        return Async::Promise<PST_SSIZE_T>::rejected(eptr);
1121
0
                    });
1122
0
        }
1123
0
        catch (const std::runtime_error& e)
1124
0
        {
1125
0
            return Async::Promise<PST_SSIZE_T>::rejected(e);
1126
0
        }
1127
0
    }
1128
1129
    // Compress using the requested content encoding, if supported, before
1130
    //  sending bits to client. User responsible for setting Content-Encoding
1131
    //  header...
1132
    void ResponseWriter::setCompression(const Pistache::Http::Header::Encoding _contentEncoding)
1133
0
    {
1134
0
        switch (_contentEncoding)
1135
0
        {
1136
1137
#ifdef PISTACHE_USE_CONTENT_ENCODING_BROTLI
1138
        // Application requested Brotli compression...
1139
        case Http::Header::Encoding::Br:
1140
            contentEncoding_ = Http::Header::Encoding::Br;
1141
            break;
1142
#endif
1143
1144
#ifdef PISTACHE_USE_CONTENT_ENCODING_ZSTD
1145
        case Http::Header::Encoding::Zstd:
1146
            contentEncoding_ = Http::Header::Encoding::Zstd;
1147
            break;
1148
#endif
1149
1150
#ifdef PISTACHE_USE_CONTENT_ENCODING_DEFLATE
1151
        // Application requested deflate compression...
1152
        case Http::Header::Encoding::Deflate:
1153
            contentEncoding_ = Http::Header::Encoding::Deflate;
1154
            break;
1155
#endif
1156
1157
        // Application requested identity encoding which means no compression...
1158
0
        case Http::Header::Encoding::Identity:
1159
0
            contentEncoding_ = Http::Header::Encoding::Identity;
1160
0
            break;
1161
1162
        // Any other type is not supported...
1163
0
        default:
1164
0
            throw std::runtime_error("Unsupported content encoding compression requested.");
1165
0
        }
1166
0
    }
1167
1168
    Async::Promise<PST_SSIZE_T> serveFile(ResponseWriter& writer,
1169
                                          const std::string& fileName,
1170
                                          const Mime::MediaType& contentType)
1171
0
    {
1172
0
        struct stat sb;
1173
1174
0
        int fd = PST_FILE_OPEN(fileName.c_str(), PST_O_RDONLY);
1175
0
        if (fd == -1)
1176
0
        {
1177
0
            PST_DECL_SE_ERR_P_EXTRA;
1178
0
            std::string str_error(PST_STRERROR_R_ERRNO);
1179
0
            if (errno == ENOENT)
1180
0
            {
1181
0
                throw HttpError(Http::Code::Not_Found, std::move(str_error));
1182
0
            }
1183
            // eles if TODO
1184
            /* @Improvement: maybe could we check for errno here and emit a different
1185
       error message
1186
    */
1187
0
            else
1188
0
            {
1189
0
                throw HttpError(Http::Code::Internal_Server_Error, std::move(str_error));
1190
0
            }
1191
0
        }
1192
1193
0
        int res = ::fstat(fd, &sb);
1194
1195
0
        PST_FILE_CLOSE(fd); // Done with fd, close before error can be thrown
1196
0
        if (res == -1)
1197
0
        {
1198
0
            throw HttpError(Code::Internal_Server_Error, "");
1199
0
        }
1200
1201
0
        auto* buf = writer.rdbuf();
1202
1203
0
        std::ostream os(buf);
1204
1205
0
#define PST_OUT(...)                                      \
1206
0
    do                                                    \
1207
0
    {                                                     \
1208
0
        __VA_ARGS__;                                      \
1209
0
        if (!os)                                          \
1210
0
        {                                                 \
1211
0
            return Async::Promise<PST_SSIZE_T>::rejected( \
1212
0
                Error("Response exceeded buffer size"));  \
1213
0
        }                                                 \
1214
0
    } while (0);
1215
1216
0
        auto setContentType = [&](const Mime::MediaType& contentType) {
1217
0
            auto& headers = writer.headers();
1218
0
            auto ct       = headers.tryGet<Header::ContentType>();
1219
0
            if (ct)
1220
0
                ct->setMime(contentType);
1221
0
            else
1222
0
                headers.add<Header::ContentType>(contentType);
1223
0
        };
1224
1225
0
        PST_OUT(writeStatusLine(writer.response_.version(), Http::Code::Ok, *buf));
1226
0
        if (contentType.isValid())
1227
0
        {
1228
0
            setContentType(contentType);
1229
0
        }
1230
0
        else
1231
0
        {
1232
0
            auto mime = Mime::MediaType::fromFile(fileName.c_str());
1233
0
            if (mime.isValid())
1234
0
                setContentType(mime);
1235
0
        }
1236
1237
0
        PST_OUT(writeHeaders(writer.headers(), *buf));
1238
1239
0
        const size_t len = static_cast<size_t>(sb.st_size);
1240
1241
0
        PST_OUT(writeHeader<Header::ContentLength>(os, len));
1242
1243
0
        PST_OUT(os << crlf);
1244
1245
0
        auto* transport = writer.transport_;
1246
0
        auto peer       = writer.peer();
1247
0
        auto sockFd     = peer->fd(); // may be PS_FD_EMPTY
1248
1249
0
        auto buffer = buf->buffer();
1250
0
        return transport->asyncWrite(sockFd, buffer,
1251
#ifdef _USE_LIBEVENT_LIKE_APPLE
1252
                                     0, // MSG_MORE unsupported in macos sendmsg
1253
                                        // Instead, we set TCP_NOPUSH via
1254
                                        // setsockopt (see "man tcp").
1255
                                     true // use msg_more_style
1256
#else
1257
0
                                     MSG_MORE
1258
0
#endif
1259
0
                                     )
1260
0
            .then(
1261
0
                [=](PST_SSIZE_T) {
1262
0
                    return transport->asyncWrite(sockFd, FileBuffer(fileName));
1263
0
                },
1264
0
                Async::Throw);
1265
1266
0
#undef PST_OUT
1267
0
    }
1268
1269
    Private::ParserImpl<Http::Request>::ParserImpl(size_t maxDataSize)
1270
2.27k
        : ParserBase(maxDataSize)
1271
2.27k
        , request()
1272
2.27k
        , time_(std::chrono::steady_clock::now())
1273
2.27k
    {
1274
2.27k
        allSteps[0] = std::make_unique<RequestLineStep>(&request);
1275
2.27k
        allSteps[1] = std::make_unique<HeadersStep>(&request);
1276
2.27k
        allSteps[2] = std::make_unique<BodyStep>(&request);
1277
2.27k
    }
1278
1279
    void Private::ParserImpl<Http::Request>::reset()
1280
0
    {
1281
0
        ParserBase::reset();
1282
1283
0
        request = Request();
1284
0
        time_   = std::chrono::steady_clock::now();
1285
0
    }
1286
1287
    Private::ParserImpl<Http::Response>::ParserImpl(size_t maxDataSize)
1288
279
        : ParserBase(maxDataSize)
1289
279
        , response()
1290
279
    {
1291
279
        allSteps[0] = std::make_unique<ResponseLineStep>(&response);
1292
279
        allSteps[1] = std::make_unique<HeadersStep>(&response);
1293
279
        allSteps[2] = std::make_unique<BodyStep>(&response);
1294
279
    }
1295
1296
    void Handler::onInput(const char* buffer, size_t len,
1297
                          const std::shared_ptr<Tcp::Peer>& peer)
1298
0
    {
1299
0
        PS_TIMEDBG_START_ARGS("input len %u", len);
1300
1301
0
        auto parser   = getParser(peer);
1302
0
        auto& request = parser->request;
1303
0
        try
1304
0
        {
1305
0
            if (!parser->feed(buffer, len))
1306
0
            {
1307
0
                PS_LOG_DEBUG("parser returned false");
1308
1309
0
                parser->reset();
1310
0
                throw HttpError(Code::Request_Entity_Too_Large,
1311
0
                                "Request exceeded maximum buffer size");
1312
0
            }
1313
1314
0
            auto state = parser->parse();
1315
1316
0
            if (state == Private::State::Done)
1317
0
            {
1318
0
                PS_LOG_DEBUG("Creating response");
1319
1320
0
                ResponseWriter response(request.version(), transport(), this, peer);
1321
1322
#ifdef LIBSTDCPP_SMARTPTR_LOCK_FIXME
1323
                request.associatePeer(peer);
1324
#endif
1325
1326
0
                request.copyAddress(peer->address());
1327
1328
0
                auto connection = request.headers().tryGet<Header::Connection>();
1329
1330
0
                if (connection)
1331
0
                {
1332
0
                    PS_LOG_DEBUG("Response connection control");
1333
1334
0
                    response.headers().add<Header::Connection>(connection->control());
1335
0
                }
1336
0
                else
1337
0
                {
1338
0
                    PS_LOG_DEBUG("Response connection close");
1339
1340
0
                    response.headers().add<Header::Connection>(ConnectionControl::Close);
1341
0
                }
1342
1343
0
                PS_LOG_DEBUG("Calling peer->setIdle");
1344
0
                peer->setIdle(false); // change peer state to not idle
1345
1346
0
                PS_LOG_DEBUG("Calling onRequest");
1347
0
                onRequest(request, std::move(response));
1348
1349
0
                PS_LOG_DEBUG("Calling parser->reset");
1350
0
                parser->reset();
1351
0
            }
1352
0
        }
1353
0
        catch (const HttpError& err)
1354
0
        {
1355
0
            PS_LOG_DEBUG("HTTP Error");
1356
1357
0
            ResponseWriter response(request.version(), transport(), this, peer);
1358
0
            response.send(static_cast<Code>(err.code()), err.reason());
1359
0
            parser->reset();
1360
0
        }
1361
1362
0
        catch (const std::exception& e)
1363
0
        {
1364
0
            PS_LOG_DEBUG("HTTP exception");
1365
1366
0
            ResponseWriter response(request.version(), transport(), this, peer);
1367
0
            response.send(Code::Internal_Server_Error, e.what());
1368
0
            parser->reset();
1369
0
        }
1370
0
    }
1371
1372
    void Handler::onConnection(const std::shared_ptr<Tcp::Peer>& peer)
1373
0
    {
1374
0
        peer->putData(ParserData, std::make_shared<RequestParser>(maxRequestSize_));
1375
0
    }
1376
1377
    void Handler::onTimeout(const Request& /*request*/,
1378
                            ResponseWriter response)
1379
0
    {
1380
0
        response.send(Code::Request_Timeout);
1381
0
    }
1382
1383
0
    Timeout::~Timeout() { disarm(); }
1384
1385
    void Timeout::disarm()
1386
0
    {
1387
0
        if (transport && armed)
1388
0
        {
1389
0
            transport->disarmTimer(timerFd);
1390
0
        }
1391
0
    }
1392
1393
0
    bool Timeout::isArmed() const { return armed; }
1394
1395
    Timeout::Timeout(Tcp::Transport* transport_, Http::Version version, Handler* handler_,
1396
                     std::weak_ptr<Tcp::Peer> peer_)
1397
0
        : handler(handler_)
1398
0
        , version(version)
1399
0
        , transport(transport_)
1400
0
        , armed(false)
1401
0
        , timerFd(PS_FD_EMPTY)
1402
0
        , peer(peer_)
1403
0
    { }
1404
1405
    void Timeout::onTimeout(uint64_t /*numWakeup*/)
1406
0
    {
1407
0
        auto sp = peer.lock();
1408
0
        if (!sp)
1409
0
            return;
1410
1411
0
        ResponseWriter response(version, transport, handler, peer);
1412
0
        auto parser         = Handler::getParser(sp);
1413
0
        const auto& request = parser->request;
1414
0
        handler->onTimeout(request, std::move(response));
1415
0
    }
1416
1417
0
    void Handler::setMaxRequestSize(size_t value) { maxRequestSize_ = value; }
1418
1419
0
    size_t Handler::getMaxRequestSize() const { return maxRequestSize_; }
1420
1421
0
    void Handler::setMaxResponseSize(size_t value) { maxResponseSize_ = value; }
1422
1423
0
    size_t Handler::getMaxResponseSize() const { return maxResponseSize_; }
1424
1425
    std::shared_ptr<RequestParser>
1426
    Handler::getParser(const std::shared_ptr<Tcp::Peer>& peer)
1427
0
    {
1428
0
        return std::static_pointer_cast<RequestParser>(peer->getData(ParserData));
1429
0
    }
1430
1431
} // namespace Pistache::Http