package webtransport
import (
"context"
"crypto/tls"
"errors"
"fmt"
"net/http"
"net/url"
"sync"
"time"
"github.com/quic-go/quic-go"
"github.com/quic-go/quic-go/http3"
"github.com/quic-go/quic-go/quicvarint"
)
var errNoWebTransport = errors .New ("server didn't enable WebTransport" )
type Dialer struct {
TLSClientConfig *tls .Config
QUICConfig *quic .Config
StreamReorderingTimeout time .Duration
DialAddr func (ctx context .Context , addr string , tlsCfg *tls .Config , cfg *quic .Config ) (quic .EarlyConnection , error )
ctx context .Context
ctxCancel context .CancelFunc
initOnce sync .Once
conns sessionManager
}
func (d *Dialer ) init () {
timeout := d .StreamReorderingTimeout
if timeout == 0 {
timeout = 5 * time .Second
}
d .conns = *newSessionManager (timeout )
d .ctx , d .ctxCancel = context .WithCancel (context .Background ())
}
func (d *Dialer ) Dial (ctx context .Context , urlStr string , reqHdr http .Header ) (*http .Response , *Session , error ) {
d .initOnce .Do (func () { d .init () })
quicConf := d .QUICConfig
if quicConf == nil {
quicConf = &quic .Config {EnableDatagrams : true }
} else if !d .QUICConfig .EnableDatagrams {
return nil , nil , errors .New ("webtransport: DATAGRAM support required, enable it via QUICConfig.EnableDatagrams" )
}
tlsConf := d .TLSClientConfig
if tlsConf == nil {
tlsConf = &tls .Config {}
} else {
tlsConf = tlsConf .Clone ()
}
if len (tlsConf .NextProtos ) == 0 {
tlsConf .NextProtos = []string {http3 .NextProtoH3 }
}
u , err := url .Parse (urlStr )
if err != nil {
return nil , nil , err
}
if reqHdr == nil {
reqHdr = http .Header {}
}
reqHdr .Set (webTransportDraftOfferHeaderKey , "1" )
req := &http .Request {
Method : http .MethodConnect ,
Header : reqHdr ,
Proto : "webtransport" ,
Host : u .Host ,
URL : u ,
}
req = req .WithContext (ctx )
dialAddr := d .DialAddr
if dialAddr == nil {
dialAddr = quic .DialAddrEarly
}
qconn , err := dialAddr (ctx , u .Host , tlsConf , quicConf )
if err != nil {
return nil , nil , err
}
tr := &http3 .Transport {
EnableDatagrams : true ,
StreamHijacker : func (ft http3 .FrameType , connTracingID quic .ConnectionTracingID , str quic .Stream , e error ) (hijacked bool , err error ) {
if isWebTransportError (e ) {
return true , nil
}
if ft != webTransportFrameType {
return false , nil
}
id , err := quicvarint .Read (quicvarint .NewReader (str ))
if err != nil {
if isWebTransportError (err ) {
return true , nil
}
return false , err
}
d .conns .AddStream (connTracingID , str , sessionID (id ))
return true , nil
},
UniStreamHijacker : func (st http3 .StreamType , connTracingID quic .ConnectionTracingID , str quic .ReceiveStream , err error ) (hijacked bool ) {
if st != webTransportUniStreamType && !isWebTransportError (err ) {
return false
}
d .conns .AddUniStream (connTracingID , str )
return true
},
}
conn := tr .NewClientConn (qconn )
select {
case <- conn .ReceivedSettings ():
case <- d .ctx .Done ():
return nil , nil , context .Cause (d .ctx )
}
settings := conn .Settings ()
if !settings .EnableExtendedConnect {
return nil , nil , errors .New ("server didn't enable Extended CONNECT" )
}
if !settings .EnableDatagrams {
return nil , nil , errors .New ("server didn't enable HTTP/3 datagram support" )
}
if settings .Other == nil {
return nil , nil , errNoWebTransport
}
s , ok := settings .Other [settingsEnableWebtransport ]
if !ok || s != 1 {
return nil , nil , errNoWebTransport
}
requestStr , err := conn .OpenRequestStream (ctx )
if err != nil {
return nil , nil , err
}
if err := requestStr .SendRequestHeader (req ); err != nil {
return nil , nil , err
}
rsp , err := requestStr .ReadResponse ()
if err != nil {
return nil , nil , err
}
if rsp .StatusCode < 200 || rsp .StatusCode >= 300 {
return rsp , nil , fmt .Errorf ("received status %d" , rsp .StatusCode )
}
return rsp , d .conns .AddSession (conn , sessionID (requestStr .StreamID ()), requestStr ), nil
}
func (d *Dialer ) Close () error {
d .ctxCancel ()
return nil
}
The pages are generated with Golds v0.8.2 . (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu .
PR and bug reports are welcome and can be submitted to the issue list .
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds .