nghttpx: Return 400 for multiple CLs for HTTP/1 upstream

This commit is contained in:
Tatsuhiro Tsujikawa 2015-01-20 22:55:01 +09:00
parent 506177e1bd
commit f1bec6f05c
4 changed files with 58 additions and 9 deletions

View File

@ -1,9 +1,11 @@
package nghttp2
import (
"bufio"
"fmt"
"github.com/bradfitz/http2"
"github.com/bradfitz/http2/hpack"
"io"
"io/ioutil"
"net/http"
"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) {
st := newServerTester(nil, t, noopHandler)
defer st.Close()

View File

@ -286,11 +286,31 @@ const std::string &Downstream::get_assembled_request_cookie() const {
return assembled_request_cookie_;
}
void Downstream::index_request_headers() {
for (auto &kv : request_headers_) {
int Downstream::index_request_headers() {
for (size_t i = 0; i < request_headers_.size(); ++i) {
auto &kv = request_headers_[i];
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 {
@ -758,11 +778,13 @@ void Downstream::inspect_http1_request() {
}
}
auto idx = request_hdidx_[http2::HD_TRANSFER_ENCODING];
if (idx != -1 &&
util::strifind(request_headers_[idx].value.c_str(), "chunked")) {
if (idx != -1) {
request_content_length_ = -1;
if (util::strifind(request_headers_[idx].value.c_str(), "chunked")) {
chunked_request_ = true;
}
}
}
void Downstream::inspect_http1_response() {
auto idx = response_hdidx_[http2::HD_TRANSFER_ENCODING];

View File

@ -100,8 +100,10 @@ public:
Headers crumble_request_cookie();
void assemble_request_cookie();
const std::string &get_assembled_request_cookie() const;
// Lower the request header field names and indexes request headers
void index_request_headers();
// Lower the request header field names and indexes 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
// multiple header have |name| as name, return last occurrence from
// the beginning. If no such header is found, returns nullptr.

View File

@ -157,7 +157,9 @@ int htp_hdrs_completecb(http_parser *htp) {
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();