Created
August 30, 2019 15:32
-
-
Save rickcrawford/ae35a1412dfd5ad06133c502ab355710 to your computer and use it in GitHub Desktop.
HTTP3 server example.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"crypto/tls" | |
"fmt" | |
"log" | |
"net" | |
"net/http" | |
"os" | |
"sync" | |
"time" | |
"github.com/lucas-clemente/quic-go/http3" | |
) | |
// Server is the HTTP server implementation. | |
type Server struct { | |
Server *http.Server | |
quicServer *http3.Server | |
listener net.Listener | |
lock sync.Mutex | |
} | |
// Listen creates an active listener for s that can be | |
// used to serve requests. | |
func (s *Server) Listen() (net.Listener, error) { | |
if s.Server == nil { | |
return nil, fmt.Errorf("Server field is nil") | |
} | |
ln, err := net.Listen("tcp", s.Server.Addr) | |
if tcpLn, ok := ln.(*net.TCPListener); ok { | |
ln = tcpKeepAliveListener{TCPListener: tcpLn} | |
} | |
return tls.NewListener(ln, s.Server.TLSConfig), err | |
} | |
// ListenPacket creates udp connection for QUIC if it is enabled, | |
func (s *Server) ListenPacket() (net.PacketConn, error) { | |
udpAddr, err := net.ResolveUDPAddr("udp", s.Server.Addr) | |
if err != nil { | |
return nil, err | |
} | |
return net.ListenUDP("udp", udpAddr) | |
} | |
// Serve serves requests on ln. It blocks until ln is closed. | |
func (s *Server) Serve(ln net.Listener) error { | |
s.lock.Lock() | |
s.listener = ln | |
s.lock.Unlock() | |
err := s.Server.Serve(ln) | |
if err == http.ErrServerClosed { | |
err = nil | |
} | |
if s.quicServer != nil { | |
s.quicServer.Close() | |
} | |
return err | |
} | |
// ServePacket serves QUIC requests on pc until it is closed. | |
func (s *Server) ServePacket(pc net.PacketConn) error { | |
if s.quicServer != nil { | |
err := s.quicServer.Serve(pc.(*net.UDPConn)) | |
return fmt.Errorf("serving QUIC connections: %v", err) | |
} | |
return nil | |
} | |
// ServeHTTP is the entry point of all HTTP requests. | |
func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { | |
fmt.Fprintln(w, "test") | |
} | |
func (s *Server) wrapWithSvcHeaders(previousHandler http.Handler) http.HandlerFunc { | |
return func(w http.ResponseWriter, r *http.Request) { | |
s.quicServer.SetQuicHeaders(w.Header()) | |
previousHandler.ServeHTTP(w, r) | |
} | |
} | |
type tcpKeepAliveListener struct { | |
*net.TCPListener | |
} | |
// Accept accepts the connection with a keep-alive enabled. | |
func (ln tcpKeepAliveListener) Accept() (c net.Conn, err error) { | |
tc, err := ln.AcceptTCP() | |
if err != nil { | |
return | |
} | |
tc.SetKeepAlive(true) | |
tc.SetKeepAlivePeriod(3 * time.Minute) | |
return tc, nil | |
} | |
func (ln tcpKeepAliveListener) File() (*os.File, error) { | |
return ln.TCPListener.File() | |
} | |
func NewServer(tlsConfig *tls.Config, addr string) *Server { | |
s := &Server{ | |
Server: &http.Server{ | |
Addr: addr, | |
TLSConfig: tlsConfig, | |
}, | |
} | |
s.quicServer = &http3.Server{Server: s.Server} | |
s.Server.Handler = s.wrapWithSvcHeaders(s) | |
// s.Server.Handler = s | |
return s | |
} | |
func main() { | |
cer, err := tls.LoadX509KeyPair("cert.pem", "priv.key") | |
if err != nil { | |
log.Println(err) | |
return | |
} | |
config := &tls.Config{ | |
// MinVersion: tls.VersionTLS13, | |
// CurvePreferences: []tls.CurveID{tls.CurveP521, tls.CurveP384, tls.CurveP256}, | |
// PreferServerCipherSuites: true, | |
Certificates: []tls.Certificate{cer}, | |
NextProtos: []string{"h3-20", "h2", "http/1.1"}, | |
} | |
s := NewServer(config, ":8443") | |
pc, err := s.ListenPacket() | |
ln, err := s.Listen() | |
go func() { | |
s.Serve(ln) | |
}() | |
s.ServePacket(pc) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment