Commit f1bec6f0 authored by Tatsuhiro Tsujikawa's avatar Tatsuhiro Tsujikawa

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

parent 506177e1
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) { ...@@ -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 { ...@@ -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_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;
}
} }
http2::index_headers(request_hdidx_, request_headers_); 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,10 +778,12 @@ void Downstream::inspect_http1_request() { ...@@ -758,10 +778,12 @@ 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() {
......
...@@ -100,8 +100,10 @@ public: ...@@ -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) { ...@@ -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();
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment