[server] Add initial support for HTTP/3 over QUIC.

This commit is contained in:
Ciprian Dorin Craciun 2021-12-16 23:10:11 +02:00
parent f492bc9d3b
commit b5abe6edab
2 changed files with 161 additions and 14 deletions

View file

@ -31,6 +31,9 @@ import "github.com/colinmarc/cdb"
import "github.com/valyala/fasthttp"
import "github.com/valyala/fasthttp/reuseport"
import "github.com/lucas-clemente/quic-go"
import "github.com/lucas-clemente/quic-go/http3"
import . "github.com/volution/kawipiko/lib/common"
import . "github.com/volution/kawipiko/lib/server"
@ -43,6 +46,7 @@ type server struct {
httpServer *fasthttp.Server
httpsServer *fasthttp.Server
https2Server *http.Server
quicServer *http3.Server
cdbReader *cdb.CDB
cachedFileFingerprints map[string][]byte
cachedDataMeta map[string][]byte
@ -51,6 +55,7 @@ type server struct {
securityHeadersTls bool
http1Disabled bool
http2Disabled bool
http3AltSvc string
debug bool
quiet bool
dummy bool
@ -343,6 +348,10 @@ func (_server *server) Serve (_context *fasthttp.RequestCtx) () {
_responseHeaders.AddBytesKV (StringToBytes ("X-Frame-Options"), StringToBytes ("sameorigin"))
}
if _server.http3AltSvc != "" {
_responseHeaders.AddBytesKV (StringToBytes ("Alt-Svc"), StringToBytes (_server.http3AltSvc))
}
if _server.debug {
log.Printf ("[dd] [b15f3cad] serving for `%s`...\n", *_requestUriString)
}
@ -372,6 +381,10 @@ func (_server *server) ServeStatic (_context *fasthttp.RequestCtx, _status uint,
_responseHeaders.AddBytesKV (StringToBytes ("Cache-Control"), StringToBytes ("private, no-cache, no-store"))
}
if _server.http3AltSvc != "" {
_responseHeaders.AddBytesKV (StringToBytes ("Alt-Svc"), StringToBytes (_server.http3AltSvc))
}
_response.SetStatusCode (int (_status))
_response.SetBodyRaw (_data)
}
@ -390,6 +403,10 @@ func (_server *server) ServeRedirect (_context *fasthttp.RequestCtx, _status uin
_responseHeaders.AddBytesKV (StringToBytes ("Cache-Control"), StringToBytes ("private, no-cache, no-store"))
}
if _server.http3AltSvc != "" {
_responseHeaders.AddBytesKV (StringToBytes ("Alt-Svc"), StringToBytes (_server.http3AltSvc))
}
_response.SetStatusCode (int (_status))
}
@ -433,14 +450,28 @@ func (_server *server) ServeHTTP (_response http.ResponseWriter, _request *http.
switch _request.ProtoMajor {
case 1 :
_requestProtoUnsupported = _server.http1Disabled || (_request.ProtoMinor < 0) || (_request.ProtoMinor > 1)
if _server.debug && !_requestProtoUnsupported {
log.Printf ("[dd] [670e36d4] using Go HTTP/1 for `%s`...", _request.URL.Path)
}
case 2 :
_requestProtoUnsupported = _server.http2Disabled || (_request.ProtoMinor != 0)
if _server.debug && !_requestProtoUnsupported {
log.Printf ("[dd] [524cd64b] using Go HTTP/2 for `%s`...", _request.URL.Path)
}
case 3 :
_requestProtoUnsupported = (_server.quicServer == nil) || (_request.ProtoMinor != 0)
if _server.debug && !_requestProtoUnsupported {
log.Printf ("[dd] [be95da51] using QUIC HTTP/3 for `%s`...", _request.URL.Path)
}
default :
_requestProtoUnsupported = true
}
if _requestProtoUnsupported {
_request.Close = true
_response.WriteHeader (http.StatusHTTPVersionNotSupported)
if !_server.quiet {
log.Printf ("[ww] [4c44e3c0] protocol HTTP/%d not supported for `%s`!", _request.ProtoMajor, _request.URL.Path)
}
return
}
@ -536,8 +567,10 @@ func main_0 () (error) {
var _bind string
var _bindTls string
var _bindTls2 string
var _bindQuic string
var _http1Disabled bool
var _http2Disabled bool
var _http3AltSvc string
var _tlsPrivate string
var _tlsPublic string
var _archivePath string
@ -575,8 +608,10 @@ func main_0 () (error) {
_bind_0 := _flags.String ("bind", "", "")
_bindTls_0 := _flags.String ("bind-tls", "", "")
_bindTls2_0 := _flags.String ("bind-tls-2", "", "")
_bindQuic_0 := _flags.String ("bind-quic", "", "")
_http1Disabled_0 := _flags.Bool ("http1-disable", false, "")
_http2Disabled_0 := _flags.Bool ("http2-disable", false, "")
_http3AltSvc_0 := _flags.String ("http3-alt-svc", "", "")
_archivePath_0 := _flags.String ("archive", "", "")
_archiveInmem_0 := _flags.Bool ("archive-inmem", false, "")
_archiveMmap_0 := _flags.Bool ("archive-mmap", false, "")
@ -607,8 +642,10 @@ func main_0 () (error) {
_bind = *_bind_0
_bindTls = *_bindTls_0
_bindTls2 = *_bindTls2_0
_bindQuic = *_bindQuic_0
_http1Disabled = *_http1Disabled_0
_http2Disabled = *_http2Disabled_0
_http3AltSvc = *_http3AltSvc_0
_archivePath = *_archivePath_0
_archiveInmem = *_archiveInmem_0
_archiveMmap = *_archiveMmap_0
@ -638,7 +675,7 @@ func main_0 () (error) {
_isFirst = true
}
if (_bind == "") && (_bindTls == "") && (_bindTls2 == "") {
if (_bind == "") && (_bindTls == "") && (_bindTls2 == "") && (_bindQuic == "") {
AbortError (nil, "[6edd9512] expected bind address argument!")
}
if (*_tlsBundle_0 != "") && ((*_tlsPrivate_0 != "") || (*_tlsPublic_0 != "")) {
@ -654,7 +691,7 @@ func main_0 () (error) {
if ((_tlsPrivate != "") && (_tlsPublic == "")) || ((_tlsPublic != "") && (_tlsPrivate == "")) {
AbortError (nil, "[6e5b42e4] TLS private/public must be specified together!")
}
if ((_tlsPrivate != "") || (_tlsPublic != "")) && ((_bindTls == "") && (_bindTls2 == "")) {
if ((_tlsPrivate != "") || (_tlsPublic != "")) && ((_bindTls == "") && (_bindTls2 == "") && (_bindQuic == "")) {
AbortError (nil, "[4e31f251] TLS certificate specified, but TLS not enabled!")
}
@ -667,6 +704,22 @@ func main_0 () (error) {
if _http2Disabled && (_bindTls == "") && (_bindTls2 == "") {
log.Printf ("[ww] [1ed4864c] HTTP/2 is only available with TLS!\n")
}
if (_http3AltSvc != "") && (_bindQuic == "") {
log.Printf ("[ww] [93510d2a] HTTP/3 Alt-Svc is only available with QUIC!\n")
}
if (_http3AltSvc == "") && (_bindQuic != "") {
log.Printf ("[ww] [225bda04] HTTP/3 Alt-Svc is mandatory with `--bind-quic`!\n")
}
if (_http3AltSvc != "") {
if strings.HasPrefix (_http3AltSvc, "h3=") {
// NOP
} else if _host, _port, _error := net.SplitHostPort (_http3AltSvc); _error == nil {
_endpoint := net.JoinHostPort (_host, _port)
_http3AltSvc = fmt.Sprintf ("h3=\"%s\"", _endpoint)
} else {
AbortError (nil, "[1a5476b1] HTTP/3 Alt-Svc is invalid!")
}
}
if !_dummy {
if _archivePath == "" {
@ -698,10 +751,6 @@ func main_0 () (error) {
}
}
if (_processes > 1) && ((_profileCpu != "") || (_profileMem != "")) {
AbortError (nil, "[cd18d250] multi-process and profiling are mutually exclusive!")
}
if _processes < 1 {
_processes = 1
}
@ -722,6 +771,13 @@ func main_0 () (error) {
if (_limitMemory != 0) && ((_limitMemory > (16 * 1024)) || (_limitMemory < 128)) {
AbortError (nil, "[2781f54c] maximum memory limit is between 128 and 16384 MiB!")
}
if (_processes > 1) && ((_profileCpu != "") || (_profileMem != "")) {
AbortError (nil, "[cd18d250] multi-process and profiling are mutually exclusive!")
}
if (_processes > 1) && (_bindQuic != "") {
AbortError (nil, "[d6db77ba] QUIC is only available with a single process!")
}
}
@ -803,6 +859,9 @@ func main_0 () (error) {
if _http2Disabled {
_processArguments = append (_processArguments, "--http2-disabled")
}
if _http3AltSvc != "" {
_processArguments = append (_processArguments, "--http3-alt-svc", _http3AltSvc)
}
if _archivePath != "" {
_processArguments = append (_processArguments, "--archive", _archivePath)
}
@ -1238,6 +1297,7 @@ func main_0 () (error) {
securityHeadersEnabled : _securityHeadersEnabled,
http1Disabled : _http1Disabled,
http2Disabled : _http2Disabled,
http3AltSvc : _http3AltSvc,
quiet : _quiet,
debug : _debug,
dummy : _dummy,
@ -1374,6 +1434,50 @@ func main_0 () (error) {
_quicServer := & http3.Server {}
_quicServer.Server = & http.Server {
Addr : _bindQuic,
Handler : _server,
TLSConfig : nil,
MaxHeaderBytes : _httpsServer.ReadBufferSize,
ReadTimeout : _httpsServer.ReadTimeout,
ReadHeaderTimeout : _httpsServer.ReadTimeout,
WriteTimeout : _httpsServer.WriteTimeout,
IdleTimeout : _httpsServer.IdleTimeout,
}
_quicServer.QuicConfig = & quic.Config {
Versions : []quic.VersionNumber {
quic.Version1,
},
HandshakeIdleTimeout : 6 * time.Second,
MaxIdleTimeout : _httpsServer.IdleTimeout,
MaxIncomingStreams : 1024,
MaxIncomingUniStreams : 1024,
InitialConnectionReceiveWindow : 1 * 1024 * 1024,
MaxConnectionReceiveWindow : 4 * 1024 * 1024,
InitialStreamReceiveWindow : 512 * 1024,
MaxStreamReceiveWindow : 2 * 1024 * 1024,
KeepAlive : true,
}
_quicTlsConfig := _tlsConfig.Clone ()
_quicServer.Server.TLSConfig = _quicTlsConfig
if _timeoutDisabled {
_httpServer.ReadTimeout = 0
@ -1389,6 +1493,12 @@ func main_0 () (error) {
_https2Server.WriteTimeout = 0
_https2Server.IdleTimeout = 0
// NOTE: Are these actually used by QUIC?
_quicServer.ReadTimeout = 0
_quicServer.ReadHeaderTimeout = 0
_quicServer.WriteTimeout = 0
_quicServer.IdleTimeout = 0
}
@ -1424,6 +1534,9 @@ func main_0 () (error) {
panic ("[d784a82c]")
}
}
if _bindQuic != "" {
log.Printf ("[ii] [b958617a] listening on `https://%s/` (using QUIC supporting TLS with HTTP/3 only);", _bindQuic)
}
}
@ -1530,10 +1643,14 @@ func main_0 () (error) {
if _https2Listener != nil {
_server.https2Server = _https2Server
}
if _bindQuic != "" {
_server.quicServer = _quicServer
}
_httpServer = nil
_httpsServer = nil
_https2Server = nil
_quicServer = nil
@ -1556,6 +1673,9 @@ func main_0 () (error) {
if _error := _server.httpServer.Serve (_httpListener); _error != nil {
AbortError (_error, "[44f45c67] failed executing server!")
}
if !_quiet {
log.Printf ("[ii] [aca4a14f] stopped FastHTTP server;\n")
}
} ()
}
@ -1564,11 +1684,14 @@ func main_0 () (error) {
go func () () {
defer _waiter.Done ()
if !_quiet {
log.Printf ("[ii] [83cb1f6f] starting FastHTTP server (with TLS)...\n")
log.Printf ("[ii] [83cb1f6f] starting FastHTTP server (for TLS)...\n")
}
if _error := _server.httpsServer.Serve (_httpsListener); _error != nil {
AbortError (_error, "[b2d50852] failed executing server!")
}
if !_quiet {
log.Printf ("[ii] [ee4180b7] stopped FastHTTP server (for TLS);\n")
}
} ()
}
@ -1582,6 +1705,25 @@ func main_0 () (error) {
if _error := _server.https2Server.Serve (_https2Listener); (_error != nil) && (_error != http.ErrServerClosed) {
AbortError (_error, "[9f6d28f4] failed executing server!")
}
if !_quiet {
log.Printf ("[ii] [9a487770] stopped Go HTTP server;\n")
}
} ()
}
if _server.quicServer != nil {
_waiter.Add (1)
go func () () {
defer _waiter.Done ()
if !_quiet {
log.Printf ("[ii] [4cf834b0] starting QUIC server...\n")
}
if _error := _server.quicServer.Serve (nil); (_error != nil) && (_error.Error () != "server closed") {
AbortError (_error, "[73e700c5] failed executing server!")
}
if !_quiet {
log.Printf ("[ii] [0a9d72e9] stopped QUIC server;\n")
}
} ()
}
@ -1603,9 +1745,6 @@ func main_0 () (error) {
log.Printf ("[ii] [8eea3f63] stopping FastHTTP server...\n")
}
_server.httpServer.Shutdown ()
if !_quiet {
log.Printf ("[ii] [aca4a14f] stopped FastHTTP server;\n")
}
} ()
}
if _splitListenerClose != nil {
@ -1623,9 +1762,6 @@ func main_0 () (error) {
log.Printf ("[ii] [ff651007] stopping FastHTTP server (for TLS)...\n")
}
_server.httpsServer.Shutdown ()
if !_quiet {
log.Printf ("[ii] [ee4180b7] stopped FastHTTP server (for TLS);\n")
}
} ()
}
if _server.https2Server != nil {
@ -1636,9 +1772,18 @@ func main_0 () (error) {
log.Printf ("[ii] [9ae5a25b] stopping Go HTTP server...\n")
}
_server.https2Server.Shutdown (context.TODO ())
} ()
}
if _server.quicServer != nil {
_waiter.Add (1)
go func () () {
defer _waiter.Done ()
if !_quiet {
log.Printf ("[ii] [9a487770] stopped Go HTTP server;\n")
log.Printf ("[ii] [41dab8c2] stopping QUIC server...\n")
}
_server.quicServer.CloseGracefully (1 * time.Second)
time.Sleep (1 * time.Second)
_server.quicServer.Close ()
} ()
}
if true {

View file

@ -19,9 +19,11 @@
--bind <ip>:<port> (HTTP, only HTTP/1.1)
--bind-tls <ip>:<port> (HTTPS, only HTTP/1.1)
--bind-tls-2 <ip>:<port> (HTTPS, with HTTP/2)
--bind-quic <ip>:<port> (HTTPS, with HTTP/3)
--http1-disable
--http2-disable
--http3-alt-svc <ip>:<port>
--tls-bundle <path> (TLS certificate bundle)
--tls-public <path> (TLS certificate public)