fixed conflict

This commit is contained in:
Lucas Pardue 2014-11-23 21:10:51 +00:00
commit 6e178653a5
18 changed files with 179 additions and 141 deletions

1
contrib/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
nghttpx-init

View File

@ -436,51 +436,41 @@ static size_t count_encoded_length(size_t n, size_t prefix)
{ {
size_t k = (1 << prefix) - 1; size_t k = (1 << prefix) - 1;
size_t len = 0; size_t len = 0;
if(n >= k) {
n -= k; if(n < k) {
++len;
} else {
return 1; return 1;
} }
do {
++len; n -= k;
if(n >= 128) { ++len;
n >>= 7;
} else { for(; n >= 128; n >>= 7, ++len);
break;
} return len + 1;
} while(n);
return len;
} }
static size_t encode_length(uint8_t *buf, size_t n, size_t prefix) static size_t encode_length(uint8_t *buf, size_t n, size_t prefix)
{ {
size_t k = (1 << prefix) - 1; size_t k = (1 << prefix) - 1;
size_t len = 0; uint8_t *begin = buf;
*buf &= ~k; *buf &= ~k;
if(n < k) { if(n < k) {
*buf++ |= n; *buf |= n;
return 1; return 1;
} }
*buf++ |= k; *buf++ |= k;
n -= k; n -= k;
++len;
do { for(; n >= 128; n >>= 7) {
++len; *buf++ = (1 << 7) | (n & 0x7f);
if(n >= 128) { }
*buf++ = (1 << 7) | (n & 0x7f);
n >>= 7; *buf++ = (uint8_t)n;
} else {
*buf++ = (uint8_t)n; return (size_t)(buf - begin);
break;
}
} while(n);
return len;
} }
/* /*

View File

@ -462,33 +462,42 @@ void eventcb(bufferevent *bev, short events, void *ptr)
unsigned int next_proto_len; unsigned int next_proto_len;
SSL_get0_next_proto_negotiated(client->ssl, SSL_get0_next_proto_negotiated(client->ssl,
&next_proto, &next_proto_len); &next_proto, &next_proto_len);
for(int i = 0; i < 2; ++i) {
if(next_proto) {
if(util::check_h2_is_selected(next_proto, next_proto_len)) {
client->session = util::make_unique<Http2Session>(client);
} else {
#ifdef HAVE_SPDYLAY
auto spdy_version = spdylay_npn_get_version(next_proto,
next_proto_len);
if(spdy_version) {
client->session = util::make_unique<SpdySession>(client,
spdy_version);
} else {
debug_nextproto_error();
client->fail();
return;
}
#else // !HAVE_SPDYLAY
debug_nextproto_error();
client->fail();
return;
#endif // !HAVE_SPDYLAY
}
}
#if OPENSSL_VERSION_NUMBER >= 0x10002000L
SSL_get0_alpn_selected(client->ssl, &next_proto, &next_proto_len);
#else // OPENSSL_VERSION_NUMBER < 0x10002000L
break;
#endif // OPENSSL_VERSION_NUMBER < 0x10002000L
}
if(!next_proto) { if(!next_proto) {
debug_nextproto_error(); debug_nextproto_error();
client->fail(); client->fail();
return; return;
} }
if(util::check_h2_is_selected(next_proto, next_proto_len)) {
client->session = util::make_unique<Http2Session>(client);
} else {
#ifdef HAVE_SPDYLAY
auto spdy_version = spdylay_npn_get_version(next_proto,
next_proto_len);
if(spdy_version) {
client->session = util::make_unique<SpdySession>(client,
spdy_version);
} else {
debug_nextproto_error();
client->fail();
return;
}
#else // !HAVE_SPDYLAY
debug_nextproto_error();
client->fail();
return;
#endif // !HAVE_SPDYLAY
}
} else { } else {
switch(config.no_tls_proto) { switch(config.no_tls_proto) {
case Config::PROTO_HTTP2: case Config::PROTO_HTTP2:
@ -974,6 +983,16 @@ int main(int argc, char **argv)
SSL_CTX_set_next_proto_select_cb(ssl_ctx, SSL_CTX_set_next_proto_select_cb(ssl_ctx,
client_select_next_proto_cb, nullptr); client_select_next_proto_cb, nullptr);
#if OPENSSL_VERSION_NUMBER >= 0x10002000L
auto proto_list = util::get_default_alpn();
#ifdef HAVE_SPDYLAY
static const char spdy_proto_list[] = "\x8spdy/3.1\x6spdy/3\x6spdy/2";
std::copy(spdy_proto_list, spdy_proto_list + sizeof(spdy_proto_list) - 1,
std::back_inserter(proto_list));
#endif // HAVE_SPDYLAY
SSL_CTX_set_alpn_protos(ssl_ctx, proto_list.data(), proto_list.size());
#endif // OPENSSL_VERSION_NUMBER >= 0x10002000L
std::vector<std::string> reqlines; std::vector<std::string> reqlines;
if(config.ifile.empty()) { if(config.ifile.empty()) {

View File

@ -1057,6 +1057,7 @@ struct HttpClient {
auto& req_stat = req->stat; auto& req_stat = req->stat;
auto request_time = (i == 0) ? stat.started_system_time : auto request_time = (i == 0) ? stat.started_system_time :
stat.started_system_time + stat.started_system_time +
std::chrono::duration_cast<std::chrono::system_clock::duration>
(req_stat.on_request_time - stat.on_started_time); (req_stat.on_request_time - stat.on_started_time);
auto wait_delta = std::chrono::duration_cast auto wait_delta = std::chrono::duration_cast

View File

@ -1197,4 +1197,9 @@ void Downstream::disable_downstream_wtimer()
disable_timer(downstream_wtimerev_); disable_timer(downstream_wtimerev_);
} }
bool Downstream::accesslog_ready() const
{
return response_http_status_ > 0;
}
} // namespace shrpx } // namespace shrpx

View File

@ -279,6 +279,9 @@ public:
void ensure_downstream_wtimer(); void ensure_downstream_wtimer();
void disable_downstream_rtimer(); void disable_downstream_rtimer();
void disable_downstream_wtimer(); void disable_downstream_wtimer();
// Returns true if accesslog can be written for this downstream.
bool accesslog_ready() const;
private: private:
Headers request_headers_; Headers request_headers_;
Headers response_headers_; Headers response_headers_;

View File

@ -142,4 +142,10 @@ bool DownstreamQueue::pending_empty() const
return pending_downstreams_.empty(); return pending_downstreams_.empty();
} }
const std::map<int32_t, std::unique_ptr<Downstream>>&
DownstreamQueue::get_active_downstreams() const
{
return active_downstreams_;
}
} // namespace shrpx } // namespace shrpx

View File

@ -61,6 +61,8 @@ public:
// Returns first Downstream object in pending_downstreams_. This // Returns first Downstream object in pending_downstreams_. This
// does not pop the first one. If queue is empty, returns nullptr. // does not pop the first one. If queue is empty, returns nullptr.
Downstream* pending_top() const; Downstream* pending_top() const;
const std::map<int32_t, std::unique_ptr<Downstream>>&
get_active_downstreams() const;
private: private:
// Downstream objects, not processed yet // Downstream objects, not processed yet
std::map<int32_t, std::unique_ptr<Downstream>> pending_downstreams_; std::map<int32_t, std::unique_ptr<Downstream>> pending_downstreams_;

View File

@ -67,7 +67,24 @@ Http2DownstreamConnection::~Http2DownstreamConnection()
downstream_->disable_downstream_rtimer(); downstream_->disable_downstream_rtimer();
downstream_->disable_downstream_wtimer(); downstream_->disable_downstream_wtimer();
if(submit_rst_stream(downstream_) == 0) { uint32_t error_code;
if(downstream_->get_request_state() == Downstream::STREAM_CLOSED &&
downstream_->get_upgraded()) {
// For upgraded connection, send NO_ERROR. Should we consider
// request states other than Downstream::STREAM_CLOSED ?
error_code = NGHTTP2_NO_ERROR;
} else {
error_code = NGHTTP2_INTERNAL_ERROR;
}
if(LOG_ENABLED(INFO)) {
DCLOG(INFO, this) << "Submit RST_STREAM for DOWNSTREAM:"
<< downstream_ << ", stream_id="
<< downstream_->get_downstream_stream_id()
<< ", error_code=" << error_code;
}
if(submit_rst_stream(downstream_, error_code) == 0) {
http2session_->notify(); http2session_->notify();
} }
@ -196,64 +213,53 @@ ssize_t http2_data_read_callback(nghttp2_session *session,
return NGHTTP2_ERR_DEFERRED; return NGHTTP2_ERR_DEFERRED;
} }
auto body = dconn->get_request_body_buf(); auto body = dconn->get_request_body_buf();
int nread = 0;
for(;;) { auto nread = evbuffer_remove(body, buf, length);
nread = evbuffer_remove(body, buf, length); if(nread == -1) {
if(nread == -1) { DCLOG(FATAL, dconn) << "evbuffer_remove() failed";
DCLOG(FATAL, dconn) << "evbuffer_remove() failed"; return NGHTTP2_ERR_CALLBACK_FAILURE;
}
if(nread > 0) {
// This is important because it will handle flow control
// stuff.
if(downstream->get_upstream()->resume_read
(SHRPX_NO_BUFFER, downstream, nread) != 0) {
// In this case, downstream may be deleted.
return NGHTTP2_ERR_CALLBACK_FAILURE; return NGHTTP2_ERR_CALLBACK_FAILURE;
} }
if(nread > 0) { // Check dconn is still alive because Upstream::resume_read()
// This is important because it will handle flow control // may delete downstream which will delete dconn.
// stuff. if(sd->dconn == nullptr) {
if(downstream->get_upstream()->resume_read return NGHTTP2_ERR_DEFERRED;
(SHRPX_NO_BUFFER, downstream, nread) != 0) {
// In this case, downstream may be deleted.
return NGHTTP2_ERR_CALLBACK_FAILURE;
}
// Check dconn is still alive because Upstream::resume_read()
// may delete downstream which will delete dconn.
if(sd->dconn == nullptr) {
return NGHTTP2_ERR_DEFERRED;
}
break;
}
if(downstream->get_request_state() == Downstream::MSG_COMPLETE) {
if(!downstream->get_upgrade_request() ||
(downstream->get_response_state() == Downstream::HEADER_COMPLETE &&
!downstream->get_upgraded())) {
*data_flags |= NGHTTP2_DATA_FLAG_EOF;
} else {
downstream->disable_downstream_wtimer();
return NGHTTP2_ERR_DEFERRED;
}
break;
} else {
if(evbuffer_get_length(body) == 0) {
// Check get_request_state() == MSG_COMPLETE just in case
if(downstream->get_request_state() == Downstream::MSG_COMPLETE) {
*data_flags |= NGHTTP2_DATA_FLAG_EOF;
break;
}
downstream->disable_downstream_wtimer();
return NGHTTP2_ERR_DEFERRED;
}
} }
} }
if(evbuffer_get_length(body) > 0 && !downstream->get_output_buffer_full()) { if(evbuffer_get_length(body) == 0 &&
downstream->get_request_state() == Downstream::MSG_COMPLETE &&
// If connection is upgraded, don't set EOF flag, since HTTP/1
// will set MSG_COMPLETE to request state after upgrade response
// header is seen.
(!downstream->get_upgrade_request() ||
(downstream->get_response_state() == Downstream::HEADER_COMPLETE &&
!downstream->get_upgraded()))) {
*data_flags |= NGHTTP2_DATA_FLAG_EOF;
}
if(evbuffer_get_length(body) > 0) {
downstream->reset_downstream_wtimer(); downstream->reset_downstream_wtimer();
} else { } else {
downstream->disable_downstream_wtimer(); downstream->disable_downstream_wtimer();
} }
if(nread == 0 && (*data_flags & NGHTTP2_DATA_FLAG_EOF) == 0) {
downstream->disable_downstream_wtimer();
return NGHTTP2_ERR_DEFERRED;
}
return nread; return nread;
} }
} // namespace } // namespace

View File

@ -781,11 +781,6 @@ int on_stream_close_callback
// For tunneled connection, we have to submit RST_STREAM to // For tunneled connection, we have to submit RST_STREAM to
// upstream *after* whole response body is sent. We just set // upstream *after* whole response body is sent. We just set
// MSG_COMPLETE here. Upstream will take care of that. // MSG_COMPLETE here. Upstream will take care of that.
if(LOG_ENABLED(INFO)) {
SSLOG(INFO, http2session) << "RST_STREAM against tunneled stream "
<< "stream_id="
<< stream_id;
}
downstream->get_upstream()->on_downstream_body_complete(downstream); downstream->get_upstream()->on_downstream_body_complete(downstream);
downstream->set_response_state(Downstream::MSG_COMPLETE); downstream->set_response_state(Downstream::MSG_COMPLETE);
} else if(error_code == NGHTTP2_NO_ERROR) { } else if(error_code == NGHTTP2_NO_ERROR) {

View File

@ -1102,7 +1102,9 @@ ssize_t downstream_data_read_callback(nghttp2_session *session,
return NGHTTP2_ERR_CALLBACK_FAILURE; return NGHTTP2_ERR_CALLBACK_FAILURE;
} }
if(nread == 0 && auto body_empty = evbuffer_get_length(body) == 0;
if(body_empty &&
downstream->get_response_state() == Downstream::MSG_COMPLETE) { downstream->get_response_state() == Downstream::MSG_COMPLETE) {
*data_flags |= NGHTTP2_DATA_FLAG_EOF; *data_flags |= NGHTTP2_DATA_FLAG_EOF;
@ -1122,10 +1124,10 @@ ssize_t downstream_data_read_callback(nghttp2_session *session,
} }
} }
if(evbuffer_get_length(body) > 0) { if(body_empty) {
downstream->reset_upstream_wtimer();
} else {
downstream->disable_upstream_wtimer(); downstream->disable_upstream_wtimer();
} else {
downstream->reset_upstream_wtimer();
} }
if(nread > 0 && downstream->resume_read(SHRPX_NO_BUFFER, nread) != 0) { if(nread > 0 && downstream->resume_read(SHRPX_NO_BUFFER, nread) != 0) {
@ -1206,7 +1208,7 @@ void Http2Upstream::add_pending_downstream
void Http2Upstream::remove_downstream(Downstream *downstream) void Http2Upstream::remove_downstream(Downstream *downstream)
{ {
if(downstream->get_response_state() == Downstream::MSG_COMPLETE) { if(downstream->accesslog_ready()) {
handler_->write_accesslog(downstream); handler_->write_accesslog(downstream);
} }
@ -1468,6 +1470,12 @@ void Http2Upstream::reset_timeouts()
} }
void Http2Upstream::on_handler_delete() void Http2Upstream::on_handler_delete()
{} {
for(auto& ent : downstream_queue_.get_active_downstreams()) {
if(ent.second->accesslog_ready()) {
handler_->write_accesslog(ent.second.get());
}
}
}
} // namespace shrpx } // namespace shrpx

View File

@ -740,8 +740,7 @@ void HttpsUpstream::attach_downstream(std::unique_ptr<Downstream> downstream)
void HttpsUpstream::delete_downstream() void HttpsUpstream::delete_downstream()
{ {
if(downstream_ && if(downstream_ && downstream_->accesslog_ready()) {
downstream_->get_response_state() == Downstream::MSG_COMPLETE) {
handler_->write_accesslog(downstream_.get()); handler_->write_accesslog(downstream_.get());
} }
@ -975,8 +974,7 @@ void HttpsUpstream::reset_timeouts()
void HttpsUpstream::on_handler_delete() void HttpsUpstream::on_handler_delete()
{ {
if(downstream_ && if(downstream_ && downstream_->accesslog_ready()) {
downstream_->get_response_state() == Downstream::MSG_COMPLETE) {
handler_->write_accesslog(downstream_.get()); handler_->write_accesslog(downstream_.get());
} }
} }

View File

@ -920,7 +920,7 @@ Downstream* SpdyUpstream::add_pending_downstream
void SpdyUpstream::remove_downstream(Downstream *downstream) void SpdyUpstream::remove_downstream(Downstream *downstream)
{ {
if(downstream->get_response_state() == Downstream::MSG_COMPLETE) { if(downstream->accesslog_ready()) {
handler_->write_accesslog(downstream); handler_->write_accesslog(downstream);
} }
@ -1157,6 +1157,13 @@ void SpdyUpstream::reset_timeouts()
} }
void SpdyUpstream::on_handler_delete() void SpdyUpstream::on_handler_delete()
{} {
for(auto& ent : downstream_queue_.get_active_downstreams()) {
if(ent.second->accesslog_ready()) {
handler_->write_accesslog(ent.second.get());
}
}
}
} // namespace shrpx } // namespace shrpx

View File

@ -522,7 +522,8 @@ std::string format_iso8601(const T& tp)
char buf[128]; char buf[128];
auto nwrite = strftime(buf, sizeof(buf), "%Y-%m-%dT%H:%M:%S", &tms); auto nwrite = strftime(buf, sizeof(buf), "%Y-%m-%dT%H:%M:%S", &tms);
snprintf(&buf[nwrite], sizeof(buf) - nwrite, ".%03ldZ", t.count() % 1000); snprintf(&buf[nwrite], sizeof(buf) - nwrite, ".%03ldZ",
static_cast<int>(t.count() % 1000));
return buf; return buf;
} }

View File

@ -75,7 +75,7 @@ if (parser->upgrade) {
HTTP needs to know where the end of the stream is. For example, sometimes HTTP needs to know where the end of the stream is. For example, sometimes
servers send responses without Content-Length and expect the client to servers send responses without Content-Length and expect the client to
consume input (for the body) until EOF. To tell http_parser about EOF, give consume input (for the body) until EOF. To tell http_parser about EOF, give
`0` as the forth parameter to `http_parser_execute()`. Callbacks and errors `0` as the fourth parameter to `http_parser_execute()`. Callbacks and errors
can still be encountered during an EOF, so one must still be prepared can still be encountered during an EOF, so one must still be prepared
to receive them. to receive them.
@ -110,7 +110,7 @@ followed by non-HTTP data.
information the Web Socket protocol.) information the Web Socket protocol.)
To support this, the parser will treat this as a normal HTTP message without a To support this, the parser will treat this as a normal HTTP message without a
body. Issuing both on_headers_complete and on_message_complete callbacks. However body, issuing both on_headers_complete and on_message_complete callbacks. However
http_parser_execute() will stop parsing at the end of the headers and return. http_parser_execute() will stop parsing at the end of the headers and return.
The user is expected to check if `parser->upgrade` has been set to 1 after The user is expected to check if `parser->upgrade` has been set to 1 after
@ -145,7 +145,7 @@ buffer to avoid copying memory around if this fits your application.
Reading headers may be a tricky task if you read/parse headers partially. Reading headers may be a tricky task if you read/parse headers partially.
Basically, you need to remember whether last header callback was field or value Basically, you need to remember whether last header callback was field or value
and apply following logic: and apply the following logic:
(on_header_field and on_header_value shortened to on_h_*) (on_header_field and on_header_value shortened to on_h_*)
------------------------ ------------ -------------------------------------------- ------------------------ ------------ --------------------------------------------

View File

@ -925,7 +925,7 @@ size_t http_parser_execute (http_parser *parser,
case 'G': parser->method = HTTP_GET; break; case 'G': parser->method = HTTP_GET; break;
case 'H': parser->method = HTTP_HEAD; break; case 'H': parser->method = HTTP_HEAD; break;
case 'L': parser->method = HTTP_LOCK; break; case 'L': parser->method = HTTP_LOCK; break;
case 'M': parser->method = HTTP_MKCOL; /* or MOVE, MKACTIVITY, MERGE, M-SEARCH */ break; case 'M': parser->method = HTTP_MKCOL; /* or MOVE, MKACTIVITY, MERGE, M-SEARCH, MKCALENDAR */ break;
case 'N': parser->method = HTTP_NOTIFY; break; case 'N': parser->method = HTTP_NOTIFY; break;
case 'O': parser->method = HTTP_OPTIONS; break; case 'O': parser->method = HTTP_OPTIONS; break;
case 'P': parser->method = HTTP_POST; case 'P': parser->method = HTTP_POST;
@ -977,6 +977,8 @@ size_t http_parser_execute (http_parser *parser,
parser->method = HTTP_MSEARCH; parser->method = HTTP_MSEARCH;
} else if (parser->index == 2 && ch == 'A') { } else if (parser->index == 2 && ch == 'A') {
parser->method = HTTP_MKACTIVITY; parser->method = HTTP_MKACTIVITY;
} else if (parser->index == 3 && ch == 'A') {
parser->method = HTTP_MKCALENDAR;
} else { } else {
SET_ERRNO(HPE_INVALID_METHOD); SET_ERRNO(HPE_INVALID_METHOD);
goto error; goto error;
@ -1388,18 +1390,6 @@ size_t http_parser_execute (http_parser *parser,
break; break;
} }
if (ch == CR) {
parser->state = s_header_almost_done;
CALLBACK_DATA(header_field);
break;
}
if (ch == LF) {
parser->state = s_header_field_start;
CALLBACK_DATA(header_field);
break;
}
SET_ERRNO(HPE_INVALID_HEADER_TOKEN); SET_ERRNO(HPE_INVALID_HEADER_TOKEN);
goto error; goto error;
} }
@ -2142,7 +2132,7 @@ http_parser_parse_url(const char *buf, size_t buflen, int is_connect,
u->port = u->field_set = 0; u->port = u->field_set = 0;
s = is_connect ? s_req_server_start : s_req_spaces_before_url; s = is_connect ? s_req_server_start : s_req_spaces_before_url;
uf = old_uf = UF_MAX; old_uf = UF_MAX;
for (p = buf; p < buf + buflen; p++) { for (p = buf; p < buf + buflen; p++) {
s = parse_url_char(s, *p); s = parse_url_char(s, *p);

View File

@ -76,7 +76,7 @@ typedef struct http_parser_settings http_parser_settings;
* HEAD request which may contain 'Content-Length' or 'Transfer-Encoding: * HEAD request which may contain 'Content-Length' or 'Transfer-Encoding:
* chunked' headers that indicate the presence of a body. * chunked' headers that indicate the presence of a body.
* *
* http_data_cb does not return data chunks. It will be call arbitrarally * http_data_cb does not return data chunks. It will be called arbitrarily
* many times for each string. E.G. you might get 10 callbacks for "on_url" * many times for each string. E.G. you might get 10 callbacks for "on_url"
* each providing just a few characters more data. * each providing just a few characters more data.
*/ */
@ -117,6 +117,8 @@ typedef int (*http_cb) (http_parser*);
/* RFC-5789 */ \ /* RFC-5789 */ \
XX(24, PATCH, PATCH) \ XX(24, PATCH, PATCH) \
XX(25, PURGE, PURGE) \ XX(25, PURGE, PURGE) \
/* CalDAV */ \
XX(26, MKCALENDAR, MKCALENDAR) \
enum http_method enum http_method
{ {
@ -278,13 +280,15 @@ struct http_parser_url {
* unsigned major = (version >> 16) & 255; * unsigned major = (version >> 16) & 255;
* unsigned minor = (version >> 8) & 255; * unsigned minor = (version >> 8) & 255;
* unsigned patch = version & 255; * unsigned patch = version & 255;
* printf("http_parser v%u.%u.%u\n", major, minor, version); * printf("http_parser v%u.%u.%u\n", major, minor, patch);
*/ */
unsigned long http_parser_version(void); unsigned long http_parser_version(void);
void http_parser_init(http_parser *parser, enum http_parser_type type); void http_parser_init(http_parser *parser, enum http_parser_type type);
/* Executes the parser. Returns number of parsed bytes. Sets
* `parser->http_errno` on error. */
size_t http_parser_execute(http_parser *parser, size_t http_parser_execute(http_parser *parser,
const http_parser_settings *settings, const http_parser_settings *settings,
const char *data, const char *data,

View File

@ -2207,7 +2207,6 @@ print_error (const char *raw, size_t error_location)
break; break;
case '\n': case '\n':
char_len = 2;
fprintf(stderr, "\\n\n"); fprintf(stderr, "\\n\n");
if (this_line) goto print; if (this_line) goto print;
@ -2910,15 +2909,11 @@ test_simple (const char *buf, enum http_errno err_expected)
{ {
parser_init(HTTP_REQUEST); parser_init(HTTP_REQUEST);
size_t parsed;
int pass;
enum http_errno err; enum http_errno err;
parsed = parse(buf, strlen(buf)); parse(buf, strlen(buf));
pass = (parsed == strlen(buf));
err = HTTP_PARSER_ERRNO(parser); err = HTTP_PARSER_ERRNO(parser);
parsed = parse(NULL, 0); parse(NULL, 0);
pass &= (parsed == 0);
parser_free(); parser_free();
@ -3476,6 +3471,13 @@ main (void)
test_simple(buf, HPE_INVALID_METHOD); test_simple(buf, HPE_INVALID_METHOD);
} }
// illegal header field name line folding
test_simple("GET / HTTP/1.1\r\n"
"name\r\n"
" : value\r\n"
"\r\n",
HPE_INVALID_HEADER_TOKEN);
const char *dumbfuck2 = const char *dumbfuck2 =
"GET / HTTP/1.1\r\n" "GET / HTTP/1.1\r\n"
"X-SSL-Bullshit: -----BEGIN CERTIFICATE-----\r\n" "X-SSL-Bullshit: -----BEGIN CERTIFICATE-----\r\n"