Files
wehub-resource-sync bf9395e022
CI / license-header (push) Has been skipped
CI / e2e-dry-run (push) Has been skipped
CI / fast-gate (push) Failing after 0s
Test PR Label Logic / test-pr-labels (push) Failing after 1s
Skill Format Check / check-format (push) Failing after 2s
CI / security (push) Failing after 5s
CI / unit-test (push) Has been skipped
CI / lint (push) Has been skipped
CI / script-test (push) Has been skipped
CI / deterministic-gate (push) Has been skipped
CI / coverage (push) Has been skipped
CI / results (push) Has been cancelled
CI / deadcode (push) Has been cancelled
CI / e2e-live (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:22:54 +08:00

107 lines
2.5 KiB
Go

// Copyright (c) 2026 Lark Technologies Pte. Ltd.
// SPDX-License-Identifier: MIT
// Package protocol defines the newline-delimited JSON wire format used over IPC.
package protocol
import (
"bufio"
"encoding/json"
"errors"
"fmt"
"io"
"net"
"time"
)
const MaxFrameBytes = 1 << 20 // reject larger frames to bound reader buffer growth
// ErrFrameTooLarge is returned by ReadFrame when a single frame exceeds MaxFrameBytes.
var ErrFrameTooLarge = errors.New("protocol: frame exceeds MaxFrameBytes")
const WriteTimeout = 5 * time.Second // bound writes against wedged peer kernel buffer
type typeEnvelope struct {
Type string `json:"type"`
}
func Encode(w io.Writer, msg interface{}) error {
data, err := json.Marshal(msg)
if err != nil {
return fmt.Errorf("protocol encode: %w", err)
}
data = append(data, '\n')
_, err = w.Write(data)
return err
}
func EncodeWithDeadline(conn net.Conn, msg interface{}, timeout time.Duration) error {
if err := conn.SetWriteDeadline(time.Now().Add(timeout)); err != nil {
return err
}
return Encode(conn, msg)
}
// ReadFrame reads one newline-delimited message; caps at MaxFrameBytes to defang slowloris.
func ReadFrame(br *bufio.Reader) ([]byte, error) {
var buf []byte
for {
chunk, err := br.ReadSlice('\n')
switch err {
case nil:
if len(buf) == 0 {
return chunk, nil
}
if len(buf)+len(chunk) > MaxFrameBytes {
return nil, ErrFrameTooLarge
}
return append(buf, chunk...), nil
case bufio.ErrBufferFull:
if len(buf)+len(chunk) > MaxFrameBytes {
return nil, ErrFrameTooLarge
}
buf = append(buf, chunk...)
default:
return nil, err
}
}
}
func Decode(line []byte) (interface{}, error) {
var env typeEnvelope
if err := json.Unmarshal(line, &env); err != nil {
return nil, fmt.Errorf("protocol decode type: %w", err)
}
var msg interface{}
switch env.Type {
case MsgTypeHello:
msg = &Hello{}
case MsgTypeHelloAck:
msg = &HelloAck{}
case MsgTypeEvent:
msg = &Event{}
case MsgTypeBye:
msg = &Bye{}
case MsgTypePreShutdownCheck:
msg = &PreShutdownCheck{}
case MsgTypePreShutdownAck:
msg = &PreShutdownAck{}
case MsgTypeStatusQuery:
msg = &StatusQuery{}
case MsgTypeStatusResponse:
msg = &StatusResponse{}
case MsgTypeShutdown:
msg = &Shutdown{}
case MsgTypeSourceStatus:
msg = &SourceStatus{}
default:
return nil, fmt.Errorf("protocol: unknown message type %q", env.Type)
}
if err := json.Unmarshal(line, msg); err != nil {
return nil, fmt.Errorf("protocol decode %s: %w", env.Type, err)
}
return msg, nil
}