diff --git a/examples/htparse/htparse.c b/examples/htparse/htparse.c index 3e5d6c6..1fcbd51 100644 --- a/examples/htparse/htparse.c +++ b/examples/htparse/htparse.c @@ -1118,6 +1118,7 @@ htparser_run(htparser * p, htparse_hooks * hooks, const char * data, size_t len) if (p->type == htp_type_request) { p->state = s_spaces_after_digit; } else if (p->type == htp_type_response) { + p->status = 0; p->state = s_status; } diff --git a/examples/shrpx_downstream.cc b/examples/shrpx_downstream.cc index 6103afb..e210d69 100644 --- a/examples/shrpx_downstream.cc +++ b/examples/shrpx_downstream.cc @@ -42,6 +42,7 @@ Downstream::Downstream(Upstream *upstream, int stream_id, int priority) bev_(0), stream_id_(stream_id), priority_(priority), + counter_(1), ioctrl_(0), request_state_(INITIAL), request_major_(1), @@ -53,6 +54,7 @@ Downstream::Downstream(Upstream *upstream, int stream_id, int priority) response_major_(1), response_minor_(1), chunked_response_(false), + response_connection_close_(false), response_htp_(htparser_new()), response_body_buf_(0) { @@ -82,6 +84,41 @@ Downstream::~Downstream() } } +int Downstream::get_counter() const +{ + return counter_; +} + +void Downstream::reuse(int stream_id) +{ + stream_id_ = stream_id; + ++counter_; + request_state_ = INITIAL; +} + +void Downstream::idle() +{ + stream_id_ = -1; + priority_ = 0; + ioctrl_.force_resume_read(); + request_state_ = IDLE; + request_method_.clear(); + request_path_.clear(); + request_major_ = 1; + request_minor_ = 1; + chunked_request_ = false; + request_connection_close_ = false; + request_headers_.clear(); + + response_state_ = INITIAL; + response_http_status_ = 0; + response_major_ = 1; + response_minor_ = 1; + chunked_response_ = false; + response_connection_close_ = false; + response_headers_.clear(); +} + void Downstream::pause_read(IOCtrlReason reason) { ioctrl_.pause_read(reason); @@ -110,8 +147,8 @@ void check_transfer_encoding_chunked(bool *chunked, } // namespace namespace { -void check_request_connection(bool *connection_close, - const Headers::value_type &item) +void check_connection_close(bool *connection_close, + const Headers::value_type &item) { if(util::strieq(item.first.c_str(), "connection")) { if(util::strifind(item.second.c_str(), "close")) { @@ -134,7 +171,7 @@ void Downstream::set_last_request_header_value(const std::string& value) Headers::value_type &item = request_headers_.back(); item.second = value; check_transfer_encoding_chunked(&chunked_request_, item); - check_request_connection(&request_connection_close_, item); + check_connection_close(&request_connection_close_, item); } void Downstream::set_request_method(const std::string& method) @@ -255,7 +292,9 @@ int Downstream::push_request_headers() } hdrs += "\r\n"; } - hdrs += "Connection: close\r\n"; + if(request_connection_close_) { + hdrs += "Connection: close\r\n"; + } if(!xff_found) { hdrs += "X-Forwarded-For: "; hdrs += upstream_->get_client_handler()->get_ipaddr(); @@ -327,6 +366,7 @@ void Downstream::set_last_response_header_value(const std::string& value) Headers::value_type &item = response_headers_.back(); item.second = value; check_transfer_encoding_chunked(&chunked_response_, item); + check_connection_close(&response_connection_close_, item); } unsigned int Downstream::get_response_http_status() const @@ -364,6 +404,11 @@ bool Downstream::get_chunked_response() const return chunked_response_; } +bool Downstream::get_response_connection_close() const +{ + return response_connection_close_; +} + namespace { int htp_hdrs_completecb(htparser *htp) { diff --git a/examples/shrpx_downstream.h b/examples/shrpx_downstream.h index 7edfcc7..36004aa 100644 --- a/examples/shrpx_downstream.h +++ b/examples/shrpx_downstream.h @@ -57,6 +57,9 @@ public: void pause_read(IOCtrlReason reason); bool resume_read(IOCtrlReason reason); void force_resume_read(); + void idle(); + void reuse(int stream_id); + int get_counter() const; // downstream request API const Headers& get_request_headers() const; void add_request_header(const std::string& name, const std::string& value); @@ -78,7 +81,8 @@ public: HEADER_COMPLETE, MSG_COMPLETE, STREAM_CLOSED, - CONNECT_FAIL + CONNECT_FAIL, + IDLE }; void set_request_state(int state); int get_request_state() const; @@ -93,6 +97,7 @@ public: int get_response_major() const; int get_response_minor() const; bool get_chunked_response() const; + bool get_response_connection_close() const; int parse_http_response(); void set_response_state(int state); int get_response_state() const; @@ -103,6 +108,7 @@ private: bufferevent *bev_; int32_t stream_id_; int priority_; + int counter_; IOControl ioctrl_; int request_state_; std::string request_method_; @@ -118,6 +124,7 @@ private: int response_major_; int response_minor_; bool chunked_response_; + bool response_connection_close_; Headers response_headers_; htparser *response_htp_; // This buffer is used to temporarily store downstream response diff --git a/examples/shrpx_https_upstream.cc b/examples/shrpx_https_upstream.cc index a2ca1aa..bbd3b7c 100644 --- a/examples/shrpx_https_upstream.cc +++ b/examples/shrpx_https_upstream.cc @@ -76,8 +76,14 @@ int htp_msg_begin(htparser *htp) HttpsUpstream *upstream; upstream = reinterpret_cast(htparser_get_userdata(htp)); upstream->reset_current_header_length(); - Downstream *downstream = new Downstream(upstream, 0, 0); - upstream->add_downstream(downstream); + Downstream *downstream = upstream->get_top_downstream(); + if(downstream) { + // Keep-Alived connection + downstream->reuse(0); + } else { + downstream = new Downstream(upstream, 0, 0); + upstream->add_downstream(downstream); + } return 0; } } // namespace @@ -160,11 +166,13 @@ int htp_hdrs_completecb(htparser *htp) downstream->push_request_headers(); downstream->set_request_state(Downstream::HEADER_COMPLETE); - int rv = downstream->start_connection(); - if(rv != 0) { - LOG(ERROR) << "Upstream connection failed"; - downstream->set_request_state(Downstream::CONNECT_FAIL); - return 1; + if(downstream->get_counter() == 1) { + int rv = downstream->start_connection(); + if(rv != 0) { + LOG(ERROR) << "Upstream connection failed"; + downstream->set_request_state(Downstream::CONNECT_FAIL); + return 1; + } } return 0; } @@ -298,12 +306,21 @@ void https_downstream_readcb(bufferevent *bev, void *ptr) Downstream *downstream = reinterpret_cast(ptr); HttpsUpstream *upstream; upstream = static_cast(downstream->get_upstream()); + if(downstream->get_request_state() == Downstream::IDLE) { + upstream->pop_downstream(); + delete downstream; + return; + } int rv = downstream->parse_http_response(); if(rv == 0) { if(downstream->get_response_state() == Downstream::MSG_COMPLETE) { assert(downstream == upstream->get_top_downstream()); - upstream->pop_downstream(); - delete downstream; + if(downstream->get_response_connection_close()) { + upstream->pop_downstream(); + delete downstream; + } else { + downstream->idle(); + } upstream->resume_read(SHRPX_MSG_BLOCK); } else { ClientHandler *handler = upstream->get_client_handler(); @@ -344,27 +361,28 @@ void https_downstream_eventcb(bufferevent *bev, short events, void *ptr) LOG(INFO) << "Downstream connection established. downstream " << downstream; } - } - if(events & BEV_EVENT_EOF) { + } else if(events & BEV_EVENT_EOF) { if(ENABLE_LOG) { LOG(INFO) << "Downstream EOF. stream_id=" << downstream->get_stream_id(); } - if(downstream->get_response_state() == Downstream::HEADER_COMPLETE) { - // Server may indicate the end of the request by EOF - if(ENABLE_LOG) { - LOG(INFO) << "Assuming downstream content-length is 0 byte"; + if(downstream->get_request_state() != Downstream::IDLE) { + if(downstream->get_response_state() == Downstream::HEADER_COMPLETE) { + // Server may indicate the end of the request by EOF + if(ENABLE_LOG) { + LOG(INFO) << "Assuming downstream content-length is 0 byte"; + } + upstream->on_downstream_body_complete(downstream); + //downstream->set_response_state(Downstream::MSG_COMPLETE); + } else if(downstream->get_response_state() == Downstream::MSG_COMPLETE) { + // Nothing to do + } else { + // error + if(ENABLE_LOG) { + LOG(INFO) << "Treated as downstream error"; + } + upstream->error_reply(502); } - upstream->on_downstream_body_complete(downstream); - //downstream->set_response_state(Downstream::MSG_COMPLETE); - } else if(downstream->get_response_state() == Downstream::MSG_COMPLETE) { - // Nothing to do - } else { - // error - if(ENABLE_LOG) { - LOG(INFO) << "Treated as downstream error"; - } - upstream->error_reply(502); } upstream->pop_downstream(); delete downstream;