fix(runtime): account for smux frame overhead in wire payload cap

This commit is contained in:
zarazaex69
2026-05-19 23:34:37 +03:00
parent 9e7d0836a3
commit 74bb402289
6 changed files with 33 additions and 15 deletions

View File

@@ -13,10 +13,10 @@ import (
"github.com/openlibrecommunity/olcrtc/internal/auth"
"github.com/openlibrecommunity/olcrtc/internal/client"
"github.com/openlibrecommunity/olcrtc/internal/control"
"github.com/openlibrecommunity/olcrtc/internal/crypto"
enginebuiltin "github.com/openlibrecommunity/olcrtc/internal/engine/builtin"
"github.com/openlibrecommunity/olcrtc/internal/logger"
"github.com/openlibrecommunity/olcrtc/internal/names"
"github.com/openlibrecommunity/olcrtc/internal/runtime"
"github.com/openlibrecommunity/olcrtc/internal/server"
"github.com/openlibrecommunity/olcrtc/internal/transport"
"github.com/openlibrecommunity/olcrtc/internal/transport/datachannel"
@@ -549,7 +549,7 @@ func validateTrafficConfig(cfg Config) error {
func trafficConfig(cfg Config) (transport.TrafficConfig, error) {
if cfg.TrafficMaxPayloadSize < 0 || (cfg.TrafficMaxPayloadSize > 0 &&
cfg.TrafficMaxPayloadSize <= crypto.WireOverhead) {
cfg.TrafficMaxPayloadSize < runtime.MinSmuxWirePayload) {
return transport.TrafficConfig{}, ErrTrafficMaxPayloadSizeInvalid
}
minDelay, err := parseOptionalNonNegativeDuration(cfg.TrafficMinDelay)

View File

@@ -8,7 +8,7 @@ import (
"time"
"github.com/openlibrecommunity/olcrtc/internal/control"
"github.com/openlibrecommunity/olcrtc/internal/crypto"
"github.com/openlibrecommunity/olcrtc/internal/runtime"
)
const testBadDuration = "nope"
@@ -511,10 +511,10 @@ func TestValidate(t *testing.T) {
want: ErrTrafficMaxPayloadSizeInvalid,
},
{
name: "traffic rejects payload smaller than crypto overhead",
name: "traffic rejects payload too small for encrypted smux frame",
cfg: func() Config {
cfg := base
cfg.TrafficMaxPayloadSize = crypto.WireOverhead
cfg.TrafficMaxPayloadSize = runtime.MinSmuxWirePayload - 1
return cfg
}(),
want: ErrTrafficMaxPayloadSizeInvalid,

View File

@@ -52,9 +52,10 @@ func TestSmuxConfig(t *testing.T) {
t.Fatalf("smuxConfig(0) = %+v", cfg)
}
capped := smuxConfig(4096)
if capped.MaxFrameSize != 4096-cryptopkg.WireOverhead {
want := 4096 - runtime.SmuxWireOverhead
if capped.MaxFrameSize != want {
t.Fatalf("smuxConfig(4096).MaxFrameSize = %d, want %d",
capped.MaxFrameSize, 4096-cryptopkg.WireOverhead)
capped.MaxFrameSize, want)
}
}

View File

@@ -17,6 +17,19 @@ import (
"github.com/xtaci/smux"
)
const (
// SmuxFrameOverhead is the fixed smux frame header size. MaxFrameSize
// caps only the smux payload, while muxconn encrypts and sends the whole
// smux frame as one transport message.
SmuxFrameOverhead = 8
// SmuxWireOverhead is the non-payload overhead added around each smux
// frame before it reaches the transport payload limit.
SmuxWireOverhead = crypto.WireOverhead + SmuxFrameOverhead
// MinSmuxWirePayload is the smallest useful encrypted transport payload
// cap that can still carry a non-empty smux frame.
MinSmuxWirePayload = SmuxWireOverhead + 1
)
// ErrKeyRequired is returned when no encryption key is provided.
var ErrKeyRequired = errors.New("key required (use -key <hex>)")
@@ -44,15 +57,15 @@ func SetupCipher(keyHex string) (*crypto.Cipher, error) {
// SmuxConfig returns the tuned smux config used on both ends. Both peers
// must agree on Version and MaxFrameSize. maxWirePayload, when > 0,
// constrains the max frame size to fit under the transport's per-message
// payload cap minus the AEAD wire overhead.
// constrains the smux payload size so the encrypted whole smux frame fits
// under the transport's per-message payload cap.
func SmuxConfig(maxWirePayload int) *smux.Config {
cfg := smux.DefaultConfig()
cfg.Version = 2
cfg.KeepAliveDisabled = false
cfg.MaxFrameSize = 32768
if maxWirePayload > crypto.WireOverhead {
maxFrameSize := maxWirePayload - crypto.WireOverhead
if maxWirePayload >= MinSmuxWirePayload {
maxFrameSize := maxWirePayload - SmuxWireOverhead
if maxFrameSize < cfg.MaxFrameSize {
cfg.MaxFrameSize = maxFrameSize
}

View File

@@ -44,12 +44,15 @@ func TestSmuxConfigDefault(t *testing.T) {
}
func TestSmuxConfigShrinks(t *testing.T) {
// 100-byte wire payload minus crypto overhead is far below default 32768,
// so MaxFrameSize must shrink.
// 100-byte wire payload minus smux+crypto overhead is far below default
// 32768, so MaxFrameSize must shrink.
cfg := runtime.SmuxConfig(100)
if cfg.MaxFrameSize >= 32768 {
t.Fatalf("MaxFrameSize = %d, want shrunk", cfg.MaxFrameSize)
}
if cfg.MaxFrameSize+runtime.SmuxWireOverhead != 100 {
t.Fatalf("wire size = %d, want 100", cfg.MaxFrameSize+runtime.SmuxWireOverhead)
}
}
func TestHealthTrackerEmitsOnEveryChange(t *testing.T) {

View File

@@ -53,9 +53,10 @@ func TestSmuxConfig(t *testing.T) {
t.Fatalf("smuxConfig(0) = %+v", cfg)
}
capped := smuxConfig(4096)
if capped.MaxFrameSize != 4096-cryptopkg.WireOverhead {
want := 4096 - runtime.SmuxWireOverhead
if capped.MaxFrameSize != want {
t.Fatalf("smuxConfig(4096).MaxFrameSize = %d, want %d",
capped.MaxFrameSize, 4096-cryptopkg.WireOverhead)
capped.MaxFrameSize, want)
}
}