Skip to content

Commit 579e631

Browse files
authored
FEAT: server graceful shutdown (#50)
- Wait for active connections to finish before server shutdown - Test server startup
1 parent dd5e713 commit 579e631

File tree

3 files changed

+80
-3
lines changed

3 files changed

+80
-3
lines changed

main.go

+28-2
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,39 @@
11
package main
22

33
import (
4+
"context"
45
"log"
6+
"net/http"
7+
"os"
8+
"os/signal"
9+
"syscall"
10+
"time"
511

612
"github.com/xray-web/web-check-api/config"
713
"github.com/xray-web/web-check-api/server"
814
)
915

1016
func main() {
11-
s := server.New(config.New())
12-
log.Println(s.Run())
17+
srv := server.New(config.New())
18+
19+
done := make(chan os.Signal, 1)
20+
signal.Notify(done, os.Interrupt, syscall.SIGINT, syscall.SIGTERM)
21+
22+
go func() {
23+
if err := srv.Run(); err != nil && err != http.ErrServerClosed {
24+
log.Fatalf("listen: %v\n", err)
25+
}
26+
}()
27+
28+
<-done
29+
30+
ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
31+
defer func() {
32+
// extra handling here, databases etc
33+
cancel()
34+
}()
35+
36+
if err := srv.Shutdown(ctx); err != nil {
37+
log.Fatalf("Server Shutdown Failed:%+v", err)
38+
}
1339
}

server/server.go

+11-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package server
22

33
import (
4+
"context"
45
"fmt"
56
"log"
67
"net/http"
@@ -14,10 +15,12 @@ type Server struct {
1415
conf config.Config
1516
mux *http.ServeMux
1617
checks *checks.Checks
18+
srv *http.Server
1719
}
1820

1921
func New(conf config.Config) *Server {
2022
return &Server{
23+
srv: &http.Server{},
2124
conf: conf,
2225
mux: http.NewServeMux(),
2326
checks: checks.NewChecks(),
@@ -49,12 +52,19 @@ func (s *Server) routes() {
4952
s.mux.Handle("GET /api/social-tags", handlers.HandleGetSocialTags(s.checks.SocialTags))
5053
s.mux.Handle("GET /api/tls", handlers.HandleTLS(s.checks.Tls))
5154
s.mux.Handle("GET /api/trace-route", handlers.HandleTraceRoute())
55+
56+
s.srv.Handler = s.CORS(s.mux)
5257
}
5358

5459
func (s *Server) Run() error {
5560
s.routes()
5661

5762
addr := fmt.Sprintf("%s:%s", s.conf.Host, s.conf.Port)
5863
log.Printf("Server started, listening on: %v\n", addr)
59-
return http.ListenAndServe(addr, s.CORS(s.mux))
64+
s.srv.Addr = addr
65+
return s.srv.ListenAndServe()
66+
}
67+
68+
func (s *Server) Shutdown(ctx context.Context) error {
69+
return s.srv.Shutdown(ctx)
6070
}

server/server_test.go

+41
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package server
2+
3+
import (
4+
"net/http"
5+
"net/http/httptest"
6+
"testing"
7+
"time"
8+
9+
"github.com/stretchr/testify/assert"
10+
"github.com/xray-web/web-check-api/config"
11+
"golang.org/x/net/context"
12+
)
13+
14+
func TestServer(t *testing.T) {
15+
t.Parallel()
16+
17+
t.Run("start server", func(t *testing.T) {
18+
t.Parallel()
19+
20+
srv := New(config.New())
21+
srv.routes()
22+
ts := httptest.NewServer(srv.CORS(srv.mux))
23+
defer ts.Close()
24+
25+
// wait up tot 10 seconds for health check to return 200
26+
ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(+10*time.Second))
27+
defer cancel()
28+
for {
29+
req, err := http.NewRequestWithContext(ctx, http.MethodGet, ts.URL+"/health", nil)
30+
assert.NoError(t, err)
31+
resp, err := http.DefaultClient.Do(req)
32+
if err == nil && resp.StatusCode == http.StatusOK {
33+
break
34+
}
35+
}
36+
ctx, cancel = context.WithTimeout(context.Background(), 5*time.Second)
37+
defer cancel()
38+
err := srv.Shutdown(ctx)
39+
assert.NoError(t, err)
40+
})
41+
}

0 commit comments

Comments
 (0)