• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

CrowCpp / Crow / 676

11 Jun 2025 12:15PM UTC coverage: 87.636% (+0.04%) from 87.595%
676

push

gh-actions

gittiver
Fix "load balancing" not working

Before this commit, the load balancing does not work.
The foundation is there, but in practice, the next connection is always picked.
For example, if there are 3 contexts available, it will always go: 0, 1, 2, 0, 1, 2, 0, 1, 2, etc.
If 0 is a long running connection, and 1 and 2 are short, it will pick 0 for the next connection and then block waiting for that long running connection to end even though 1 and 2 are free.
With this fix, the above example would result in this sequence: 0, 1, 2, 1, 2, 1, 2, 1, 2 (until 0 is done with its long running connection).

7 of 7 new or added lines in 2 files covered. (100.0%)

3 existing lines in 1 file now uncovered.

4026 of 4594 relevant lines covered (87.64%)

811.02 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

96.84
/include/crow/parser.h
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*)
504✔
20
        {
21
            return 0;
504✔
22
        }
23
        static int on_method(http_parser* self_)
1,596✔
24
        {
25
            HTTPParser* self = static_cast<HTTPParser*>(self_);
1,596✔
26
            self->req.method = static_cast<HTTPMethod>(self->method);
1,596✔
27

28
            return 0;
1,596✔
29
        }
30
        static int on_url(http_parser* self_, const char* at, size_t length)
492✔
31
        {
32
            HTTPParser* self = static_cast<HTTPParser*>(self_);
492✔
33
            self->req.raw_url.insert(self->req.raw_url.end(), at, at + length);
492✔
34
            self->req.url_params = query_string(self->req.raw_url);
492✔
35
            self->req.url = self->req.raw_url.substr(0, self->qs_point != 0 ? self->qs_point : std::string::npos);
492✔
36

37
            self->process_url();
492✔
38

39
            return 0;
486✔
40
        }
41
        static int on_header_field(http_parser* self_, const char* at, size_t length)
618✔
42
        {
43
            HTTPParser* self = static_cast<HTTPParser*>(self_);
618✔
44
            switch (self->header_building_state)
618✔
45
            {
46
                case 0:
618✔
47
                    if (!self->header_value.empty())
618✔
48
                    {
49
                        self->req.headers.emplace(std::move(self->header_field), std::move(self->header_value));
306✔
50
                    }
51
                    self->header_field.assign(at, at + length);
618✔
52
                    self->header_building_state = 1;
618✔
53
                    break;
618✔
UNCOV
54
                case 1:
×
UNCOV
55
                    self->header_field.insert(self->header_field.end(), at, at + length);
×
UNCOV
56
                    break;
×
57
            }
58
            return 0;
618✔
59
        }
60
        static int on_header_value(http_parser* self_, const char* at, size_t length)
624✔
61
        {
62
            HTTPParser* self = static_cast<HTTPParser*>(self_);
624✔
63
            switch (self->header_building_state)
624✔
64
            {
65
                case 0:
6✔
66
                    self->header_value.insert(self->header_value.end(), at, at + length);
6✔
67
                    break;
6✔
68
                case 1:
618✔
69
                    self->header_building_state = 0;
618✔
70
                    self->header_value.assign(at, at + length);
618✔
71
                    break;
618✔
72
            }
73
            return 0;
624✔
74
        }
75
        static int on_headers_complete(http_parser* self_)
468✔
76
        {
77
            HTTPParser* self = static_cast<HTTPParser*>(self_);
468✔
78
            if (!self->header_field.empty())
468✔
79
            {
80
                self->req.headers.emplace(std::move(self->header_field), std::move(self->header_value));
312✔
81
            }
82

83
            self->set_connection_parameters();
474✔
84

85
            self->process_header();
468✔
86
            return 0;
474✔
87
        }
88
        static int on_body(http_parser* self_, const char* at, size_t length)
18✔
89
        {
90
            HTTPParser* self = static_cast<HTTPParser*>(self_);
18✔
91
            self->req.body.insert(self->req.body.end(), at, at + length);
18✔
92
            return 0;
18✔
93
        }
94
        static int on_message_complete(http_parser* self_)
474✔
95
        {
96
            HTTPParser* self = static_cast<HTTPParser*>(self_);
474✔
97

98
            self->message_complete = true;
474✔
99
            self->process_message();
474✔
100
            return 0;
474✔
101
        }
102
        HTTPParser(Handler* handler):
594✔
103
          http_parser(),
104
          handler_(handler)
594✔
105
        {
106
            http_parser_init(this);
594✔
107
        }
594✔
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)
912✔
112
        {
113
            if (message_complete)
912✔
114
                return true;
54✔
115

116
            const static http_parser_settings settings_{
117
              on_message_begin,
118
              on_method,
119
              on_url,
120
              on_header_field,
121
              on_header_value,
122
              on_headers_complete,
123
              on_body,
124
              on_message_complete,
125
            };
126

127
            int nparsed = http_parser_execute(this, &settings_, buffer, length);
858✔
128
            if (http_errno != CHPE_OK)
858✔
129
            {
130
                return false;
78✔
131
            }
132
            return nparsed == length;
780✔
133
        }
134

135
        bool done()
390✔
136
        {
137
            return feed(nullptr, 0);
390✔
138
        }
139

140
        void clear()
1,302✔
141
        {
142
            req = crow::request();
1,302✔
143
            header_field.clear();
1,296✔
144
            header_value.clear();
1,296✔
145
            header_building_state = 0;
1,302✔
146
            qs_point = 0;
1,302✔
147
            message_complete = false;
1,302✔
148
            state = CROW_NEW_MESSAGE();
1,302✔
149
        }
1,302✔
150

151
        inline void process_url()
492✔
152
        {
153
            handler_->handle_url();
492✔
154
        }
492✔
155

156
        inline void process_header()
474✔
157
        {
158
            handler_->handle_header();
474✔
159
        }
474✔
160

161
        inline void process_message()
468✔
162
        {
163
            handler_->handle();
468✔
164
        }
474✔
165

166
        inline void set_connection_parameters()
474✔
167
        {
168
            req.http_ver_major = http_major;
474✔
169
            req.http_ver_minor = http_minor;
474✔
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
            req.keep_alive = (http_major == 1 && http_minor == 0) ?
918✔
175
                               ((flags & F_CONNECTION_KEEP_ALIVE) ? true : false) :
30✔
176
                               ((http_major == 1 && http_minor == 1) ? true : false);
444✔
177

178
            // HTTP1.1 = only close if close header exists, HTTP1.0 = always close unless keep_alive header exists, HTTP?.?= never close
179
            req.close_connection = (http_major == 1 && http_minor == 0) ?
918✔
180
                                     ((flags & F_CONNECTION_KEEP_ALIVE) ? false : true) :
30✔
181
                                     ((http_major == 1 && http_minor == 1) ? ((flags & F_CONNECTION_CLOSE) ? true : false) : false);
444✔
182
            req.upgrade = static_cast<bool>(upgrade);
474✔
183
        }
474✔
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
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc