-
Notifications
You must be signed in to change notification settings - Fork 1
/
main.go
198 lines (176 loc) · 5.33 KB
/
main.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
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
package main
import (
"crypto/rand"
"crypto/tls"
"io"
"log"
mrand "math/rand"
"net/http"
"net/url"
"time"
"github.com/quic-go/quic-go"
"github.com/quic-go/quic-go/http3"
)
func main() {
// Load config file
c := LoadConfig()
h3addr := c.H3Addr
domains := c.TLS
destination := c.Destinations
trace := c.Trace
filelog := c.FileLog
log.Println("server listening on " + h3addr)
// Generate TLS config for HTTP/3 server
// Config for multiple Domains
tconf := tls.Config{
GetConfigForClient: func(chi *tls.ClientHelloInfo) (*tls.Config, error) {
sni := Domainy{}
sni_e := sni.From(chi.ServerName)
if sni_e == nil {
for _, domain := range domains.Domains {
if domain.SubDomainsSupport {
// Certificate covers all sub-domains
sn := Domainy{}
sn_e := sn.From(domain.ServerName)
if sn_e != nil {
log.Println("Invalid server name from config")
continue
}
if sni.DomainName == sn.DomainName {
// Load Certificate
cert, cert_e := tls.LoadX509KeyPair(
domain.CertPath,
domain.KeyPath,
)
if cert_e != nil {
log.Fatalln(cert_e.Error())
}
return &tls.Config{
Rand: rand.Reader,
NextProtos: []string{"h3", "h2", "http/1.1"},
Certificates: []tls.Certificate{cert},
}, nil
}
} else {
// DO exact match
sn := Domainy{}
sn_e := sn.From(domain.ServerName)
if sn_e != nil {
log.Println("Invalid server name from config")
continue
}
if sni.Subdomain == sn.Subdomain && sni.DomainName == sn.DomainName {
// Load Certificate
cert, cert_e := tls.LoadX509KeyPair(
domain.CertPath,
domain.KeyPath,
)
if cert_e != nil {
log.Fatalln(cert_e.Error())
}
return &tls.Config{
Rand: rand.Reader,
NextProtos: []string{"h3", "h2", "http/1.1"},
Certificates: []tls.Certificate{cert},
}, nil
}
}
}
}
// If no match found return default Certificate
// No server name support or using ip
cert, cert_e := tls.LoadX509KeyPair(domains.Default.CertPath, domains.Default.KeyPath)
if cert_e != nil {
log.Fatalln(cert_e.Error())
}
return &tls.Config{
Rand: rand.Reader,
NextProtos: []string{"h3", "h2", "http/1.1"},
Certificates: []tls.Certificate{cert},
}, nil
},
}
// Generate QUIC config
qconf := quic.Config{
HandshakeIdleTimeout: time.Duration(c.QUIC.HandshakeIdleTimeout) * time.Second,
MaxIdleTimeout: time.Duration(c.QUIC.MaxIdleTimeout) * time.Second,
InitialStreamReceiveWindow: c.QUIC.InitialStreamReceiveWindow,
MaxStreamReceiveWindow: c.QUIC.MaxStreamReceiveWindow,
InitialConnectionReceiveWindow: c.QUIC.InitialConnectionReceiveWindow,
MaxConnectionReceiveWindow: c.QUIC.MaxConnectionReceiveWindow,
MaxIncomingStreams: c.QUIC.MaxIncomingStreams,
MaxIncomingUniStreams: c.QUIC.MaxIncomingUniStreams,
DisablePathMTUDiscovery: c.QUIC.DisablePathMTUDiscovery,
Allow0RTT: c.QUIC.Allow0RTT,
}
// HTTP/3 Server
// Handler refers to incoming HTTP request handler
server := http3.Server{
Addr: h3addr,
QUICConfig: &qconf,
TLSConfig: &tconf,
Handler: H3Handler(h3addr, destination, trace),
}
// Logging to file setup
if filelog.Enable {
LogFile(&server, filelog.Level)
}
defer server.Close()
// Start Listening
log.Fatalln(server.ListenAndServe())
}
// Handle HTTP Request
func H3Handler(H3Addr string, destinations []Destination, trace bool) http.Handler {
mux := http.NewServeMux()
// Register multiple paths
for _, dest := range destinations {
mux.HandleFunc(dest.Path, func(w http.ResponseWriter, r *http.Request) {
// ID for Tracing
id := mrand.Intn(65535)
// Create an HTTP client on TCP socket
// {InsecureSkipVerify: true} is required if H1 server Scheme is HTTPS and using self signed certificate
tr := &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
}
h1Client := &http.Client{Transport: tr}
// Trace HTTP/3 incoming request
if trace {
TraceLogReq(r.Method, r.URL.Path, r.Proto, r.RemoteAddr, id)
}
// Generate HTTP/1.1 request
h1req := http.Request{Method: r.Method, URL: &url.URL{Scheme: dest.Scheme, Host: dest.Addr, Path: r.URL.Path}}
// Set H3 request header to h1 request header
h1req.Header = r.Header
// Set H1 Extra Headers from config
MergeMap(h1req.Header, dest.H1ReqHeaders)
// Set H3 request body to h1 request body
h1req.Body = r.Body
// Make HTTP/1.1 request
response, h1_err := h1Client.Do(&h1req)
if h1_err != nil {
log.Println(h1_err.Error())
w.WriteHeader(500)
return
}
// Trace HTTP/1.1 incoming response
if trace {
TraceLogResp(response.Proto, response.StatusCode, id)
}
// Set H3 Response Headers
h3Headers := w.Header()
MergeMap(h3Headers, response.Header)
// Set H3 Extra Headers from config
MergeMap(h3Headers, dest.H3RespHeaders)
defer response.Body.Close()
// Write H1 Response StatusCode to H3 Response StatusCode
w.WriteHeader(response.StatusCode)
// Write H1 Response Body to H3 Response Body
_, e := io.Copy(w, response.Body)
if e != nil {
log.Println(e.Error())
return
}
})
}
return mux
}