// Copyright 2023 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn . //go:build plus package http3 import ( "context" "errors" "fmt" "github.com/quic-go/quic-go" http3quic "github.com/quic-go/quic-go/http3" "log" "net" "net/http" ) type Server struct { Addr string Handler http.Handler ConnState func(conn net.Conn, state http.ConnState) ConnContext ContextFunc rawServer *http3quic.Server isClosed bool listener Listener } func (this *Server) Serve(listener Listener) error { if listener == nil { return errors.New("listener must not be nil") } this.listener = listener this.rawServer = &http3quic.Server{ Addr: this.Addr, Handler: this.Handler, } for { conn, err := listener.Accept(context.Background(), this.ConnContext) if err != nil { if this.isClosed { return nil } continue } go func() { defer func() { if r := recover(); r != nil { log.Println(fmt.Sprintf("[HTTP3]goroutine panic: %v", r)) } }() // 通知ConnState if this.ConnState != nil { netConn, isNetConn := conn.(net.Conn) if isNetConn { this.ConnState(netConn, http.StateNew) this.ConnState(netConn, http.StateActive) } } _ = this.rawServer.ServeQUICConn(conn) // 关闭连接 _ = conn.CloseWithError(quic.ApplicationErrorCode(quic.NoError), "") // 通知ConnState if this.ConnState != nil { netConn, isNetConn := conn.(net.Conn) if isNetConn { this.ConnState(netConn, http.StateClosed) } } }() } } func (this *Server) SetQuicHeaders(headers http.Header) { if this.isClosed { return } headers.Set("Alt-Svc", `h3="`+this.Addr+`"; ma=2592000,h3-29="`+this.Addr+`"; ma=2592000`) } func (this *Server) Close() error { this.isClosed = true _ = this.listener.Close() return this.rawServer.Close() }