refactor(agent/stream): rename payload.go to header.go and update protocol header format

- Rename payload.go to header.go for clarity
- Add HostLength and PortLength fields to avoid NUL-terminated string scanning
- Add padding bytes to make struct size match headerSize
- Remove unused StreamRequestPayload struct and WriteTo method
- Add runtime size validation in init()
- Update PROTOCOL.md documentation
This commit is contained in:
yusing
2026-01-07 21:08:04 +08:00
parent 039ae26696
commit c8f617108e
2 changed files with 32 additions and 39 deletions

View File

@@ -7,15 +7,18 @@ This package implements a small header-based handshake that allows an authentica
The on-wire header is a fixed-size binary blob:
- `Version` (8 bytes)
- `HostLength` (1 byte)
- `Host` (255 bytes, NUL padded)
- `PortLength` (1 byte)
- `Port` (5 bytes, NUL padded)
- `Checksum` (4 bytes, big-endian CRC32)
- `Padding` (14 bytes)
Total: `headerSize = 8 + 255 + 5 + 4 = 272` bytes.
Total: `headerSize = 8 + 1 + 255 + 1 + 5 + 4 + 14 = 288` bytes.
Checksum is `crc32.ChecksumIEEE(header[0:headerSize-4])`.
See [`StreamRequestHeader`](payload.go:26).
See [`StreamRequestHeader`](header.go:10).
## TCP behavior

View File

@@ -1,12 +1,11 @@
package stream
import (
"bytes"
"encoding/binary"
"errors"
"fmt"
"hash/crc32"
"io"
"reflect"
"unsafe"
)
@@ -16,7 +15,7 @@ const (
portSize = 5
checksumSize = 4 // crc32 checksum
headerSize = versionSize + hostSize + portSize + checksumSize
headerSize = 288
)
var version = [versionSize]byte{'0', '.', '1', '.', '0', 0, 0, 0}
@@ -24,15 +23,23 @@ var version = [versionSize]byte{'0', '.', '1', '.', '0', 0, 0, 0}
var ErrInvalidHeader = errors.New("invalid header")
type StreamRequestHeader struct {
Version [versionSize]byte
Host [hostSize]byte
Port [portSize]byte
Version [versionSize]byte
HostLength uint8
Host [hostSize]byte
PortLength uint8
Port [portSize]byte
Checksum [checksumSize]byte
_ [14]byte // padding to make the header size match the size of the struct
}
type StreamRequestPayload struct {
StreamRequestHeader
Data []byte
func init() {
if headerSize != reflect.TypeFor[StreamRequestHeader]().Size() {
panic("headerSize does not match the size of StreamRequestHeader")
}
}
func NewStreamRequestHeader(host, port string) (*StreamRequestHeader, error) {
@@ -44,7 +51,9 @@ func NewStreamRequestHeader(host, port string) (*StreamRequestHeader, error) {
}
header := &StreamRequestHeader{}
copy(header.Version[:], version[:])
header.HostLength = uint8(len(host))
copy(header.Host[:], host)
header.PortLength = uint8(len(port))
copy(header.Port[:], port)
header.updateChecksum()
return header, nil
@@ -54,39 +63,20 @@ func ToHeader(buf [headerSize]byte) *StreamRequestHeader {
return (*StreamRequestHeader)(unsafe.Pointer(&buf[0]))
}
// WriteTo implements the io.WriterTo interface.
func (p *StreamRequestPayload) WriteTo(w io.Writer) (n int64, err error) {
n1, err := w.Write(p.StreamRequestHeader.Bytes())
if err != nil {
return
}
if len(p.Data) == 0 {
return int64(n1), nil
}
n2, err := w.Write(p.Data)
if err != nil {
return
}
return int64(n1) + int64(n2), nil
}
func (h *StreamRequestHeader) GetHostPort() (string, string) {
hostEnd := bytes.IndexByte(h.Host[:], 0)
portEnd := bytes.IndexByte(h.Port[:], 0)
if hostEnd == -1 {
hostEnd = hostSize
}
if portEnd == -1 {
portEnd = portSize
}
return string(h.Host[:hostEnd]), string(h.Port[:portEnd])
return string(h.Host[:h.HostLength]), string(h.Port[:h.PortLength])
}
func (h *StreamRequestHeader) Validate() bool {
if h.Version != version {
return false
}
if h.HostLength > hostSize {
return false
}
if h.PortLength > portSize {
return false
}
return h.validateChecksum()
}
@@ -101,9 +91,9 @@ func (h *StreamRequestHeader) validateChecksum() bool {
}
func (h *StreamRequestHeader) BytesWithoutChecksum() []byte {
return unsafe.Slice((*byte)(unsafe.Pointer(h)), headerSize-checksumSize)
return (*[headerSize - checksumSize]byte)(unsafe.Pointer(h))[:]
}
func (h *StreamRequestHeader) Bytes() []byte {
return unsafe.Slice((*byte)(unsafe.Pointer(h)), headerSize)
return (*[headerSize]byte)(unsafe.Pointer(h))[:]
}