1package http3 2 3import ( 4 "bytes" 5 "context" 6 "crypto/tls" 7 "errors" 8 "fmt" 9 "io" 10 "net" 11 "net/http" 12 "runtime" 13 "strings" 14 "sync" 15 "sync/atomic" 16 "time" 17 18 "github.com/lucas-clemente/quic-go" 19 "github.com/lucas-clemente/quic-go/internal/handshake" 20 "github.com/lucas-clemente/quic-go/internal/protocol" 21 "github.com/lucas-clemente/quic-go/internal/utils" 22 "github.com/lucas-clemente/quic-go/quicvarint" 23 "github.com/marten-seemann/qpack" 24) 25 26// allows mocking of quic.Listen and quic.ListenAddr 27var ( 28 quicListen = quic.ListenEarly 29 quicListenAddr = quic.ListenAddrEarly 30) 31 32const ( 33 nextProtoH3Draft29 = "h3-29" 34 nextProtoH3 = "h3" 35) 36 37const ( 38 streamTypeControlStream = 0 39 streamTypePushStream = 1 40 streamTypeQPACKEncoderStream = 2 41 streamTypeQPACKDecoderStream = 3 42) 43 44func versionToALPN(v protocol.VersionNumber) string { 45 if v == protocol.Version1 { 46 return nextProtoH3 47 } 48 if v == protocol.VersionTLS || v == protocol.VersionDraft29 { 49 return nextProtoH3Draft29 50 } 51 return "" 52} 53 54// contextKey is a value for use with context.WithValue. It's used as 55// a pointer so it fits in an interface{} without allocation. 56type contextKey struct { 57 name string 58} 59 60func (k *contextKey) String() string { return "quic-go/http3 context value " + k.name } 61 62// ServerContextKey is a context key. It can be used in HTTP 63// handlers with Context.Value to access the server that 64// started the handler. The associated value will be of 65// type *http3.Server. 66var ServerContextKey = &contextKey{"http3-server"} 67 68type requestError struct { 69 err error 70 streamErr errorCode 71 connErr errorCode 72} 73 74func newStreamError(code errorCode, err error) requestError { 75 return requestError{err: err, streamErr: code} 76} 77 78func newConnError(code errorCode, err error) requestError { 79 return requestError{err: err, connErr: code} 80} 81 82// Server is a HTTP/3 server. 83type Server struct { 84 *http.Server 85 86 // By providing a quic.Config, it is possible to set parameters of the QUIC connection. 87 // If nil, it uses reasonable default values. 88 QuicConfig *quic.Config 89 90 // Enable support for HTTP/3 datagrams. 91 // If set to true, QuicConfig.EnableDatagram will be set. 92 // See https://www.ietf.org/archive/id/draft-schinazi-masque-h3-datagram-02.html. 93 EnableDatagrams bool 94 95 port uint32 // used atomically 96 97 mutex sync.Mutex 98 listeners map[*quic.EarlyListener]struct{} 99 closed utils.AtomicBool 100 101 loggerOnce sync.Once 102 logger utils.Logger 103} 104 105// ListenAndServe listens on the UDP address s.Addr and calls s.Handler to handle HTTP/3 requests on incoming connections. 106func (s *Server) ListenAndServe() error { 107 if s.Server == nil { 108 return errors.New("use of http3.Server without http.Server") 109 } 110 return s.serveImpl(s.TLSConfig, nil) 111} 112 113// ListenAndServeTLS listens on the UDP address s.Addr and calls s.Handler to handle HTTP/3 requests on incoming connections. 114func (s *Server) ListenAndServeTLS(certFile, keyFile string) error { 115 var err error 116 certs := make([]tls.Certificate, 1) 117 certs[0], err = tls.LoadX509KeyPair(certFile, keyFile) 118 if err != nil { 119 return err 120 } 121 // We currently only use the cert-related stuff from tls.Config, 122 // so we don't need to make a full copy. 123 config := &tls.Config{ 124 Certificates: certs, 125 } 126 return s.serveImpl(config, nil) 127} 128 129// Serve an existing UDP connection. 130// It is possible to reuse the same connection for outgoing connections. 131// Closing the server does not close the packet conn. 132func (s *Server) Serve(conn net.PacketConn) error { 133 return s.serveImpl(s.TLSConfig, conn) 134} 135 136func (s *Server) serveImpl(tlsConf *tls.Config, conn net.PacketConn) error { 137 if s.closed.Get() { 138 return http.ErrServerClosed 139 } 140 if s.Server == nil { 141 return errors.New("use of http3.Server without http.Server") 142 } 143 s.loggerOnce.Do(func() { 144 s.logger = utils.DefaultLogger.WithPrefix("server") 145 }) 146 147 // The tls.Config we pass to Listen needs to have the GetConfigForClient callback set. 148 // That way, we can get the QUIC version and set the correct ALPN value. 149 baseConf := &tls.Config{ 150 GetConfigForClient: func(ch *tls.ClientHelloInfo) (*tls.Config, error) { 151 // determine the ALPN from the QUIC version used 152 proto := nextProtoH3Draft29 153 if qconn, ok := ch.Conn.(handshake.ConnWithVersion); ok { 154 if qconn.GetQUICVersion() == protocol.Version1 { 155 proto = nextProtoH3 156 } 157 } 158 config := tlsConf 159 if tlsConf.GetConfigForClient != nil { 160 getConfigForClient := tlsConf.GetConfigForClient 161 var err error 162 conf, err := getConfigForClient(ch) 163 if err != nil { 164 return nil, err 165 } 166 if conf != nil { 167 config = conf 168 } 169 } 170 if config == nil { 171 return nil, nil 172 } 173 config = config.Clone() 174 config.NextProtos = []string{proto} 175 return config, nil 176 }, 177 } 178 179 var ln quic.EarlyListener 180 var err error 181 quicConf := s.QuicConfig 182 if quicConf == nil { 183 quicConf = &quic.Config{} 184 } else { 185 quicConf = s.QuicConfig.Clone() 186 } 187 if s.EnableDatagrams { 188 quicConf.EnableDatagrams = true 189 } 190 if conn == nil { 191 ln, err = quicListenAddr(s.Addr, baseConf, quicConf) 192 } else { 193 ln, err = quicListen(conn, baseConf, quicConf) 194 } 195 if err != nil { 196 return err 197 } 198 s.addListener(&ln) 199 defer s.removeListener(&ln) 200 201 for { 202 sess, err := ln.Accept(context.Background()) 203 if err != nil { 204 return err 205 } 206 go s.handleConn(sess) 207 } 208} 209 210// We store a pointer to interface in the map set. This is safe because we only 211// call trackListener via Serve and can track+defer untrack the same pointer to 212// local variable there. We never need to compare a Listener from another caller. 213func (s *Server) addListener(l *quic.EarlyListener) { 214 s.mutex.Lock() 215 if s.listeners == nil { 216 s.listeners = make(map[*quic.EarlyListener]struct{}) 217 } 218 s.listeners[l] = struct{}{} 219 s.mutex.Unlock() 220} 221 222func (s *Server) removeListener(l *quic.EarlyListener) { 223 s.mutex.Lock() 224 delete(s.listeners, l) 225 s.mutex.Unlock() 226} 227 228func (s *Server) handleConn(sess quic.EarlySession) { 229 decoder := qpack.NewDecoder(nil) 230 231 // send a SETTINGS frame 232 str, err := sess.OpenUniStream() 233 if err != nil { 234 s.logger.Debugf("Opening the control stream failed.") 235 return 236 } 237 buf := &bytes.Buffer{} 238 quicvarint.Write(buf, streamTypeControlStream) // stream type 239 (&settingsFrame{Datagram: s.EnableDatagrams}).Write(buf) 240 str.Write(buf.Bytes()) 241 242 go s.handleUnidirectionalStreams(sess) 243 244 // Process all requests immediately. 245 // It's the client's responsibility to decide which requests are eligible for 0-RTT. 246 for { 247 str, err := sess.AcceptStream(context.Background()) 248 if err != nil { 249 s.logger.Debugf("Accepting stream failed: %s", err) 250 return 251 } 252 go func() { 253 rerr := s.handleRequest(sess, str, decoder, func() { 254 sess.CloseWithError(quic.ApplicationErrorCode(errorFrameUnexpected), "") 255 }) 256 if rerr.err != nil || rerr.streamErr != 0 || rerr.connErr != 0 { 257 s.logger.Debugf("Handling request failed: %s", err) 258 if rerr.streamErr != 0 { 259 str.CancelWrite(quic.StreamErrorCode(rerr.streamErr)) 260 } 261 if rerr.connErr != 0 { 262 var reason string 263 if rerr.err != nil { 264 reason = rerr.err.Error() 265 } 266 sess.CloseWithError(quic.ApplicationErrorCode(rerr.connErr), reason) 267 } 268 return 269 } 270 str.Close() 271 }() 272 } 273} 274 275func (s *Server) handleUnidirectionalStreams(sess quic.EarlySession) { 276 for { 277 str, err := sess.AcceptUniStream(context.Background()) 278 if err != nil { 279 s.logger.Debugf("accepting unidirectional stream failed: %s", err) 280 return 281 } 282 283 go func(str quic.ReceiveStream) { 284 streamType, err := quicvarint.Read(&byteReaderImpl{str}) 285 if err != nil { 286 s.logger.Debugf("reading stream type on stream %d failed: %s", str.StreamID(), err) 287 return 288 } 289 // We're only interested in the control stream here. 290 switch streamType { 291 case streamTypeControlStream: 292 case streamTypeQPACKEncoderStream, streamTypeQPACKDecoderStream: 293 // Our QPACK implementation doesn't use the dynamic table yet. 294 // TODO: check that only one stream of each type is opened. 295 return 296 case streamTypePushStream: // only the server can push 297 sess.CloseWithError(quic.ApplicationErrorCode(errorStreamCreationError), "") 298 return 299 default: 300 str.CancelRead(quic.StreamErrorCode(errorStreamCreationError)) 301 return 302 } 303 f, err := parseNextFrame(str) 304 if err != nil { 305 sess.CloseWithError(quic.ApplicationErrorCode(errorFrameError), "") 306 return 307 } 308 sf, ok := f.(*settingsFrame) 309 if !ok { 310 sess.CloseWithError(quic.ApplicationErrorCode(errorMissingSettings), "") 311 return 312 } 313 if !sf.Datagram { 314 return 315 } 316 // If datagram support was enabled on our side as well as on the client side, 317 // we can expect it to have been negotiated both on the transport and on the HTTP/3 layer. 318 // Note: ConnectionState() will block until the handshake is complete (relevant when using 0-RTT). 319 if s.EnableDatagrams && !sess.ConnectionState().SupportsDatagrams { 320 sess.CloseWithError(quic.ApplicationErrorCode(errorSettingsError), "missing QUIC Datagram support") 321 } 322 }(str) 323 } 324} 325 326func (s *Server) maxHeaderBytes() uint64 { 327 if s.Server.MaxHeaderBytes <= 0 { 328 return http.DefaultMaxHeaderBytes 329 } 330 return uint64(s.Server.MaxHeaderBytes) 331} 332 333func (s *Server) handleRequest(sess quic.Session, str quic.Stream, decoder *qpack.Decoder, onFrameError func()) requestError { 334 frame, err := parseNextFrame(str) 335 if err != nil { 336 return newStreamError(errorRequestIncomplete, err) 337 } 338 hf, ok := frame.(*headersFrame) 339 if !ok { 340 return newConnError(errorFrameUnexpected, errors.New("expected first frame to be a HEADERS frame")) 341 } 342 if hf.Length > s.maxHeaderBytes() { 343 return newStreamError(errorFrameError, fmt.Errorf("HEADERS frame too large: %d bytes (max: %d)", hf.Length, s.maxHeaderBytes())) 344 } 345 headerBlock := make([]byte, hf.Length) 346 if _, err := io.ReadFull(str, headerBlock); err != nil { 347 return newStreamError(errorRequestIncomplete, err) 348 } 349 hfs, err := decoder.DecodeFull(headerBlock) 350 if err != nil { 351 // TODO: use the right error code 352 return newConnError(errorGeneralProtocolError, err) 353 } 354 req, err := requestFromHeaders(hfs) 355 if err != nil { 356 // TODO: use the right error code 357 return newStreamError(errorGeneralProtocolError, err) 358 } 359 360 req.RemoteAddr = sess.RemoteAddr().String() 361 req.Body = newRequestBody(str, onFrameError) 362 363 if s.logger.Debug() { 364 s.logger.Infof("%s %s%s, on stream %d", req.Method, req.Host, req.RequestURI, str.StreamID()) 365 } else { 366 s.logger.Infof("%s %s%s", req.Method, req.Host, req.RequestURI) 367 } 368 369 ctx := str.Context() 370 ctx = context.WithValue(ctx, ServerContextKey, s) 371 ctx = context.WithValue(ctx, http.LocalAddrContextKey, sess.LocalAddr()) 372 req = req.WithContext(ctx) 373 r := newResponseWriter(str, s.logger) 374 defer func() { 375 if !r.usedDataStream() { 376 r.Flush() 377 } 378 }() 379 handler := s.Handler 380 if handler == nil { 381 handler = http.DefaultServeMux 382 } 383 384 var panicked bool 385 func() { 386 defer func() { 387 if p := recover(); p != nil { 388 // Copied from net/http/server.go 389 const size = 64 << 10 390 buf := make([]byte, size) 391 buf = buf[:runtime.Stack(buf, false)] 392 s.logger.Errorf("http: panic serving: %v\n%s", p, buf) 393 panicked = true 394 } 395 }() 396 handler.ServeHTTP(r, req) 397 }() 398 399 if !r.usedDataStream() { 400 if panicked { 401 r.WriteHeader(500) 402 } else { 403 r.WriteHeader(200) 404 } 405 // If the EOF was read by the handler, CancelRead() is a no-op. 406 str.CancelRead(quic.StreamErrorCode(errorNoError)) 407 } 408 return requestError{} 409} 410 411// Close the server immediately, aborting requests and sending CONNECTION_CLOSE frames to connected clients. 412// Close in combination with ListenAndServe() (instead of Serve()) may race if it is called before a UDP socket is established. 413func (s *Server) Close() error { 414 s.closed.Set(true) 415 416 s.mutex.Lock() 417 defer s.mutex.Unlock() 418 419 var err error 420 for ln := range s.listeners { 421 if cerr := (*ln).Close(); cerr != nil && err == nil { 422 err = cerr 423 } 424 } 425 return err 426} 427 428// CloseGracefully shuts down the server gracefully. The server sends a GOAWAY frame first, then waits for either timeout to trigger, or for all running requests to complete. 429// CloseGracefully in combination with ListenAndServe() (instead of Serve()) may race if it is called before a UDP socket is established. 430func (s *Server) CloseGracefully(timeout time.Duration) error { 431 // TODO: implement 432 return nil 433} 434 435// SetQuicHeaders can be used to set the proper headers that announce that this server supports QUIC. 436// The values that are set depend on the port information from s.Server.Addr, and currently look like this (if Addr has port 443): 437// Alt-Svc: quic=":443"; ma=2592000; v="33,32,31,30" 438func (s *Server) SetQuicHeaders(hdr http.Header) error { 439 port := atomic.LoadUint32(&s.port) 440 441 if port == 0 { 442 // Extract port from s.Server.Addr 443 _, portStr, err := net.SplitHostPort(s.Server.Addr) 444 if err != nil { 445 return err 446 } 447 portInt, err := net.LookupPort("tcp", portStr) 448 if err != nil { 449 return err 450 } 451 port = uint32(portInt) 452 atomic.StoreUint32(&s.port, port) 453 } 454 455 // This code assumes that we will use protocol.SupportedVersions if no quic.Config is passed. 456 supportedVersions := protocol.SupportedVersions 457 if s.QuicConfig != nil && len(s.QuicConfig.Versions) > 0 { 458 supportedVersions = s.QuicConfig.Versions 459 } 460 altSvc := make([]string, 0, len(supportedVersions)) 461 for _, version := range supportedVersions { 462 v := versionToALPN(version) 463 if len(v) > 0 { 464 altSvc = append(altSvc, fmt.Sprintf(`%s=":%d"; ma=2592000`, v, port)) 465 } 466 } 467 hdr.Add("Alt-Svc", strings.Join(altSvc, ",")) 468 return nil 469} 470 471// ListenAndServeQUIC listens on the UDP network address addr and calls the 472// handler for HTTP/3 requests on incoming connections. http.DefaultServeMux is 473// used when handler is nil. 474func ListenAndServeQUIC(addr, certFile, keyFile string, handler http.Handler) error { 475 server := &Server{ 476 Server: &http.Server{ 477 Addr: addr, 478 Handler: handler, 479 }, 480 } 481 return server.ListenAndServeTLS(certFile, keyFile) 482} 483 484// ListenAndServe listens on the given network address for both, TLS and QUIC 485// connetions in parallel. It returns if one of the two returns an error. 486// http.DefaultServeMux is used when handler is nil. 487// The correct Alt-Svc headers for QUIC are set. 488func ListenAndServe(addr, certFile, keyFile string, handler http.Handler) error { 489 // Load certs 490 var err error 491 certs := make([]tls.Certificate, 1) 492 certs[0], err = tls.LoadX509KeyPair(certFile, keyFile) 493 if err != nil { 494 return err 495 } 496 // We currently only use the cert-related stuff from tls.Config, 497 // so we don't need to make a full copy. 498 config := &tls.Config{ 499 Certificates: certs, 500 } 501 502 // Open the listeners 503 udpAddr, err := net.ResolveUDPAddr("udp", addr) 504 if err != nil { 505 return err 506 } 507 udpConn, err := net.ListenUDP("udp", udpAddr) 508 if err != nil { 509 return err 510 } 511 defer udpConn.Close() 512 513 tcpAddr, err := net.ResolveTCPAddr("tcp", addr) 514 if err != nil { 515 return err 516 } 517 tcpConn, err := net.ListenTCP("tcp", tcpAddr) 518 if err != nil { 519 return err 520 } 521 defer tcpConn.Close() 522 523 tlsConn := tls.NewListener(tcpConn, config) 524 defer tlsConn.Close() 525 526 // Start the servers 527 httpServer := &http.Server{ 528 Addr: addr, 529 TLSConfig: config, 530 } 531 532 quicServer := &Server{ 533 Server: httpServer, 534 } 535 536 if handler == nil { 537 handler = http.DefaultServeMux 538 } 539 httpServer.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 540 quicServer.SetQuicHeaders(w.Header()) 541 handler.ServeHTTP(w, r) 542 }) 543 544 hErr := make(chan error) 545 qErr := make(chan error) 546 go func() { 547 hErr <- httpServer.Serve(tlsConn) 548 }() 549 go func() { 550 qErr <- quicServer.Serve(udpConn) 551 }() 552 553 select { 554 case err := <-hErr: 555 quicServer.Close() 556 return err 557 case err := <-qErr: 558 // Cannot close the HTTP server or wait for requests to complete properly :/ 559 return err 560 } 561} 562