caddyhttp: Enable HTTP/3 by default (#4707)

This commit is contained in:
Matt Holt 2022-08-15 12:01:58 -06:00 committed by GitHub
parent e2a5e2293a
commit c79c08627d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 210 additions and 191 deletions

View file

@ -16,6 +16,7 @@ package caddyhttp
import (
"context"
"crypto/tls"
"encoding/json"
"errors"
"fmt"
@ -25,6 +26,7 @@ import (
"runtime"
"strings"
"sync"
"sync/atomic"
"time"
"github.com/caddyserver/caddy/v2"
@ -37,6 +39,8 @@ import (
// Server describes an HTTP server.
type Server struct {
activeRequests int64 // accessed atomically
// Socket addresses to which to bind listeners. Accepts
// [network addresses](/docs/conventions#network-addresses)
// that may include port ranges. Listener addresses must
@ -112,21 +116,35 @@ type Server struct {
// to a non-null, empty struct.
Logs *ServerLogConfig `json:"logs,omitempty"`
// Enable experimental HTTP/3 support. Note that HTTP/3 is not a
// finished standard and has extremely limited client support.
// This field is not subject to compatibility promises.
ExperimentalHTTP3 bool `json:"experimental_http3,omitempty"`
// Enables H2C ("Cleartext HTTP/2" or "H2 over TCP") support,
// which will serve HTTP/2 over plaintext TCP connections if
// the client supports it. Because this is not implemented by the
// Go standard library, using H2C is incompatible with most
// of the other options for this server. Do not enable this
// Protocols specifies which HTTP protocols to enable.
// Supported values are:
//
// - `h1` (HTTP/1.1)
// - `h2` (HTTP/2)
// - `h2c` (cleartext HTTP/2)
// - `h3` (HTTP/3)
//
// If enabling `h2` or `h2c`, `h1` must also be enabled;
// this is due to current limitations in the Go standard
// library.
//
// HTTP/2 operates only over TLS (HTTPS). HTTP/3 opens
// a UDP socket to serve QUIC connections.
//
// H2C operates over plain TCP if the client supports it;
// however, because this is not implemented by the Go
// standard library, other server options are not compatible
// and will not be applied to H2C requests. Do not enable this
// only to achieve maximum client compatibility. In practice,
// very few clients implement H2C, and even fewer require it.
// This setting applies only to unencrypted HTTP listeners.
// ⚠️ Experimental feature; subject to change or removal.
AllowH2C bool `json:"allow_h2c,omitempty"`
// Enabling H2C can be useful for serving/proxying gRPC
// if encryption is not possible or desired.
//
// We recommend for most users to simply let Caddy use the
// default settings.
//
// Default: `[h1 h2 h3]`
Protocols []string `json:"protocols,omitempty"`
name string
@ -152,7 +170,12 @@ type Server struct {
func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Server", "Caddy")
// advertise HTTP/3, if enabled
if s.h3server != nil {
// keep track of active requests for QUIC transport purposes (See AcceptToken callback in quic.Config)
atomic.AddInt64(&s.activeRequests, 1)
defer atomic.AddInt64(&s.activeRequests, -1)
err := s.h3server.SetQuicHeaders(w.Header())
if err != nil {
s.logger.Error("setting HTTP/3 Alt-Svc header", zap.Error(err))
@ -445,6 +468,30 @@ func (s *Server) findLastRouteWithHostMatcher() int {
return lastIndex
}
// serveHTTP3 creates a QUIC listener, configures an HTTP/3 server if
// not already done, and then uses that server to serve HTTP/3 over
// the listener, with Server s as the handler.
func (s *Server) serveHTTP3(hostport string, tlsCfg *tls.Config) error {
h3ln, err := caddy.ListenQUIC(hostport, tlsCfg, &s.activeRequests)
if err != nil {
return fmt.Errorf("starting HTTP/3 QUIC listener: %v", err)
}
// create HTTP/3 server if not done already
if s.h3server == nil {
s.h3server = &http3.Server{
Handler: s,
TLSConfig: tlsCfg,
MaxHeaderBytes: s.MaxHeaderBytes,
}
}
//nolint:errcheck
go s.h3server.ServeListener(h3ln)
return nil
}
// HTTPErrorConfig determines how to handle errors
// from the HTTP handlers.
type HTTPErrorConfig struct {
@ -509,6 +556,16 @@ func (s *Server) shouldLogRequest(r *http.Request) bool {
return true
}
// protocol returns true if the protocol proto is configured/enabled.
func (s *Server) protocol(proto string) bool {
for _, p := range s.Protocols {
if p == proto {
return true
}
}
return false
}
// ServerLogConfig describes a server's logging configuration. If
// enabled without customization, all requests to this server are
// logged to the default logger; logger destinations may be