/
server.go
138 lines (118 loc) · 2.71 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
133
134
135
136
137
138
package sweb
import (
"context"
"net"
"net/http"
"os"
"sync"
"time"
"github.com/go-chi/chi"
"github.com/go-chi/chi/middleware"
ll "github.com/grimdork/loglines"
)
// Server structure.
type Server struct {
sync.RWMutex
sync.WaitGroup
http.Server
web *chi.Mux
starthooks []StartHook
stophooks []StopHook
staticpath string
}
const (
// WEBHOST default
WEBHOST = "127.0.0.1"
// WEBPORT default
WEBPORT = "80"
// WEBSTATIC default
WEBSTATIC = "static"
)
// New server init. Reads settings from environment:
// WEBHOST - default 127.0.0.1
// WEBPORT - default 15000
// WEBSTATIC - default "./static/"
func New() *Server {
srv := &Server{}
srv.Init()
srv.InitMiddleware()
srv.InitRouter()
return srv
}
// Init sets up the basics, but no routes.
func (srv *Server) Init() {
srv.web = chi.NewRouter()
// Default timeouts
srv.Server.IdleTimeout = time.Second * 30
srv.Server.ReadTimeout = time.Second * 30
srv.Server.ReadHeaderTimeout = time.Second * 5
srv.Server.WriteTimeout = time.Second * 30
}
// InitMiddleware sets up basic middleware on the root web route.
// These are RealIP and RequestID from chi, a logger for visits and HTML headers.
func (srv *Server) InitMiddleware() {
srv.web.Use(
middleware.RealIP,
middleware.RequestID,
srv.addLogger,
AddHTMLHeaders,
)
}
// Use passes middleware to the root web route.
func (srv *Server) Use(middlewares ...func(http.Handler) http.Handler) {
srv.web.Use(middlewares...)
}
// InitRouter creates the default root router which loads files from the WEBSTATIC path.
func (srv *Server) InitRouter() {
srv.WebGet("/", srv.Static)
srv.WebGets("/{page}", func(r chi.Router) {
r.Get("/*", srv.Static)
r.Options("/", Preflight)
})
}
// Start serving, reconfiguring from any changed environment variables.
func (srv *Server) Start() error {
for _, cb := range srv.starthooks {
err := cb()
if err != nil {
return err
}
}
srv.Lock()
defer srv.Unlock()
srv.staticpath = Getenv("WEBSTATIC", WEBSTATIC)
addr := net.JoinHostPort(
Getenv("WEBHOST", WEBHOST),
Getenv("WEBPORT", WEBPORT),
)
listener, err := net.Listen("tcp", addr)
if err != nil {
return err
}
srv.Add(1)
ll.Msg("Starting web server on http://%s", addr)
go func() {
srv.Handler = srv.web
err = srv.Serve(listener)
if err != nil && err != http.ErrServerClosed {
ll.Err("Error running server: %s", err.Error())
os.Exit(2)
}
ll.Msg("Stopped web server.")
srv.Done()
}()
return nil
}
// Stop serving.
func (srv *Server) Stop() {
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
defer cancel()
err := srv.Shutdown(ctx)
if err != nil {
ll.Err("Shutdown error: %s", err.Error())
}
for _, cb := range srv.stophooks {
cb()
}
srv.Wait()
}