mirror of
https://github.com/golang/net.git
synced 2026-04-01 02:47:08 +09:00
internal/http3 was written back in go1.24, and relies on synctest.Run and goexperiment.synctest. This prevents us from running tests with newer versions of Go. This CL updates the existing build constraint and synctest usages to go1.25 so we can still run tests using go1.25 and tip. Support for running synctest with go1.24 is not kept, since go1.26 release (and therefore x/net go.mod updating to go1.25) is expected soon. Change-Id: Iebfa82ebd1da4a06ba613ce0f45051f4691037fc Reviewed-on: https://go-review.googlesource.com/c/net/+/734940 Reviewed-by: Damien Neil <dneil@google.com> Reviewed-by: Nicholas Husin <husin@google.com> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
171 lines
4.0 KiB
Go
171 lines
4.0 KiB
Go
// Copyright 2025 The Go Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package http3
|
|
|
|
import (
|
|
"context"
|
|
"net/http"
|
|
"sync"
|
|
|
|
"golang.org/x/net/quic"
|
|
)
|
|
|
|
// A Server is an HTTP/3 server.
|
|
// The zero value for Server is a valid server.
|
|
type Server struct {
|
|
// Handler to invoke for requests, http.DefaultServeMux if nil.
|
|
Handler http.Handler
|
|
|
|
// Config is the QUIC configuration used by the server.
|
|
// The Config may be nil.
|
|
//
|
|
// ListenAndServe may clone and modify the Config.
|
|
// The Config must not be modified after calling ListenAndServe.
|
|
Config *quic.Config
|
|
|
|
initOnce sync.Once
|
|
}
|
|
|
|
func (s *Server) init() {
|
|
s.initOnce.Do(func() {
|
|
s.Config = initConfig(s.Config)
|
|
if s.Handler == nil {
|
|
s.Handler = http.DefaultServeMux
|
|
}
|
|
})
|
|
}
|
|
|
|
// ListenAndServe listens on the UDP network address addr
|
|
// and then calls Serve to handle requests on incoming connections.
|
|
func (s *Server) ListenAndServe(addr string) error {
|
|
s.init()
|
|
e, err := quic.Listen("udp", addr, s.Config)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return s.Serve(e)
|
|
}
|
|
|
|
// Serve accepts incoming connections on the QUIC endpoint e,
|
|
// and handles requests from those connections.
|
|
func (s *Server) Serve(e *quic.Endpoint) error {
|
|
s.init()
|
|
for {
|
|
qconn, err := e.Accept(context.Background())
|
|
if err != nil {
|
|
return err
|
|
}
|
|
go newServerConn(qconn)
|
|
}
|
|
}
|
|
|
|
type serverConn struct {
|
|
qconn *quic.Conn
|
|
|
|
genericConn // for handleUnidirectionalStream
|
|
enc qpackEncoder
|
|
dec qpackDecoder
|
|
}
|
|
|
|
func newServerConn(qconn *quic.Conn) {
|
|
sc := &serverConn{
|
|
qconn: qconn,
|
|
}
|
|
sc.enc.init()
|
|
|
|
// Create control stream and send SETTINGS frame.
|
|
// TODO: Time out on creating stream.
|
|
controlStream, err := newConnStream(context.Background(), sc.qconn, streamTypeControl)
|
|
if err != nil {
|
|
return
|
|
}
|
|
controlStream.writeSettings()
|
|
controlStream.Flush()
|
|
|
|
sc.acceptStreams(sc.qconn, sc)
|
|
}
|
|
|
|
func (sc *serverConn) handleControlStream(st *stream) error {
|
|
// "A SETTINGS frame MUST be sent as the first frame of each control stream [...]"
|
|
// https://www.rfc-editor.org/rfc/rfc9114.html#section-7.2.4-2
|
|
if err := st.readSettings(func(settingsType, settingsValue int64) error {
|
|
switch settingsType {
|
|
case settingsMaxFieldSectionSize:
|
|
_ = settingsValue // TODO
|
|
case settingsQPACKMaxTableCapacity:
|
|
_ = settingsValue // TODO
|
|
case settingsQPACKBlockedStreams:
|
|
_ = settingsValue // TODO
|
|
default:
|
|
// Unknown settings types are ignored.
|
|
}
|
|
return nil
|
|
}); err != nil {
|
|
return err
|
|
}
|
|
|
|
for {
|
|
ftype, err := st.readFrameHeader()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
switch ftype {
|
|
case frameTypeCancelPush:
|
|
// "If a server receives a CANCEL_PUSH frame for a push ID
|
|
// that has not yet been mentioned by a PUSH_PROMISE frame,
|
|
// this MUST be treated as a connection error of type H3_ID_ERROR."
|
|
// https://www.rfc-editor.org/rfc/rfc9114.html#section-7.2.3-8
|
|
return &connectionError{
|
|
code: errH3IDError,
|
|
message: "CANCEL_PUSH for unsent push ID",
|
|
}
|
|
case frameTypeGoaway:
|
|
return errH3NoError
|
|
default:
|
|
// Unknown frames are ignored.
|
|
if err := st.discardUnknownFrame(ftype); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func (sc *serverConn) handleEncoderStream(*stream) error {
|
|
// TODO
|
|
return nil
|
|
}
|
|
|
|
func (sc *serverConn) handleDecoderStream(*stream) error {
|
|
// TODO
|
|
return nil
|
|
}
|
|
|
|
func (sc *serverConn) handlePushStream(*stream) error {
|
|
// "[...] if a server receives a client-initiated push stream,
|
|
// this MUST be treated as a connection error of type H3_STREAM_CREATION_ERROR."
|
|
// https://www.rfc-editor.org/rfc/rfc9114.html#section-6.2.2-3
|
|
return &connectionError{
|
|
code: errH3StreamCreationError,
|
|
message: "client created push stream",
|
|
}
|
|
}
|
|
|
|
func (sc *serverConn) handleRequestStream(st *stream) error {
|
|
// TODO
|
|
return nil
|
|
}
|
|
|
|
// abort closes the connection with an error.
|
|
func (sc *serverConn) abort(err error) {
|
|
if e, ok := err.(*connectionError); ok {
|
|
sc.qconn.Abort(&quic.ApplicationError{
|
|
Code: uint64(e.code),
|
|
Reason: e.message,
|
|
})
|
|
} else {
|
|
sc.qconn.Abort(err)
|
|
}
|
|
}
|