Coverage Report

Created: 2026-04-10 06:23

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/crow/include/crow/parser.h
Line
Count
Source
1
#pragma once
2
3
#include <string>
4
#include <unordered_map>
5
#include <algorithm>
6
7
#include "crow/http_request.h"
8
#include "crow/http_parser_merged.h"
9
10
namespace crow
11
{
12
    /// A wrapper for `nodejs/http-parser`.
13
14
    ///
15
    /// Used to generate a \ref crow.request from the TCP socket buffer.
16
    template<typename Handler>
17
    struct HTTPParser : public http_parser
18
    {
19
        static int on_message_begin(http_parser*)
20
0
        {
21
0
            return 0;
22
0
        }
Unexecuted instantiation: crow::HTTPParser<DummyHandler>::on_message_begin(crow::http_parser*)
Unexecuted instantiation: crow::HTTPParser<crow::Connection<crow::UnixSocketAdaptor, crow::Crow<>> >::on_message_begin(crow::http_parser*)
Unexecuted instantiation: crow::HTTPParser<crow::Connection<crow::SocketAdaptor, crow::Crow<>> >::on_message_begin(crow::http_parser*)
23
        static int on_method(http_parser* self_)
24
0
        {
25
0
            HTTPParser* self = static_cast<HTTPParser*>(self_);
26
0
            self->req.method = static_cast<HTTPMethod>(self->method);
27
28
0
            return 0;
29
0
        }
Unexecuted instantiation: crow::HTTPParser<DummyHandler>::on_method(crow::http_parser*)
Unexecuted instantiation: crow::HTTPParser<crow::Connection<crow::UnixSocketAdaptor, crow::Crow<>> >::on_method(crow::http_parser*)
Unexecuted instantiation: crow::HTTPParser<crow::Connection<crow::SocketAdaptor, crow::Crow<>> >::on_method(crow::http_parser*)
30
        static int on_url(http_parser* self_, const char* at, size_t length)
31
0
        {
32
0
            HTTPParser* self = static_cast<HTTPParser*>(self_);
33
0
            self->req.raw_url.insert(self->req.raw_url.end(), at, at + length);
34
0
            self->req.url_params = query_string(self->req.raw_url);
35
0
            self->req.url = self->req.raw_url.substr(0, self->qs_point != 0 ? self->qs_point : std::string::npos);
36
37
0
            self->process_url();
38
39
0
            return 0;
40
0
        }
Unexecuted instantiation: crow::HTTPParser<DummyHandler>::on_url(crow::http_parser*, char const*, unsigned long)
Unexecuted instantiation: crow::HTTPParser<crow::Connection<crow::UnixSocketAdaptor, crow::Crow<>> >::on_url(crow::http_parser*, char const*, unsigned long)
Unexecuted instantiation: crow::HTTPParser<crow::Connection<crow::SocketAdaptor, crow::Crow<>> >::on_url(crow::http_parser*, char const*, unsigned long)
41
        static int on_header_field(http_parser* self_, const char* at, size_t length)
42
0
        {
43
0
            HTTPParser* self = static_cast<HTTPParser*>(self_);
44
0
            switch (self->header_building_state)
45
0
            {
46
0
                case 0:
47
0
                    if (!self->header_value.empty())
48
0
                    {
49
0
                        self->req.headers.emplace(std::move(self->header_field), std::move(self->header_value));
50
0
                    }
51
0
                    self->header_field.assign(at, at + length);
52
0
                    self->header_building_state = 1;
53
0
                    break;
54
0
                case 1:
55
0
                    self->header_field.insert(self->header_field.end(), at, at + length);
56
0
                    break;
57
0
            }
58
0
            return 0;
59
0
        }
Unexecuted instantiation: crow::HTTPParser<DummyHandler>::on_header_field(crow::http_parser*, char const*, unsigned long)
Unexecuted instantiation: crow::HTTPParser<crow::Connection<crow::UnixSocketAdaptor, crow::Crow<>> >::on_header_field(crow::http_parser*, char const*, unsigned long)
Unexecuted instantiation: crow::HTTPParser<crow::Connection<crow::SocketAdaptor, crow::Crow<>> >::on_header_field(crow::http_parser*, char const*, unsigned long)
60
        static int on_header_value(http_parser* self_, const char* at, size_t length)
61
0
        {
62
0
            HTTPParser* self = static_cast<HTTPParser*>(self_);
63
0
            switch (self->header_building_state)
64
0
            {
65
0
                case 0:
66
0
                    self->header_value.insert(self->header_value.end(), at, at + length);
67
0
                    break;
68
0
                case 1:
69
0
                    self->header_building_state = 0;
70
0
                    self->header_value.assign(at, at + length);
71
0
                    break;
72
0
            }
73
0
            return 0;
74
0
        }
Unexecuted instantiation: crow::HTTPParser<DummyHandler>::on_header_value(crow::http_parser*, char const*, unsigned long)
Unexecuted instantiation: crow::HTTPParser<crow::Connection<crow::UnixSocketAdaptor, crow::Crow<>> >::on_header_value(crow::http_parser*, char const*, unsigned long)
Unexecuted instantiation: crow::HTTPParser<crow::Connection<crow::SocketAdaptor, crow::Crow<>> >::on_header_value(crow::http_parser*, char const*, unsigned long)
75
        static int on_headers_complete(http_parser* self_)
76
0
        {
77
0
            HTTPParser* self = static_cast<HTTPParser*>(self_);
78
0
            if (!self->header_field.empty())
79
0
            {
80
0
                self->req.headers.emplace(std::move(self->header_field), std::move(self->header_value));
81
0
            }
82
83
0
            self->set_connection_parameters();
84
85
0
            self->process_header();
86
0
            return 0;
87
0
        }
Unexecuted instantiation: crow::HTTPParser<DummyHandler>::on_headers_complete(crow::http_parser*)
Unexecuted instantiation: crow::HTTPParser<crow::Connection<crow::UnixSocketAdaptor, crow::Crow<>> >::on_headers_complete(crow::http_parser*)
Unexecuted instantiation: crow::HTTPParser<crow::Connection<crow::SocketAdaptor, crow::Crow<>> >::on_headers_complete(crow::http_parser*)
88
        static int on_body(http_parser* self_, const char* at, size_t length)
89
0
        {
90
0
            HTTPParser* self = static_cast<HTTPParser*>(self_);
91
0
            self->req.body.insert(self->req.body.end(), at, at + length);
92
0
            return 0;
93
0
        }
Unexecuted instantiation: crow::HTTPParser<DummyHandler>::on_body(crow::http_parser*, char const*, unsigned long)
Unexecuted instantiation: crow::HTTPParser<crow::Connection<crow::UnixSocketAdaptor, crow::Crow<>> >::on_body(crow::http_parser*, char const*, unsigned long)
Unexecuted instantiation: crow::HTTPParser<crow::Connection<crow::SocketAdaptor, crow::Crow<>> >::on_body(crow::http_parser*, char const*, unsigned long)
94
        static int on_message_complete(http_parser* self_)
95
0
        {
96
0
            HTTPParser* self = static_cast<HTTPParser*>(self_);
97
98
0
            self->message_complete = true;
99
0
            self->process_message();
100
0
            return 0;
101
0
        }
Unexecuted instantiation: crow::HTTPParser<DummyHandler>::on_message_complete(crow::http_parser*)
Unexecuted instantiation: crow::HTTPParser<crow::Connection<crow::UnixSocketAdaptor, crow::Crow<>> >::on_message_complete(crow::http_parser*)
Unexecuted instantiation: crow::HTTPParser<crow::Connection<crow::SocketAdaptor, crow::Crow<>> >::on_message_complete(crow::http_parser*)
102
        HTTPParser(Handler* handler):
103
0
          http_parser(),
104
0
          handler_(handler)
105
0
        {
106
0
            http_parser_init(this);
107
0
        }
Unexecuted instantiation: crow::HTTPParser<DummyHandler>::HTTPParser(DummyHandler*)
Unexecuted instantiation: crow::HTTPParser<crow::Connection<crow::UnixSocketAdaptor, crow::Crow<>> >::HTTPParser(crow::Connection<crow::UnixSocketAdaptor, crow::Crow<>>*)
Unexecuted instantiation: crow::HTTPParser<crow::Connection<crow::SocketAdaptor, crow::Crow<>> >::HTTPParser(crow::Connection<crow::SocketAdaptor, crow::Crow<>>*)
108
109
        // return false on error
110
        /// Parse a buffer into the different sections of an HTTP request.
111
        bool feed(const char* buffer, int length)
112
0
        {
113
0
            if (message_complete)
114
0
                return true;
115
116
0
            const static http_parser_settings settings_{
117
0
              on_message_begin,
118
0
              on_method,
119
0
              on_url,
120
0
              on_header_field,
121
0
              on_header_value,
122
0
              on_headers_complete,
123
0
              on_body,
124
0
              on_message_complete,
125
0
            };
126
127
0
            int nparsed = http_parser_execute(this, &settings_, buffer, length);
128
0
            if (http_errno != CHPE_OK)
129
0
            {
130
0
                return false;
131
0
            }
132
0
            return nparsed == length;
133
0
        }
Unexecuted instantiation: crow::HTTPParser<DummyHandler>::feed(char const*, int)
Unexecuted instantiation: crow::HTTPParser<crow::Connection<crow::UnixSocketAdaptor, crow::Crow<>> >::feed(char const*, int)
Unexecuted instantiation: crow::HTTPParser<crow::Connection<crow::SocketAdaptor, crow::Crow<>> >::feed(char const*, int)
134
135
        bool done()
136
0
        {
137
0
            return feed(nullptr, 0);
138
0
        }
Unexecuted instantiation: crow::HTTPParser<DummyHandler>::done()
Unexecuted instantiation: crow::HTTPParser<crow::Connection<crow::UnixSocketAdaptor, crow::Crow<>> >::done()
Unexecuted instantiation: crow::HTTPParser<crow::Connection<crow::SocketAdaptor, crow::Crow<>> >::done()
139
140
        void clear()
141
0
        {
142
0
            req = crow::request();
143
0
            header_field.clear();
144
0
            header_value.clear();
145
0
            header_building_state = 0;
146
0
            qs_point = 0;
147
0
            message_complete = false;
148
0
            state = CROW_NEW_MESSAGE();
149
0
        }
Unexecuted instantiation: crow::HTTPParser<crow::Connection<crow::UnixSocketAdaptor, crow::Crow<>> >::clear()
Unexecuted instantiation: crow::HTTPParser<crow::Connection<crow::SocketAdaptor, crow::Crow<>> >::clear()
150
151
        inline void process_url()
152
0
        {
153
0
            handler_->handle_url();
154
0
        }
Unexecuted instantiation: crow::HTTPParser<DummyHandler>::process_url()
Unexecuted instantiation: crow::HTTPParser<crow::Connection<crow::UnixSocketAdaptor, crow::Crow<>> >::process_url()
Unexecuted instantiation: crow::HTTPParser<crow::Connection<crow::SocketAdaptor, crow::Crow<>> >::process_url()
155
156
        inline void process_header()
157
0
        {
158
0
            handler_->handle_header();
159
0
        }
Unexecuted instantiation: crow::HTTPParser<DummyHandler>::process_header()
Unexecuted instantiation: crow::HTTPParser<crow::Connection<crow::UnixSocketAdaptor, crow::Crow<>> >::process_header()
Unexecuted instantiation: crow::HTTPParser<crow::Connection<crow::SocketAdaptor, crow::Crow<>> >::process_header()
160
161
        inline void process_message()
162
0
        {
163
0
            handler_->handle();
164
0
        }
Unexecuted instantiation: crow::HTTPParser<DummyHandler>::process_message()
Unexecuted instantiation: crow::HTTPParser<crow::Connection<crow::UnixSocketAdaptor, crow::Crow<>> >::process_message()
Unexecuted instantiation: crow::HTTPParser<crow::Connection<crow::SocketAdaptor, crow::Crow<>> >::process_message()
165
166
        inline void set_connection_parameters()
167
0
        {
168
0
            req.http_ver_major = http_major;
169
0
            req.http_ver_minor = http_minor;
170
171
            //NOTE(EDev): it seems that the problem is with crow's policy on closing the connection for HTTP_VERSION < 1.0, the behaviour for that in crow is "don't close the connection, but don't send a keep-alive either"
172
173
            // HTTP1.1 = always send keep_alive, HTTP1.0 = only send if header exists, HTTP?.? = never send
174
0
            req.keep_alive = (http_major == 1 && http_minor == 0) ?
175
0
                               ((flags & F_CONNECTION_KEEP_ALIVE) ? true : false) :
176
0
                               ((http_major == 1 && http_minor == 1) ? true : false);
177
178
            // HTTP1.1 = only close if close header exists, HTTP1.0 = always close unless keep_alive header exists, HTTP?.?= never close
179
0
            req.close_connection = (http_major == 1 && http_minor == 0) ?
180
0
                                     ((flags & F_CONNECTION_KEEP_ALIVE) ? false : true) :
181
0
                                     ((http_major == 1 && http_minor == 1) ? ((flags & F_CONNECTION_CLOSE) ? true : false) : false);
182
0
            req.upgrade = static_cast<bool>(upgrade);
183
0
        }
Unexecuted instantiation: crow::HTTPParser<DummyHandler>::set_connection_parameters()
Unexecuted instantiation: crow::HTTPParser<crow::Connection<crow::UnixSocketAdaptor, crow::Crow<>> >::set_connection_parameters()
Unexecuted instantiation: crow::HTTPParser<crow::Connection<crow::SocketAdaptor, crow::Crow<>> >::set_connection_parameters()
184
185
        /// The final request that this parser outputs.
186
        ///
187
        /// Data parsed is put directly into this object as soon as the related callback returns. (e.g. the request will have the cooorect method as soon as on_method() returns)
188
        request req;
189
190
    private:
191
        int header_building_state = 0;
192
        bool message_complete = false;
193
        std::string header_field;
194
        std::string header_value;
195
196
        Handler* handler_; ///< This is currently an HTTP connection object (\ref crow.Connection).
197
    };
198
} // namespace crow
199
200
#undef CROW_NEW_MESSAGE
201
#undef CROW_start_state