nghttpx: More freedom for custom response headers
This commit is contained in:
parent
05a761b628
commit
65f2b16132
|
@ -1138,6 +1138,61 @@ ssize_t downstream_data_read_callback(nghttp2_session *session,
|
||||||
}
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
int Http2Upstream::send_reply(Downstream *downstream, const uint8_t *body,
|
||||||
|
size_t bodylen) {
|
||||||
|
int rv;
|
||||||
|
|
||||||
|
nghttp2_data_provider data_prd, *data_prd_ptr = nullptr;
|
||||||
|
|
||||||
|
if (bodylen) {
|
||||||
|
data_prd.source.ptr = downstream;
|
||||||
|
data_prd.read_callback = downstream_data_read_callback;
|
||||||
|
data_prd_ptr = &data_prd;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto status_code_str = util::utos(downstream->get_response_http_status());
|
||||||
|
auto &headers = downstream->get_response_headers();
|
||||||
|
// 2 for :status and server
|
||||||
|
auto nva = std::vector<nghttp2_nv>(2 + headers.size());
|
||||||
|
nva.push_back(http2::make_nv_ls(":status", status_code_str));
|
||||||
|
|
||||||
|
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_TE:
|
||||||
|
case http2::HD_TRANSFER_ENCODING:
|
||||||
|
case http2::HD_UPGRADE:
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
nva.push_back(http2::make_nv(kv.name, kv.value, kv.no_index));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!downstream->get_response_header(http2::HD_SERVER)) {
|
||||||
|
nva.push_back(http2::make_nv_lc("server", get_config()->server_name));
|
||||||
|
}
|
||||||
|
|
||||||
|
rv = nghttp2_submit_response(session_, downstream->get_stream_id(),
|
||||||
|
nva.data(), nva.size(), data_prd_ptr);
|
||||||
|
if (nghttp2_is_fatal(rv)) {
|
||||||
|
ULOG(FATAL, this) << "nghttp2_submit_response() failed: "
|
||||||
|
<< nghttp2_strerror(rv);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto buf = downstream->get_response_buf();
|
||||||
|
|
||||||
|
buf->append(body, bodylen);
|
||||||
|
|
||||||
|
downstream->set_response_state(Downstream::MSG_COMPLETE);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
int Http2Upstream::error_reply(Downstream *downstream,
|
int Http2Upstream::error_reply(Downstream *downstream,
|
||||||
unsigned int status_code) {
|
unsigned int status_code) {
|
||||||
int rv;
|
int rv;
|
||||||
|
|
|
@ -78,6 +78,8 @@ public:
|
||||||
|
|
||||||
virtual void on_handler_delete();
|
virtual void on_handler_delete();
|
||||||
virtual int on_downstream_reset(bool no_retry);
|
virtual int on_downstream_reset(bool no_retry);
|
||||||
|
virtual int send_reply(Downstream *downstream, const uint8_t *body,
|
||||||
|
size_t bodylen);
|
||||||
|
|
||||||
bool get_flow_control() const;
|
bool get_flow_control() const;
|
||||||
// Perform HTTP/2 upgrade from |upstream|. On success, this object
|
// Perform HTTP/2 upgrade from |upstream|. On success, this object
|
||||||
|
|
|
@ -725,6 +725,62 @@ int HttpsUpstream::downstream_error(DownstreamConnection *dconn, int events) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int HttpsUpstream::send_reply(Downstream *downstream, const uint8_t *body,
|
||||||
|
size_t bodylen) {
|
||||||
|
auto major = downstream->get_request_major();
|
||||||
|
auto minor = downstream->get_request_minor();
|
||||||
|
|
||||||
|
auto connection_close = false;
|
||||||
|
if (major <= 0 || (major == 1 && minor == 0)) {
|
||||||
|
connection_close = true;
|
||||||
|
} else {
|
||||||
|
auto c = downstream->get_response_header(http2::HD_CONNECTION);
|
||||||
|
if (c && util::strieq_l("close", c->value)) {
|
||||||
|
connection_close = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (connection_close) {
|
||||||
|
downstream->set_response_connection_close(true);
|
||||||
|
handler_->set_should_close_after_write(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto output = downstream->get_response_buf();
|
||||||
|
|
||||||
|
output->append("HTTP/1.1 ");
|
||||||
|
auto status_str =
|
||||||
|
http2::get_status_string(downstream->get_response_http_status());
|
||||||
|
output->append(status_str.c_str(), status_str.size());
|
||||||
|
|
||||||
|
for (auto &kv : downstream->get_response_headers()) {
|
||||||
|
if (kv.name.empty() || kv.name[0] == ':') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
auto name = kv.name;
|
||||||
|
http2::capitalize(name, 0);
|
||||||
|
output->append(name.c_str(), name.size());
|
||||||
|
output->append(": ");
|
||||||
|
output->append(kv.value.c_str(), kv.value.size());
|
||||||
|
output->append("\r\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!downstream->get_response_header(http2::HD_SERVER)) {
|
||||||
|
output->append("Server: ");
|
||||||
|
output->append(get_config()->server_name,
|
||||||
|
strlen(get_config()->server_name));
|
||||||
|
output->append("\r\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
output->append("\r\n");
|
||||||
|
|
||||||
|
output->append(body, bodylen);
|
||||||
|
|
||||||
|
downstream->add_response_sent_bodylen(bodylen);
|
||||||
|
downstream->set_response_state(Downstream::MSG_COMPLETE);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
void HttpsUpstream::error_reply(unsigned int status_code) {
|
void HttpsUpstream::error_reply(unsigned int status_code) {
|
||||||
auto html = http::create_error_html(status_code);
|
auto html = http::create_error_html(status_code);
|
||||||
auto downstream = get_downstream();
|
auto downstream = get_downstream();
|
||||||
|
|
|
@ -74,6 +74,8 @@ public:
|
||||||
|
|
||||||
virtual void on_handler_delete();
|
virtual void on_handler_delete();
|
||||||
virtual int on_downstream_reset(bool no_retry);
|
virtual int on_downstream_reset(bool no_retry);
|
||||||
|
virtual int send_reply(Downstream *downstream, const uint8_t *body,
|
||||||
|
size_t bodylen);
|
||||||
|
|
||||||
void reset_current_header_length();
|
void reset_current_header_length();
|
||||||
void log_response_headers(const std::string &hdrs) const;
|
void log_response_headers(const std::string &hdrs) const;
|
||||||
|
|
|
@ -62,7 +62,10 @@ int MRubyContext::run_request_proc(Downstream *downstream, RProc *proc) {
|
||||||
(void)res;
|
(void)res;
|
||||||
|
|
||||||
if (mrb_->exc) {
|
if (mrb_->exc) {
|
||||||
rv = -1;
|
// If response has been committed, ignore error
|
||||||
|
if (downstream->get_response_state() != Downstream::MSG_COMPLETE) {
|
||||||
|
rv = -1;
|
||||||
|
}
|
||||||
auto error =
|
auto error =
|
||||||
mrb_str_ptr(mrb_funcall(mrb_, mrb_obj_value(mrb_->exc), "inspect", 0));
|
mrb_str_ptr(mrb_funcall(mrb_, mrb_obj_value(mrb_->exc), "inspect", 0));
|
||||||
|
|
||||||
|
|
|
@ -31,6 +31,7 @@
|
||||||
|
|
||||||
#include "shrpx_downstream.h"
|
#include "shrpx_downstream.h"
|
||||||
#include "shrpx_upstream.h"
|
#include "shrpx_upstream.h"
|
||||||
|
#include "shrpx_client_handler.h"
|
||||||
#include "shrpx_mruby.h"
|
#include "shrpx_mruby.h"
|
||||||
#include "shrpx_mruby_module.h"
|
#include "shrpx_mruby_module.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
@ -139,7 +140,7 @@ mrb_value response_set_header(mrb_state *mrb, mrb_value self) {
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
mrb_value response_end(mrb_state *mrb, mrb_value self) {
|
mrb_value response_return(mrb_state *mrb, mrb_value self) {
|
||||||
auto data = static_cast<MRubyAssocData *>(mrb->ud);
|
auto data = static_cast<MRubyAssocData *>(mrb->ud);
|
||||||
auto downstream = data->downstream;
|
auto downstream = data->downstream;
|
||||||
int rv;
|
int rv;
|
||||||
|
@ -151,14 +152,9 @@ mrb_value response_end(mrb_state *mrb, mrb_value self) {
|
||||||
mrb_value val;
|
mrb_value val;
|
||||||
mrb_get_args(mrb, "|o", &val);
|
mrb_get_args(mrb, "|o", &val);
|
||||||
|
|
||||||
const char *body = nullptr;
|
const uint8_t *body = nullptr;
|
||||||
size_t bodylen = 0;
|
size_t bodylen = 0;
|
||||||
|
|
||||||
if (!mrb_nil_p(val)) {
|
|
||||||
body = RSTRING_PTR(val);
|
|
||||||
bodylen = RSTRING_LEN(val);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (downstream->get_response_http_status() == 0) {
|
if (downstream->get_response_http_status() == 0) {
|
||||||
downstream->set_response_http_status(200);
|
downstream->set_response_http_status(200);
|
||||||
}
|
}
|
||||||
|
@ -168,6 +164,11 @@ mrb_value response_end(mrb_state *mrb, mrb_value self) {
|
||||||
data->response_headers_dirty = false;
|
data->response_headers_dirty = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (downstream->expect_response_body() && !mrb_nil_p(val)) {
|
||||||
|
body = reinterpret_cast<const uint8_t *>(RSTRING_PTR(val));
|
||||||
|
bodylen = RSTRING_LEN(val);
|
||||||
|
}
|
||||||
|
|
||||||
auto cl = downstream->get_response_header(http2::HD_CONTENT_LENGTH);
|
auto cl = downstream->get_response_header(http2::HD_CONTENT_LENGTH);
|
||||||
if (cl) {
|
if (cl) {
|
||||||
cl->value = util::utos(bodylen);
|
cl->value = util::utos(bodylen);
|
||||||
|
@ -179,17 +180,14 @@ mrb_value response_end(mrb_state *mrb, mrb_value self) {
|
||||||
|
|
||||||
auto upstream = downstream->get_upstream();
|
auto upstream = downstream->get_upstream();
|
||||||
|
|
||||||
rv = upstream->on_downstream_header_complete(downstream);
|
rv = upstream->send_reply(downstream, body, bodylen);
|
||||||
if (rv != 0) {
|
if (rv != 0) {
|
||||||
mrb_raise(mrb, E_RUNTIME_ERROR, "could not send response");
|
mrb_raise(mrb, E_RUNTIME_ERROR, "could not send response");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (downstream->expect_response_body()) {
|
auto handler = upstream->get_client_handler();
|
||||||
auto output = downstream->get_response_buf();
|
|
||||||
output->append(body, bodylen);
|
|
||||||
}
|
|
||||||
|
|
||||||
downstream->set_response_state(Downstream::MSG_COMPLETE);
|
handler->signal_write();
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
@ -213,7 +211,8 @@ void init_response_class(mrb_state *mrb, RClass *module) {
|
||||||
MRB_ARGS_NONE());
|
MRB_ARGS_NONE());
|
||||||
mrb_define_method(mrb, response_class, "set_header", response_set_header,
|
mrb_define_method(mrb, response_class, "set_header", response_set_header,
|
||||||
MRB_ARGS_REQ(2));
|
MRB_ARGS_REQ(2));
|
||||||
mrb_define_method(mrb, response_class, "end", response_end, MRB_ARGS_OPT(1));
|
mrb_define_method(mrb, response_class, "return", response_return,
|
||||||
|
MRB_ARGS_OPT(1));
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace mruby
|
} // namespace mruby
|
||||||
|
|
|
@ -793,6 +793,69 @@ ssize_t spdy_data_read_callback(spdylay_session *session, int32_t stream_id,
|
||||||
}
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
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();
|
||||||
|
|
||||||
|
// 3 for :status, :version and server
|
||||||
|
auto nva = std::vector<const char *>(3 + headers.size());
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
int SpdyUpstream::error_reply(Downstream *downstream,
|
int SpdyUpstream::error_reply(Downstream *downstream,
|
||||||
unsigned int status_code) {
|
unsigned int status_code) {
|
||||||
int rv;
|
int rv;
|
||||||
|
|
|
@ -74,6 +74,9 @@ public:
|
||||||
virtual void on_handler_delete();
|
virtual void on_handler_delete();
|
||||||
virtual int on_downstream_reset(bool no_retry);
|
virtual int on_downstream_reset(bool no_retry);
|
||||||
|
|
||||||
|
virtual int send_reply(Downstream *downstream, const uint8_t *body,
|
||||||
|
size_t bodylen);
|
||||||
|
|
||||||
bool get_flow_control() const;
|
bool get_flow_control() const;
|
||||||
|
|
||||||
int consume(int32_t stream_id, size_t len);
|
int consume(int32_t stream_id, size_t len);
|
||||||
|
|
|
@ -62,6 +62,8 @@ public:
|
||||||
virtual void pause_read(IOCtrlReason reason) = 0;
|
virtual void pause_read(IOCtrlReason reason) = 0;
|
||||||
virtual int resume_read(IOCtrlReason reason, Downstream *downstream,
|
virtual int resume_read(IOCtrlReason reason, Downstream *downstream,
|
||||||
size_t consumed) = 0;
|
size_t consumed) = 0;
|
||||||
|
virtual int send_reply(Downstream *downstream, const uint8_t *body,
|
||||||
|
size_t bodylen) = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace shrpx
|
} // namespace shrpx
|
||||||
|
|
Loading…
Reference in New Issue