feat(frame): go-hua/common 帧协议独立成包并修复两处缺陷
- SetData 超长时 fmt.Errorf 结果未 return,超 65535 字节静默放行 → 改为返回错误 - ReadFrame 原实现 conn.Read 直读帧头(短读错位)且一次性 bufio.Reader 会丢弃 缓冲中的后续帧字节 → 统一改 io.ReadFull 精确读取,新增连续两帧回归测试 - 补齐单元测试(往返/流水线/超长/Marshal/RawHeader)、examples/frame 例程与 README
This commit is contained in:
@@ -0,0 +1,110 @@
|
||||
package frame
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"net"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestFrameRoundtrip(t *testing.T) {
|
||||
c1, c2 := net.Pipe()
|
||||
defer c1.Close()
|
||||
defer c2.Close()
|
||||
|
||||
f := NewFrame(FrameVersion, 0x10, 42)
|
||||
if err := f.SetData([]byte("hello frame")); err != nil {
|
||||
t.Fatalf("SetData: %v", err)
|
||||
}
|
||||
|
||||
go func() {
|
||||
if err := WriteFrame(c1, f); err != nil {
|
||||
t.Errorf("WriteFrame: %v", err)
|
||||
}
|
||||
}()
|
||||
|
||||
got, err := ReadFrame(c2)
|
||||
if err != nil {
|
||||
t.Fatalf("ReadFrame: %v", err)
|
||||
}
|
||||
if got.Version() != FrameVersion || got.Cmd() != 0x10 || got.StreamID() != 42 {
|
||||
t.Errorf("帧头不符: %s", got.String())
|
||||
}
|
||||
if string(got.Data()) != "hello frame" {
|
||||
t.Errorf("Data = %q, want hello frame", got.Data())
|
||||
}
|
||||
}
|
||||
|
||||
func TestFramePipelineRead(t *testing.T) {
|
||||
// 连续两帧不丢数据(回归:旧实现用临时 bufio.Reader 会吞掉后续帧字节)
|
||||
c1, c2 := net.Pipe()
|
||||
defer c1.Close()
|
||||
defer c2.Close()
|
||||
|
||||
go func() {
|
||||
a := NewFrame(1, 1, 1)
|
||||
if err := a.SetData([]byte("first")); err != nil {
|
||||
t.Errorf("SetData: %v", err)
|
||||
}
|
||||
b := NewFrame(1, 2, 2)
|
||||
if err := b.SetData([]byte("second")); err != nil {
|
||||
t.Errorf("SetData: %v", err)
|
||||
}
|
||||
if err := WriteFrame(c1, a); err != nil {
|
||||
t.Errorf("WriteFrame: %v", err)
|
||||
}
|
||||
if err := WriteFrame(c1, b); err != nil {
|
||||
t.Errorf("WriteFrame: %v", err)
|
||||
}
|
||||
}()
|
||||
|
||||
got1, err := ReadFrame(c2)
|
||||
if err != nil || string(got1.Data()) != "first" {
|
||||
t.Fatalf("第一帧 = %q, %v", got1.Data(), err)
|
||||
}
|
||||
got2, err := ReadFrame(c2)
|
||||
if err != nil || string(got2.Data()) != "second" {
|
||||
t.Fatalf("第二帧 = %q, %v", got2.Data(), err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetDataOverflow(t *testing.T) {
|
||||
f := NewFrame(1, 0, 0)
|
||||
if err := f.SetData(make([]byte, 65536)); err == nil {
|
||||
t.Error("超过 65535 字节应返回错误")
|
||||
}
|
||||
if err := f.SetData(make([]byte, 65535)); err != nil {
|
||||
t.Errorf("65535 字节应成功: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMarshalUnmarshal(t *testing.T) {
|
||||
f := NewFrame(1, 3, 9)
|
||||
type payload struct {
|
||||
Name string
|
||||
N int
|
||||
}
|
||||
if err := f.Marshal(payload{Name: "a", N: 1}); err != nil {
|
||||
t.Fatalf("Marshal: %v", err)
|
||||
}
|
||||
var got payload
|
||||
if err := f.Unmarshal(&got); err != nil {
|
||||
t.Fatalf("Unmarshal: %v", err)
|
||||
}
|
||||
if got.Name != "a" || got.N != 1 {
|
||||
t.Errorf("Unmarshal 结果 = %+v", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRawHeader(t *testing.T) {
|
||||
h := RawHeader{}
|
||||
h[0] = 2
|
||||
h[1] = 7
|
||||
binary.LittleEndian.PutUint16(h[2:], 300)
|
||||
binary.LittleEndian.PutUint32(h[4:], 100000)
|
||||
if h.Version() != 2 || h.Cmd() != 7 || h.Length() != 300 || h.StreamID() != 100000 {
|
||||
t.Errorf("RawHeader 解析不符: %v %v %v %v", h.Version(), h.Cmd(), h.Length(), h.StreamID())
|
||||
}
|
||||
if len(h.Bytes()) != 8 {
|
||||
t.Errorf("Bytes 长度 = %d, want 8", len(h.Bytes()))
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user