forked from yinqiwen/gsnova
-
Notifications
You must be signed in to change notification settings - Fork 0
/
http2.go
executable file
·120 lines (108 loc) · 2.67 KB
/
http2.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
package http2
import (
"crypto/tls"
"encoding/json"
"io"
"net"
"net/http"
"sync"
"golang.org/x/net/http2"
"github.com/yinqiwen/gsnova/common/helper"
"github.com/yinqiwen/gsnova/common/logger"
"github.com/yinqiwen/gsnova/common/mux"
"github.com/yinqiwen/gsnova/remote"
)
type http2Stream struct {
req *http.Request
rw http.ResponseWriter
rwLock sync.Mutex
closeCh chan struct{}
}
func (s *http2Stream) Read(b []byte) (n int, err error) {
n, err = s.req.Body.Read(b)
return
}
func (s *http2Stream) Write(b []byte) (n int, err error) {
s.rwLock.Lock()
defer s.rwLock.Unlock()
if nil == s.rw {
return 0, io.EOF
}
n, err = s.rw.Write(b)
if nil != err {
return n, err
}
if f, ok := s.rw.(http.Flusher); ok {
f.Flush()
}
return n, nil
}
func (s *http2Stream) Close() (err error) {
helper.AsyncNotify(s.closeCh)
s.req.Body.Close()
s.rwLock.Lock()
s.rw = nil
s.rwLock.Unlock()
return nil
}
type http2Handler struct {
session *mux.HTTP2MuxSession
}
func (ss *http2Handler) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
//log.Printf("New HTTP2 stream %v", req)
s := &http2Stream{}
s.req = req
s.rw = rw
s.closeCh = make(chan struct{}, 1)
rw.WriteHeader(200)
err := ss.session.OfferStream(&helper.TimeoutReadWriteCloser{ReadWriteCloser: s})
if nil != err {
logger.Error("%v", err)
return
}
<-s.closeCh
}
func servHTTP2(lp net.Listener, addr string, config *tls.Config) {
for {
conn, err := lp.Accept()
if nil != err {
continue
}
muxSession := mux.NewHTTP2ServerMuxSession(conn)
go remote.ServProxyMuxSession(muxSession)
server := &http.Server{
Addr: addr,
TLSConfig: config,
}
http2Server := &http2.Server{
MaxConcurrentStreams: 4096,
PermitProhibitedCipherSuites: true,
}
opt := &http2.ServeConnOpts{}
opt.BaseConfig = server
opt.Handler = &http2Handler{session: muxSession}
go func() {
tlsconn := tls.Server(conn, config)
err = tlsconn.Handshake()
if nil != err {
logger.Error("TLS handshake failed:%v", err)
muxSession.Close()
return
}
stateData, _ := json.MarshalIndent(tlsconn.ConnectionState(), "", " ")
logger.Notice("Recv conn state : %s", string(stateData))
http2Server.ServeConn(tlsconn, opt)
muxSession.Close()
}()
}
}
func StartHTTTP2ProxyServer(addr string, config *tls.Config) error {
lp, err := net.Listen("tcp", addr)
if nil != err {
logger.Error("[ERROR]Failed to listen TCP address:%s with reason:%v", addr, err)
return err
}
logger.Info("Listen on HTTP2 address:%s", addr)
servHTTP2(lp, addr, config)
return nil
}