forked from q191201771/lal
-
Notifications
You must be signed in to change notification settings - Fork 0
/
http_server.go
162 lines (137 loc) · 4.06 KB
/
http_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
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
// Copyright 2021, Chef. All rights reserved.
// https://github.com/forkiss/lal
//
// Use of this source code is governed by a MIT-style license
// that can be found in the License file.
//
// Author: Chef (191201771@qq.com)
package base
import (
"crypto/tls"
"net"
"net/http"
"reflect"
"github.com/forkiss/naza/pkg/nazaerrors"
)
// TODO(chef)
// - 考虑移入naza中
// - 考虑增加一个pattern全部未命中的mux回调
const (
NetworkTcp = "tcp"
)
type LocalAddrCtx struct {
IsHttps bool
Addr string
CertFile string
KeyFile string
Network string
}
type HttpServerManager struct {
addr2ServerCtx map[string]*ServerCtx
}
type ServerCtx struct {
addrCtx LocalAddrCtx
listener net.Listener
httpServer http.Server
mux *http.ServeMux
pattern2Handler map[string]Handler
}
func NewHttpServerManager() *HttpServerManager {
return &HttpServerManager{
addr2ServerCtx: make(map[string]*ServerCtx),
}
}
type Handler func(http.ResponseWriter, *http.Request)
// AddListen
//
// @param addrCtx IsHttps 是否为https
// 注意,如果要为相同的路由同时绑定http和https,那么应该调用该函数两次,分别将该参数设置为true和false
// Addr 监听地址,内部会为其建立监听
// http和https不能够使用相同的地址
// 注意,多次调用,允许使用相同的地址绑定不同的`pattern`
// CertFile
// KeyFile
// Network 如果为空默认为NetworkTcp="tcp"
//
// @param pattern 必须以`/`开始,并以`/`结束
// 注意,如果是`/`,则在其他所有pattern都匹配失败后,做为兜底匹配成功
// 相同的pattern不能绑定不同的`handler`回调函数(显然,我们无法为相同的监听地址,相同的路径绑定多个回调函数)
//
func (s *HttpServerManager) AddListen(addrCtx LocalAddrCtx, pattern string, handler Handler) error {
var (
ctx *ServerCtx
mux *http.ServeMux
ok bool
)
if addrCtx.Addr == "" {
return ErrAddrEmpty
}
// 监听地址是否已经创建过
ctx, ok = s.addr2ServerCtx[addrCtx.Addr]
if !ok {
l, err := listen(addrCtx)
if err != nil {
return err
}
mux = http.NewServeMux()
ctx = &ServerCtx{
addrCtx: addrCtx,
listener: l,
httpServer: http.Server{
Handler: mux,
},
mux: mux,
pattern2Handler: make(map[string]Handler),
}
s.addr2ServerCtx[addrCtx.Addr] = ctx
}
// 路径相同,比较回调函数是否相同
// 如果回调函数也相同,意味着重复绑定,这种情况是允许的,忽略掉就行了
// 如果回调函数不同,返回错误
if prevHandler, ok := ctx.pattern2Handler[pattern]; ok {
if reflect.ValueOf(prevHandler).Pointer() == reflect.ValueOf(handler).Pointer() {
return nil
} else {
return ErrMultiRegisterForPattern
}
}
ctx.pattern2Handler[pattern] = handler
ctx.mux.HandleFunc(pattern, handler)
return nil
}
func (s *HttpServerManager) RunLoop() error {
errChan := make(chan error, len(s.addr2ServerCtx))
for _, v := range s.addr2ServerCtx {
go func(ctx *ServerCtx) {
errChan <- ctx.httpServer.Serve(ctx.listener)
_ = ctx.httpServer.Close()
}(v)
}
// 阻塞直到接到第一个error
return <-errChan
}
func (s *HttpServerManager) Dispose() error {
var es []error
for _, v := range s.addr2ServerCtx {
err := v.httpServer.Close()
es = append(es, err)
}
return nazaerrors.CombineErrors(es...)
}
// ---------------------------------------------------------------------------------------------------------------------
// 为传入的`Addr`地址创建http或https监听
//
func listen(ctx LocalAddrCtx) (net.Listener, error) {
if ctx.Network == "" {
ctx.Network = NetworkTcp
}
if !ctx.IsHttps {
return net.Listen(ctx.Network, ctx.Addr)
}
cert, err := tls.LoadX509KeyPair(ctx.CertFile, ctx.KeyFile)
if err != nil {
return nil, err
}
tlsConfig := &tls.Config{Certificates: []tls.Certificate{cert}}
return tls.Listen(ctx.Network, ctx.Addr, tlsConfig)
}