encoding: address slice alias concerns with documentation, and/or cloning

This commit is contained in:
Cassondra Foesch 2021-03-24 20:29:37 +00:00
parent c496ef994c
commit 47ea397efe
3 changed files with 219 additions and 125 deletions

View File

@ -19,8 +19,8 @@ type Buffer struct {
off int
}
// NewBuffer creates and initializes a new Buffer using buf as its initial contents.
// The new Buffer takes ownership of buf, and the caller should not use buf after this call.
// NewBuffer creates and initializes a new buffer using buf as its initial contents.
// The new buffer takes ownership of buf, and the caller should not use buf after this call.
//
// In most cases, new(Buffer) (or just declaring a Buffer variable) is sufficient to initialize a Buffer.
func NewBuffer(buf []byte) *Buffer {
@ -35,15 +35,6 @@ func NewMarshalBuffer(size int) *Buffer {
return NewBuffer(make([]byte, 4+1+4+size))
}
// StartPacket resets and initializes the Buffer to be ready to start marshaling a Packet body into.
// It truncates the buffer, reserves space for uint32(length), then appends the packetType and requestID.
func (b *Buffer) StartPacket(packetType PacketType, requestID uint32) {
b.b = append(b.b[:0], make([]byte, 4)...)
b.AppendUint8(uint8(packetType))
b.AppendUint32(requestID)
}
// Bytes returns a slice of length b.Len() holding the unconsumed bytes in the Buffer.
// The slice is valid for use only until the next buffer modification
// (that is, only until the next call to an Append or Consume method).
@ -51,15 +42,37 @@ func (b *Buffer) Bytes() []byte {
return b.b[b.off:]
}
// Packet finalizes the packet started from NewMarshalPacket.
// Len returns the number of unconsumed bytes in the buffer.
func (b *Buffer) Len() int { return len(b.b) - b.off }
// Cap returns the capacity of the buffers underlying byte slice,
// that is, the total space allocated for the buffers data.
func (b *Buffer) Cap() int { return cap(b.b) }
// Reset resets the buffer to be empty, but it retains the underlying storage for use by future Appends.
func (b *Buffer) Reset() {
b.b = b.b[:0]
b.off = 0
}
// StartPacket resets and initializes the buffer to be ready to start marshaling a packet into.
// It truncates the buffer, reserves space for uint32(length), then appends the given packetType and requestID.
func (b *Buffer) StartPacket(packetType PacketType, requestID uint32) {
b.b, b.off = append(b.b[:0], make([]byte, 4)...), 0
b.AppendUint8(uint8(packetType))
b.AppendUint32(requestID)
}
// Packet finalizes the packet started from StartPacket.
// It is expected that this will end the ownership of the underlying byte-slice,
// and so the returned byte-slices may potentially be returned to a memory pool the same as any other byte-slice.
// The caller should not use this Buffer at all after this call.
// and so the returned byte-slices may be reused the same as any other byte-slice,
// the caller should not use this buffer after this call.
//
// It writes the packet body length into the first four bytes of the Buffer in network byte order (big endian).
// The packet body length is the size of the Buffer less the 4-byte length itself, plus the length of payload.
// It writes the packet body length into the first four bytes of the buffer in network byte order (big endian).
// The packet body length is the length of this buffer less the 4-byte length itself, plus the length of payload.
//
// It is assumed that no Consume methods have been called on this Buffer,
// It is assumed that no Consume methods have been called on this buffer,
// and so it returns the whole underlying slice.
func (b *Buffer) Packet(payload []byte) (header, payloadPassThru []byte, err error) {
b.PutLength(len(b.b) - 4 + len(payload))
@ -67,15 +80,8 @@ func (b *Buffer) Packet(payload []byte) (header, payloadPassThru []byte, err err
return b.b, payload, nil
}
// Len returns the number of unconsumed bytes in the Buffer.
func (b *Buffer) Len() int { return len(b.b) - b.off }
// Cap returns the capacity of the Buffers underlying byte slice,
// that is, the total space allocated for the buffers data.
func (b *Buffer) Cap() int { return cap(b.b) }
// ConsumeUint8 consumes a single byte from the Buffer.
// If Buffer does not have enough data, it will return ErrShortPacket.
// ConsumeUint8 consumes a single byte from the buffer.
// If the buffer does not have enough data, it will return ErrShortPacket.
func (b *Buffer) ConsumeUint8() (uint8, error) {
if b.Len() < 1 {
return 0, ErrShortPacket
@ -86,13 +92,13 @@ func (b *Buffer) ConsumeUint8() (uint8, error) {
return v, nil
}
// AppendUint8 appends a single byte into the Buffer.
// AppendUint8 appends a single byte into the buffer.
func (b *Buffer) AppendUint8(v uint8) {
b.b = append(b.b, v)
}
// ConsumeBool consumes a single byte from the Buffer, and returns true if that byte is non-zero.
// If Buffer does not have enough data, it will return ErrShortPacket.
// ConsumeBool consumes a single byte from the buffer, and returns true if that byte is non-zero.
// If the buffer does not have enough data, it will return ErrShortPacket.
func (b *Buffer) ConsumeBool() (bool, error) {
v, err := b.ConsumeUint8()
if err != nil {
@ -102,7 +108,7 @@ func (b *Buffer) ConsumeBool() (bool, error) {
return v != 0, nil
}
// AppendBool appends a single bool into the Buffer.
// AppendBool appends a single bool into the buffer.
// It encodes it as a single byte, with false as 0, and true as 1.
func (b *Buffer) AppendBool(v bool) {
if v {
@ -112,8 +118,8 @@ func (b *Buffer) AppendBool(v bool) {
}
}
// ConsumeUint16 consumes a single uint16 from the Buffer, in network byte order (big-endian).
// If Buffer does not have enough data, it will return ErrShortPacket.
// ConsumeUint16 consumes a single uint16 from the buffer, in network byte order (big-endian).
// If the buffer does not have enough data, it will return ErrShortPacket.
func (b *Buffer) ConsumeUint16() (uint16, error) {
if b.Len() < 2 {
return 0, ErrShortPacket
@ -124,7 +130,7 @@ func (b *Buffer) ConsumeUint16() (uint16, error) {
return v, nil
}
// AppendUint16 appends single uint16 into the Buffer, in network byte order (big-endian).
// AppendUint16 appends single uint16 into the buffer, in network byte order (big-endian).
func (b *Buffer) AppendUint16(v uint16) {
b.b = append(b.b,
byte(v>>8),
@ -134,13 +140,13 @@ func (b *Buffer) AppendUint16(v uint16) {
// unmarshalUint32 is used internally to read the packet length.
// It is unsafe, and so not exported.
// Even in this package, its use should be avoided.
// Even within this package, its use should be avoided.
func unmarshalUint32(b []byte) uint32 {
return binary.BigEndian.Uint32(b)
return binary.BigEndian.Uint32(b[:4])
}
// ConsumeUint32 consumes a single uint32 from the Buffer, in network byte order (big-endian).
// If Buffer does not have enough data, it will return ErrShortPacket.
// ConsumeUint32 consumes a single uint32 from the buffer, in network byte order (big-endian).
// If the buffer does not have enough data, it will return ErrShortPacket.
func (b *Buffer) ConsumeUint32() (uint32, error) {
if b.Len() < 4 {
return 0, ErrShortPacket
@ -151,7 +157,7 @@ func (b *Buffer) ConsumeUint32() (uint32, error) {
return v, nil
}
// AppendUint32 appends a single uint32 into the Buffer, in network byte order (big-endian).
// AppendUint32 appends a single uint32 into the buffer, in network byte order (big-endian).
func (b *Buffer) AppendUint32(v uint32) {
b.b = append(b.b,
byte(v>>24),
@ -161,8 +167,8 @@ func (b *Buffer) AppendUint32(v uint32) {
)
}
// ConsumeUint64 consumes a single uint64 from the Buffer, in network byte order (big-endian).
// If Buffer does not have enough data, it will return ErrShortPacket.
// ConsumeUint64 consumes a single uint64 from the buffer, in network byte order (big-endian).
// If the buffer does not have enough data, it will return ErrShortPacket.
func (b *Buffer) ConsumeUint64() (uint64, error) {
if b.Len() < 8 {
return 0, ErrShortPacket
@ -173,7 +179,7 @@ func (b *Buffer) ConsumeUint64() (uint64, error) {
return v, nil
}
// AppendUint64 appends a single uint64 into the Buffer, in network byte order (big-endian).
// AppendUint64 appends a single uint64 into the buffer, in network byte order (big-endian).
func (b *Buffer) AppendUint64(v uint64) {
b.b = append(b.b,
byte(v>>56),
@ -187,8 +193,8 @@ func (b *Buffer) AppendUint64(v uint64) {
)
}
// ConsumeInt64 consumes a single int64 from the Buffer, in network byte order (big-endian) with twos complement.
// If Buffer does not have enough data, it will return ErrShortPacket.
// ConsumeInt64 consumes a single int64 from the buffer, in network byte order (big-endian) with twos complement.
// If the buffer does not have enough data, it will return ErrShortPacket.
func (b *Buffer) ConsumeInt64() (int64, error) {
u, err := b.ConsumeUint64()
if err != nil {
@ -198,14 +204,20 @@ func (b *Buffer) ConsumeInt64() (int64, error) {
return int64(u), err
}
// AppendInt64 appends a single uint64 into the Buffer, in network byte order (big-endian) with twos complement.
func (b *Buffer) AppendInt64(v uint64) {
// AppendInt64 appends a single int64 into the buffer, in network byte order (big-endian) with twos complement.
func (b *Buffer) AppendInt64(v int64) {
b.AppendUint64(uint64(v))
}
// ConsumeByteSlice consumes a single string of raw binary data from the Buffer.
// ConsumeByteSlice consumes a single string of raw binary data from the buffer.
// A string is a uint32 length, followed by that number of raw bytes.
// If Buffer does not have enough data, or defines a length larger than available, it will return ErrShortPacket.
// If the buffer does not have enough data, or defines a length larger than available, it will return ErrShortPacket.
//
// The returned slice aliases the buffer contents, and is valid only as long as the buffer is not reused
// (that is, only until the next call to Reset, PutLength, StartPacket, or UnmarshalBinary).
//
// In no case will any Consume calls return overlapping slice aliases,
// and Append calls are guaranteed to not disturb this slice alias.
func (b *Buffer) ConsumeByteSlice() ([]byte, error) {
length, err := b.ConsumeUint32()
if err != nil {
@ -224,16 +236,16 @@ func (b *Buffer) ConsumeByteSlice() ([]byte, error) {
return v, nil
}
// AppendByteSlice appends a single string of raw binary data into the Buffer.
// AppendByteSlice appends a single string of raw binary data into the buffer.
// A string is a uint32 length, followed by that number of raw bytes.
func (b *Buffer) AppendByteSlice(v []byte) {
b.AppendUint32(uint32(len(v)))
b.b = append(b.b, v...)
}
// ConsumeString consumes a single string of binary data from the Buffer.
// ConsumeString consumes a single string of binary data from the buffer.
// A string is a uint32 length, followed by that number of raw bytes.
// If Buffer does not have enough data, or defines a length larger than available, it will return ErrShortPacket.
// If the buffer does not have enough data, or defines a length larger than available, it will return ErrShortPacket.
//
// NOTE: Go implicitly assumes that strings contain UTF-8 encoded data.
// All caveats on using arbitrary binary data in Go strings applies.
@ -246,13 +258,13 @@ func (b *Buffer) ConsumeString() (string, error) {
return string(v), nil
}
// AppendString appends a single string of binary data into the Buffer.
// AppendString appends a single string of binary data into the buffer.
// A string is a uint32 length, followed by that number of raw bytes.
func (b *Buffer) AppendString(v string) {
b.AppendByteSlice([]byte(v))
}
// PutLength writes the given size into the first four bytes of the Buffer in network byte order (big endian).
// PutLength writes the given size into the first four bytes of the buffer in network byte order (big endian).
func (b *Buffer) PutLength(size int) {
if len(b.b) < 4 {
b.b = append(b.b, make([]byte, 4-len(b.b))...)
@ -261,19 +273,21 @@ func (b *Buffer) PutLength(size int) {
binary.BigEndian.PutUint32(b.b, uint32(size))
}
// MarshalBinary returns the remaining binary data in the Buffer as a byte slice.
// This aliases the internal buffer, and so comes with the same caveats as Bytes().
//
// This function is a thin wrapper of Bytes() solely to implement encoding.BinaryMarshaler.
// MarshalBinary returns a clone of the full internal buffer.
func (b *Buffer) MarshalBinary() ([]byte, error) {
return b.Bytes(), nil
clone := make([]byte, len(b.b))
n := copy(clone, b.b)
return clone[:n], nil
}
// UnmarshalBinary sets the internal buffer of b to be data, and zeros any internal offset.
// To avoid additional allocations,
// UnmarshalBinary takes ownership of buf, and the caller should not use buf after this call.
// UnmarshalBinary sets the internal buffer of b to be a clone of data, and zeros the internal offset.
func (b *Buffer) UnmarshalBinary(data []byte) error {
b.b = data
if grow := len(data) - len(b.b); grow > 0 {
b.b = append(b.b, make([]byte, grow)...)
}
n := copy(b.b, data)
b.b = b.b[:n]
b.off = 0
return nil
}

View File

@ -53,6 +53,13 @@ func newPacketFromType(typ PacketType) (Packet, error) {
// RawPacket implements the general packet format from draft-ietf-secsh-filexfer-02
//
// RawPacket is intended for use in clients receiving responses,
// where a response will be expected to be of a limited number of types,
// and unmarshaling unknown/unexpected response packets is unnecessary.
//
// For servers expecting to receive arbitrary request packet types,
// use RequestPacket.
//
// Defined in https://tools.ietf.org/html/draft-ietf-secsh-filexfer-02#section-3
type RawPacket struct {
Type PacketType
@ -61,6 +68,13 @@ type RawPacket struct {
Data Buffer
}
// Reset clears the pointers and reference-semantic variables of RawPacket,
// releasing underlying resources, and making them and the RawPacket suitable to be reused,
// so long as no other references have been kept.
func (p *RawPacket) Reset() {
p.Data = Buffer{}
}
// MarshalPacket returns p as a two-part binary encoding of p.
//
// The internal p.RequestID is overridden by the reqid argument.
@ -76,14 +90,17 @@ func (p *RawPacket) MarshalPacket(reqid uint32, b []byte) (header, payload []byt
}
// MarshalBinary returns p as the binary encoding of p.
//
// This is a convenience implementation primarily intended for tests,
// because it is inefficient with allocations.
func (p *RawPacket) MarshalBinary() ([]byte, error) {
return ComposePacket(p.MarshalPacket(p.RequestID, nil))
}
// UnmarshalFrom decodes a RawPacket from the given Buffer into p.
//
// The Data field will take ownership of the underyling byte slice of buf.
// The caller should not use buf after this call.
// The Data field will alias the passed in Buffer,
// so the buffer passed in should not be reused before RawPacket.Reset().
func (p *RawPacket) UnmarshalFrom(buf *Buffer) error {
typ, err := buf.ConsumeUint8()
if err != nil {
@ -103,16 +120,27 @@ func (p *RawPacket) UnmarshalFrom(buf *Buffer) error {
// UnmarshalBinary decodes a full raw packet out of the given data.
// It is assumed that the uint32(length) has already been consumed to receive the data.
//
// NOTE: To avoid extra allocations, UnmarshalBinary aliases the given byte slice.
// This is a convenience implementation primarily intended for tests,
// because this must clone the given data byte slice,
// as Data is not allowed to alias any part of the data byte slice.
func (p *RawPacket) UnmarshalBinary(data []byte) error {
return p.UnmarshalFrom(NewBuffer(data))
clone := make([]byte, len(data))
n := copy(clone, data)
return p.UnmarshalFrom(NewBuffer(clone[:n]))
}
// readPacket reads a uint32 length-prefixed binary data packet from r.
// If the given buffer is less than 4-bytes, it allocates a new buffer of size DefaultMaxPacketLength.
func readPacket(r io.Reader, b []byte) ([]byte, error) {
if len(b) < 4 {
b = make([]byte, DefaultMaxPacketLength)
// using the given byte slice as a backing array.
//
// If the packet length read from r is bigger than maxPacketLength,
// or greater than math.MaxInt32 on a 32-bit implementation,
// then a `ErrLongPacket` error will be returned.
//
// If the given byte slice is insufficient to hold the packet,
// then it will be extended to fill the packet size.
func readPacket(r io.Reader, b []byte, maxPacketLength uint32) ([]byte, error) {
if len(b) < 64 {
b = make([]byte, 64)
}
if _, err := io.ReadFull(r, b[:4]); err != nil {
@ -120,45 +148,65 @@ func readPacket(r io.Reader, b []byte) ([]byte, error) {
}
length := unmarshalUint32(b)
if length < 1 {
if int(length) < 5 {
if int(length) < 0 {
// Only possible when strconv.IntSize == 32,
// the packet length is longer than math.MaxInt32,
// and thus longer than any possible slice.
return nil, ErrLongPacket
}
// Must have at least uint8(type) and uint32(request-id)
return nil, ErrShortPacket
}
if length > uint32(len(b)) {
if length > maxPacketLength {
return nil, ErrLongPacket
}
if int(length) > cap(b) {
// We know int(length) must be positive, because of tests above.
b = make([]byte, length)
}
n, err := io.ReadFull(r, b[:length])
return b[:n], err
}
// ReadFrom reads a full raw packet out of the given reader.
func (p *RawPacket) ReadFrom(r io.Reader, b []byte) error {
b, err := readPacket(r, b)
// ReadFrom provides a simple functional packet reader,
// using the given byte slice as a backing array.
//
// To protect against potential denial of service attacks,
// if the read packet length is longer than maxPacketLength,
// then no packet data will be read, and ErrLongPacket will be returned.
// (On 32-bit int architectures, all packets >= 2^31 in length
// will return ErrLongPacket regardless of maxPacketLength.)
//
// If the read packet length is longer than cap(b),
// then a throw-away slice will allocated to meet the exact packet length.
// This can be used to limit the length of reused buffers,
// while still allowing reception of occassional large packets.
//
// The Data field may alias the passed in byte slice,
// so the byte slice passed in should not be reused before RawPacket.Reset().
func (p *RawPacket) ReadFrom(r io.Reader, b []byte, maxPacketLength uint32) error {
b, err := readPacket(r, b, maxPacketLength)
if err != nil {
return err
}
return p.UnmarshalBinary(b)
}
// RequestPacket decodes a full RequestPacket from the internal Data based on the Type.
// Prefer using a RequestPacket directly, rather than going indirectly through RawPacket.
func (p *RawPacket) RequestPacket() (*RequestPacket, error) {
packet, err := newPacketFromType(p.Type)
if err != nil {
return nil, err
}
packet.UnmarshalPacketBody(&p.Data)
return &RequestPacket{
RequestID: p.RequestID,
Request: packet,
}, nil
return p.UnmarshalFrom(NewBuffer(b))
}
// RequestPacket implements the general packet format from draft-ietf-secsh-filexfer-02
// but also automatically decode/encodes valid request packets (2 < type < 100 || type == 200).
//
// RequestPacket is intended for use in servers receiving requests,
// where any arbitrary request may be received, and so decoding them automatically
// is useful.
//
// For clients expecting to receive specific response packet types,
// where automatic unmarshaling of the packet body does not make sense,
// use RawPacket.
//
// Defined in https://tools.ietf.org/html/draft-ietf-secsh-filexfer-02#section-3
type RequestPacket struct {
@ -167,8 +215,9 @@ type RequestPacket struct {
Request Packet
}
// Reset clears the pointers and reference-semantic variables of RequestPacket,
// making it suitable to be put into a sync.Pool.
// Reset clears the pointers and reference-semantic variables in RequestPacket,
// releasing underlying resources, and making them and the RequestPacket suitable to be reused,
// so long as no other references have been kept.
func (p *RequestPacket) Reset() {
p.Request = nil
}
@ -185,11 +234,17 @@ func (p *RequestPacket) MarshalPacket(reqid uint32, b []byte) (header, payload [
}
// MarshalBinary returns p as the binary encoding of p.
//
// This is a convenience implementation primarily intended for tests,
// because it is inefficient with allocations.
func (p *RequestPacket) MarshalBinary() ([]byte, error) {
return ComposePacket(p.MarshalPacket(p.RequestID, nil))
}
// UnmarshalFrom decodes a RequestPacket from the given Buffer into p.
//
// The Request field may alias the passed in Buffer, (e.g. SSH_FXP_WRITE),
// so the buffer passed in should not be reused before RequestPacket.Reset().
func (p *RequestPacket) UnmarshalFrom(buf *Buffer) error {
typ, err := buf.ConsumeUint8()
if err != nil {
@ -208,20 +263,39 @@ func (p *RequestPacket) UnmarshalFrom(buf *Buffer) error {
return p.Request.UnmarshalPacketBody(buf)
}
// UnmarshalBinary decodes a full raw packet out of the given data.
// UnmarshalBinary decodes a full request packet out of the given data.
// It is assumed that the uint32(length) has already been consumed to receive the data.
//
// NOTE: To avoid extra allocations, UnmarshalBinary aliases the given byte slice.
// This is a convenience implementation primarily intended for tests,
// because this must clone the given data byte slice,
// as Request is not allowed to alias any part of the data byte slice.
func (p *RequestPacket) UnmarshalBinary(data []byte) error {
return p.UnmarshalFrom(NewBuffer(data))
clone := make([]byte, len(data))
n := copy(clone, data)
return p.UnmarshalFrom(NewBuffer(clone[:n]))
}
// ReadFrom reads a full raw packet out of the given reader.
func (p *RequestPacket) ReadFrom(r io.Reader, b []byte) error {
b, err := readPacket(r, b)
// ReadFrom provides a simple functional packet reader,
// using the given byte slice as a backing array.
//
// To protect against potential denial of service attacks,
// if the read packet length is longer than maxPacketLength,
// then no packet data will be read, and ErrLongPacket will be returned.
// (On 32-bit int architectures, all packets >= 2^31 in length
// will return ErrLongPacket regardless of maxPacketLength.)
//
// If the read packet length is longer than cap(b),
// then a throw-away slice will allocated to meet the exact packet length.
// This can be used to limit the length of reused buffers,
// while still allowing reception of occassional large packets.
//
// The Request field may alias the passed in byte slice,
// so the byte slice passed in should not be reused before RawPacket.Reset().
func (p *RequestPacket) ReadFrom(r io.Reader, b []byte, maxPacketLength uint32) error {
b, err := readPacket(r, b, maxPacketLength)
if err != nil {
return err
}
return p.UnmarshalBinary(b)
return p.UnmarshalFrom(NewBuffer(b))
}

View File

@ -7,15 +7,20 @@ import (
func TestRawPacket(t *testing.T) {
const (
id = 42
path = "foo"
id = 42
errMsg = "eof"
langTag = "en"
)
p := &RawPacket{
Type: PacketTypeStat,
Type: PacketTypeStatus,
RequestID: id,
Data: Buffer{
b: []byte{0x00, 0x00, 0x00, 0x03, 'f', 'o', 'o'},
b: []byte{
0x00, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x03, 'e', 'o', 'f',
0x00, 0x00, 0x00, 0x02, 'e', 'n',
},
},
}
@ -25,10 +30,12 @@ func TestRawPacket(t *testing.T) {
}
want := []byte{
0x00, 0x00, 0x00, 12,
17,
0x00, 0x00, 0x00, 22,
101,
0x00, 0x00, 0x00, 42,
0x00, 0x00, 0x00, 3, 'f', 'o', 'o',
0x00, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 3, 'e', 'o', 'f',
0x00, 0x00, 0x00, 2, 'e', 'n',
}
if !bytes.Equal(data, want) {
@ -37,11 +44,11 @@ func TestRawPacket(t *testing.T) {
*p = RawPacket{}
if err := p.ReadFrom(bytes.NewReader(data), nil); err != nil {
if err := p.ReadFrom(bytes.NewReader(data), nil, DefaultMaxPacketLength); err != nil {
t.Fatal("unexpected error:", err)
}
if p.Type != PacketTypeStat {
if p.Type != PacketTypeStatus {
t.Errorf("RawPacket.UnmarshalBinary(): Type was %v, but expected %v", p.Type, PacketTypeStat)
}
@ -50,29 +57,28 @@ func TestRawPacket(t *testing.T) {
}
want = []byte{
0x00, 0x00, 0x00, 3, 'f', 'o', 'o',
0x00, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 3, 'e', 'o', 'f',
0x00, 0x00, 0x00, 2, 'e', 'n',
}
if !bytes.Equal(p.Data.Bytes(), want) {
t.Errorf("RawPacket.UnmarshalBinary(): Data was %X, but expected %X", p.Data, want)
t.Fatalf("RawPacket.UnmarshalBinary(): Data was %X, but expected %X", p.Data, want)
}
rp, err := p.RequestPacket()
if err != nil {
t.Fatal("unexpected error:", err)
var resp StatusPacket
resp.UnmarshalPacketBody(&p.Data)
if resp.StatusCode != StatusEOF {
t.Errorf("UnmarshalPacketBody(RawPacket.Data): StatusCode was %v, but expected %v", resp.StatusCode, StatusEOF)
}
if rp.RequestID != uint32(id) {
t.Errorf("RawPacket.RequestPacket(): RequestID was %d, but expected %d", rp.RequestID, id)
if resp.ErrorMessage != errMsg {
t.Errorf("UnmarshalPacketBody(RawPacket.Data): ErrorMessage was %q, but expected %q", resp.ErrorMessage, errMsg)
}
req, ok := rp.Request.(*StatPacket)
if !ok {
t.Fatalf("unexpected Request type was %T, but expected %T", rp.Request, req)
}
if req.Path != path {
t.Errorf("RawPacket.RequestPacket(): Request.Path was %q, but expected %q", req.Path, path)
if resp.LanguageTag != langTag {
t.Errorf("UnmarshalPacketBody(RawPacket.Data): LanguageTag was %q, but expected %q", resp.LanguageTag, langTag)
}
}
@ -107,7 +113,7 @@ func TestRequestPacket(t *testing.T) {
*p = RequestPacket{}
if err := p.ReadFrom(bytes.NewReader(data), nil); err != nil {
if err := p.ReadFrom(bytes.NewReader(data), nil, DefaultMaxPacketLength); err != nil {
t.Fatal("unexpected error:", err)
}