-
Notifications
You must be signed in to change notification settings - Fork 4.8k
/
parser_impl.cc
226 lines (189 loc) · 7.19 KB
/
parser_impl.cc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
#include "common/http/http1/parser_impl.h"
#include <llhttp.h>
#include <iostream>
#include "common/common/assert.h"
#include "common/http/http1/parser.h"
namespace Envoy {
namespace Http {
namespace Http1 {
namespace {
ParserStatus intToStatus(int rc) {
// See
// https://github.com/nodejs/http-parser/blob/5c5b3ac62662736de9e71640a8dc16da45b32503/http_parser.h#L72.
switch (rc) {
case -1:
return ParserStatus::Error;
case 0:
return ParserStatus::Success;
case 1:
return ParserStatus::NoBody;
case 2:
return ParserStatus::NoBodyData;
case 21:
return ParserStatus::Paused;
default:
return ParserStatus::Unknown;
}
}
} // namespace
class HttpParserImpl::Impl {
public:
Impl(llhttp_type_t type) {
llhttp_init(&parser_, type, &settings_);
llhttp_set_lenient_chunked_length(&parser_, 1);
llhttp_set_lenient_headers(&parser_, 1);
}
Impl(llhttp_type_t type, void* data) : Impl(type) {
parser_.data = data;
settings_ = {
[](llhttp_t* parser) -> int {
auto* conn_impl = static_cast<ParserCallbacks*>(parser->data);
auto status = conn_impl->onMessageBegin();
return conn_impl->setAndCheckCallbackStatus(std::move(status));
},
[](llhttp_t* parser, const char* at, size_t length) -> int {
auto* conn_impl = static_cast<ParserCallbacks*>(parser->data);
auto status = conn_impl->onUrl(at, length);
return conn_impl->setAndCheckCallbackStatus(std::move(status));
},
nullptr, // on_status
[](llhttp_t* parser, const char* at, size_t length) -> int {
auto* conn_impl = static_cast<ParserCallbacks*>(parser->data);
auto status = conn_impl->onHeaderField(at, length);
return conn_impl->setAndCheckCallbackStatus(std::move(status));
},
[](llhttp_t* parser, const char* at, size_t length) -> int {
auto* conn_impl = static_cast<ParserCallbacks*>(parser->data);
auto status = conn_impl->onHeaderValue(at, length);
return conn_impl->setAndCheckCallbackStatus(std::move(status));
},
[](llhttp_t* parser) -> int {
auto* conn_impl = static_cast<ParserCallbacks*>(parser->data);
auto statusor = conn_impl->onHeadersComplete();
return conn_impl->setAndCheckCallbackStatusOr(std::move(statusor));
},
[](llhttp_t* parser, const char* at, size_t length) -> int {
static_cast<ParserCallbacks*>(parser->data)->bufferBody(at, length);
return 0;
},
[](llhttp_t* parser) -> int {
auto* conn_impl = static_cast<ParserCallbacks*>(parser->data);
auto status = conn_impl->onMessageComplete();
return conn_impl->setAndCheckCallbackStatusOr(std::move(status));
},
[](llhttp_t* parser) -> int {
// A 0-byte chunk header is used to signal the end of the chunked body.
// When this function is called, http-parser holds the size of the chunk in
// parser->content_length. See
// https://github.com/nodejs/http-parser/blob/v2.9.3/http_parser.h#L336
const bool is_final_chunk = (parser->content_length == 0);
static_cast<ParserCallbacks*>(parser->data)->onChunkHeader(is_final_chunk);
return 0;
},
nullptr, // on_chunk_complete
nullptr, // on_url_complete
nullptr, // on_status_complete
nullptr, // on_header_field_complete
nullptr // on_header_value_complete
};
}
RcVal execute(const char* slice, int len) {
llhttp_errno_t error;
if (slice == nullptr || len == 0) {
error = llhttp_finish(&parser_);
} else {
error = llhttp_execute(&parser_, slice, len);
}
size_t nread = len;
// Adjust number of bytes read in case of error.
if (error != HPE_OK) {
nread = llhttp_get_error_pos(&parser_) - slice;
// Resume after upgrade.
if (error == HPE_PAUSED_UPGRADE) {
error = HPE_OK;
llhttp_resume_after_upgrade(&parser_);
}
}
return {nread, error};
}
void resume() { llhttp_resume(&parser_); }
ParserStatus pause() {
// llhttp can only pause by returning a paused status in user callbacks.
return ParserStatus::Paused;
}
int getErrno() { return llhttp_get_errno(&parser_); }
uint16_t statusCode() const { return parser_.status_code; }
int httpMajor() const { return parser_.http_major; }
int httpMinor() const { return parser_.http_minor; }
absl::optional<uint64_t> contentLength() const {
if (has_content_length_) {
return parser_.content_length;
}
return absl::nullopt;
}
void hasContentLength(bool val) { has_content_length_ = val; }
bool isChunked() const { return parser_.flags & F_CHUNKED; }
absl::string_view methodName() const {
return llhttp_method_name(static_cast<llhttp_method>(parser_.method));
}
int hasTransferEncoding() const { return parser_.flags & F_TRANSFER_ENCODING; }
private:
llhttp_t parser_;
llhttp_settings_s settings_;
bool has_content_length_{true};
};
HttpParserImpl::HttpParserImpl(MessageType type, ParserCallbacks* data) {
llhttp_type_t parser_type;
switch (type) {
case MessageType::Request:
parser_type = HTTP_REQUEST;
break;
case MessageType::Response:
parser_type = HTTP_RESPONSE;
break;
default:
NOT_IMPLEMENTED_GCOVR_EXCL_LINE;
}
impl_ = std::make_unique<Impl>(parser_type, data);
}
// Because we have a pointer-to-impl using std::unique_ptr, we must place the destructor in the
// same compilation unit so that the destructor has a complete definition of Impl.
HttpParserImpl::~HttpParserImpl() = default;
HttpParserImpl::RcVal HttpParserImpl::execute(const char* slice, int len) {
return impl_->execute(slice, len);
}
void HttpParserImpl::resume() { impl_->resume(); }
ParserStatus HttpParserImpl::pause() { return impl_->pause(); }
ParserStatus HttpParserImpl::getStatus() { return intToStatus(impl_->getErrno()); }
uint16_t HttpParserImpl::statusCode() const { return impl_->statusCode(); }
int HttpParserImpl::httpMajor() const { return impl_->httpMajor(); }
int HttpParserImpl::httpMinor() const { return impl_->httpMinor(); }
absl::optional<uint64_t> HttpParserImpl::contentLength() const { return impl_->contentLength(); }
void HttpParserImpl::hasContentLength(bool val) { return impl_->hasContentLength(val); }
bool HttpParserImpl::isChunked() const { return impl_->isChunked(); }
absl::string_view HttpParserImpl::methodName() const { return impl_->methodName(); }
absl::string_view HttpParserImpl::errnoName(int rc) const {
return llhttp_errno_name(static_cast<llhttp_errno>(rc));
}
int HttpParserImpl::hasTransferEncoding() const { return impl_->hasTransferEncoding(); }
int HttpParserImpl::statusToInt(const ParserStatus code) const {
// See
// https://github.com/nodejs/node/blob/5c5d044de028956e4c165b1b4fc465947e44a748/deps/llhttp/include/llhttp.h#L245.
switch (code) {
case ParserStatus::Error:
return -1;
case ParserStatus::Success:
return 0;
case ParserStatus::NoBody:
return 1;
case ParserStatus::NoBodyData:
return 2;
case ParserStatus::Paused:
return 21;
default:
NOT_REACHED_GCOVR_EXCL_LINE;
}
}
} // namespace Http1
} // namespace Http
} // namespace Envoy