mirror of
https://github.com/livekit/livekit.git
synced 2026-09-21 00:55:37 +00:00
Replace the protobuf frame layer on exchange streams with a length-prefixed StreamPreamble followed by opaque HTTP/1.1 bytes. QUIC already provides the multiplexing and per-stream flow control, so the frame layer only re-encoded a message both peers can already parse, and every SDK had to re-materialize HTTP from it. The front serializes a canonical request head from its own parsed *http.Request and never forwards the client's bytes, which is what keeps request smuggling out of the worker. Responses are read with http.ReadResponse, so informational heads need no httptrace hook and the conformance worker loses its HTTP reconstruction entirely. Completion splits by outcome: a body ends by its own framing, a failure after bytes have flowed travels in x-lk-completion / x-lk-error trailers, and a failure before any byte resets the stream with an HttpStreamResetCode. REFUSED stays distinct as the retry-safety signal, and the retry/idempotence rules are unchanged. The x-lk- prefix is reserved for this signalling: it is stripped from client-supplied request headers so a caller cannot forge an outcome, and from responses so it never reaches the end client. Exchange-stream targets are split from the escaped path so a percent-encoded '?' or '/' cannot change which resource the worker routes to. Per-request state moves to attempt.go so the Front and attempt lifecycles stop interleaving; the remaining files are grouped type-first with free helpers last. Those moves are ordering only.
74 lines
2.2 KiB
Go
74 lines
2.2 KiB
Go
// Copyright 2026 LiveKit, Inc.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package wire
|
|
|
|
import (
|
|
"encoding/binary"
|
|
"fmt"
|
|
"io"
|
|
|
|
"google.golang.org/protobuf/proto"
|
|
)
|
|
|
|
// MaxControlMessageSize bounds one framed control message so a malformed length
|
|
// prefix cannot make the reader allocate without bound.
|
|
const MaxControlMessageSize = 1 << 20
|
|
|
|
// WriteControlMessage writes a length-delimited protobuf on the control stream:
|
|
// a 4-byte big-endian length followed by the marshaled message. The stream
|
|
// carries the WorkerMessage/ServerMessage exchange.
|
|
func WriteControlMessage(w io.Writer, m proto.Message) error {
|
|
return writeLenPrefixed(w, m)
|
|
}
|
|
|
|
// ReadControlMessage reads one length-delimited protobuf written by
|
|
// WriteControlMessage into m.
|
|
func ReadControlMessage(r io.Reader, m proto.Message) error {
|
|
return readLenPrefixed(r, m, MaxControlMessageSize)
|
|
}
|
|
|
|
// writeLenPrefixed is the package's one length-delimited protobuf encoding:
|
|
// 4-byte big-endian length, then the marshaled message. The control stream and
|
|
// the per-exchange preamble share it.
|
|
func writeLenPrefixed(w io.Writer, m proto.Message) error {
|
|
b, err := proto.Marshal(m)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
var hdr [4]byte
|
|
binary.BigEndian.PutUint32(hdr[:], uint32(len(b)))
|
|
if _, err := w.Write(hdr[:]); err != nil {
|
|
return err
|
|
}
|
|
_, err = w.Write(b)
|
|
return err
|
|
}
|
|
|
|
func readLenPrefixed(r io.Reader, m proto.Message, max uint32) error {
|
|
var hdr [4]byte
|
|
if _, err := io.ReadFull(r, hdr[:]); err != nil {
|
|
return err
|
|
}
|
|
n := binary.BigEndian.Uint32(hdr[:])
|
|
if n > max {
|
|
return fmt.Errorf("message too large: %d bytes", n)
|
|
}
|
|
b := make([]byte, n)
|
|
if _, err := io.ReadFull(r, b); err != nil {
|
|
return err
|
|
}
|
|
return proto.Unmarshal(b, m)
|
|
}
|