Files
quic-go/h2quic/server.go
2016-05-31 16:02:39 +02:00

190 lines
5.0 KiB
Go

package h2quic
import (
"crypto/tls"
"errors"
"fmt"
"io/ioutil"
"net"
"net/http"
"time"
"github.com/lucas-clemente/quic-go"
"github.com/lucas-clemente/quic-go/protocol"
"github.com/lucas-clemente/quic-go/utils"
"golang.org/x/net/http2"
"golang.org/x/net/http2/hpack"
)
type streamCreator interface {
GetOrOpenStream(protocol.StreamID) (utils.Stream, error)
Close(error) error
}
// Server is a HTTP2 server listening for QUIC connections.
type Server struct {
*http.Server
// Private flag for demo, do not use
CloseAfterFirstRequest bool
port int
}
// ListenAndServe listens on the UDP address s.Addr and calls s.Handler to handle HTTP/2 requests on incoming connections.
func (s *Server) ListenAndServe() error {
if s.Server == nil {
return errors.New("use of h2quic.Server without http.Server")
}
server, err := quic.NewServer(s.Addr, s.TLSConfig, s.handleStreamCb)
if err != nil {
return err
}
return server.ListenAndServe()
}
// ListenAndServeTLS listens on the UDP address s.Addr and calls s.Handler to handle HTTP/2 requests on incoming connections.
func (s *Server) ListenAndServeTLS(certFile, keyFile string) error {
var err error
certs := make([]tls.Certificate, 1)
certs[0], err = tls.LoadX509KeyPair(certFile, keyFile)
if err != nil {
return err
}
// We currently only use the cert-related stuff from tls.Config,
// so we don't need to make a full copy.
config := &tls.Config{
Certificates: certs,
}
server, err := quic.NewServer(s.Addr, config, s.handleStreamCb)
if err != nil {
return err
}
return server.ListenAndServe()
}
// Serve should not be called, since it only works properly for TCP listeners.
func (Server) Serve(net.Listener) error {
panic("h2quic.Server.Serve should not be called, see https://godoc.org/github.com/lucas-clemente/quic-go/h2quic")
}
func (s *Server) handleStreamCb(session *quic.Session, stream utils.Stream) {
s.handleStream(session, stream)
}
func (s *Server) handleStream(session streamCreator, stream utils.Stream) {
if stream.StreamID() != 3 {
return
}
hpackDecoder := hpack.NewDecoder(4096, nil)
h2framer := http2.NewFramer(nil, stream)
go func() {
for {
if err := s.handleRequest(session, stream, hpackDecoder, h2framer); err != nil {
utils.Errorf("error handling h2 request: %s", err.Error())
return
}
}
}()
}
func (s *Server) handleRequest(session streamCreator, headerStream utils.Stream, hpackDecoder *hpack.Decoder, h2framer *http2.Framer) error {
h2frame, err := h2framer.ReadFrame()
if err != nil {
return err
}
h2headersFrame := h2frame.(*http2.HeadersFrame)
if !h2headersFrame.HeadersEnded() {
return errors.New("http2 header continuation not implemented")
}
headers, err := hpackDecoder.DecodeFull(h2headersFrame.HeaderBlockFragment())
if err != nil {
utils.Errorf("invalid http2 headers encoding: %s", err.Error())
return err
}
req, err := requestFromHeaders(headers)
if err != nil {
return err
}
utils.Infof("%s %s%s", req.Method, req.Host, req.RequestURI)
dataStream, err := session.GetOrOpenStream(protocol.StreamID(h2headersFrame.StreamID))
if err != nil {
return err
}
if h2headersFrame.StreamEnded() {
dataStream.CloseRemote(0)
}
// stream's Close() closes the write side, not the read side
req.Body = ioutil.NopCloser(dataStream)
responseWriter := newResponseWriter(headerStream, dataStream, protocol.StreamID(h2headersFrame.StreamID))
go func() {
handler := s.Handler
if handler == nil {
handler = http.DefaultServeMux
}
handler.ServeHTTP(responseWriter, req)
if responseWriter.dataStream != nil {
responseWriter.dataStream.Close()
}
if s.CloseAfterFirstRequest {
time.Sleep(100 * time.Millisecond)
session.Close(nil)
}
}()
return nil
}
// Close the server
func (s *Server) Close() error {
// TODO: implement
return nil
}
// SetQuicHeaders can be used to set the proper headers that announce that this server supports QUIC.
// The values that are set depend on the port information from s.Server.Addr, and currently look like this (if Addr has port 443):
// Alternate-Protocol: 443:quic
// Alt-Svc: quic=":443"; ma=2592000; v="33,32,31,30"
func (s *Server) SetQuicHeaders(hdr http.Header) error {
if s.port == 0 {
// Extract port from s.Server.Addr
_, portStr, err := net.SplitHostPort(s.Server.Addr)
if err != nil {
return err
}
port, err := net.LookupPort("tcp", portStr)
if err != nil {
return err
}
s.port = port
}
hdr.Add("Alternate-Protocol", fmt.Sprintf("%d:quic", s.port))
hdr.Add("Alt-Svc", fmt.Sprintf(`quic=":%d"; ma=2592000; v="%s"`, s.port, protocol.SupportedVersionsAsString))
return nil
}
// ListenAndServeQUIC listens on the UDP network address addr and calls the
// handler for HTTP/2 requests on incoming conections. http.DefaultServeMux is
// used when handler is nil.
func ListenAndServeQUIC(addr, certFile, keyFile string, handler http.Handler) error {
server := &Server{
Server: &http.Server{
Addr: addr,
Handler: handler,
},
}
return server.ListenAndServeTLS(certFile, keyFile)
}