fixed conflict
This commit is contained in:
commit
6e178653a5
|
@ -0,0 +1 @@
|
||||||
|
nghttpx-init
|
|
@ -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 {
|
|
||||||
|
n -= k;
|
||||||
++len;
|
++len;
|
||||||
if(n >= 128) {
|
|
||||||
n >>= 7;
|
for(; n >= 128; n >>= 7, ++len);
|
||||||
} else {
|
|
||||||
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;
|
|
||||||
if(n >= 128) {
|
|
||||||
*buf++ = (1 << 7) | (n & 0x7f);
|
*buf++ = (1 << 7) | (n & 0x7f);
|
||||||
n >>= 7;
|
|
||||||
} else {
|
|
||||||
*buf++ = (uint8_t)n;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
} while(n);
|
|
||||||
return len;
|
*buf++ = (uint8_t)n;
|
||||||
|
|
||||||
|
return (size_t)(buf - begin);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -462,13 +462,8 @@ 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(next_proto) {
|
||||||
debug_nextproto_error();
|
|
||||||
client->fail();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(util::check_h2_is_selected(next_proto, next_proto_len)) {
|
if(util::check_h2_is_selected(next_proto, next_proto_len)) {
|
||||||
client->session = util::make_unique<Http2Session>(client);
|
client->session = util::make_unique<Http2Session>(client);
|
||||||
} else {
|
} else {
|
||||||
|
@ -489,6 +484,20 @@ void eventcb(bufferevent *bev, short events, void *ptr)
|
||||||
return;
|
return;
|
||||||
#endif // !HAVE_SPDYLAY
|
#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) {
|
||||||
|
debug_nextproto_error();
|
||||||
|
client->fail();
|
||||||
|
return;
|
||||||
|
}
|
||||||
} 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()) {
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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_;
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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_;
|
||||||
|
|
|
@ -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,9 +213,8 @@ 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;
|
return NGHTTP2_ERR_CALLBACK_FAILURE;
|
||||||
|
@ -218,42 +234,32 @@ ssize_t http2_data_read_callback(nghttp2_session *session,
|
||||||
if(sd->dconn == nullptr) {
|
if(sd->dconn == nullptr) {
|
||||||
return NGHTTP2_ERR_DEFERRED;
|
return NGHTTP2_ERR_DEFERRED;
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(downstream->get_request_state() == Downstream::MSG_COMPLETE) {
|
if(evbuffer_get_length(body) == 0 &&
|
||||||
if(!downstream->get_upgrade_request() ||
|
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_response_state() == Downstream::HEADER_COMPLETE &&
|
||||||
!downstream->get_upgraded())) {
|
!downstream->get_upgraded()))) {
|
||||||
|
|
||||||
*data_flags |= NGHTTP2_DATA_FLAG_EOF;
|
*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();
|
if(evbuffer_get_length(body) > 0) {
|
||||||
|
|
||||||
return NGHTTP2_ERR_DEFERRED;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(evbuffer_get_length(body) > 0 && !downstream->get_output_buffer_full()) {
|
|
||||||
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
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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_*)
|
||||||
------------------------ ------------ --------------------------------------------
|
------------------------ ------------ --------------------------------------------
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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"
|
||||||
|
|
Loading…
Reference in New Issue