-
Notifications
You must be signed in to change notification settings - Fork 402
/
server.go
132 lines (111 loc) · 3.29 KB
/
server.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
// Copyright (C) 2019 Storj Labs, Inc.
// See LICENSE for copying information.
package httpserver
import (
"context"
"crypto/tls"
"net"
"net/http"
"time"
"github.com/zeebo/errs"
"go.uber.org/zap"
"golang.org/x/sync/errgroup"
"storj.io/storj/private/errs2"
)
const (
// DefaultShutdownTimeout is the default ShutdownTimeout (see Config)
DefaultShutdownTimeout = time.Second * 10
)
// Config holds the HTTP server configuration
type Config struct {
// Name is the name of the server. It is only used for logging. It can
// be empty.
Name string
// Address is the address to bind the server to. It must be set.
Address string
// Handler is the HTTP handler to be served. It must be set.
Handler http.Handler
// TLSConfig is the TLS configuration for the server. It is optional.
TLSConfig *tls.Config
// ShutdownTimeout controls how long to wait for requests to finish before
// returning from Run() after the context is canceled. It defaults to
// 10 seconds if unset. If set to a negative value, the server will be
// closed immediately.
ShutdownTimeout time.Duration
}
// Server is the HTTP server
type Server struct {
log *zap.Logger
name string
listener net.Listener
server *http.Server
shutdownTimeout time.Duration
}
// New creates a new URL Service Server.
func New(log *zap.Logger, config Config) (*Server, error) {
switch {
case config.Address == "":
return nil, errs.New("server address is required")
case config.Handler == nil:
return nil, errs.New("server handler is required")
}
listener, err := net.Listen("tcp", config.Address)
if err != nil {
return nil, errs.New("unable to listen on %s: %v", config.Address, err)
}
server := &http.Server{
Handler: config.Handler,
TLSConfig: config.TLSConfig,
ErrorLog: zap.NewStdLog(log),
}
if config.ShutdownTimeout == 0 {
config.ShutdownTimeout = DefaultShutdownTimeout
}
if config.Name != "" {
log = log.With(zap.String("server", config.Name))
}
return &Server{
log: log,
name: config.Name,
listener: listener,
server: server,
shutdownTimeout: config.ShutdownTimeout,
}, nil
}
// Run runs the server until it's either closed or it errors.
func (server *Server) Run(ctx context.Context) (err error) {
ctx, cancel := context.WithCancel(ctx)
var group errgroup.Group
group.Go(func() error {
<-ctx.Done()
server.log.Info("Server shutting down")
return shutdownWithTimeout(server.server, server.shutdownTimeout)
})
group.Go(func() (err error) {
defer cancel()
server.log.With(zap.String("addr", server.Addr())).Sugar().Info("Server started")
if server.server.TLSConfig == nil {
err = server.server.Serve(server.listener)
} else {
err = server.server.ServeTLS(server.listener, "", "")
}
if err == http.ErrServerClosed {
return nil
}
server.log.With(zap.Error(err)).Error("Server closed unexpectedly")
return err
})
return group.Wait()
}
// Addr returns the public address.
func (server *Server) Addr() string {
return server.listener.Addr().String()
}
func shutdownWithTimeout(server *http.Server, timeout time.Duration) error {
if timeout < 0 {
return server.Close()
}
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
return errs2.IgnoreCanceled(server.Shutdown(ctx))
}