Documentation
¶
Overview ¶
Package protocol defines the wire format spoken between the vrok CLI and the vrok relay.
The design rule is that the relay is a router, not a store: it forwards HTTP semantics verbatim and never holds a file. Requests and responses keep their methods, headers, status codes and Range handling, so a browser talking to the relay behaves exactly as it would talking to the CLI directly — which is what preserves video seeking, resumable downloads and caching.
Index ¶
- Constants
- Variables
- func Encode(t Type, payload any) ([]byte, error)
- func EncodeFrame(f Frame) []byte
- func IsExpectedClose(err error) bool
- func Payload[T any](e Envelope) (T, error)
- type BodyStream
- type Cancel
- type Conn
- type Envelope
- type Error
- type Frame
- type Register
- type Registered
- type Request
- type Response
- type Type
Constants ¶
const AgentPath = "/_vrok/agent"
AgentPath is the relay endpoint where agents open their tunnel.
const MaxFrameData = 32 << 10
MaxFrameData is the largest payload carried in one frame. 32 KiB matches the buffer io.Copy uses, so bodies are forwarded without extra copying or re-chunking.
const MaxMessageSize = 4 << 20
MaxMessageSize caps one inbound WebSocket message. Body frames are at most MaxFrameData plus a header; the largest control message is a request envelope carrying a visitor's headers, which net/http already bounds at 1 MiB before JSON escaping. Without a cap, gorilla/websocket buffers a message of any size, so one peer could exhaust the other's memory with a single frame.
const Version = 1
Version is the protocol revision. The relay rejects agents it cannot speak to rather than guessing.
Variables ¶
var ErrClosed = errors.New("protocol: connection closed")
ErrClosed is returned once a connection has been closed.
var ErrShortFrame = errors.New("protocol: binary frame shorter than its header")
ErrShortFrame reports a binary frame that is too small to be valid.
var ErrStreamClosed = errors.New("protocol: stream closed")
ErrStreamClosed reports a write to a stream nobody will read.
Functions ¶
func IsExpectedClose ¶
IsExpectedClose reports whether err is a normal end of connection rather than a fault worth logging.
Types ¶
type BodyStream ¶
type BodyStream struct {
// contains filtered or unexported fields
}
BodyStream is the receiving half of one body stream: a bounded queue of frames exposed as an io.Reader.
Both ends of the tunnel need it — the relay to assemble response bodies, the agent to assemble request bodies — so it lives with the protocol rather than being written twice.
An io.Pipe would also work but has no buffer at all, so the shared connection read loop would have to hand over every chunk synchronously and would stall on each one.
func (*BodyStream) Close ¶
func (b *BodyStream) Close(err error)
Close ends the stream. A nil error means a normal end of body; any other error surfaces from Read, so the consumer can tell a truncated transfer from a complete one.
func (*BodyStream) Push ¶
func (b *BodyStream) Push(data []byte) error
Push appends a frame's payload. The data is copied because the caller's buffer is reused by the next read from the connection.
type Conn ¶
type Conn struct {
// contains filtered or unexported fields
}
Conn is a multiplexed, concurrency-safe view of one WebSocket connection.
A WebSocket allows only one writer at a time, which is easy to violate once several HTTP streams share the connection. Serialising every write behind one mutex here means no caller has to remember that rule.
func (*Conn) Close ¶
Close shuts the connection down, attempting a clean WebSocket close first so the peer learns this was deliberate rather than a network failure.
func (*Conn) Copy ¶
Copy streams src to the peer as body frames, terminated by an end frame. The end frame is sent even when the read fails, so the receiving side is never left waiting for a stream that will not continue.
type Envelope ¶
type Envelope struct {
Type Type `json:"type"`
Payload json.RawMessage `json:"payload,omitempty"`
}
Envelope wraps a control message with its type so the receiver can decode the payload into the right struct.
type Error ¶
type Error struct {
Stream uint64 `json:"stream,omitempty"`
Code string `json:"code,omitempty"`
Message string `json:"message"`
}
Error reports a failure. A zero Stream means the whole connection failed.
type Frame ¶
type Frame struct {
// Stream matches the Request that opened this body stream.
Stream uint64
// End marks the last frame; a frame may be both final and empty.
End bool
// Data is the payload, at most MaxFrameData bytes.
Data []byte
}
Frame is one chunk of a request or response body.
Bodies travel as binary frames rather than inside JSON because base64 would inflate every byte of every download by a third, and because a frame can be forwarded straight into an io.Writer without re-encoding.
func DecodeFrame ¶
DecodeFrame parses a frame. The returned Data aliases the input buffer, so callers that retain it past the read loop must copy it.
type Register ¶
type Register struct {
Version int `json:"version"`
// https://a82kd9.example.com.
ShareID string `json:"share_id"`
// Token is the share secret. The relay never inspects it; it is echoed
// back so the agent can confirm the relay is talking about its share.
Token string `json:"token"`
// Auth is an optional relay credential for private deployments.
Auth string `json:"auth,omitempty"`
// Agent identifies the client build, for diagnostics.
Agent string `json:"agent,omitempty"`
}
Register claims a hostname label on the relay.
type Registered ¶
type Registered struct {
// URL is the public origin the share is now reachable on.
URL string `json:"url"`
// Hostname is the full hostname the relay assigned.
Hostname string `json:"hostname"`
}
Registered confirms a successful registration.
type Request ¶
type Request struct {
// Stream correlates this request with its body and response frames.
Stream uint64 `json:"stream"`
Method string `json:"method"`
// URI is the request target including the query string, exactly as the
// visitor sent it.
URI string `json:"uri"`
Header map[string][]string `json:"header,omitempty"`
// RemoteAddr is the visitor's address, forwarded for logging only.
RemoteAddr string `json:"remote_addr,omitempty"`
// HasBody tells the agent whether to expect body frames.
HasBody bool `json:"has_body"`
}
Request describes an inbound HTTP request the relay wants served.
type Response ¶
type Response struct {
Stream uint64 `json:"stream"`
Status int `json:"status"`
Header map[string][]string `json:"header,omitempty"`
}
Response carries the status line and headers of a reply.
type Type ¶
type Type string
Type identifies a control message.
const ( // TypeRegister claims a hostname. Agent to relay, first message. TypeRegister Type = "register" // TypeRegistered confirms the claim and reports the public URL. TypeRegistered Type = "registered" // TypeRequest starts a proxied request. Relay to agent. TypeRequest Type = "request" // TypeResponse returns status and headers. Agent to relay. TypeResponse Type = "response" // TypeCancel abandons a stream. Either direction. TypeCancel Type = "cancel" // TypeError reports a failure, either fatal to the connection or scoped // to one stream. TypeError Type = "error" )
Control message types.