diff --git a/integration-tests/nghttpx_test.go b/integration-tests/nghttpx_test.go index eb03d015..861a067b 100644 --- a/integration-tests/nghttpx_test.go +++ b/integration-tests/nghttpx_test.go @@ -96,6 +96,44 @@ func TestH1H1ConnectFailure(t *testing.T) { } } +func TestH1H1GracefulShutdown(t *testing.T) { + st := newServerTester(nil, t, noopHandler) + defer st.Close() + + res, err := st.http1(requestParam{ + name: "TestH1H1GracefulShutdown-1", + }) + if err != nil { + t.Fatalf("Error st.http1() = %v", err) + } + + if got, want := res.status, 200; got != want { + t.Errorf("status: %v; want %v", got, want) + } + + st.cmd.Process.Signal(syscall.SIGQUIT) + + res, err = st.http1(requestParam{ + name: "TestH1H1GracefulShutdown-2", + }) + if err != nil { + t.Fatalf("Error st.http1() = %v", err) + } + + if got, want := res.status, 200; got != want { + t.Errorf("status: %v; want %v", got, want) + } + + if got, want := res.connClose, true; got != want { + t.Errorf("res.connClose: %v; want %v", got, want) + } + + want := io.EOF + if _, err := st.conn.Read(nil); err == nil || err != want { + t.Errorf("st.conn.Read(): %v; want %v", err, want) + } +} + func TestH1H2ConnectFailure(t *testing.T) { st := newServerTester([]string{"--http2-bridge"}, t, noopHandler) defer st.Close() diff --git a/integration-tests/server_tester.go b/integration-tests/server_tester.go index 47bf7c0d..dc2376b0 100644 --- a/integration-tests/server_tester.go +++ b/integration-tests/server_tester.go @@ -275,9 +275,10 @@ func (st *serverTester) http1(rp requestParam) (*serverResponse, error) { resp.Body.Close() res := &serverResponse{ - status: resp.StatusCode, - header: resp.Header, - body: respBody, + status: resp.StatusCode, + header: resp.Header, + body: respBody, + connClose: resp.Close, } return res, nil @@ -534,6 +535,7 @@ type serverResponse struct { connErr bool // true if HTTP/2 connection error spdyGoAwayErrCode spdy.GoAwayStatus // status code received in SPDY RST_STREAM spdyRstErrCode spdy.RstStreamStatus // status code received in SPDY GOAWAY + connClose bool // Conection: close is included in response header in HTTP/1 test } func cloneHeader(h http.Header) http.Header { diff --git a/src/shrpx_https_upstream.cc b/src/shrpx_https_upstream.cc index 9ef2a9e3..f6be0aaa 100644 --- a/src/shrpx_https_upstream.cc +++ b/src/shrpx_https_upstream.cc @@ -664,6 +664,12 @@ int HttpsUpstream::on_downstream_header_complete(Downstream *downstream) { return 0; } + // after graceful shutdown commenced, add connection: close header + // field. + if (worker_config->graceful_shutdown) { + downstream->set_response_connection_close(true); + } + // We check downstream->get_response_connection_close() in case when // the Content-Length is not available. if (!downstream->get_request_connection_close() &&