diff --git a/internal/proxy/passthrough_test.go b/internal/proxy/passthrough_test.go
index 1b34821..abaf334 100644
--- a/internal/proxy/passthrough_test.go
+++ b/internal/proxy/passthrough_test.go
@@ -192,6 +192,36 @@ func wantRequestFields(t *testing.T, line logLine, host string, sent, received i
}
}
+func TestAddsNoContentTypeTheAppDidNotSend(t *testing.T) {
+ t.Parallel()
+
+ const typed = "text/plain"
+
+ app := startApp(t, func(w http.ResponseWriter, r *http.Request) {
+ if r.URL.Path == "/typed" {
+ w.Header().Set("Content-Type", typed)
+ } else {
+ // The key with no value keeps Go's server from adding one.
+ w.Header()["Content-Type"] = nil
+ }
+
+ _, _ = io.WriteString(w, "
page")
+ })
+ addr, _ := startProxy(t, app.URL, nil)
+
+ got := get(t, addr, "/")
+ if got.header.Values("Content-Type") != nil {
+ t.Errorf("client got Content-Type %q, want none",
+ got.header.Get("Content-Type"))
+ }
+
+ got = get(t, addr, "/typed")
+ if got.header.Get("Content-Type") != typed {
+ t.Errorf("client got Content-Type %q, want %q",
+ got.header.Get("Content-Type"), typed)
+ }
+}
+
func TestStreamsTheRequestBody(t *testing.T) {
t.Parallel()
diff --git a/internal/proxy/request.go b/internal/proxy/request.go
index e4e09af..e9620d9 100644
--- a/internal/proxy/request.go
+++ b/internal/proxy/request.go
@@ -431,6 +431,13 @@ func (rq *request) modifyResponse(res *http.Response) error {
return errResponseTooLarge
}
+ // Go's server adds a Content-Type it guesses from the body to a
+ // response whose headers have no Content-Type key. The key with no
+ // value sends none, as the app sent none.
+ if res.Header.Get("Content-Type") == "" {
+ rq.out.Header()["Content-Type"] = nil
+ }
+
res.Body = &responseBody{body: limitBody(res.Body, maxBytes), rq: rq}
rq.startClientResponseTimeout()
diff --git a/internal/proxy/request_internal_test.go b/internal/proxy/request_internal_test.go
new file mode 100644
index 0000000..ce26481
--- /dev/null
+++ b/internal/proxy/request_internal_test.go
@@ -0,0 +1,55 @@
+package proxy
+
+import (
+ "io"
+ "net/http"
+ "net/http/httptest"
+ "testing"
+
+ "sneak.berlin/go/smallwebwaf/internal/config"
+)
+
+func TestAnAnswerWithoutAContentTypeGetsNone(t *testing.T) {
+ t.Parallel()
+
+ srv := httptest.NewServer(http.HandlerFunc(
+ func(w http.ResponseWriter, _ *http.Request) {
+ rq := &request{
+ h: &handler{config: &config.Config{}},
+ out: &responseWriter{ResponseWriter: w},
+ }
+ app := &http.Response{
+ StatusCode: http.StatusOK,
+ Header: http.Header{},
+ Body: http.NoBody,
+ }
+
+ err := rq.modifyResponse(app)
+ if err != nil {
+ t.Errorf("modifyResponse: %v", err)
+ }
+
+ // The headers go out with this, the first part of the body,
+ // which Go's server would guess a Content-Type from.
+ _, _ = io.WriteString(w, "page")
+ }))
+ t.Cleanup(srv.Close)
+
+ req, err := http.NewRequestWithContext(t.Context(), http.MethodGet, srv.URL,
+ http.NoBody)
+ if err != nil {
+ t.Fatalf("new request: %v", err)
+ }
+
+ res, err := srv.Client().Do(req)
+ if err != nil {
+ t.Fatalf("get: %v", err)
+ }
+
+ _ = res.Body.Close()
+
+ if res.Header.Values("Content-Type") != nil {
+ t.Errorf("client got Content-Type %q, want none",
+ res.Header.Get("Content-Type"))
+ }
+}