Merge branch 'nghttpx-trailer'
This commit is contained in:
commit
bff5a28d12
|
@ -211,6 +211,41 @@ func TestH1H1HTTP10NoHostRewrite(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
// TestH1H1RequestTrailer tests request trailer part is forwarded to
|
||||
// backend.
|
||||
func TestH1H1RequestTrailer(t *testing.T) {
|
||||
st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) {
|
||||
buf := make([]byte, 4096)
|
||||
for {
|
||||
_, err := r.Body.Read(buf)
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatalf("r.Body.Read() = %v", err)
|
||||
}
|
||||
}
|
||||
if got, want := r.Trailer.Get("foo"), "bar"; got != want {
|
||||
t.Errorf("r.Trailer.Get(foo): %v; want %v", got, want)
|
||||
}
|
||||
})
|
||||
defer st.Close()
|
||||
|
||||
res, err := st.http1(requestParam{
|
||||
name: "TestH1H1RequestTrailer",
|
||||
body: []byte("1"),
|
||||
trailer: []hpack.HeaderField{
|
||||
pair("foo", "bar"),
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Error st.http1() = %v", err)
|
||||
}
|
||||
if got, want := res.status, 200; got != want {
|
||||
t.Errorf("res.status: %v; want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// TestH1H2ConnectFailure tests that server handles the situation that
|
||||
// connection attempt to HTTP/2 backend failed.
|
||||
func TestH1H2ConnectFailure(t *testing.T) {
|
||||
|
|
|
@ -520,6 +520,41 @@ func TestH2H1ServerPush(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
// TestH2H1RequestTrailer tests request trailer part is forwarded to
|
||||
// backend.
|
||||
func TestH2H1RequestTrailer(t *testing.T) {
|
||||
st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) {
|
||||
buf := make([]byte, 4096)
|
||||
for {
|
||||
_, err := r.Body.Read(buf)
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatalf("r.Body.Read() = %v", err)
|
||||
}
|
||||
}
|
||||
if got, want := r.Trailer.Get("foo"), "bar"; got != want {
|
||||
t.Errorf("r.Trailer.Get(foo): %v; want %v", got, want)
|
||||
}
|
||||
})
|
||||
defer st.Close()
|
||||
|
||||
res, err := st.http2(requestParam{
|
||||
name: "TestH2H1RequestTrailer",
|
||||
body: []byte("1"),
|
||||
trailer: []hpack.HeaderField{
|
||||
pair("foo", "bar"),
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Error st.http2() = %v", err)
|
||||
}
|
||||
if got, want := res.status, 200; got != want {
|
||||
t.Errorf("res.status: %v; want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// TestH2H1GracefulShutdown tests graceful shutdown.
|
||||
func TestH2H1GracefulShutdown(t *testing.T) {
|
||||
st := newServerTester(nil, t, noopHandler)
|
||||
|
|
|
@ -255,6 +255,27 @@ type requestParam struct {
|
|||
path string // path, defaults to /
|
||||
header []hpack.HeaderField // additional request header fields
|
||||
body []byte // request body
|
||||
trailer []hpack.HeaderField // trailer part
|
||||
}
|
||||
|
||||
// wrapper for request body to set trailer part
|
||||
type chunkedBodyReader struct {
|
||||
trailer []hpack.HeaderField
|
||||
trailerWritten bool
|
||||
body io.Reader
|
||||
req *http.Request
|
||||
}
|
||||
|
||||
func (cbr *chunkedBodyReader) Read(p []byte) (n int, err error) {
|
||||
// document says that we have to set http.Request.Trailer
|
||||
// after request was sent and before body returns EOF.
|
||||
if !cbr.trailerWritten {
|
||||
cbr.trailerWritten = true
|
||||
for _, h := range cbr.trailer {
|
||||
cbr.req.Trailer.Set(h.Name, h.Value)
|
||||
}
|
||||
}
|
||||
return cbr.body.Read(p)
|
||||
}
|
||||
|
||||
func (st *serverTester) http1(rp requestParam) (*serverResponse, error) {
|
||||
|
@ -264,8 +285,16 @@ func (st *serverTester) http1(rp requestParam) (*serverResponse, error) {
|
|||
}
|
||||
|
||||
var body io.Reader
|
||||
var cbr *chunkedBodyReader
|
||||
if rp.body != nil {
|
||||
body = bytes.NewBuffer(rp.body)
|
||||
if len(rp.trailer) != 0 {
|
||||
cbr = &chunkedBodyReader{
|
||||
trailer: rp.trailer,
|
||||
body: body,
|
||||
}
|
||||
body = cbr
|
||||
}
|
||||
}
|
||||
req, err := http.NewRequest(method, st.url, body)
|
||||
if err != nil {
|
||||
|
@ -275,7 +304,15 @@ func (st *serverTester) http1(rp requestParam) (*serverResponse, error) {
|
|||
req.Header.Add(h.Name, h.Value)
|
||||
}
|
||||
req.Header.Add("Test-Case", rp.name)
|
||||
|
||||
if cbr != nil {
|
||||
cbr.req = req
|
||||
// this makes request use chunked encoding
|
||||
req.ContentLength = -1
|
||||
req.Trailer = make(http.Header)
|
||||
for _, h := range cbr.trailer {
|
||||
req.Trailer.Set(h.Name, "")
|
||||
}
|
||||
}
|
||||
if err := req.Write(st.conn); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -473,7 +510,7 @@ func (st *serverTester) http2(rp requestParam) (*serverResponse, error) {
|
|||
|
||||
err := st.fr.WriteHeaders(http2.HeadersFrameParam{
|
||||
StreamID: id,
|
||||
EndStream: len(rp.body) == 0,
|
||||
EndStream: len(rp.body) == 0 && len(rp.trailer) == 0,
|
||||
EndHeaders: true,
|
||||
BlockFragment: st.headerBlkBuf.Bytes(),
|
||||
})
|
||||
|
@ -483,7 +520,23 @@ func (st *serverTester) http2(rp requestParam) (*serverResponse, error) {
|
|||
|
||||
if len(rp.body) != 0 {
|
||||
// TODO we assume rp.body fits in 1 frame
|
||||
if err := st.fr.WriteData(id, true, rp.body); err != nil {
|
||||
if err := st.fr.WriteData(id, len(rp.trailer) == 0, rp.body); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if len(rp.trailer) != 0 {
|
||||
st.headerBlkBuf.Reset()
|
||||
for _, h := range rp.trailer {
|
||||
_ = st.enc.WriteField(h)
|
||||
}
|
||||
err := st.fr.WriteHeaders(http2.HeadersFrameParam{
|
||||
StreamID: id,
|
||||
EndStream: true,
|
||||
EndHeaders: true,
|
||||
BlockFragment: st.headerBlkBuf.Bytes(),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
|
|
@ -690,12 +690,22 @@ int Http2Handler::submit_file_response(const std::string &status,
|
|||
http2::make_nv_ls("content-length", content_length),
|
||||
http2::make_nv_ll("cache-control", "max-age=3600"),
|
||||
http2::make_nv_ls("date", sessions_->get_cached_date()),
|
||||
http2::make_nv_ll("", ""));
|
||||
http2::make_nv_ll("", ""), http2::make_nv_ll("", ""));
|
||||
size_t nvlen = 5;
|
||||
if (last_modified != 0) {
|
||||
last_modified_str = util::http_date(last_modified);
|
||||
nva[nvlen++] = http2::make_nv_ls("last-modified", last_modified_str);
|
||||
}
|
||||
auto &trailer = get_config()->trailer;
|
||||
std::string trailer_names;
|
||||
if (!trailer.empty()) {
|
||||
trailer_names = trailer[0].name;
|
||||
for (size_t i = 1; i < trailer.size(); ++i) {
|
||||
trailer_names += ", ";
|
||||
trailer_names += trailer[i].name;
|
||||
}
|
||||
nva[nvlen++] = http2::make_nv_ls("trailer", trailer_names);
|
||||
}
|
||||
return nghttp2_submit_response(session_, stream->stream_id, nva.data(), nvlen,
|
||||
data_prd);
|
||||
}
|
||||
|
@ -832,7 +842,6 @@ ssize_t file_read_callback(nghttp2_session *session, int32_t stream_id,
|
|||
|
||||
auto config = hd->get_config();
|
||||
if (!config->trailer.empty()) {
|
||||
*data_flags |= NGHTTP2_DATA_FLAG_NO_END_STREAM;
|
||||
std::vector<nghttp2_nv> nva;
|
||||
nva.reserve(config->trailer.size());
|
||||
for (auto &kv : config->trailer) {
|
||||
|
@ -840,7 +849,11 @@ ssize_t file_read_callback(nghttp2_session *session, int32_t stream_id,
|
|||
}
|
||||
rv = nghttp2_submit_trailer(session, stream_id, nva.data(), nva.size());
|
||||
if (rv != 0) {
|
||||
*data_flags &= ~NGHTTP2_DATA_FLAG_NO_END_STREAM;
|
||||
if (nghttp2_is_fatal(rv)) {
|
||||
return NGHTTP2_ERR_CALLBACK_FAILURE;
|
||||
}
|
||||
} else {
|
||||
*data_flags |= NGHTTP2_DATA_FLAG_NO_END_STREAM;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -230,7 +230,6 @@ void copy_headers_to_nva(std::vector<nghttp2_nv> &nva, const Headers &headers) {
|
|||
case HD_KEEP_ALIVE:
|
||||
case HD_PROXY_CONNECTION:
|
||||
case HD_SERVER:
|
||||
case HD_TRAILER:
|
||||
case HD_TRANSFER_ENCODING:
|
||||
case HD_UPGRADE:
|
||||
case HD_VIA:
|
||||
|
@ -256,7 +255,6 @@ void build_http1_headers_from_headers(std::string &hdrs,
|
|||
case HD_KEEP_ALIVE:
|
||||
case HD_PROXY_CONNECTION:
|
||||
case HD_SERVER:
|
||||
case HD_TRAILER:
|
||||
case HD_UPGRADE:
|
||||
case HD_VIA:
|
||||
case HD_X_FORWARDED_FOR:
|
||||
|
|
|
@ -376,6 +376,16 @@ int submit_request(HttpClient *client, const Headers &headers, Request *req) {
|
|||
nva.push_back(http2::make_nv(kv.name, kv.value, kv.no_index));
|
||||
}
|
||||
|
||||
std::string trailer_names;
|
||||
if (!config.trailer.empty()) {
|
||||
trailer_names = config.trailer[0].name;
|
||||
for (size_t i = 1; i < config.trailer.size(); ++i) {
|
||||
trailer_names += ", ";
|
||||
trailer_names += config.trailer[i].name;
|
||||
}
|
||||
nva.push_back(http2::make_nv_ls("trailer", trailer_names));
|
||||
}
|
||||
|
||||
auto stream_id =
|
||||
nghttp2_submit_request(client->session, &req->pri_spec, nva.data(),
|
||||
nva.size(), req->data_prd, req);
|
||||
|
@ -2128,7 +2138,6 @@ ssize_t file_read_callback(nghttp2_session *session, int32_t stream_id,
|
|||
if (nread == 0) {
|
||||
*data_flags |= NGHTTP2_DATA_FLAG_EOF;
|
||||
if (!config.trailer.empty()) {
|
||||
*data_flags |= NGHTTP2_DATA_FLAG_NO_END_STREAM;
|
||||
std::vector<nghttp2_nv> nva;
|
||||
nva.reserve(config.trailer.size());
|
||||
for (auto &kv : config.trailer) {
|
||||
|
@ -2136,7 +2145,11 @@ ssize_t file_read_callback(nghttp2_session *session, int32_t stream_id,
|
|||
}
|
||||
rv = nghttp2_submit_trailer(session, stream_id, nva.data(), nva.size());
|
||||
if (rv != 0) {
|
||||
*data_flags &= ~NGHTTP2_DATA_FLAG_NO_END_STREAM;
|
||||
if (nghttp2_is_fatal(rv)) {
|
||||
return NGHTTP2_ERR_CALLBACK_FAILURE;
|
||||
}
|
||||
} else {
|
||||
*data_flags |= NGHTTP2_DATA_FLAG_NO_END_STREAM;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
|
|
@ -120,8 +120,9 @@ Downstream::Downstream(Upstream *upstream, int32_t stream_id, int32_t priority)
|
|||
response_minor_(1), upgrade_request_(false), upgraded_(false),
|
||||
http2_upgrade_seen_(false), chunked_request_(false),
|
||||
request_connection_close_(false), request_header_key_prev_(false),
|
||||
request_http2_expect_body_(false), chunked_response_(false),
|
||||
response_connection_close_(false), response_header_key_prev_(false),
|
||||
request_trailer_key_prev_(false), request_http2_expect_body_(false),
|
||||
chunked_response_(false), response_connection_close_(false),
|
||||
response_header_key_prev_(false), response_trailer_key_prev_(false),
|
||||
expect_final_response_(false), request_pending_(false) {
|
||||
|
||||
ev_timer_init(&upstream_rtimer_, &upstream_rtimeoutcb, 0.,
|
||||
|
@ -287,6 +288,45 @@ const std::string &Downstream::get_assembled_request_cookie() const {
|
|||
return assembled_request_cookie_;
|
||||
}
|
||||
|
||||
namespace {
|
||||
void add_header(bool &key_prev, size_t &sum, Headers &headers, std::string name,
|
||||
std::string value) {
|
||||
key_prev = true;
|
||||
sum += name.size() + value.size();
|
||||
headers.emplace_back(std::move(name), std::move(value));
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
void append_last_header_key(bool key_prev, size_t &sum, Headers &headers,
|
||||
const char *data, size_t len) {
|
||||
assert(key_prev);
|
||||
sum += len;
|
||||
auto &item = headers.back();
|
||||
item.name.append(data, len);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
void append_last_header_value(bool key_prev, size_t &sum, Headers &headers,
|
||||
const char *data, size_t len) {
|
||||
assert(!key_prev);
|
||||
sum += len;
|
||||
auto &item = headers.back();
|
||||
item.value.append(data, len);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
void set_last_header_value(bool &key_prev, size_t &sum, Headers &headers,
|
||||
const char *data, size_t len) {
|
||||
key_prev = false;
|
||||
sum += len;
|
||||
auto &item = headers.back();
|
||||
item.value.assign(data, len);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
int index_headers(http2::HeaderIndex &hdidx, Headers &headers,
|
||||
int64_t &content_length) {
|
||||
|
@ -333,16 +373,13 @@ Downstream::get_request_header(const std::string &name) const {
|
|||
}
|
||||
|
||||
void Downstream::add_request_header(std::string name, std::string value) {
|
||||
request_header_key_prev_ = true;
|
||||
request_headers_sum_ += name.size() + value.size();
|
||||
request_headers_.emplace_back(std::move(name), std::move(value));
|
||||
add_header(request_header_key_prev_, request_headers_sum_, request_headers_,
|
||||
std::move(name), std::move(value));
|
||||
}
|
||||
|
||||
void Downstream::set_last_request_header_value(std::string value) {
|
||||
request_header_key_prev_ = false;
|
||||
request_headers_sum_ += value.size();
|
||||
Headers::value_type &item = request_headers_.back();
|
||||
item.value = std::move(value);
|
||||
void Downstream::set_last_request_header_value(const char *data, size_t len) {
|
||||
set_last_header_value(request_header_key_prev_, request_headers_sum_,
|
||||
request_headers_, data, len);
|
||||
}
|
||||
|
||||
void Downstream::add_request_header(const uint8_t *name, size_t namelen,
|
||||
|
@ -359,18 +396,14 @@ bool Downstream::get_request_header_key_prev() const {
|
|||
}
|
||||
|
||||
void Downstream::append_last_request_header_key(const char *data, size_t len) {
|
||||
assert(request_header_key_prev_);
|
||||
request_headers_sum_ += len;
|
||||
auto &item = request_headers_.back();
|
||||
item.name.append(data, len);
|
||||
append_last_header_key(request_header_key_prev_, request_headers_sum_,
|
||||
request_headers_, data, len);
|
||||
}
|
||||
|
||||
void Downstream::append_last_request_header_value(const char *data,
|
||||
size_t len) {
|
||||
assert(!request_header_key_prev_);
|
||||
request_headers_sum_ += len;
|
||||
auto &item = request_headers_.back();
|
||||
item.value.append(data, len);
|
||||
append_last_header_value(request_header_key_prev_, request_headers_sum_,
|
||||
request_headers_, data, len);
|
||||
}
|
||||
|
||||
void Downstream::clear_request_headers() {
|
||||
|
@ -382,6 +415,45 @@ size_t Downstream::get_request_headers_sum() const {
|
|||
return request_headers_sum_;
|
||||
}
|
||||
|
||||
void Downstream::add_request_trailer(const uint8_t *name, size_t namelen,
|
||||
const uint8_t *value, size_t valuelen,
|
||||
bool no_index, int16_t token) {
|
||||
// we never index trailer part. Header size limit should be applied
|
||||
// to all request header fields combined.
|
||||
request_headers_sum_ += namelen + valuelen;
|
||||
http2::add_header(request_trailers_, name, namelen, value, valuelen, no_index,
|
||||
-1);
|
||||
}
|
||||
|
||||
const Headers &Downstream::get_request_trailers() const {
|
||||
return request_trailers_;
|
||||
}
|
||||
|
||||
void Downstream::add_request_trailer(std::string name, std::string value) {
|
||||
add_header(request_trailer_key_prev_, request_headers_sum_, request_trailers_,
|
||||
std::move(name), std::move(value));
|
||||
}
|
||||
|
||||
void Downstream::set_last_request_trailer_value(const char *data, size_t len) {
|
||||
set_last_header_value(request_trailer_key_prev_, request_headers_sum_,
|
||||
request_trailers_, data, len);
|
||||
}
|
||||
|
||||
bool Downstream::get_request_trailer_key_prev() const {
|
||||
return request_trailer_key_prev_;
|
||||
}
|
||||
|
||||
void Downstream::append_last_request_trailer_key(const char *data, size_t len) {
|
||||
append_last_header_key(request_trailer_key_prev_, request_headers_sum_,
|
||||
request_trailers_, data, len);
|
||||
}
|
||||
|
||||
void Downstream::append_last_request_trailer_value(const char *data,
|
||||
size_t len) {
|
||||
append_last_header_value(request_trailer_key_prev_, request_headers_sum_,
|
||||
request_trailers_, data, len);
|
||||
}
|
||||
|
||||
void Downstream::set_request_method(std::string method) {
|
||||
request_method_ = std::move(method);
|
||||
}
|
||||
|
@ -592,16 +664,13 @@ void Downstream::rewrite_location_response_header(
|
|||
}
|
||||
|
||||
void Downstream::add_response_header(std::string name, std::string value) {
|
||||
response_header_key_prev_ = true;
|
||||
response_headers_sum_ += name.size() + value.size();
|
||||
response_headers_.emplace_back(std::move(name), std::move(value));
|
||||
add_header(response_header_key_prev_, response_headers_sum_,
|
||||
response_headers_, std::move(name), std::move(value));
|
||||
}
|
||||
|
||||
void Downstream::set_last_response_header_value(std::string value) {
|
||||
response_header_key_prev_ = false;
|
||||
response_headers_sum_ += value.size();
|
||||
auto &item = response_headers_.back();
|
||||
item.value = std::move(value);
|
||||
void Downstream::set_last_response_header_value(const char *data, size_t len) {
|
||||
set_last_header_value(response_header_key_prev_, response_headers_sum_,
|
||||
response_headers_, data, len);
|
||||
}
|
||||
|
||||
void Downstream::add_response_header(std::string name, std::string value,
|
||||
|
@ -626,18 +695,14 @@ bool Downstream::get_response_header_key_prev() const {
|
|||
}
|
||||
|
||||
void Downstream::append_last_response_header_key(const char *data, size_t len) {
|
||||
assert(response_header_key_prev_);
|
||||
response_headers_sum_ += len;
|
||||
auto &item = response_headers_.back();
|
||||
item.name.append(data, len);
|
||||
append_last_header_key(response_header_key_prev_, response_headers_sum_,
|
||||
response_headers_, data, len);
|
||||
}
|
||||
|
||||
void Downstream::append_last_response_header_value(const char *data,
|
||||
size_t len) {
|
||||
assert(!response_header_key_prev_);
|
||||
response_headers_sum_ += len;
|
||||
auto &item = response_headers_.back();
|
||||
item.value.append(data, len);
|
||||
append_last_header_value(response_header_key_prev_, response_headers_sum_,
|
||||
response_headers_, data, len);
|
||||
}
|
||||
|
||||
void Downstream::clear_response_headers() {
|
||||
|
@ -649,10 +714,48 @@ size_t Downstream::get_response_headers_sum() const {
|
|||
return response_headers_sum_;
|
||||
}
|
||||
|
||||
const Headers &Downstream::get_response_trailers() const {
|
||||
return response_trailers_;
|
||||
}
|
||||
|
||||
void Downstream::add_response_trailer(const uint8_t *name, size_t namelen,
|
||||
const uint8_t *value, size_t valuelen,
|
||||
bool no_index, int16_t token) {
|
||||
response_headers_sum_ += namelen + valuelen;
|
||||
http2::add_header(response_trailers_, name, namelen, value, valuelen,
|
||||
no_index, -1);
|
||||
}
|
||||
|
||||
unsigned int Downstream::get_response_http_status() const {
|
||||
return response_http_status_;
|
||||
}
|
||||
|
||||
void Downstream::add_response_trailer(std::string name, std::string value) {
|
||||
add_header(response_trailer_key_prev_, response_headers_sum_,
|
||||
response_trailers_, std::move(name), std::move(value));
|
||||
}
|
||||
|
||||
void Downstream::set_last_response_trailer_value(const char *data, size_t len) {
|
||||
set_last_header_value(response_trailer_key_prev_, response_headers_sum_,
|
||||
response_trailers_, data, len);
|
||||
}
|
||||
|
||||
bool Downstream::get_response_trailer_key_prev() const {
|
||||
return response_trailer_key_prev_;
|
||||
}
|
||||
|
||||
void Downstream::append_last_response_trailer_key(const char *data,
|
||||
size_t len) {
|
||||
append_last_header_key(response_trailer_key_prev_, response_headers_sum_,
|
||||
response_trailers_, data, len);
|
||||
}
|
||||
|
||||
void Downstream::append_last_response_trailer_value(const char *data,
|
||||
size_t len) {
|
||||
append_last_header_value(response_trailer_key_prev_, response_headers_sum_,
|
||||
response_trailers_, data, len);
|
||||
}
|
||||
|
||||
void Downstream::set_response_http_status(unsigned int status) {
|
||||
response_http_status_ = status;
|
||||
}
|
||||
|
|
|
@ -113,7 +113,7 @@ public:
|
|||
// no such header is found, returns nullptr.
|
||||
const Headers::value_type *get_request_header(const std::string &name) const;
|
||||
void add_request_header(std::string name, std::string value);
|
||||
void set_last_request_header_value(std::string value);
|
||||
void set_last_request_header_value(const char *data, size_t len);
|
||||
|
||||
void add_request_header(const uint8_t *name, size_t namelen,
|
||||
const uint8_t *value, size_t valuelen, bool no_index,
|
||||
|
@ -127,6 +127,16 @@ public:
|
|||
|
||||
size_t get_request_headers_sum() const;
|
||||
|
||||
const Headers &get_request_trailers() const;
|
||||
void add_request_trailer(const uint8_t *name, size_t namelen,
|
||||
const uint8_t *value, size_t valuelen, bool no_index,
|
||||
int16_t token);
|
||||
void add_request_trailer(std::string name, std::string value);
|
||||
void set_last_request_trailer_value(const char *data, size_t len);
|
||||
bool get_request_trailer_key_prev() const;
|
||||
void append_last_request_trailer_key(const char *data, size_t len);
|
||||
void append_last_request_trailer_value(const char *data, size_t len);
|
||||
|
||||
void set_request_method(std::string method);
|
||||
const std::string &get_request_method() const;
|
||||
void set_request_path(std::string path);
|
||||
|
@ -201,7 +211,7 @@ public:
|
|||
// Rewrites the location response header field.
|
||||
void rewrite_location_response_header(const std::string &upstream_scheme);
|
||||
void add_response_header(std::string name, std::string value);
|
||||
void set_last_response_header_value(std::string value);
|
||||
void set_last_response_header_value(const char *data, size_t len);
|
||||
|
||||
void add_response_header(std::string name, std::string value, int16_t token);
|
||||
void add_response_header(const uint8_t *name, size_t namelen,
|
||||
|
@ -216,6 +226,16 @@ public:
|
|||
|
||||
size_t get_response_headers_sum() const;
|
||||
|
||||
const Headers &get_response_trailers() const;
|
||||
void add_response_trailer(const uint8_t *name, size_t namelen,
|
||||
const uint8_t *value, size_t valuelen,
|
||||
bool no_index, int16_t token);
|
||||
void add_response_trailer(std::string name, std::string value);
|
||||
void set_last_response_trailer_value(const char *data, size_t len);
|
||||
bool get_response_trailer_key_prev() const;
|
||||
void append_last_response_trailer_key(const char *data, size_t len);
|
||||
void append_last_response_trailer_value(const char *data, size_t len);
|
||||
|
||||
unsigned int get_response_http_status() const;
|
||||
void set_response_http_status(unsigned int status);
|
||||
void set_response_major(int major);
|
||||
|
@ -308,6 +328,11 @@ private:
|
|||
Headers request_headers_;
|
||||
Headers response_headers_;
|
||||
|
||||
// trailer part. For HTTP/1.1, trailer part is only included with
|
||||
// chunked encoding. For HTTP/2, there is no such limit.
|
||||
Headers request_trailers_;
|
||||
Headers response_trailers_;
|
||||
|
||||
std::chrono::high_resolution_clock::time_point request_start_time_;
|
||||
|
||||
std::string request_method_;
|
||||
|
@ -385,11 +410,13 @@ private:
|
|||
bool chunked_request_;
|
||||
bool request_connection_close_;
|
||||
bool request_header_key_prev_;
|
||||
bool request_trailer_key_prev_;
|
||||
bool request_http2_expect_body_;
|
||||
|
||||
bool chunked_response_;
|
||||
bool response_connection_close_;
|
||||
bool response_header_key_prev_;
|
||||
bool response_trailer_key_prev_;
|
||||
bool expect_final_response_;
|
||||
// true if downstream request is pending because backend connection
|
||||
// has not been established or should be checked before use;
|
||||
|
|
|
@ -159,6 +159,7 @@ ssize_t http2_data_read_callback(nghttp2_session *session, int32_t stream_id,
|
|||
uint8_t *buf, size_t length,
|
||||
uint32_t *data_flags,
|
||||
nghttp2_data_source *source, void *user_data) {
|
||||
int rv;
|
||||
auto sd = static_cast<StreamData *>(
|
||||
nghttp2_session_get_stream_user_data(session, stream_id));
|
||||
if (!sd || !sd->dconn) {
|
||||
|
@ -201,6 +202,23 @@ ssize_t http2_data_read_callback(nghttp2_session *session, int32_t stream_id,
|
|||
!downstream->get_upgraded()))) {
|
||||
|
||||
*data_flags |= NGHTTP2_DATA_FLAG_EOF;
|
||||
|
||||
auto &trailers = downstream->get_request_trailers();
|
||||
if (!trailers.empty()) {
|
||||
std::vector<nghttp2_nv> nva;
|
||||
nva.reserve(trailers.size());
|
||||
http2::copy_headers_to_nva(nva, trailers);
|
||||
if (!nva.empty()) {
|
||||
rv = nghttp2_submit_trailer(session, stream_id, nva.data(), nva.size());
|
||||
if (rv != 0) {
|
||||
if (nghttp2_is_fatal(rv)) {
|
||||
return NGHTTP2_ERR_CALLBACK_FAILURE;
|
||||
}
|
||||
} else {
|
||||
*data_flags |= NGHTTP2_DATA_FLAG_NO_END_STREAM;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!input_empty) {
|
||||
|
@ -275,7 +293,7 @@ int Http2DownstreamConnection::push_request_headers() {
|
|||
cookies = downstream_->crumble_request_cookie();
|
||||
}
|
||||
|
||||
// 8 means:
|
||||
// 9 means:
|
||||
// 1. :method
|
||||
// 2. :scheme
|
||||
// 3. :path
|
||||
|
@ -284,8 +302,9 @@ int Http2DownstreamConnection::push_request_headers() {
|
|||
// 6. via (optional)
|
||||
// 7. x-forwarded-for (optional)
|
||||
// 8. x-forwarded-proto (optional)
|
||||
// 9. te (optional)
|
||||
auto nva = std::vector<nghttp2_nv>();
|
||||
nva.reserve(nheader + 8 + cookies.size());
|
||||
nva.reserve(nheader + 9 + cookies.size());
|
||||
|
||||
std::string via_value;
|
||||
std::string xff_value;
|
||||
|
@ -432,6 +451,11 @@ int Http2DownstreamConnection::push_request_headers() {
|
|||
nva.push_back(http2::make_nv_ls("via", via_value));
|
||||
}
|
||||
|
||||
auto te = downstream_->get_request_header(http2::HD_TE);
|
||||
if (te) {
|
||||
nva.push_back(http2::make_nv_ls("te", te->value));
|
||||
}
|
||||
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
std::stringstream ss;
|
||||
for (auto &nv : nva) {
|
||||
|
|
|
@ -668,20 +668,35 @@ int on_header_callback(nghttp2_session *session, const nghttp2_frame *frame,
|
|||
return 0;
|
||||
}
|
||||
|
||||
if (frame->hd.type != NGHTTP2_HEADERS ||
|
||||
(frame->headers.cat != NGHTTP2_HCAT_RESPONSE &&
|
||||
!downstream->get_expect_final_response())) {
|
||||
if (frame->hd.type != NGHTTP2_HEADERS) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto trailer = frame->headers.cat != NGHTTP2_HCAT_RESPONSE &&
|
||||
!downstream->get_expect_final_response();
|
||||
|
||||
if (downstream->get_response_headers_sum() > Downstream::MAX_HEADERS_SUM) {
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
DLOG(INFO, downstream) << "Too large header block size="
|
||||
<< downstream->get_response_headers_sum();
|
||||
}
|
||||
|
||||
if (trailer) {
|
||||
// we don't care trailer part exceeds header size limit; just
|
||||
// discard it.
|
||||
return 0;
|
||||
}
|
||||
|
||||
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
|
||||
}
|
||||
|
||||
if (trailer) {
|
||||
// just store header fields for trailer part
|
||||
downstream->add_response_trailer(name, namelen, value, valuelen,
|
||||
flags & NGHTTP2_NV_FLAG_NO_INDEX, -1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto token = http2::lookup_token(name, namelen);
|
||||
|
||||
if (token == http2::HD_CONTENT_LENGTH) {
|
||||
|
@ -891,10 +906,6 @@ int on_frame_recv_callback(nghttp2_session *session, const nghttp2_frame *frame,
|
|||
if (rv != 0) {
|
||||
return 0;
|
||||
}
|
||||
} else if ((frame->hd.flags & NGHTTP2_FLAG_END_STREAM) == 0) {
|
||||
http2session->submit_rst_stream(frame->hd.stream_id,
|
||||
NGHTTP2_PROTOCOL_ERROR);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -187,8 +187,7 @@ int on_header_callback(nghttp2_session *session, const nghttp2_frame *frame,
|
|||
verbose_on_header_callback(session, frame, name, namelen, value, valuelen,
|
||||
flags, user_data);
|
||||
}
|
||||
if (frame->hd.type != NGHTTP2_HEADERS ||
|
||||
frame->headers.cat != NGHTTP2_HCAT_REQUEST) {
|
||||
if (frame->hd.type != NGHTTP2_HEADERS) {
|
||||
return 0;
|
||||
}
|
||||
auto upstream = static_cast<Http2Upstream *>(user_data);
|
||||
|
@ -207,12 +206,25 @@ int on_header_callback(nghttp2_session *session, const nghttp2_frame *frame,
|
|||
<< downstream->get_request_headers_sum();
|
||||
}
|
||||
|
||||
// just ignore header fields if this is trailer part.
|
||||
if (frame->headers.cat == NGHTTP2_HCAT_HEADERS) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (upstream->error_reply(downstream, 431) != 0) {
|
||||
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (frame->headers.cat == NGHTTP2_HCAT_HEADERS) {
|
||||
// just store header fields for trailer part
|
||||
downstream->add_request_trailer(name, namelen, value, valuelen,
|
||||
flags & NGHTTP2_NV_FLAG_NO_INDEX, -1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto token = http2::lookup_token(name, namelen);
|
||||
|
||||
if (token == http2::HD_CONTENT_LENGTH) {
|
||||
|
@ -1056,6 +1068,7 @@ ssize_t downstream_data_read_callback(nghttp2_session *session,
|
|||
size_t length, uint32_t *data_flags,
|
||||
nghttp2_data_source *source,
|
||||
void *user_data) {
|
||||
int rv;
|
||||
auto downstream = static_cast<Downstream *>(source->ptr);
|
||||
auto upstream = static_cast<Http2Upstream *>(downstream->get_upstream());
|
||||
auto body = downstream->get_response_buf();
|
||||
|
@ -1081,7 +1094,23 @@ ssize_t downstream_data_read_callback(nghttp2_session *session,
|
|||
*data_flags |= NGHTTP2_DATA_FLAG_EOF;
|
||||
|
||||
if (!downstream->get_upgraded()) {
|
||||
|
||||
auto &trailers = downstream->get_response_trailers();
|
||||
if (!trailers.empty()) {
|
||||
std::vector<nghttp2_nv> nva;
|
||||
nva.reserve(trailers.size());
|
||||
http2::copy_headers_to_nva(nva, trailers);
|
||||
if (!nva.empty()) {
|
||||
rv = nghttp2_submit_trailer(session, stream_id, nva.data(),
|
||||
nva.size());
|
||||
if (rv != 0) {
|
||||
if (nghttp2_is_fatal(rv)) {
|
||||
return NGHTTP2_ERR_CALLBACK_FAILURE;
|
||||
}
|
||||
} else {
|
||||
*data_flags |= NGHTTP2_DATA_FLAG_NO_END_STREAM;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (nghttp2_session_get_stream_remote_close(session, stream_id) == 0) {
|
||||
upstream->rst_stream(downstream, NGHTTP2_NO_ERROR);
|
||||
}
|
||||
|
|
|
@ -403,7 +403,16 @@ int HttpDownstreamConnection::end_upload_data() {
|
|||
}
|
||||
|
||||
auto output = downstream_->get_request_buf();
|
||||
output->append("0\r\n\r\n");
|
||||
auto &trailers = downstream_->get_request_trailers();
|
||||
if (trailers.empty()) {
|
||||
output->append("0\r\n\r\n");
|
||||
} else {
|
||||
output->append("0\r\n");
|
||||
std::string trailer_part;
|
||||
http2::build_http1_headers_from_headers(trailer_part, trailers);
|
||||
output->append(trailer_part.c_str(), trailer_part.size());
|
||||
output->append("\r\n");
|
||||
}
|
||||
|
||||
signal_write();
|
||||
|
||||
|
@ -555,14 +564,19 @@ int htp_hdrs_completecb(http_parser *htp) {
|
|||
namespace {
|
||||
int htp_hdr_keycb(http_parser *htp, const char *data, size_t len) {
|
||||
auto downstream = static_cast<Downstream *>(htp->data);
|
||||
if (downstream->get_response_state() != Downstream::INITIAL) {
|
||||
// ignore trailers
|
||||
return 0;
|
||||
}
|
||||
if (downstream->get_response_header_key_prev()) {
|
||||
downstream->append_last_response_header_key(data, len);
|
||||
if (downstream->get_response_state() == Downstream::INITIAL) {
|
||||
if (downstream->get_response_header_key_prev()) {
|
||||
downstream->append_last_response_header_key(data, len);
|
||||
} else {
|
||||
downstream->add_response_header(std::string(data, len), "");
|
||||
}
|
||||
} else {
|
||||
downstream->add_response_header(std::string(data, len), "");
|
||||
// trailer part
|
||||
if (downstream->get_response_trailer_key_prev()) {
|
||||
downstream->append_last_response_trailer_key(data, len);
|
||||
} else {
|
||||
downstream->add_response_trailer(std::string(data, len), "");
|
||||
}
|
||||
}
|
||||
if (downstream->get_response_headers_sum() > Downstream::MAX_HEADERS_SUM) {
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
|
@ -578,14 +592,18 @@ int htp_hdr_keycb(http_parser *htp, const char *data, size_t len) {
|
|||
namespace {
|
||||
int htp_hdr_valcb(http_parser *htp, const char *data, size_t len) {
|
||||
auto downstream = static_cast<Downstream *>(htp->data);
|
||||
if (downstream->get_response_state() != Downstream::INITIAL) {
|
||||
// ignore trailers
|
||||
return 0;
|
||||
}
|
||||
if (downstream->get_response_header_key_prev()) {
|
||||
downstream->set_last_response_header_value(std::string(data, len));
|
||||
if (downstream->get_response_state() == Downstream::INITIAL) {
|
||||
if (downstream->get_response_header_key_prev()) {
|
||||
downstream->set_last_response_header_value(data, len);
|
||||
} else {
|
||||
downstream->append_last_response_header_value(data, len);
|
||||
}
|
||||
} else {
|
||||
downstream->append_last_response_header_value(data, len);
|
||||
if (downstream->get_response_trailer_key_prev()) {
|
||||
downstream->set_last_response_trailer_value(data, len);
|
||||
} else {
|
||||
downstream->append_last_response_trailer_value(data, len);
|
||||
}
|
||||
}
|
||||
if (downstream->get_response_headers_sum() > Downstream::MAX_HEADERS_SUM) {
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
|
|
|
@ -83,14 +83,19 @@ namespace {
|
|||
int htp_hdr_keycb(http_parser *htp, const char *data, size_t len) {
|
||||
auto upstream = static_cast<HttpsUpstream *>(htp->data);
|
||||
auto downstream = upstream->get_downstream();
|
||||
if (downstream->get_request_state() != Downstream::INITIAL) {
|
||||
// ignore trailers
|
||||
return 0;
|
||||
}
|
||||
if (downstream->get_request_header_key_prev()) {
|
||||
downstream->append_last_request_header_key(data, len);
|
||||
if (downstream->get_request_state() == Downstream::INITIAL) {
|
||||
if (downstream->get_request_header_key_prev()) {
|
||||
downstream->append_last_request_header_key(data, len);
|
||||
} else {
|
||||
downstream->add_request_header(std::string(data, len), "");
|
||||
}
|
||||
} else {
|
||||
downstream->add_request_header(std::string(data, len), "");
|
||||
// trailer part
|
||||
if (downstream->get_request_trailer_key_prev()) {
|
||||
downstream->append_last_request_trailer_key(data, len);
|
||||
} else {
|
||||
downstream->add_request_trailer(std::string(data, len), "");
|
||||
}
|
||||
}
|
||||
if (downstream->get_request_headers_sum() > Downstream::MAX_HEADERS_SUM) {
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
|
@ -107,14 +112,18 @@ namespace {
|
|||
int htp_hdr_valcb(http_parser *htp, const char *data, size_t len) {
|
||||
auto upstream = static_cast<HttpsUpstream *>(htp->data);
|
||||
auto downstream = upstream->get_downstream();
|
||||
if (downstream->get_request_state() != Downstream::INITIAL) {
|
||||
// ignore trailers
|
||||
return 0;
|
||||
}
|
||||
if (downstream->get_request_header_key_prev()) {
|
||||
downstream->set_last_request_header_value(std::string(data, len));
|
||||
if (downstream->get_request_state() == Downstream::INITIAL) {
|
||||
if (downstream->get_request_header_key_prev()) {
|
||||
downstream->set_last_request_header_value(data, len);
|
||||
} else {
|
||||
downstream->append_last_request_header_value(data, len);
|
||||
}
|
||||
} else {
|
||||
downstream->append_last_request_header_value(data, len);
|
||||
if (downstream->get_request_trailer_key_prev()) {
|
||||
downstream->set_last_request_trailer_value(data, len);
|
||||
} else {
|
||||
downstream->append_last_request_trailer_value(data, len);
|
||||
}
|
||||
}
|
||||
if (downstream->get_request_headers_sum() > Downstream::MAX_HEADERS_SUM) {
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
|
@ -780,7 +789,16 @@ int HttpsUpstream::on_downstream_body(Downstream *downstream,
|
|||
int HttpsUpstream::on_downstream_body_complete(Downstream *downstream) {
|
||||
if (downstream->get_chunked_response()) {
|
||||
auto output = downstream->get_response_buf();
|
||||
output->append("0\r\n\r\n");
|
||||
auto &trailers = downstream->get_response_trailers();
|
||||
if (trailers.empty()) {
|
||||
output->append("0\r\n\r\n");
|
||||
} else {
|
||||
output->append("0\r\n");
|
||||
std::string trailer_part;
|
||||
http2::build_http1_headers_from_headers(trailer_part, trailers);
|
||||
output->append(trailer_part.c_str(), trailer_part.size());
|
||||
output->append("\r\n");
|
||||
}
|
||||
}
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
DLOG(INFO, downstream) << "HTTP response completed";
|
||||
|
|
Loading…
Reference in New Issue