-
-
Notifications
You must be signed in to change notification settings - Fork 78
/
server.go
112 lines (106 loc) · 2.52 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
package httpserver
import (
"context"
"errors"
"fmt"
"github.com/bitmagnet-io/bitmagnet/internal/boilerplate/httpserver/ginzap"
"github.com/bitmagnet-io/bitmagnet/internal/boilerplate/worker"
"github.com/gin-gonic/gin"
"go.uber.org/fx"
"go.uber.org/zap"
"net"
"net/http"
"sort"
"time"
)
type Params struct {
fx.In
Config Config
Options []Option `group:"http_server_options"`
Logger *zap.Logger
}
type Result struct {
fx.Out
Worker worker.Worker `group:"workers"`
}
func New(p Params) Result {
var s *http.Server
return Result{
Worker: worker.NewWorker(
"http_server",
fx.Hook{
OnStart: func(ctx context.Context) error {
gin.SetMode(p.Config.GinMode)
g := gin.New()
g.Use(ginzap.Ginzap(p.Logger.Named("gin"), time.RFC3339, true), gin.Recovery())
options, optionsErr := resolveOptions(p.Config.Options, p.Options)
if optionsErr != nil {
return optionsErr
}
for _, o := range options {
if buildErr := o.Apply(g); buildErr != nil {
return buildErr
}
}
s = &http.Server{
Addr: p.Config.LocalAddress,
Handler: g.Handler(),
}
ln, listenErr := net.Listen("tcp", s.Addr)
if listenErr != nil {
return listenErr
}
go (func() {
serveErr := s.Serve(ln)
if !errors.Is(serveErr, http.ErrServerClosed) {
panic(serveErr)
}
})()
return nil
},
OnStop: func(ctx context.Context) error {
if s == nil {
return nil
}
return s.Shutdown(ctx)
},
},
),
}
}
func resolveOptions(param []string, options []Option) ([]Option, error) {
paramMap := make(map[string]struct{})
for _, p := range param {
paramMap[p] = struct{}{}
}
enabledOptions := make([]Option, 0, len(options))
foundMap := make(map[string]struct{}, len(options))
for _, o := range options {
if _, ok := foundMap[o.Key()]; ok {
return nil, fmt.Errorf("duplicate http server option: '%s'", o.Key())
}
foundMap[o.Key()] = struct{}{}
enabled := false
if _, ok := paramMap["*"]; ok {
enabled = true
} else if _, ok := paramMap[o.Key()]; ok {
enabled = true
}
if enabled {
enabledOptions = append(enabledOptions, o)
}
}
for p := range paramMap {
if _, ok := foundMap[p]; !ok && p != "*" {
return nil, fmt.Errorf("unknown http server option: '%s'", p)
}
}
sort.Slice(enabledOptions, func(i, j int) bool {
return enabledOptions[i].Key() < enabledOptions[j].Key()
})
return enabledOptions, nil
}
type Option interface {
Key() string
Apply(engine *gin.Engine) error
}