2013-07-26 13:12:55 +02:00
|
|
|
/*
|
2014-03-30 12:09:21 +02:00
|
|
|
* nghttp2 - HTTP/2 C Library
|
2013-07-26 13:12:55 +02:00
|
|
|
*
|
|
|
|
* Copyright (c) 2012 Tatsuhiro Tsujikawa
|
|
|
|
*
|
|
|
|
* Permission is hereby granted, free of charge, to any person obtaining
|
|
|
|
* a copy of this software and associated documentation files (the
|
|
|
|
* "Software"), to deal in the Software without restriction, including
|
|
|
|
* without limitation the rights to use, copy, modify, merge, publish,
|
|
|
|
* distribute, sublicense, and/or sell copies of the Software, and to
|
|
|
|
* permit persons to whom the Software is furnished to do so, subject to
|
|
|
|
* the following conditions:
|
|
|
|
*
|
|
|
|
* The above copyright notice and this permission notice shall be
|
|
|
|
* included in all copies or substantial portions of the Software.
|
|
|
|
*
|
|
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
|
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
|
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
|
|
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
|
|
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
|
|
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
|
|
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
|
|
*/
|
|
|
|
#include "shrpx_spdy_upstream.h"
|
|
|
|
|
|
|
|
#include <netinet/tcp.h>
|
|
|
|
#include <assert.h>
|
|
|
|
#include <cerrno>
|
|
|
|
#include <sstream>
|
|
|
|
|
|
|
|
#include <nghttp2/nghttp2.h>
|
|
|
|
|
|
|
|
#include "shrpx_client_handler.h"
|
|
|
|
#include "shrpx_downstream.h"
|
|
|
|
#include "shrpx_downstream_connection.h"
|
|
|
|
#include "shrpx_config.h"
|
|
|
|
#include "shrpx_http.h"
|
2015-09-03 17:54:41 +02:00
|
|
|
#ifdef HAVE_MRUBY
|
2015-09-02 17:40:14 +02:00
|
|
|
#include "shrpx_mruby.h"
|
2015-09-03 17:54:41 +02:00
|
|
|
#endif // HAVE_MRUBY
|
2015-09-02 17:40:14 +02:00
|
|
|
#include "shrpx_worker.h"
|
|
|
|
#include "shrpx_http2_session.h"
|
2013-08-27 19:47:22 +02:00
|
|
|
#include "http2.h"
|
2013-07-26 13:12:55 +02:00
|
|
|
#include "util.h"
|
2015-02-05 15:21:53 +01:00
|
|
|
#include "template.h"
|
2013-07-26 13:12:55 +02:00
|
|
|
|
|
|
|
using namespace nghttp2;
|
|
|
|
|
|
|
|
namespace shrpx {
|
|
|
|
|
|
|
|
namespace {
|
2014-11-27 15:39:04 +01:00
|
|
|
ssize_t send_callback(spdylay_session *session, const uint8_t *data, size_t len,
|
|
|
|
int flags, void *user_data) {
|
|
|
|
auto upstream = static_cast<SpdyUpstream *>(user_data);
|
2015-10-02 15:42:46 +02:00
|
|
|
auto wb = upstream->get_response_buf();
|
2014-03-03 13:18:24 +01:00
|
|
|
|
2014-12-27 18:59:06 +01:00
|
|
|
if (wb->wleft() == 0) {
|
2013-07-26 13:12:55 +02:00
|
|
|
return SPDYLAY_ERR_WOULDBLOCK;
|
|
|
|
}
|
|
|
|
|
2014-12-27 18:59:06 +01:00
|
|
|
auto nread = wb->write(data, len);
|
|
|
|
|
|
|
|
return nread;
|
2013-07-26 13:12:55 +02:00
|
|
|
}
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
namespace {
|
2014-12-27 18:59:06 +01:00
|
|
|
ssize_t recv_callback(spdylay_session *session, uint8_t *buf, size_t len,
|
2014-11-27 15:39:04 +01:00
|
|
|
int flags, void *user_data) {
|
|
|
|
auto upstream = static_cast<SpdyUpstream *>(user_data);
|
2013-12-20 15:36:24 +01:00
|
|
|
auto handler = upstream->get_client_handler();
|
2014-12-27 18:59:06 +01:00
|
|
|
auto rb = handler->get_rb();
|
2015-02-13 14:41:50 +01:00
|
|
|
auto rlimit = handler->get_rlimit();
|
2014-12-27 18:59:06 +01:00
|
|
|
|
2015-01-29 14:47:37 +01:00
|
|
|
if (rb->rleft() == 0) {
|
2013-07-26 13:12:55 +02:00
|
|
|
return SPDYLAY_ERR_WOULDBLOCK;
|
|
|
|
}
|
2014-12-27 18:59:06 +01:00
|
|
|
|
2015-01-29 14:47:37 +01:00
|
|
|
auto nread = std::min(rb->rleft(), len);
|
2014-12-27 18:59:06 +01:00
|
|
|
|
2015-01-29 14:47:37 +01:00
|
|
|
memcpy(buf, rb->pos, nread);
|
2014-12-27 18:59:06 +01:00
|
|
|
rb->drain(nread);
|
2015-02-13 14:41:50 +01:00
|
|
|
rlimit->startw();
|
2014-12-27 18:59:06 +01:00
|
|
|
|
|
|
|
return nread;
|
2013-07-26 13:12:55 +02:00
|
|
|
}
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
namespace {
|
2014-11-27 15:39:04 +01:00
|
|
|
void on_stream_close_callback(spdylay_session *session, int32_t stream_id,
|
|
|
|
spdylay_status_code status_code,
|
|
|
|
void *user_data) {
|
|
|
|
auto upstream = static_cast<SpdyUpstream *>(user_data);
|
|
|
|
if (LOG_ENABLED(INFO)) {
|
2013-07-26 13:12:55 +02:00
|
|
|
ULOG(INFO, upstream) << "Stream stream_id=" << stream_id
|
|
|
|
<< " is being closed";
|
|
|
|
}
|
2015-03-11 16:17:05 +01:00
|
|
|
auto downstream = static_cast<Downstream *>(
|
|
|
|
spdylay_session_get_stream_user_data(session, stream_id));
|
2014-11-27 15:39:04 +01:00
|
|
|
if (!downstream) {
|
2014-06-01 16:44:32 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-08-21 14:22:16 +02:00
|
|
|
upstream->consume(stream_id, downstream->get_request_datalen());
|
|
|
|
|
|
|
|
downstream->reset_request_datalen();
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
if (downstream->get_request_state() == Downstream::CONNECT_FAIL) {
|
2014-06-01 16:44:32 +02:00
|
|
|
upstream->remove_downstream(downstream);
|
2014-08-18 15:59:31 +02:00
|
|
|
// downstrea was deleted
|
|
|
|
|
2014-06-01 16:44:32 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-07-22 14:41:16 +02:00
|
|
|
if (downstream->can_detach_downstream_connection()) {
|
|
|
|
// Keep-alive
|
|
|
|
downstream->detach_downstream_connection();
|
2013-07-26 13:12:55 +02:00
|
|
|
}
|
2014-06-01 16:44:32 +02:00
|
|
|
|
2015-07-22 14:41:16 +02:00
|
|
|
downstream->set_request_state(Downstream::STREAM_CLOSED);
|
|
|
|
|
2014-06-01 16:44:32 +02:00
|
|
|
// At this point, downstream read may be paused.
|
|
|
|
|
|
|
|
// If shrpx_downstream::push_request_headers() failed, the
|
|
|
|
// error is handled here.
|
|
|
|
upstream->remove_downstream(downstream);
|
2014-08-18 15:59:31 +02:00
|
|
|
// downstrea was deleted
|
|
|
|
|
2014-06-01 16:44:32 +02:00
|
|
|
// How to test this case? Request sufficient large download
|
|
|
|
// and make client send RST_STREAM after it gets first DATA
|
|
|
|
// frame chunk.
|
2013-07-26 13:12:55 +02:00
|
|
|
}
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
namespace {
|
2014-11-27 15:39:04 +01:00
|
|
|
void on_ctrl_recv_callback(spdylay_session *session, spdylay_frame_type type,
|
|
|
|
spdylay_frame *frame, void *user_data) {
|
|
|
|
auto upstream = static_cast<SpdyUpstream *>(user_data);
|
|
|
|
switch (type) {
|
2013-07-26 13:12:55 +02:00
|
|
|
case SPDYLAY_SYN_STREAM: {
|
2014-11-27 15:39:04 +01:00
|
|
|
if (LOG_ENABLED(INFO)) {
|
2013-07-26 13:12:55 +02:00
|
|
|
ULOG(INFO, upstream) << "Received upstream SYN_STREAM stream_id="
|
|
|
|
<< frame->syn_stream.stream_id;
|
|
|
|
}
|
2014-08-18 15:59:31 +02:00
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
auto downstream = upstream->add_pending_downstream(
|
|
|
|
frame->syn_stream.stream_id, frame->syn_stream.pri);
|
2014-08-18 15:59:31 +02:00
|
|
|
|
2014-08-09 11:47:45 +02:00
|
|
|
downstream->reset_upstream_rtimer();
|
2013-07-26 13:12:55 +02:00
|
|
|
|
2013-10-25 14:50:56 +02:00
|
|
|
auto nv = frame->syn_stream.nv;
|
2014-07-05 11:22:40 +02:00
|
|
|
|
2015-01-20 17:03:56 +01:00
|
|
|
if (LOG_ENABLED(INFO)) {
|
|
|
|
std::stringstream ss;
|
|
|
|
for (size_t i = 0; nv[i]; i += 2) {
|
|
|
|
ss << TTY_HTTP_HD << nv[i] << TTY_RST << ": " << nv[i + 1] << "\n";
|
|
|
|
}
|
|
|
|
ULOG(INFO, upstream) << "HTTP request headers. stream_id="
|
|
|
|
<< downstream->get_stream_id() << "\n" << ss.str();
|
|
|
|
}
|
|
|
|
|
2015-04-29 14:27:36 +02:00
|
|
|
size_t num_headers = 0;
|
|
|
|
size_t header_buffer = 0;
|
|
|
|
for (size_t i = 0; nv[i]; i += 2) {
|
|
|
|
++num_headers;
|
2015-06-09 17:29:03 +02:00
|
|
|
// shut up scan-build
|
|
|
|
assert(nv[i + 1]);
|
2015-04-29 14:27:36 +02:00
|
|
|
header_buffer += strlen(nv[i]) + strlen(nv[i + 1]);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (header_buffer > get_config()->header_field_buffer ||
|
|
|
|
num_headers > get_config()->max_header_fields) {
|
|
|
|
upstream->rst_stream(downstream, SPDYLAY_INTERNAL_ERROR);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
for (size_t i = 0; nv[i]; i += 2) {
|
2015-01-04 15:22:39 +01:00
|
|
|
downstream->add_request_header(nv[i], nv[i + 1]);
|
2013-07-26 13:12:55 +02:00
|
|
|
}
|
2014-11-18 16:56:44 +01:00
|
|
|
|
2015-01-20 17:03:56 +01:00
|
|
|
if (downstream->index_request_headers() != 0) {
|
2015-01-25 07:37:09 +01:00
|
|
|
if (upstream->error_reply(downstream, 400) != 0) {
|
|
|
|
ULOG(FATAL, upstream) << "error_reply failed";
|
|
|
|
}
|
2015-01-20 17:03:56 +01:00
|
|
|
return;
|
|
|
|
}
|
2015-01-04 15:22:39 +01:00
|
|
|
|
|
|
|
auto path = downstream->get_request_header(http2::HD__PATH);
|
|
|
|
auto scheme = downstream->get_request_header(http2::HD__SCHEME);
|
|
|
|
auto host = downstream->get_request_header(http2::HD__HOST);
|
|
|
|
auto method = downstream->get_request_header(http2::HD__METHOD);
|
2014-11-18 16:56:44 +01:00
|
|
|
|
2015-06-09 16:15:02 +02:00
|
|
|
if (!method) {
|
|
|
|
upstream->rst_stream(downstream, SPDYLAY_PROTOCOL_ERROR);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto method_token = http2::lookup_method_token(method->value);
|
|
|
|
if (method_token == -1) {
|
|
|
|
if (upstream->error_reply(downstream, 501) != 0) {
|
|
|
|
ULOG(FATAL, upstream) << "error_reply failed";
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto is_connect = method_token == HTTP_CONNECT;
|
|
|
|
if (!path || !host || !http2::non_empty_value(host) ||
|
|
|
|
!http2::non_empty_value(path) ||
|
2015-01-04 15:22:39 +01:00
|
|
|
(!is_connect && (!scheme || !http2::non_empty_value(scheme)))) {
|
2015-06-09 16:15:02 +02:00
|
|
|
upstream->rst_stream(downstream, SPDYLAY_PROTOCOL_ERROR);
|
2013-07-26 13:12:55 +02:00
|
|
|
return;
|
|
|
|
}
|
2013-10-25 14:50:56 +02:00
|
|
|
|
2015-06-15 17:09:22 +02:00
|
|
|
// For other than CONNECT method, path must start with "/", except
|
|
|
|
// for OPTIONS method, which can take "*" as path.
|
|
|
|
if (!is_connect && path->value[0] != '/' &&
|
|
|
|
(method_token != HTTP_OPTIONS || path->value != "*")) {
|
|
|
|
upstream->rst_stream(downstream, SPDYLAY_PROTOCOL_ERROR);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-06-09 16:15:02 +02:00
|
|
|
downstream->set_request_method(method_token);
|
2014-11-27 15:39:04 +01:00
|
|
|
if (is_connect) {
|
2015-01-04 15:22:39 +01:00
|
|
|
downstream->set_request_http2_authority(path->value);
|
2013-07-26 13:12:55 +02:00
|
|
|
} else {
|
2015-01-04 15:22:39 +01:00
|
|
|
downstream->set_request_http2_scheme(scheme->value);
|
|
|
|
downstream->set_request_http2_authority(host->value);
|
2015-07-11 10:50:58 +02:00
|
|
|
if (get_config()->http2_proxy || get_config()->client_proxy) {
|
|
|
|
downstream->set_request_path(path->value);
|
2015-09-03 17:14:09 +02:00
|
|
|
} else if (method_token == HTTP_OPTIONS && path->value == "*") {
|
|
|
|
// Server-wide OPTIONS request. Path is empty.
|
2015-07-11 10:50:58 +02:00
|
|
|
} else {
|
|
|
|
downstream->set_request_path(http2::rewrite_clean_path(
|
|
|
|
std::begin(path->value), std::end(path->value)));
|
|
|
|
}
|
2013-07-26 13:12:55 +02:00
|
|
|
}
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
if (!(frame->syn_stream.hd.flags & SPDYLAY_CTRL_FLAG_FIN)) {
|
2014-07-03 12:59:10 +02:00
|
|
|
downstream->set_request_http2_expect_body(true);
|
|
|
|
}
|
|
|
|
|
2014-06-15 09:14:00 +02:00
|
|
|
downstream->inspect_http2_request();
|
2013-07-26 13:12:55 +02:00
|
|
|
|
|
|
|
downstream->set_request_state(Downstream::HEADER_COMPLETE);
|
2015-09-02 17:40:14 +02:00
|
|
|
|
2015-09-03 17:54:41 +02:00
|
|
|
#ifdef HAVE_MRUBY
|
2015-09-02 17:40:14 +02:00
|
|
|
auto handler = upstream->get_client_handler();
|
|
|
|
auto worker = handler->get_worker();
|
|
|
|
auto mruby_ctx = worker->get_mruby_context();
|
|
|
|
|
|
|
|
if (mruby_ctx->run_on_request_proc(downstream) != 0) {
|
|
|
|
if (upstream->error_reply(downstream, 500) != 0) {
|
|
|
|
ULOG(FATAL, upstream) << "error_reply failed";
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
2015-09-03 17:54:41 +02:00
|
|
|
#endif // HAVE_MRUBY
|
2015-09-02 17:40:14 +02:00
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
if (frame->syn_stream.hd.flags & SPDYLAY_CTRL_FLAG_FIN) {
|
2015-01-17 13:31:28 +01:00
|
|
|
if (!downstream->validate_request_bodylen()) {
|
|
|
|
upstream->rst_stream(downstream, SPDYLAY_PROTOCOL_ERROR);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-08-09 11:47:45 +02:00
|
|
|
downstream->disable_upstream_rtimer();
|
2013-07-26 13:12:55 +02:00
|
|
|
downstream->set_request_state(Downstream::MSG_COMPLETE);
|
|
|
|
}
|
2014-08-16 14:29:20 +02:00
|
|
|
|
2015-09-02 17:40:14 +02:00
|
|
|
if (downstream->get_response_state() == Downstream::MSG_COMPLETE) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-12-04 17:07:00 +01:00
|
|
|
upstream->start_downstream(downstream);
|
2014-08-16 14:29:20 +02:00
|
|
|
|
2013-07-26 13:12:55 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} // namespace
|
|
|
|
|
2014-12-04 17:07:00 +01:00
|
|
|
void SpdyUpstream::start_downstream(Downstream *downstream) {
|
|
|
|
if (downstream_queue_.can_activate(
|
|
|
|
downstream->get_request_http2_authority())) {
|
2015-03-11 16:17:05 +01:00
|
|
|
initiate_downstream(downstream);
|
2014-12-04 17:07:00 +01:00
|
|
|
return;
|
2014-08-16 14:29:20 +02:00
|
|
|
}
|
2014-12-04 17:07:00 +01:00
|
|
|
|
2015-03-11 16:17:05 +01:00
|
|
|
downstream_queue_.mark_blocked(downstream);
|
2014-08-16 14:29:20 +02:00
|
|
|
}
|
|
|
|
|
2015-03-11 16:17:05 +01:00
|
|
|
void SpdyUpstream::initiate_downstream(Downstream *downstream) {
|
2014-11-27 15:39:04 +01:00
|
|
|
int rv = downstream->attach_downstream_connection(
|
2015-07-09 19:52:11 +02:00
|
|
|
handler_->get_downstream_connection(downstream));
|
2014-11-27 15:39:04 +01:00
|
|
|
if (rv != 0) {
|
2014-08-16 14:29:20 +02:00
|
|
|
// If downstream connection fails, issue RST_STREAM.
|
2015-03-11 16:17:05 +01:00
|
|
|
rst_stream(downstream, SPDYLAY_INTERNAL_ERROR);
|
2014-08-16 14:29:20 +02:00
|
|
|
downstream->set_request_state(Downstream::CONNECT_FAIL);
|
2014-08-18 15:59:31 +02:00
|
|
|
|
2015-03-11 16:17:05 +01:00
|
|
|
downstream_queue_.mark_failure(downstream);
|
2014-08-18 15:59:31 +02:00
|
|
|
|
2014-08-16 14:29:20 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
rv = downstream->push_request_headers();
|
2014-11-27 15:39:04 +01:00
|
|
|
if (rv != 0) {
|
2015-03-11 16:17:05 +01:00
|
|
|
rst_stream(downstream, SPDYLAY_INTERNAL_ERROR);
|
2014-08-18 15:59:31 +02:00
|
|
|
|
2015-03-11 16:17:05 +01:00
|
|
|
downstream_queue_.mark_failure(downstream);
|
2014-08-16 14:29:20 +02:00
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-03-11 16:17:05 +01:00
|
|
|
downstream_queue_.mark_active(downstream);
|
2014-08-16 14:29:20 +02:00
|
|
|
}
|
|
|
|
|
2013-07-26 13:12:55 +02:00
|
|
|
namespace {
|
2014-11-27 15:39:04 +01:00
|
|
|
void on_data_chunk_recv_callback(spdylay_session *session, uint8_t flags,
|
|
|
|
int32_t stream_id, const uint8_t *data,
|
|
|
|
size_t len, void *user_data) {
|
|
|
|
auto upstream = static_cast<SpdyUpstream *>(user_data);
|
2015-03-11 16:17:05 +01:00
|
|
|
auto downstream = static_cast<Downstream *>(
|
|
|
|
spdylay_session_get_stream_user_data(session, stream_id));
|
2014-06-01 16:44:32 +02:00
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
if (!downstream) {
|
2014-08-21 14:22:16 +02:00
|
|
|
upstream->consume(stream_id, len);
|
|
|
|
|
2014-06-01 16:44:32 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-08-09 11:47:45 +02:00
|
|
|
downstream->reset_upstream_rtimer();
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
if (downstream->push_upload_data_chunk(data, len) != 0) {
|
2014-06-01 16:44:32 +02:00
|
|
|
upstream->rst_stream(downstream, SPDYLAY_INTERNAL_ERROR);
|
2014-08-21 14:22:16 +02:00
|
|
|
|
|
|
|
upstream->consume(stream_id, len);
|
|
|
|
|
2014-06-01 16:44:32 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
if (!upstream->get_flow_control()) {
|
2014-06-01 16:44:32 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// If connection-level window control is not enabled (e.g,
|
|
|
|
// spdy/3), spdylay_session_get_recv_data_length() is always
|
|
|
|
// returns 0.
|
2014-11-27 15:39:04 +01:00
|
|
|
if (spdylay_session_get_recv_data_length(session) >
|
|
|
|
std::max(SPDYLAY_INITIAL_WINDOW_SIZE,
|
|
|
|
1 << get_config()->http2_upstream_connection_window_bits)) {
|
|
|
|
if (LOG_ENABLED(INFO)) {
|
2014-06-01 16:44:32 +02:00
|
|
|
ULOG(INFO, upstream)
|
2014-11-27 15:39:04 +01:00
|
|
|
<< "Flow control error on connection: "
|
|
|
|
<< "recv_window_size="
|
|
|
|
<< spdylay_session_get_recv_data_length(session) << ", window_size="
|
|
|
|
<< (1 << get_config()->http2_upstream_connection_window_bits);
|
2013-07-26 13:12:55 +02:00
|
|
|
}
|
2014-06-01 16:44:32 +02:00
|
|
|
spdylay_session_fail_session(session, SPDYLAY_GOAWAY_PROTOCOL_ERROR);
|
|
|
|
return;
|
|
|
|
}
|
2014-11-27 15:39:04 +01:00
|
|
|
if (spdylay_session_get_stream_recv_data_length(session, stream_id) >
|
|
|
|
std::max(SPDYLAY_INITIAL_WINDOW_SIZE,
|
|
|
|
1 << get_config()->http2_upstream_window_bits)) {
|
|
|
|
if (LOG_ENABLED(INFO)) {
|
|
|
|
ULOG(INFO, upstream) << "Flow control error: recv_window_size="
|
|
|
|
<< spdylay_session_get_stream_recv_data_length(
|
|
|
|
session, stream_id)
|
|
|
|
<< ", initial_window_size="
|
|
|
|
<< (1 << get_config()->http2_upstream_window_bits);
|
2013-07-26 13:12:55 +02:00
|
|
|
}
|
2014-06-01 16:44:32 +02:00
|
|
|
upstream->rst_stream(downstream, SPDYLAY_FLOW_CONTROL_ERROR);
|
|
|
|
return;
|
2013-10-29 16:00:58 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
void on_data_recv_callback(spdylay_session *session, uint8_t flags,
|
2014-11-27 15:39:04 +01:00
|
|
|
int32_t stream_id, int32_t length, void *user_data) {
|
|
|
|
auto upstream = static_cast<SpdyUpstream *>(user_data);
|
2015-03-11 16:17:05 +01:00
|
|
|
auto downstream = static_cast<Downstream *>(
|
|
|
|
spdylay_session_get_stream_user_data(session, stream_id));
|
2014-11-27 15:39:04 +01:00
|
|
|
if (downstream && (flags & SPDYLAY_DATA_FLAG_FIN)) {
|
2015-01-17 13:31:28 +01:00
|
|
|
if (!downstream->validate_request_bodylen()) {
|
|
|
|
upstream->rst_stream(downstream, SPDYLAY_PROTOCOL_ERROR);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-08-09 11:47:45 +02:00
|
|
|
downstream->disable_upstream_rtimer();
|
2013-10-29 16:00:58 +01:00
|
|
|
downstream->end_upload_data();
|
|
|
|
downstream->set_request_state(Downstream::MSG_COMPLETE);
|
2013-07-26 13:12:55 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
void on_ctrl_not_send_callback(spdylay_session *session,
|
2014-11-27 15:39:04 +01:00
|
|
|
spdylay_frame_type type, spdylay_frame *frame,
|
|
|
|
int error_code, void *user_data) {
|
|
|
|
auto upstream = static_cast<SpdyUpstream *>(user_data);
|
2015-01-21 14:49:00 +01:00
|
|
|
if (LOG_ENABLED(INFO)) {
|
|
|
|
ULOG(INFO, upstream) << "Failed to send control frame type=" << type
|
|
|
|
<< ", error_code=" << error_code << ":"
|
|
|
|
<< spdylay_strerror(error_code);
|
|
|
|
}
|
2015-01-17 11:33:30 +01:00
|
|
|
if (type == SPDYLAY_SYN_REPLY && error_code != SPDYLAY_ERR_STREAM_CLOSED &&
|
|
|
|
error_code != SPDYLAY_ERR_STREAM_CLOSING) {
|
2013-07-26 13:12:55 +02:00
|
|
|
// To avoid stream hanging around, issue RST_STREAM.
|
2013-12-20 15:36:24 +01:00
|
|
|
auto stream_id = frame->syn_reply.stream_id;
|
2015-03-11 16:17:05 +01:00
|
|
|
// TODO Could be always nullptr
|
|
|
|
auto downstream = static_cast<Downstream *>(
|
|
|
|
spdylay_session_get_stream_user_data(session, stream_id));
|
2014-11-27 15:39:04 +01:00
|
|
|
if (downstream) {
|
2013-07-26 13:12:55 +02:00
|
|
|
upstream->rst_stream(downstream, SPDYLAY_INTERNAL_ERROR);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
void on_ctrl_recv_parse_error_callback(spdylay_session *session,
|
|
|
|
spdylay_frame_type type,
|
|
|
|
const uint8_t *head, size_t headlen,
|
|
|
|
const uint8_t *payload,
|
|
|
|
size_t payloadlen, int error_code,
|
2014-11-27 15:39:04 +01:00
|
|
|
void *user_data) {
|
|
|
|
auto upstream = static_cast<SpdyUpstream *>(user_data);
|
|
|
|
if (LOG_ENABLED(INFO)) {
|
2013-07-26 13:12:55 +02:00
|
|
|
ULOG(INFO, upstream) << "Failed to parse received control frame. type="
|
2014-11-27 15:39:04 +01:00
|
|
|
<< type << ", error_code=" << error_code << ":"
|
2013-07-26 13:12:55 +02:00
|
|
|
<< spdylay_strerror(error_code);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
void on_unknown_ctrl_recv_callback(spdylay_session *session,
|
|
|
|
const uint8_t *head, size_t headlen,
|
|
|
|
const uint8_t *payload, size_t payloadlen,
|
2014-11-27 15:39:04 +01:00
|
|
|
void *user_data) {
|
|
|
|
auto upstream = static_cast<SpdyUpstream *>(user_data);
|
|
|
|
if (LOG_ENABLED(INFO)) {
|
2013-07-26 13:12:55 +02:00
|
|
|
ULOG(INFO, upstream) << "Received unknown control frame.";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
namespace {
|
2014-03-30 12:09:21 +02:00
|
|
|
// Infer upstream RST_STREAM status code from downstream HTTP/2
|
2013-07-26 13:12:55 +02:00
|
|
|
// error code.
|
2014-11-27 15:39:04 +01:00
|
|
|
uint32_t infer_upstream_rst_stream_status_code(uint32_t downstream_error_code) {
|
2013-07-26 13:12:55 +02:00
|
|
|
// Only propagate *_REFUSED_STREAM so that upstream client can
|
|
|
|
// resend request.
|
2014-11-27 15:39:04 +01:00
|
|
|
if (downstream_error_code == NGHTTP2_REFUSED_STREAM) {
|
2013-07-26 13:12:55 +02:00
|
|
|
return SPDYLAY_REFUSED_STREAM;
|
|
|
|
} else {
|
|
|
|
return SPDYLAY_INTERNAL_ERROR;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
SpdyUpstream::SpdyUpstream(uint16_t version, ClientHandler *handler)
|
2015-01-02 04:53:27 +01:00
|
|
|
: downstream_queue_(
|
|
|
|
get_config()->http2_proxy
|
|
|
|
? get_config()->downstream_connections_per_host
|
|
|
|
: get_config()->downstream_proto == PROTO_HTTP
|
|
|
|
? get_config()->downstream_connections_per_frontend
|
|
|
|
: 0,
|
|
|
|
!get_config()->http2_proxy),
|
2014-12-04 17:07:00 +01:00
|
|
|
handler_(handler), session_(nullptr) {
|
2015-07-19 10:55:37 +02:00
|
|
|
spdylay_session_callbacks callbacks{};
|
2013-07-26 13:12:55 +02:00
|
|
|
callbacks.send_callback = send_callback;
|
|
|
|
callbacks.recv_callback = recv_callback;
|
|
|
|
callbacks.on_stream_close_callback = on_stream_close_callback;
|
|
|
|
callbacks.on_ctrl_recv_callback = on_ctrl_recv_callback;
|
|
|
|
callbacks.on_data_chunk_recv_callback = on_data_chunk_recv_callback;
|
2013-10-29 16:00:58 +01:00
|
|
|
callbacks.on_data_recv_callback = on_data_recv_callback;
|
2013-07-26 13:12:55 +02:00
|
|
|
callbacks.on_ctrl_not_send_callback = on_ctrl_not_send_callback;
|
|
|
|
callbacks.on_ctrl_recv_parse_error_callback =
|
2014-11-27 15:39:04 +01:00
|
|
|
on_ctrl_recv_parse_error_callback;
|
2013-07-26 13:12:55 +02:00
|
|
|
callbacks.on_unknown_ctrl_recv_callback = on_unknown_ctrl_recv_callback;
|
|
|
|
|
|
|
|
int rv;
|
|
|
|
rv = spdylay_session_server_new(&session_, version, &callbacks, this);
|
|
|
|
assert(rv == 0);
|
|
|
|
|
2015-06-21 07:32:47 +02:00
|
|
|
uint32_t max_buffer = 64_k;
|
2015-04-29 14:27:36 +02:00
|
|
|
rv = spdylay_session_set_option(session_,
|
|
|
|
SPDYLAY_OPT_MAX_RECV_CTRL_FRAME_BUFFER,
|
|
|
|
&max_buffer, sizeof(max_buffer));
|
|
|
|
assert(rv == 0);
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
if (version >= SPDYLAY_PROTO_SPDY3) {
|
2013-07-26 13:12:55 +02:00
|
|
|
int val = 1;
|
|
|
|
flow_control_ = true;
|
2013-11-20 16:15:17 +01:00
|
|
|
initial_window_size_ = 1 << get_config()->http2_upstream_window_bits;
|
2014-11-27 15:39:04 +01:00
|
|
|
rv = spdylay_session_set_option(
|
|
|
|
session_, SPDYLAY_OPT_NO_AUTO_WINDOW_UPDATE2, &val, sizeof(val));
|
2013-07-26 13:12:55 +02:00
|
|
|
assert(rv == 0);
|
|
|
|
} else {
|
|
|
|
flow_control_ = false;
|
|
|
|
initial_window_size_ = 0;
|
|
|
|
}
|
|
|
|
// TODO Maybe call from outside?
|
2015-02-05 16:06:01 +01:00
|
|
|
std::array<spdylay_settings_entry, 2> entry;
|
2013-07-26 13:12:55 +02:00
|
|
|
entry[0].settings_id = SPDYLAY_SETTINGS_MAX_CONCURRENT_STREAMS;
|
2013-11-04 10:14:05 +01:00
|
|
|
entry[0].value = get_config()->http2_max_concurrent_streams;
|
2013-07-26 13:12:55 +02:00
|
|
|
entry[0].flags = SPDYLAY_ID_FLAG_SETTINGS_NONE;
|
|
|
|
|
|
|
|
entry[1].settings_id = SPDYLAY_SETTINGS_INITIAL_WINDOW_SIZE;
|
|
|
|
entry[1].value = initial_window_size_;
|
|
|
|
entry[1].flags = SPDYLAY_ID_FLAG_SETTINGS_NONE;
|
|
|
|
|
2015-02-05 16:06:01 +01:00
|
|
|
rv = spdylay_submit_settings(session_, SPDYLAY_FLAG_SETTINGS_NONE,
|
|
|
|
entry.data(), entry.size());
|
2013-07-26 13:12:55 +02:00
|
|
|
assert(rv == 0);
|
2013-11-20 16:15:17 +01:00
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
if (version >= SPDYLAY_PROTO_SPDY3_1 &&
|
|
|
|
get_config()->http2_upstream_connection_window_bits > 16) {
|
|
|
|
int32_t delta = (1 << get_config()->http2_upstream_connection_window_bits) -
|
|
|
|
SPDYLAY_INITIAL_WINDOW_SIZE;
|
2013-11-20 16:15:17 +01:00
|
|
|
rv = spdylay_submit_window_update(session_, 0, delta);
|
|
|
|
assert(rv == 0);
|
|
|
|
}
|
|
|
|
|
2014-12-27 18:59:06 +01:00
|
|
|
handler_->reset_upstream_read_timeout(
|
|
|
|
get_config()->http2_upstream_read_timeout);
|
|
|
|
|
|
|
|
handler_->signal_write();
|
2013-07-26 13:12:55 +02:00
|
|
|
}
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
SpdyUpstream::~SpdyUpstream() { spdylay_session_del(session_); }
|
2013-07-26 13:12:55 +02:00
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
int SpdyUpstream::on_read() {
|
2013-07-26 13:12:55 +02:00
|
|
|
int rv = 0;
|
2014-03-03 13:18:24 +01:00
|
|
|
|
|
|
|
rv = spdylay_session_recv(session_);
|
2014-11-27 15:39:04 +01:00
|
|
|
if (rv < 0) {
|
|
|
|
if (rv != SPDYLAY_ERR_EOF) {
|
2013-07-26 13:12:55 +02:00
|
|
|
ULOG(ERROR, this) << "spdylay_session_recv() returned error: "
|
|
|
|
<< spdylay_strerror(rv);
|
|
|
|
}
|
2014-03-03 13:18:24 +01:00
|
|
|
return rv;
|
2013-07-26 13:12:55 +02:00
|
|
|
}
|
|
|
|
|
2014-12-27 18:59:06 +01:00
|
|
|
handler_->signal_write();
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2013-07-26 13:12:55 +02:00
|
|
|
|
|
|
|
// After this function call, downstream may be deleted.
|
2014-12-27 18:59:06 +01:00
|
|
|
int SpdyUpstream::on_write() {
|
2013-07-26 13:12:55 +02:00
|
|
|
int rv = 0;
|
2014-03-03 13:18:24 +01:00
|
|
|
|
2015-10-02 15:42:46 +02:00
|
|
|
if (wb_.rleft() == 0) {
|
|
|
|
wb_.reset();
|
|
|
|
}
|
|
|
|
|
2014-03-03 13:18:24 +01:00
|
|
|
rv = spdylay_session_send(session_);
|
2014-11-27 15:39:04 +01:00
|
|
|
if (rv != 0) {
|
2013-07-26 13:12:55 +02:00
|
|
|
ULOG(ERROR, this) << "spdylay_session_send() returned error: "
|
|
|
|
<< spdylay_strerror(rv);
|
2014-03-03 13:18:24 +01:00
|
|
|
return rv;
|
2013-07-26 13:12:55 +02:00
|
|
|
}
|
2014-03-04 16:23:33 +01:00
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
if (spdylay_session_want_read(session_) == 0 &&
|
2015-10-02 15:42:46 +02:00
|
|
|
spdylay_session_want_write(session_) == 0 && wb_.rleft() == 0) {
|
2014-11-27 15:39:04 +01:00
|
|
|
if (LOG_ENABLED(INFO)) {
|
2014-03-03 13:18:24 +01:00
|
|
|
ULOG(INFO, this) << "No more read/write for this SPDY session";
|
2013-07-26 13:12:55 +02:00
|
|
|
}
|
2014-03-03 13:18:24 +01:00
|
|
|
return -1;
|
2013-07-26 13:12:55 +02:00
|
|
|
}
|
2014-03-03 13:18:24 +01:00
|
|
|
return 0;
|
2013-07-26 13:12:55 +02:00
|
|
|
}
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
ClientHandler *SpdyUpstream::get_client_handler() const { return handler_; }
|
2013-07-26 13:12:55 +02:00
|
|
|
|
2014-12-27 18:59:06 +01:00
|
|
|
int SpdyUpstream::downstream_read(DownstreamConnection *dconn) {
|
2013-12-20 15:36:24 +01:00
|
|
|
auto downstream = dconn->get_downstream();
|
2014-12-27 18:59:06 +01:00
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
if (downstream->get_response_state() == Downstream::MSG_RESET) {
|
2013-07-26 13:12:55 +02:00
|
|
|
// The downstream stream was reset (canceled). In this case,
|
|
|
|
// RST_STREAM to the upstream and delete downstream connection
|
|
|
|
// here. Deleting downstream will be taken place at
|
|
|
|
// on_stream_close_callback.
|
2014-12-27 18:59:06 +01:00
|
|
|
rst_stream(downstream,
|
|
|
|
infer_upstream_rst_stream_status_code(
|
|
|
|
downstream->get_response_rst_stream_error_code()));
|
2014-08-18 17:16:51 +02:00
|
|
|
downstream->pop_downstream_connection();
|
2014-06-01 16:44:32 +02:00
|
|
|
dconn = nullptr;
|
2015-01-19 15:44:23 +01:00
|
|
|
} else if (downstream->get_response_state() == Downstream::MSG_BAD_HEADER) {
|
|
|
|
if (error_reply(downstream, 502) != 0) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
downstream->pop_downstream_connection();
|
|
|
|
// dconn was deleted
|
|
|
|
dconn = nullptr;
|
2013-07-26 13:12:55 +02:00
|
|
|
} else {
|
2014-06-01 16:44:32 +02:00
|
|
|
auto rv = downstream->on_read();
|
2015-02-04 13:15:58 +01:00
|
|
|
if (rv == SHRPX_ERR_EOF) {
|
2014-12-27 18:59:06 +01:00
|
|
|
return downstream_eof(dconn);
|
|
|
|
}
|
2015-09-03 19:46:35 +02:00
|
|
|
if (rv == SHRPX_ERR_DCONN_CANCELED) {
|
|
|
|
downstream->pop_downstream_connection();
|
2015-09-04 15:34:40 +02:00
|
|
|
handler_->signal_write();
|
2015-09-03 19:46:35 +02:00
|
|
|
return 0;
|
|
|
|
}
|
2014-11-27 15:39:04 +01:00
|
|
|
if (rv != 0) {
|
2015-02-04 13:15:58 +01:00
|
|
|
if (rv != SHRPX_ERR_NETWORK) {
|
2014-12-27 18:59:06 +01:00
|
|
|
if (LOG_ENABLED(INFO)) {
|
|
|
|
DCLOG(INFO, dconn) << "HTTP parser failure";
|
2013-07-26 13:12:55 +02:00
|
|
|
}
|
|
|
|
}
|
2014-12-27 18:59:06 +01:00
|
|
|
return downstream_error(dconn, Downstream::EVENT_ERROR);
|
2013-07-26 13:12:55 +02:00
|
|
|
}
|
2015-07-22 14:41:16 +02:00
|
|
|
if (downstream->can_detach_downstream_connection()) {
|
2015-01-30 17:11:55 +01:00
|
|
|
// Keep-alive
|
|
|
|
downstream->detach_downstream_connection();
|
|
|
|
}
|
2013-07-26 13:12:55 +02:00
|
|
|
}
|
2014-12-27 18:59:06 +01:00
|
|
|
|
|
|
|
handler_->signal_write();
|
2013-07-26 13:12:55 +02:00
|
|
|
// At this point, downstream may be deleted.
|
2014-12-27 18:59:06 +01:00
|
|
|
|
|
|
|
return 0;
|
2013-07-26 13:12:55 +02:00
|
|
|
}
|
|
|
|
|
2014-12-27 18:59:06 +01:00
|
|
|
int SpdyUpstream::downstream_write(DownstreamConnection *dconn) {
|
|
|
|
int rv;
|
|
|
|
rv = dconn->on_write();
|
2015-02-04 13:15:58 +01:00
|
|
|
if (rv == SHRPX_ERR_NETWORK) {
|
2014-12-27 18:59:06 +01:00
|
|
|
return downstream_error(dconn, Downstream::EVENT_ERROR);
|
2013-07-26 13:12:55 +02:00
|
|
|
}
|
2014-12-27 18:59:06 +01:00
|
|
|
if (rv != 0) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
return 0;
|
2013-07-26 13:12:55 +02:00
|
|
|
}
|
|
|
|
|
2014-12-27 18:59:06 +01:00
|
|
|
int SpdyUpstream::downstream_eof(DownstreamConnection *dconn) {
|
2013-12-20 15:36:24 +01:00
|
|
|
auto downstream = dconn->get_downstream();
|
2014-06-01 16:44:32 +02:00
|
|
|
|
2014-12-27 18:59:06 +01:00
|
|
|
if (LOG_ENABLED(INFO)) {
|
|
|
|
DCLOG(INFO, dconn) << "EOF. stream_id=" << downstream->get_stream_id();
|
2014-06-01 16:44:32 +02:00
|
|
|
}
|
2014-12-27 18:59:06 +01:00
|
|
|
|
|
|
|
// Delete downstream connection. If we don't delete it here, it will
|
|
|
|
// be pooled in on_stream_close_callback.
|
|
|
|
downstream->pop_downstream_connection();
|
|
|
|
// dconn was deleted
|
|
|
|
dconn = nullptr;
|
|
|
|
// downstream wil be deleted in on_stream_close_callback.
|
|
|
|
if (downstream->get_response_state() == Downstream::HEADER_COMPLETE) {
|
|
|
|
// Server may indicate the end of the request by EOF
|
2014-11-27 15:39:04 +01:00
|
|
|
if (LOG_ENABLED(INFO)) {
|
2014-12-27 18:59:06 +01:00
|
|
|
ULOG(INFO, this) << "Downstream body was ended by EOF";
|
2013-07-26 13:12:55 +02:00
|
|
|
}
|
2014-12-27 18:59:06 +01:00
|
|
|
downstream->set_response_state(Downstream::MSG_COMPLETE);
|
|
|
|
|
|
|
|
// For tunneled connection, MSG_COMPLETE signals
|
|
|
|
// downstream_data_read_callback to send RST_STREAM after pending
|
|
|
|
// response body is sent. This is needed to ensure that RST_STREAM
|
|
|
|
// is sent after all pending data are sent.
|
|
|
|
on_downstream_body_complete(downstream);
|
|
|
|
} else if (downstream->get_response_state() != Downstream::MSG_COMPLETE) {
|
|
|
|
// If stream was not closed, then we set MSG_COMPLETE and let
|
|
|
|
// on_stream_close_callback delete downstream.
|
|
|
|
if (error_reply(downstream, 502) != 0) {
|
|
|
|
return -1;
|
2014-06-01 16:44:32 +02:00
|
|
|
}
|
2014-12-27 18:59:06 +01:00
|
|
|
}
|
|
|
|
handler_->signal_write();
|
|
|
|
// At this point, downstream may be deleted.
|
|
|
|
return 0;
|
|
|
|
}
|
2013-07-26 13:12:55 +02:00
|
|
|
|
2014-12-27 18:59:06 +01:00
|
|
|
int SpdyUpstream::downstream_error(DownstreamConnection *dconn, int events) {
|
|
|
|
auto downstream = dconn->get_downstream();
|
|
|
|
|
|
|
|
if (LOG_ENABLED(INFO)) {
|
|
|
|
if (events & Downstream::EVENT_ERROR) {
|
|
|
|
DCLOG(INFO, dconn) << "Downstream network/general error";
|
|
|
|
} else {
|
|
|
|
DCLOG(INFO, dconn) << "Timeout";
|
2013-07-26 13:12:55 +02:00
|
|
|
}
|
2014-12-27 18:59:06 +01:00
|
|
|
if (downstream->get_upgraded()) {
|
|
|
|
DCLOG(INFO, dconn) << "Note: this is tunnel connection";
|
2014-06-01 16:44:32 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-12-27 18:59:06 +01:00
|
|
|
// Delete downstream connection. If we don't delete it here, it will
|
|
|
|
// be pooled in on_stream_close_callback.
|
|
|
|
downstream->pop_downstream_connection();
|
|
|
|
// dconn was deleted
|
|
|
|
dconn = nullptr;
|
2014-06-01 16:44:32 +02:00
|
|
|
|
2014-12-27 18:59:06 +01:00
|
|
|
if (downstream->get_response_state() == Downstream::MSG_COMPLETE) {
|
|
|
|
// For SSL tunneling, we issue RST_STREAM. For other types of
|
|
|
|
// stream, we don't have to do anything since response was
|
|
|
|
// complete.
|
|
|
|
if (downstream->get_upgraded()) {
|
2015-01-17 13:35:36 +01:00
|
|
|
// We want "NO_ERROR" error code but SPDY does not have such
|
|
|
|
// code for RST_STREAM.
|
|
|
|
rst_stream(downstream, SPDYLAY_INTERNAL_ERROR);
|
2014-12-27 18:59:06 +01:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (downstream->get_response_state() == Downstream::HEADER_COMPLETE) {
|
2014-11-27 15:39:04 +01:00
|
|
|
if (downstream->get_upgraded()) {
|
2014-12-27 18:59:06 +01:00
|
|
|
on_downstream_body_complete(downstream);
|
|
|
|
} else {
|
2015-01-17 13:35:36 +01:00
|
|
|
rst_stream(downstream, SPDYLAY_INTERNAL_ERROR);
|
2014-06-01 16:44:32 +02:00
|
|
|
}
|
2013-07-26 13:12:55 +02:00
|
|
|
} else {
|
2014-12-27 18:59:06 +01:00
|
|
|
unsigned int status;
|
|
|
|
if (events & Downstream::EVENT_TIMEOUT) {
|
|
|
|
status = 504;
|
2013-07-26 13:12:55 +02:00
|
|
|
} else {
|
2014-12-27 18:59:06 +01:00
|
|
|
status = 502;
|
|
|
|
}
|
|
|
|
if (error_reply(downstream, status) != 0) {
|
|
|
|
return -1;
|
2013-07-26 13:12:55 +02:00
|
|
|
}
|
2014-06-01 16:44:32 +02:00
|
|
|
}
|
2014-12-27 18:59:06 +01:00
|
|
|
downstream->set_response_state(Downstream::MSG_COMPLETE);
|
2013-07-26 13:12:55 +02:00
|
|
|
}
|
2014-12-27 18:59:06 +01:00
|
|
|
handler_->signal_write();
|
|
|
|
// At this point, downstream may be deleted.
|
|
|
|
return 0;
|
2013-07-26 13:12:55 +02:00
|
|
|
}
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
int SpdyUpstream::rst_stream(Downstream *downstream, int status_code) {
|
|
|
|
if (LOG_ENABLED(INFO)) {
|
|
|
|
ULOG(INFO, this) << "RST_STREAM stream_id=" << downstream->get_stream_id();
|
2013-07-26 13:12:55 +02:00
|
|
|
}
|
|
|
|
int rv;
|
|
|
|
rv = spdylay_submit_rst_stream(session_, downstream->get_stream_id(),
|
|
|
|
status_code);
|
2014-11-27 15:39:04 +01:00
|
|
|
if (rv < SPDYLAY_ERR_FATAL) {
|
2013-07-26 13:12:55 +02:00
|
|
|
ULOG(FATAL, this) << "spdylay_submit_rst_stream() failed: "
|
|
|
|
<< spdylay_strerror(rv);
|
|
|
|
DIE();
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
namespace {
|
2014-11-27 15:39:04 +01:00
|
|
|
ssize_t spdy_data_read_callback(spdylay_session *session, int32_t stream_id,
|
|
|
|
uint8_t *buf, size_t length, int *eof,
|
|
|
|
spdylay_data_source *source, void *user_data) {
|
|
|
|
auto downstream = static_cast<Downstream *>(source->ptr);
|
|
|
|
auto upstream = static_cast<SpdyUpstream *>(downstream->get_upstream());
|
2014-12-27 18:59:06 +01:00
|
|
|
auto body = downstream->get_response_buf();
|
2013-07-26 13:12:55 +02:00
|
|
|
assert(body);
|
2014-11-06 13:14:14 +01:00
|
|
|
|
2015-01-20 17:41:17 +01:00
|
|
|
auto dconn = downstream->get_downstream_connection();
|
|
|
|
|
|
|
|
if (body->rleft() == 0 && dconn &&
|
|
|
|
downstream->get_response_state() != Downstream::MSG_COMPLETE) {
|
|
|
|
// Try to read more if buffer is empty. This will help small
|
|
|
|
// buffer and make priority handling a bit better.
|
|
|
|
if (upstream->downstream_read(dconn) != 0) {
|
|
|
|
return SPDYLAY_ERR_CALLBACK_FAILURE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-12-27 18:59:06 +01:00
|
|
|
auto nread = body->remove(buf, length);
|
|
|
|
auto body_empty = body->rleft() == 0;
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
if (nread == 0 &&
|
|
|
|
downstream->get_response_state() == Downstream::MSG_COMPLETE) {
|
|
|
|
if (!downstream->get_upgraded()) {
|
2013-07-26 13:12:55 +02:00
|
|
|
*eof = 1;
|
|
|
|
} else {
|
|
|
|
// For tunneling, issue RST_STREAM to finish the stream.
|
2014-11-27 15:39:04 +01:00
|
|
|
if (LOG_ENABLED(INFO)) {
|
|
|
|
ULOG(INFO, upstream)
|
|
|
|
<< "RST_STREAM to tunneled stream stream_id=" << stream_id;
|
2013-07-26 13:12:55 +02:00
|
|
|
}
|
2014-11-27 15:39:04 +01:00
|
|
|
upstream->rst_stream(
|
|
|
|
downstream, infer_upstream_rst_stream_status_code(
|
|
|
|
downstream->get_response_rst_stream_error_code()));
|
2013-07-26 13:12:55 +02:00
|
|
|
}
|
|
|
|
}
|
2014-05-16 14:42:30 +02:00
|
|
|
|
2014-12-27 18:59:06 +01:00
|
|
|
if (body_empty) {
|
2014-08-09 11:47:45 +02:00
|
|
|
downstream->disable_upstream_wtimer();
|
2014-12-27 18:59:06 +01:00
|
|
|
} else {
|
|
|
|
downstream->reset_upstream_wtimer();
|
2014-08-09 11:47:45 +02:00
|
|
|
}
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
if (nread > 0 && downstream->resume_read(SHRPX_NO_BUFFER, nread) != 0) {
|
2014-08-21 14:22:16 +02:00
|
|
|
return SPDYLAY_ERR_CALLBACK_FAILURE;
|
|
|
|
}
|
2014-05-16 14:42:30 +02:00
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
if (nread == 0 && *eof != 1) {
|
2014-08-21 14:22:16 +02:00
|
|
|
return SPDYLAY_ERR_DEFERRED;
|
2013-07-26 13:12:55 +02:00
|
|
|
}
|
2014-05-16 14:42:30 +02:00
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
if (nread > 0) {
|
2014-11-18 16:56:44 +01:00
|
|
|
downstream->add_response_sent_bodylen(nread);
|
|
|
|
}
|
|
|
|
|
2013-07-26 13:12:55 +02:00
|
|
|
return nread;
|
|
|
|
}
|
|
|
|
} // namespace
|
|
|
|
|
2015-09-03 15:29:16 +02:00
|
|
|
int SpdyUpstream::send_reply(Downstream *downstream, const uint8_t *body,
|
|
|
|
size_t bodylen) {
|
|
|
|
int rv;
|
|
|
|
|
|
|
|
spdylay_data_provider data_prd, *data_prd_ptr = nullptr;
|
|
|
|
if (bodylen) {
|
|
|
|
data_prd.source.ptr = downstream;
|
|
|
|
data_prd.read_callback = spdy_data_read_callback;
|
|
|
|
data_prd_ptr = &data_prd;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto status_string =
|
|
|
|
http2::get_status_string(downstream->get_response_http_status());
|
|
|
|
|
|
|
|
auto &headers = downstream->get_response_headers();
|
|
|
|
|
2015-09-03 19:46:35 +02:00
|
|
|
auto nva = std::vector<const char *>();
|
2015-09-03 15:29:16 +02:00
|
|
|
// 3 for :status, :version and server
|
2015-09-03 19:46:35 +02:00
|
|
|
nva.reserve(3 + headers.size());
|
2015-09-03 15:29:16 +02:00
|
|
|
|
|
|
|
nva.push_back(":status");
|
|
|
|
nva.push_back(status_string.c_str());
|
|
|
|
nva.push_back(":version");
|
|
|
|
nva.push_back("HTTP/1.1");
|
|
|
|
|
|
|
|
for (auto &kv : headers) {
|
|
|
|
if (kv.name.empty() || kv.name[0] == ':') {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
switch (kv.token) {
|
|
|
|
case http2::HD_CONNECTION:
|
|
|
|
case http2::HD_KEEP_ALIVE:
|
|
|
|
case http2::HD_PROXY_CONNECTION:
|
|
|
|
case http2::HD_TRANSFER_ENCODING:
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
nva.push_back(kv.name.c_str());
|
|
|
|
nva.push_back(kv.value.c_str());
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!downstream->get_response_header(http2::HD_SERVER)) {
|
|
|
|
nva.push_back("server");
|
|
|
|
nva.push_back(get_config()->server_name);
|
|
|
|
}
|
|
|
|
|
|
|
|
nva.push_back(nullptr);
|
|
|
|
|
|
|
|
rv = spdylay_submit_response(session_, downstream->get_stream_id(),
|
|
|
|
nva.data(), data_prd_ptr);
|
|
|
|
if (rv < SPDYLAY_ERR_FATAL) {
|
|
|
|
ULOG(FATAL, this) << "spdylay_submit_response() failed: "
|
|
|
|
<< spdylay_strerror(rv);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto buf = downstream->get_response_buf();
|
|
|
|
|
|
|
|
buf->append(body, bodylen);
|
|
|
|
|
|
|
|
downstream->set_response_state(Downstream::MSG_COMPLETE);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
int SpdyUpstream::error_reply(Downstream *downstream,
|
|
|
|
unsigned int status_code) {
|
2013-07-26 13:12:55 +02:00
|
|
|
int rv;
|
2013-12-20 15:36:24 +01:00
|
|
|
auto html = http::create_error_html(status_code);
|
2014-07-05 11:22:40 +02:00
|
|
|
downstream->set_response_http_status(status_code);
|
2014-12-27 18:59:06 +01:00
|
|
|
auto body = downstream->get_response_buf();
|
|
|
|
body->append(html.c_str(), html.size());
|
2013-07-26 13:12:55 +02:00
|
|
|
downstream->set_response_state(Downstream::MSG_COMPLETE);
|
|
|
|
|
|
|
|
spdylay_data_provider data_prd;
|
|
|
|
data_prd.source.ptr = downstream;
|
|
|
|
data_prd.read_callback = spdy_data_read_callback;
|
|
|
|
|
2015-09-07 16:11:23 +02:00
|
|
|
auto lgconf = log_config();
|
|
|
|
lgconf->update_tstamp(std::chrono::system_clock::now());
|
|
|
|
|
2013-07-26 13:12:55 +02:00
|
|
|
std::string content_length = util::utos(html.size());
|
2013-10-02 16:29:44 +02:00
|
|
|
std::string status_string = http2::get_status_string(status_code);
|
2014-11-27 15:39:04 +01:00
|
|
|
const char *nv[] = {":status", status_string.c_str(),
|
|
|
|
":version", "http/1.1",
|
|
|
|
"content-type", "text/html; charset=UTF-8",
|
|
|
|
"server", get_config()->server_name,
|
|
|
|
"content-length", content_length.c_str(),
|
2015-09-07 16:11:23 +02:00
|
|
|
"date", lgconf->time_http_str.c_str(),
|
2014-11-27 15:39:04 +01:00
|
|
|
nullptr};
|
2013-07-26 13:12:55 +02:00
|
|
|
|
|
|
|
rv = spdylay_submit_response(session_, downstream->get_stream_id(), nv,
|
|
|
|
&data_prd);
|
2014-11-27 15:39:04 +01:00
|
|
|
if (rv < SPDYLAY_ERR_FATAL) {
|
2013-07-26 13:12:55 +02:00
|
|
|
ULOG(FATAL, this) << "spdylay_submit_response() failed: "
|
|
|
|
<< spdylay_strerror(rv);
|
2015-01-21 14:55:00 +01:00
|
|
|
return -1;
|
2013-07-26 13:12:55 +02:00
|
|
|
}
|
2014-07-05 11:22:40 +02:00
|
|
|
|
2013-07-26 13:12:55 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
Downstream *SpdyUpstream::add_pending_downstream(int32_t stream_id,
|
|
|
|
int32_t priority) {
|
2015-04-07 15:13:01 +02:00
|
|
|
auto downstream = make_unique<Downstream>(this, handler_->get_mcpool(),
|
|
|
|
stream_id, priority);
|
2015-03-11 16:17:05 +01:00
|
|
|
spdylay_session_set_stream_user_data(session_, stream_id, downstream.get());
|
2014-08-18 15:59:31 +02:00
|
|
|
auto res = downstream.get();
|
|
|
|
|
|
|
|
downstream_queue_.add_pending(std::move(downstream));
|
|
|
|
|
|
|
|
return res;
|
2013-07-26 13:12:55 +02:00
|
|
|
}
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
void SpdyUpstream::remove_downstream(Downstream *downstream) {
|
|
|
|
if (downstream->accesslog_ready()) {
|
2014-11-18 16:56:44 +01:00
|
|
|
handler_->write_accesslog(downstream);
|
|
|
|
}
|
|
|
|
|
2015-03-11 16:17:05 +01:00
|
|
|
spdylay_session_set_stream_user_data(session_, downstream->get_stream_id(),
|
|
|
|
nullptr);
|
|
|
|
|
|
|
|
auto next_downstream = downstream_queue_.remove_and_get_blocked(downstream);
|
2014-08-16 14:29:20 +02:00
|
|
|
|
2014-12-04 17:07:00 +01:00
|
|
|
if (next_downstream) {
|
2015-03-11 16:17:05 +01:00
|
|
|
initiate_downstream(next_downstream);
|
2014-12-04 17:07:00 +01:00
|
|
|
}
|
2013-07-26 13:12:55 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// WARNING: Never call directly or indirectly spdylay_session_send or
|
|
|
|
// spdylay_session_recv. These calls may delete downstream.
|
2014-11-27 15:39:04 +01:00
|
|
|
int SpdyUpstream::on_downstream_header_complete(Downstream *downstream) {
|
|
|
|
if (downstream->get_non_final_response()) {
|
2014-07-23 16:32:57 +02:00
|
|
|
// SPDY does not support non-final response. We could send it
|
|
|
|
// with HEADERS and final response in SYN_REPLY, but it is not
|
|
|
|
// official way.
|
|
|
|
downstream->clear_response_headers();
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-09-03 17:54:41 +02:00
|
|
|
#ifdef HAVE_MRUBY
|
2015-09-02 18:32:15 +02:00
|
|
|
auto worker = handler_->get_worker();
|
|
|
|
auto mruby_ctx = worker->get_mruby_context();
|
|
|
|
|
|
|
|
if (mruby_ctx->run_on_response_proc(downstream) != 0) {
|
|
|
|
if (error_reply(downstream, 500) != 0) {
|
|
|
|
return -1;
|
|
|
|
}
|
2015-09-12 11:33:07 +02:00
|
|
|
// Returning -1 will signal deletion of dconn.
|
2015-09-02 18:32:15 +02:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (downstream->get_response_state() == Downstream::MSG_COMPLETE) {
|
|
|
|
return -1;
|
|
|
|
}
|
2015-09-03 17:54:41 +02:00
|
|
|
#endif // HAVE_MRUBY
|
2015-09-02 18:32:15 +02:00
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
if (LOG_ENABLED(INFO)) {
|
2013-07-26 13:12:55 +02:00
|
|
|
DLOG(INFO, downstream) << "HTTP response header completed";
|
|
|
|
}
|
2015-01-04 15:22:39 +01:00
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
if (!get_config()->http2_proxy && !get_config()->client_proxy &&
|
|
|
|
!get_config()->no_location_rewrite) {
|
2015-01-04 15:22:39 +01:00
|
|
|
downstream->rewrite_location_response_header(
|
2015-02-08 11:41:45 +01:00
|
|
|
downstream->get_request_http2_scheme());
|
2013-12-28 09:02:43 +01:00
|
|
|
}
|
2013-07-26 13:12:55 +02:00
|
|
|
size_t nheader = downstream->get_response_headers().size();
|
2014-08-14 15:45:21 +02:00
|
|
|
// 8 means server, :status, :version and possible via header field.
|
2015-02-05 15:21:53 +01:00
|
|
|
auto nv = make_unique<const char *[]>(
|
2014-11-27 15:39:04 +01:00
|
|
|
nheader * 2 + 8 + get_config()->add_response_headers.size() * 2 + 1);
|
2014-04-26 07:56:08 +02:00
|
|
|
|
2013-07-26 13:12:55 +02:00
|
|
|
size_t hdidx = 0;
|
|
|
|
std::string via_value;
|
2014-11-27 15:39:04 +01:00
|
|
|
std::string status_string =
|
|
|
|
http2::get_status_string(downstream->get_response_http_status());
|
2013-10-02 16:29:44 +02:00
|
|
|
nv[hdidx++] = ":status";
|
|
|
|
nv[hdidx++] = status_string.c_str();
|
2013-07-26 13:12:55 +02:00
|
|
|
nv[hdidx++] = ":version";
|
|
|
|
nv[hdidx++] = "HTTP/1.1";
|
2014-11-27 15:39:04 +01:00
|
|
|
for (auto &hd : downstream->get_response_headers()) {
|
2015-01-04 15:22:39 +01:00
|
|
|
if (hd.name.empty() || hd.name.c_str()[0] == ':') {
|
|
|
|
continue;
|
2013-07-26 13:12:55 +02:00
|
|
|
}
|
2015-02-08 06:07:01 +01:00
|
|
|
switch (hd.token) {
|
2015-01-04 15:22:39 +01:00
|
|
|
case http2::HD_CONNECTION:
|
|
|
|
case http2::HD_KEEP_ALIVE:
|
|
|
|
case http2::HD_PROXY_CONNECTION:
|
|
|
|
case http2::HD_TRANSFER_ENCODING:
|
|
|
|
case http2::HD_VIA:
|
|
|
|
case http2::HD_SERVER:
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
nv[hdidx++] = hd.name.c_str();
|
|
|
|
nv[hdidx++] = hd.value.c_str();
|
2013-07-26 13:12:55 +02:00
|
|
|
}
|
2014-08-14 15:45:21 +02:00
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
if (!get_config()->http2_proxy && !get_config()->client_proxy) {
|
2014-08-14 15:45:21 +02:00
|
|
|
nv[hdidx++] = "server";
|
|
|
|
nv[hdidx++] = get_config()->server_name;
|
2015-01-04 15:22:39 +01:00
|
|
|
} else {
|
|
|
|
auto server = downstream->get_response_header(http2::HD_SERVER);
|
|
|
|
if (server) {
|
|
|
|
nv[hdidx++] = "server";
|
|
|
|
nv[hdidx++] = server->value.c_str();
|
|
|
|
}
|
2014-08-14 15:45:21 +02:00
|
|
|
}
|
|
|
|
|
2015-01-26 16:19:57 +01:00
|
|
|
auto via = downstream->get_response_header(http2::HD_VIA);
|
|
|
|
if (get_config()->no_via) {
|
|
|
|
if (via) {
|
|
|
|
nv[hdidx++] = "via";
|
|
|
|
nv[hdidx++] = via->value.c_str();
|
|
|
|
}
|
|
|
|
} else {
|
2015-01-04 15:22:39 +01:00
|
|
|
if (via) {
|
|
|
|
via_value = via->value;
|
2013-07-26 13:12:55 +02:00
|
|
|
via_value += ", ";
|
|
|
|
}
|
2014-11-27 15:39:04 +01:00
|
|
|
via_value += http::create_via_header_value(
|
|
|
|
downstream->get_response_major(), downstream->get_response_minor());
|
2013-07-26 13:12:55 +02:00
|
|
|
nv[hdidx++] = "via";
|
|
|
|
nv[hdidx++] = via_value.c_str();
|
|
|
|
}
|
2014-04-26 07:56:08 +02:00
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
for (auto &p : get_config()->add_response_headers) {
|
2014-04-26 07:56:08 +02:00
|
|
|
nv[hdidx++] = p.first.c_str();
|
|
|
|
nv[hdidx++] = p.second.c_str();
|
|
|
|
}
|
|
|
|
|
2013-07-26 13:12:55 +02:00
|
|
|
nv[hdidx++] = 0;
|
2014-11-27 15:39:04 +01:00
|
|
|
if (LOG_ENABLED(INFO)) {
|
2013-07-26 13:12:55 +02:00
|
|
|
std::stringstream ss;
|
2014-11-27 15:39:04 +01:00
|
|
|
for (size_t i = 0; nv[i]; i += 2) {
|
|
|
|
ss << TTY_HTTP_HD << nv[i] << TTY_RST << ": " << nv[i + 1] << "\n";
|
2013-07-26 13:12:55 +02:00
|
|
|
}
|
|
|
|
ULOG(INFO, this) << "HTTP response headers. stream_id="
|
2014-11-27 15:39:04 +01:00
|
|
|
<< downstream->get_stream_id() << "\n" << ss.str();
|
2013-07-26 13:12:55 +02:00
|
|
|
}
|
|
|
|
spdylay_data_provider data_prd;
|
|
|
|
data_prd.source.ptr = downstream;
|
|
|
|
data_prd.read_callback = spdy_data_read_callback;
|
|
|
|
|
|
|
|
int rv;
|
2013-09-06 18:52:46 +02:00
|
|
|
rv = spdylay_submit_response(session_, downstream->get_stream_id(), nv.get(),
|
2013-07-26 13:12:55 +02:00
|
|
|
&data_prd);
|
2014-11-27 15:39:04 +01:00
|
|
|
if (rv != 0) {
|
2013-07-26 13:12:55 +02:00
|
|
|
ULOG(FATAL, this) << "spdylay_submit_response() failed";
|
|
|
|
return -1;
|
|
|
|
}
|
2014-07-05 11:22:40 +02:00
|
|
|
|
2013-07-26 13:12:55 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// WARNING: Never call directly or indirectly spdylay_session_send or
|
|
|
|
// spdylay_session_recv. These calls may delete downstream.
|
|
|
|
int SpdyUpstream::on_downstream_body(Downstream *downstream,
|
2014-04-03 11:54:15 +02:00
|
|
|
const uint8_t *data, size_t len,
|
2014-11-27 15:39:04 +01:00
|
|
|
bool flush) {
|
2014-12-27 18:59:06 +01:00
|
|
|
auto body = downstream->get_response_buf();
|
|
|
|
body->append(data, len);
|
2014-04-03 11:54:15 +02:00
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
if (flush) {
|
2014-04-03 11:54:15 +02:00
|
|
|
spdylay_session_resume_data(session_, downstream->get_stream_id());
|
2014-08-09 11:47:45 +02:00
|
|
|
|
|
|
|
downstream->ensure_upstream_wtimer();
|
2014-04-03 11:54:15 +02:00
|
|
|
}
|
2013-07-26 13:12:55 +02:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// WARNING: Never call directly or indirectly spdylay_session_send or
|
|
|
|
// spdylay_session_recv. These calls may delete downstream.
|
2014-11-27 15:39:04 +01:00
|
|
|
int SpdyUpstream::on_downstream_body_complete(Downstream *downstream) {
|
|
|
|
if (LOG_ENABLED(INFO)) {
|
2013-07-26 13:12:55 +02:00
|
|
|
DLOG(INFO, downstream) << "HTTP response completed";
|
|
|
|
}
|
2014-08-09 11:47:45 +02:00
|
|
|
|
2015-01-17 11:33:30 +01:00
|
|
|
if (!downstream->validate_response_bodylen()) {
|
|
|
|
rst_stream(downstream, SPDYLAY_PROTOCOL_ERROR);
|
|
|
|
downstream->set_response_connection_close(true);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-07-26 13:12:55 +02:00
|
|
|
spdylay_session_resume_data(session_, downstream->get_stream_id());
|
2014-08-09 11:47:45 +02:00
|
|
|
downstream->ensure_upstream_wtimer();
|
|
|
|
|
2013-07-26 13:12:55 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
bool SpdyUpstream::get_flow_control() const { return flow_control_; }
|
2013-07-26 13:12:55 +02:00
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
void SpdyUpstream::pause_read(IOCtrlReason reason) {}
|
2013-07-26 13:12:55 +02:00
|
|
|
|
2014-08-21 14:22:16 +02:00
|
|
|
int SpdyUpstream::resume_read(IOCtrlReason reason, Downstream *downstream,
|
2014-11-27 15:39:04 +01:00
|
|
|
size_t consumed) {
|
|
|
|
if (get_flow_control()) {
|
2014-08-21 14:22:16 +02:00
|
|
|
assert(downstream->get_request_datalen() >= consumed);
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
if (consume(downstream->get_stream_id(), consumed) != 0) {
|
2014-08-21 14:22:16 +02:00
|
|
|
return -1;
|
2013-07-26 13:12:55 +02:00
|
|
|
}
|
2014-08-21 14:22:16 +02:00
|
|
|
|
|
|
|
downstream->dec_request_datalen(consumed);
|
2013-07-26 13:12:55 +02:00
|
|
|
}
|
2014-08-21 14:22:16 +02:00
|
|
|
|
2014-12-27 18:59:06 +01:00
|
|
|
handler_->signal_write();
|
|
|
|
return 0;
|
2013-07-26 13:12:55 +02:00
|
|
|
}
|
|
|
|
|
2014-06-27 15:34:54 +02:00
|
|
|
int SpdyUpstream::on_downstream_abort_request(Downstream *downstream,
|
2014-11-27 15:39:04 +01:00
|
|
|
unsigned int status_code) {
|
2014-06-27 15:34:54 +02:00
|
|
|
int rv;
|
|
|
|
|
|
|
|
rv = error_reply(downstream, status_code);
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
if (rv != 0) {
|
2014-06-27 15:34:54 +02:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2014-12-27 18:59:06 +01:00
|
|
|
handler_->signal_write();
|
|
|
|
return 0;
|
2014-06-27 15:34:54 +02:00
|
|
|
}
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
int SpdyUpstream::consume(int32_t stream_id, size_t len) {
|
2014-08-21 14:22:16 +02:00
|
|
|
int rv;
|
2014-07-02 16:07:46 +02:00
|
|
|
|
2014-08-21 14:22:16 +02:00
|
|
|
rv = spdylay_session_consume(session_, stream_id, len);
|
2014-07-02 16:07:46 +02:00
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
if (rv != 0) {
|
2014-11-08 02:51:56 +01:00
|
|
|
ULOG(WARN, this) << "spdylay_session_consume() returned error: "
|
|
|
|
<< spdylay_strerror(rv);
|
2014-08-21 14:22:16 +02:00
|
|
|
return -1;
|
2014-07-02 16:07:46 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
int SpdyUpstream::on_timeout(Downstream *downstream) {
|
|
|
|
if (LOG_ENABLED(INFO)) {
|
2014-08-09 11:47:45 +02:00
|
|
|
ULOG(INFO, this) << "Stream timeout stream_id="
|
|
|
|
<< downstream->get_stream_id();
|
|
|
|
}
|
|
|
|
|
|
|
|
rst_stream(downstream, SPDYLAY_INTERNAL_ERROR);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
void SpdyUpstream::on_handler_delete() {
|
2015-03-11 16:17:05 +01:00
|
|
|
for (auto d = downstream_queue_.get_downstreams(); d; d = d->dlnext) {
|
|
|
|
if (d->get_dispatch_state() == Downstream::DISPATCH_ACTIVE &&
|
|
|
|
d->accesslog_ready()) {
|
|
|
|
handler_->write_accesslog(d);
|
2014-11-23 09:24:23 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-01-21 15:30:48 +01:00
|
|
|
int SpdyUpstream::on_downstream_reset(bool no_retry) {
|
nghttpx: Check HTTP/2 downstream connection after certain idle time
Previously when requests are issued to HTTP/2 downstream connection,
but it turns out that connection is down, handlers of those requests
are deleted. In some situations, we only know connection is down when
we write something to network, so we'd like to handle this kind of
situation in more robust manner. In this change, certain seconds
passed after last network activity, we first issue PING frame to
downstream connection before issuing new HTTP request. If writing
PING frame is failed, it means connection was lost. In this case,
instead of deleting handler, pending requests are migrated to new
HTTP2/ downstream connection, so that it can continue without
affecting upstream connection.
2014-12-08 17:30:15 +01:00
|
|
|
int rv;
|
|
|
|
|
2015-03-11 16:17:05 +01:00
|
|
|
for (auto downstream = downstream_queue_.get_downstreams(); downstream;
|
|
|
|
downstream = downstream->dlnext) {
|
|
|
|
if (downstream->get_dispatch_state() != Downstream::DISPATCH_ACTIVE) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2015-02-17 15:15:53 +01:00
|
|
|
if (!downstream->request_submission_ready()) {
|
nghttpx: Check HTTP/2 downstream connection after certain idle time
Previously when requests are issued to HTTP/2 downstream connection,
but it turns out that connection is down, handlers of those requests
are deleted. In some situations, we only know connection is down when
we write something to network, so we'd like to handle this kind of
situation in more robust manner. In this change, certain seconds
passed after last network activity, we first issue PING frame to
downstream connection before issuing new HTTP request. If writing
PING frame is failed, it means connection was lost. In this case,
instead of deleting handler, pending requests are migrated to new
HTTP2/ downstream connection, so that it can continue without
affecting upstream connection.
2014-12-08 17:30:15 +01:00
|
|
|
rst_stream(downstream, SPDYLAY_INTERNAL_ERROR);
|
|
|
|
downstream->pop_downstream_connection();
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2015-01-21 15:30:48 +01:00
|
|
|
downstream->pop_downstream_connection();
|
|
|
|
|
2015-02-02 17:47:04 +01:00
|
|
|
downstream->add_retry();
|
|
|
|
|
|
|
|
if (no_retry || downstream->no_more_retry()) {
|
2015-02-20 11:23:52 +01:00
|
|
|
goto fail;
|
2015-01-21 15:30:48 +01:00
|
|
|
}
|
|
|
|
|
nghttpx: Check HTTP/2 downstream connection after certain idle time
Previously when requests are issued to HTTP/2 downstream connection,
but it turns out that connection is down, handlers of those requests
are deleted. In some situations, we only know connection is down when
we write something to network, so we'd like to handle this kind of
situation in more robust manner. In this change, certain seconds
passed after last network activity, we first issue PING frame to
downstream connection before issuing new HTTP request. If writing
PING frame is failed, it means connection was lost. In this case,
instead of deleting handler, pending requests are migrated to new
HTTP2/ downstream connection, so that it can continue without
affecting upstream connection.
2014-12-08 17:30:15 +01:00
|
|
|
// downstream connection is clean; we can retry with new
|
|
|
|
// downstream connection.
|
|
|
|
|
|
|
|
rv = downstream->attach_downstream_connection(
|
2015-07-09 19:52:11 +02:00
|
|
|
handler_->get_downstream_connection(downstream));
|
nghttpx: Check HTTP/2 downstream connection after certain idle time
Previously when requests are issued to HTTP/2 downstream connection,
but it turns out that connection is down, handlers of those requests
are deleted. In some situations, we only know connection is down when
we write something to network, so we'd like to handle this kind of
situation in more robust manner. In this change, certain seconds
passed after last network activity, we first issue PING frame to
downstream connection before issuing new HTTP request. If writing
PING frame is failed, it means connection was lost. In this case,
instead of deleting handler, pending requests are migrated to new
HTTP2/ downstream connection, so that it can continue without
affecting upstream connection.
2014-12-08 17:30:15 +01:00
|
|
|
if (rv != 0) {
|
2015-02-20 11:23:52 +01:00
|
|
|
goto fail;
|
nghttpx: Check HTTP/2 downstream connection after certain idle time
Previously when requests are issued to HTTP/2 downstream connection,
but it turns out that connection is down, handlers of those requests
are deleted. In some situations, we only know connection is down when
we write something to network, so we'd like to handle this kind of
situation in more robust manner. In this change, certain seconds
passed after last network activity, we first issue PING frame to
downstream connection before issuing new HTTP request. If writing
PING frame is failed, it means connection was lost. In this case,
instead of deleting handler, pending requests are migrated to new
HTTP2/ downstream connection, so that it can continue without
affecting upstream connection.
2014-12-08 17:30:15 +01:00
|
|
|
}
|
2015-02-20 11:23:52 +01:00
|
|
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
fail:
|
|
|
|
if (on_downstream_abort_request(downstream, 503) != 0) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
downstream->pop_downstream_connection();
|
nghttpx: Check HTTP/2 downstream connection after certain idle time
Previously when requests are issued to HTTP/2 downstream connection,
but it turns out that connection is down, handlers of those requests
are deleted. In some situations, we only know connection is down when
we write something to network, so we'd like to handle this kind of
situation in more robust manner. In this change, certain seconds
passed after last network activity, we first issue PING frame to
downstream connection before issuing new HTTP request. If writing
PING frame is failed, it means connection was lost. In this case,
instead of deleting handler, pending requests are migrated to new
HTTP2/ downstream connection, so that it can continue without
affecting upstream connection.
2014-12-08 17:30:15 +01:00
|
|
|
}
|
|
|
|
|
2014-12-27 18:59:06 +01:00
|
|
|
handler_->signal_write();
|
nghttpx: Check HTTP/2 downstream connection after certain idle time
Previously when requests are issued to HTTP/2 downstream connection,
but it turns out that connection is down, handlers of those requests
are deleted. In some situations, we only know connection is down when
we write something to network, so we'd like to handle this kind of
situation in more robust manner. In this change, certain seconds
passed after last network activity, we first issue PING frame to
downstream connection before issuing new HTTP request. If writing
PING frame is failed, it means connection was lost. In this case,
instead of deleting handler, pending requests are migrated to new
HTTP2/ downstream connection, so that it can continue without
affecting upstream connection.
2014-12-08 17:30:15 +01:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-09-05 15:47:07 +02:00
|
|
|
int SpdyUpstream::initiate_push(Downstream *downstream, const char *uri,
|
|
|
|
size_t len) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-10-02 15:42:46 +02:00
|
|
|
int SpdyUpstream::response_riovec(struct iovec *iov, int iovcnt) const {
|
|
|
|
if (iovcnt == 0 || wb_.rleft() == 0) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
iov->iov_base = wb_.pos;
|
|
|
|
iov->iov_len = wb_.rleft();
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
void SpdyUpstream::response_drain(size_t n) { wb_.drain(n); }
|
|
|
|
|
|
|
|
bool SpdyUpstream::response_empty() const { return wb_.rleft() == 0; }
|
|
|
|
|
|
|
|
SpdyUpstream::WriteBuffer *SpdyUpstream::get_response_buf() { return &wb_; }
|
|
|
|
|
2013-07-26 13:12:55 +02:00
|
|
|
} // namespace shrpx
|