Skip to content

Commit

Permalink
fix gracefilly shutdown bug, issue #958 (#960)
Browse files Browse the repository at this point in the history
* fix gracefilly shutdown bug, issue #958

* fix golangci-lint

* add option: CloseOnShutdown into Sever

* Update server.go

Co-authored-by: Erik Dubbelboer <[email protected]>

* Update server.go

Co-authored-by: Erik Dubbelboer <[email protected]>

Co-authored-by: fujianhao3 <[email protected]>
Co-authored-by: Erik Dubbelboer <[email protected]>
  • Loading branch information
3 people authored Feb 7, 2021
1 parent 1494fdc commit a88030b
Show file tree
Hide file tree
Showing 3 changed files with 78 additions and 2 deletions.
6 changes: 6 additions & 0 deletions header_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2479,6 +2479,12 @@ func verifyResponseHeader(t *testing.T, h *ResponseHeader, expectedStatusCode, e
}
}

func verifyResponseHeaderConnection(t *testing.T, h *ResponseHeader, expectConnection string) {
if string(h.Peek(HeaderConnection)) != expectConnection {
t.Fatalf("Unexpected Connection %q. Expected %q", h.Peek(HeaderConnection), expectConnection)
}
}

func verifyRequestHeader(t *testing.T, h *RequestHeader, expectedContentLength int,
expectedRequestURI, expectedHost, expectedReferer, expectedContentType string) {
if h.ContentLength() != expectedContentLength {
Expand Down
6 changes: 6 additions & 0 deletions server.go
Original file line number Diff line number Diff line change
Expand Up @@ -375,11 +375,16 @@ type Server struct {
// which will close it when needed.
KeepHijackedConns bool


// CloseOnShutdown when true adds a `Connection: close` header when when the server is shutting down.
CloseOnShutdown bool

// StreamRequestBody enables request body streaming,
// and calls the handler sooner when given body is
// larger then the current limit.
StreamRequestBody bool


tlsConfig *tls.Config
nextProtos map[string]ServeHandler

Expand Down Expand Up @@ -2221,6 +2226,7 @@ func (s *Server) serveConn(c net.Conn) (err error) {
}

connectionClose = connectionClose || ctx.Response.ConnectionClose()
connectionClose = connectionClose || ctx.Response.ConnectionClose() || (s.CloseOnShutdown && atomic.LoadInt32(&s.stop) == 1)
if connectionClose {
ctx.Response.Header.SetCanonical(strConnection, strClose)
} else if !isHTTP11 {
Expand Down
68 changes: 66 additions & 2 deletions server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3117,7 +3117,70 @@ func TestShutdown(t *testing.T) {
t.Errorf("unexpected error: %s", err)
}
br := bufio.NewReader(conn)
verifyResponse(t, br, StatusOK, "aaa/bbb", "real response")
resp := verifyResponse(t, br, StatusOK, "aaa/bbb", "real response")
verifyResponseHeaderConnection(t, &resp.Header, "")
clientCh <- struct{}{}
}()
time.Sleep(time.Millisecond * 100)
shutdownCh := make(chan struct{})
go func() {
if err := s.Shutdown(); err != nil {
t.Errorf("unexepcted error: %s", err)
}
shutdownCh <- struct{}{}
}()
done := 0
for {
select {
case <-time.After(time.Second):
t.Fatal("shutdown took too long")
case <-serveCh:
done++
case <-clientCh:
done++
case <-shutdownCh:
done++
}
if done == 3 {
return
}
}
}

func TestCloseOnShutdown(t *testing.T) {
t.Parallel()

ln := fasthttputil.NewInmemoryListener()
s := &Server{
Handler: func(ctx *RequestCtx) {
time.Sleep(time.Millisecond * 500)
ctx.Success("aaa/bbb", []byte("real response"))
},
CloseOnShutdown: true,
}
serveCh := make(chan struct{})
go func() {
if err := s.Serve(ln); err != nil {
t.Errorf("unexepcted error: %s", err)
}
_, err := ln.Dial()
if err == nil {
t.Error("server is still listening")
}
serveCh <- struct{}{}
}()
clientCh := make(chan struct{})
go func() {
conn, err := ln.Dial()
if err != nil {
t.Errorf("unexepcted error: %s", err)
}
if _, err = conn.Write([]byte("GET / HTTP/1.1\r\nHost: google.com\r\n\r\n")); err != nil {
t.Errorf("unexpected error: %s", err)
}
br := bufio.NewReader(conn)
resp := verifyResponse(t, br, StatusOK, "aaa/bbb", "real response")
verifyResponseHeaderConnection(t, &resp.Header, "close")
clientCh <- struct{}{}
}()
time.Sleep(time.Millisecond * 100)
Expand Down Expand Up @@ -3580,7 +3643,7 @@ func TestIncompleteBodyReturnsUnexpectedEOF(t *testing.T) {
}
}

func verifyResponse(t *testing.T, r *bufio.Reader, expectedStatusCode int, expectedContentType, expectedBody string) {
func verifyResponse(t *testing.T, r *bufio.Reader, expectedStatusCode int, expectedContentType, expectedBody string) *Response {
var resp Response
if err := resp.Read(r); err != nil {
t.Fatalf("Unexpected error when parsing response: %s", err)
Expand All @@ -3590,6 +3653,7 @@ func verifyResponse(t *testing.T, r *bufio.Reader, expectedStatusCode int, expec
t.Fatalf("Unexpected body %q. Expected %q", resp.Body(), []byte(expectedBody))
}
verifyResponseHeader(t, &resp.Header, expectedStatusCode, len(resp.Body()), expectedContentType)
return &resp
}

type readWriter struct {
Expand Down

0 comments on commit a88030b

Please sign in to comment.