nghttpx: Return 400 for multiple CLs for HTTP/1 upstream
This commit is contained in:
parent
506177e1bd
commit
f1bec6f05c
|
@ -1,9 +1,11 @@
|
||||||
package nghttp2
|
package nghttp2
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bufio"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/bradfitz/http2"
|
"github.com/bradfitz/http2"
|
||||||
"github.com/bradfitz/http2/hpack"
|
"github.com/bradfitz/http2/hpack"
|
||||||
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
"testing"
|
"testing"
|
||||||
|
@ -46,6 +48,27 @@ func TestH1H1PlainGETClose(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestH1H1DuplicateRequestCL(t *testing.T) {
|
||||||
|
st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
t.Errorf("server should not forward bad request")
|
||||||
|
})
|
||||||
|
defer st.Close()
|
||||||
|
|
||||||
|
if _, err := io.WriteString(st.conn, fmt.Sprintf("GET / HTTP/1.1\r\nHost: %v\r\nTest-Case: TestH1H1DuplicateRequestCL\r\nContent-Length: 0\r\nContent-Length: 1\r\n\r\n", st.authority)); err != nil {
|
||||||
|
t.Fatalf("Error io.WriteString() = %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := http.ReadResponse(bufio.NewReader(st.conn), nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error http.ReadResponse() = %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
want := 400
|
||||||
|
if got := resp.StatusCode; got != want {
|
||||||
|
t.Errorf("status: %v; want %v", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestH2H1PlainGET(t *testing.T) {
|
func TestH2H1PlainGET(t *testing.T) {
|
||||||
st := newServerTester(nil, t, noopHandler)
|
st := newServerTester(nil, t, noopHandler)
|
||||||
defer st.Close()
|
defer st.Close()
|
||||||
|
|
|
@ -286,11 +286,31 @@ const std::string &Downstream::get_assembled_request_cookie() const {
|
||||||
return assembled_request_cookie_;
|
return assembled_request_cookie_;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Downstream::index_request_headers() {
|
int Downstream::index_request_headers() {
|
||||||
for (auto &kv : request_headers_) {
|
for (size_t i = 0; i < request_headers_.size(); ++i) {
|
||||||
|
auto &kv = request_headers_[i];
|
||||||
util::inp_strlower(kv.name);
|
util::inp_strlower(kv.name);
|
||||||
|
|
||||||
|
auto token = http2::lookup_token(
|
||||||
|
reinterpret_cast<const uint8_t *>(kv.name.c_str()), kv.name.size());
|
||||||
|
if (token < 0) {
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
http2::index_headers(request_hdidx_, request_headers_);
|
|
||||||
|
http2::index_header(request_hdidx_, token, i);
|
||||||
|
|
||||||
|
if (token == http2::HD_CONTENT_LENGTH) {
|
||||||
|
auto len = util::parse_uint(kv.value);
|
||||||
|
if (len == -1) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (request_content_length_ != -1 && request_content_length_ != len) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
request_content_length_ = len;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Headers::value_type *Downstream::get_request_header(int token) const {
|
const Headers::value_type *Downstream::get_request_header(int token) const {
|
||||||
|
@ -758,11 +778,13 @@ void Downstream::inspect_http1_request() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
auto idx = request_hdidx_[http2::HD_TRANSFER_ENCODING];
|
auto idx = request_hdidx_[http2::HD_TRANSFER_ENCODING];
|
||||||
if (idx != -1 &&
|
if (idx != -1) {
|
||||||
util::strifind(request_headers_[idx].value.c_str(), "chunked")) {
|
request_content_length_ = -1;
|
||||||
|
if (util::strifind(request_headers_[idx].value.c_str(), "chunked")) {
|
||||||
chunked_request_ = true;
|
chunked_request_ = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Downstream::inspect_http1_response() {
|
void Downstream::inspect_http1_response() {
|
||||||
auto idx = response_hdidx_[http2::HD_TRANSFER_ENCODING];
|
auto idx = response_hdidx_[http2::HD_TRANSFER_ENCODING];
|
||||||
|
|
|
@ -100,8 +100,10 @@ public:
|
||||||
Headers crumble_request_cookie();
|
Headers crumble_request_cookie();
|
||||||
void assemble_request_cookie();
|
void assemble_request_cookie();
|
||||||
const std::string &get_assembled_request_cookie() const;
|
const std::string &get_assembled_request_cookie() const;
|
||||||
// Lower the request header field names and indexes request headers
|
// Lower the request header field names and indexes request headers.
|
||||||
void index_request_headers();
|
// If there is any invalid headers (e.g., multiple Content-Length
|
||||||
|
// having different values), returns -1.
|
||||||
|
int index_request_headers();
|
||||||
// Returns pointer to the request header with the name |name|. If
|
// Returns pointer to the request header with the name |name|. If
|
||||||
// multiple header have |name| as name, return last occurrence from
|
// multiple header have |name| as name, return last occurrence from
|
||||||
// the beginning. If no such header is found, returns nullptr.
|
// the beginning. If no such header is found, returns nullptr.
|
||||||
|
|
|
@ -157,7 +157,9 @@ int htp_hdrs_completecb(http_parser *htp) {
|
||||||
ULOG(INFO, upstream) << "HTTP request headers\n" << ss.str();
|
ULOG(INFO, upstream) << "HTTP request headers\n" << ss.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
downstream->index_request_headers();
|
if (downstream->index_request_headers() != 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
downstream->inspect_http1_request();
|
downstream->inspect_http1_request();
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue