snapshot of https://github.com/centrifugal/centrifugo.git (master, 2026-09-16; blobs > 131072B omitted)
Some checks failed
test / Lint (push) Failing after 3s
test / Test with Go 1.26.8 (push) Failing after 2s

This commit is contained in:
upstream-mirror
2026-09-16 01:17:21 +05:00
commit b352a9a30d
472 changed files with 108375 additions and 0 deletions

167
internal/wt/transport.go Normal file
View File

@@ -0,0 +1,167 @@
package wt
import (
"sync"
"time"
"github.com/centrifugal/centrifugo/v6/internal/tools"
"github.com/centrifugal/centrifuge"
"github.com/centrifugal/protocol"
"github.com/quic-go/webtransport-go"
)
const transportName = "webtransport"
// defaultWebTransportMessageSizeLimit is used when message_size_limit is 0
// (which the stream decoder treats as unbounded). Matches the config default.
const defaultWebTransportMessageSizeLimit = 65536
type webtransportTransport struct {
mu sync.RWMutex
closeCh chan struct{}
protoType centrifuge.ProtocolType
session *webtransport.Session
stream *webtransport.Stream
pingPongConfig centrifuge.PingPongConfig
closed bool
protoMajor int
}
func newWebtransportTransport(protoType centrifuge.ProtocolType, session *webtransport.Session, stream *webtransport.Stream, pingPongConfig centrifuge.PingPongConfig, protoMajor int) *webtransportTransport {
return &webtransportTransport{
protoType: protoType,
closeCh: make(chan struct{}),
session: session,
stream: stream,
pingPongConfig: pingPongConfig,
protoMajor: protoMajor,
}
}
// Name implementation.
func (t *webtransportTransport) Name() string {
return transportName
}
// Protocol implementation.
func (t *webtransportTransport) Protocol() centrifuge.ProtocolType {
return t.protoType
}
// Unidirectional implementation.
func (t *webtransportTransport) Unidirectional() bool {
return false
}
// DisabledPushFlags ...
func (t *webtransportTransport) DisabledPushFlags() uint64 {
return 0
}
// ProtocolVersion ...
func (t *webtransportTransport) ProtocolVersion() centrifuge.ProtocolVersion {
return centrifuge.ProtocolVersion2
}
// Emulation ...
func (t *webtransportTransport) Emulation() bool {
return false
}
// PingPongConfig ...
func (t *webtransportTransport) PingPongConfig() centrifuge.PingPongConfig {
return t.pingPongConfig
}
// AcceptProtocol ...
func (t *webtransportTransport) AcceptProtocol() string {
return tools.GetAcceptProtocolLabel(t.protoMajor)
}
const writeTimeout = 1 * time.Second
// Write ...
func (t *webtransportTransport) Write(message []byte) error {
select {
case <-t.closeCh:
return nil
default:
protoType := protocol.TypeJSON
if t.protoType == centrifuge.ProtocolTypeProtobuf {
protoType = protocol.TypeProtobuf
}
encoder := protocol.GetDataEncoder(protoType)
defer protocol.PutDataEncoder(protoType, encoder)
_ = encoder.Encode(message)
_ = t.stream.SetWriteDeadline(time.Now().Add(writeTimeout))
_, err := t.stream.Write(encoder.Finish())
if err != nil {
return err
}
if protoType == protocol.TypeJSON {
// Need extra new line since WebTransport is stream-based, not frame-based.
_, err = t.stream.Write([]byte("\n"))
}
_ = t.stream.SetWriteDeadline(time.Time{})
return err
}
}
// WriteMany ...
func (t *webtransportTransport) WriteMany(messages ...[]byte) error {
select {
case <-t.closeCh:
return nil
default:
protoType := protocol.TypeJSON
if t.protoType == centrifuge.ProtocolTypeProtobuf {
protoType = protocol.TypeProtobuf
}
encoder := protocol.GetDataEncoder(protoType)
defer protocol.PutDataEncoder(protoType, encoder)
for i := range messages {
err := encoder.Encode(messages[i])
if err != nil {
return err
}
}
_ = t.stream.SetWriteDeadline(time.Now().Add(writeTimeout))
_, err := t.stream.Write(encoder.Finish())
if err != nil {
return err
}
if protoType == protocol.TypeJSON {
// Need extra new line since WebTransport is stream-based, not frame-based.
_, err = t.stream.Write([]byte("\n"))
}
_ = t.stream.SetWriteDeadline(time.Time{})
return err
}
}
// Close ...
func (t *webtransportTransport) Close(d centrifuge.Disconnect) error {
t.mu.Lock()
if t.closed {
t.mu.Unlock()
return nil
}
t.closed = true
close(t.closeCh)
t.mu.Unlock()
_ = t.stream.Close()
// TODO: remove this Sleep.
// We are closing session with SessionErrorCode below but browser client does not receive
// it for some reason. So for now we are sending Disconnect Pushes to pass disconnect code
// to a client. While sending Disconnect Push we hit https://github.com/quic-go/quic-go/issues/3291 –
// adding Sleep give client a chance to receive it. If code sent in CloseWithError will
// reach the client we won't need Sleep here and can disable sending Disconnect Pushes.
time.Sleep(time.Second)
return t.session.CloseWithError(webtransport.SessionErrorCode(d.Code), d.Reason)
}