A ChunkDecoder can be used to BSR Chunks. Each call to Decode will read the next chunk from the decoder's io.Reader. It handles decompressing any compressed chunk data, and verifying the CRCs of each chunk.
335 lines
7.3 KiB
Go
335 lines
7.3 KiB
Go
// Copyright (c) HashiCorp, Inc.
|
|
// SPDX-License-Identifier: MPL-2.0
|
|
|
|
package bsr_test
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/hashicorp/boundary/internal/bsr"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func TestNewHeaderChunk(t *testing.T) {
|
|
ctx := context.Background()
|
|
now := time.Now()
|
|
|
|
cases := []struct {
|
|
name string
|
|
p bsr.Protocol
|
|
d bsr.Direction
|
|
t *bsr.Timestamp
|
|
c bsr.Compression
|
|
e bsr.Encryption
|
|
sessionId string
|
|
want *bsr.HeaderChunk
|
|
wantErr error
|
|
}{
|
|
{
|
|
"valid-nocompression-noencrpytion",
|
|
bsr.Protocol("TEST"),
|
|
bsr.Inbound,
|
|
bsr.NewTimestamp(now),
|
|
bsr.NoCompression,
|
|
bsr.NoEncryption,
|
|
"sess_123456789",
|
|
&bsr.HeaderChunk{
|
|
BaseChunk: &bsr.BaseChunk{
|
|
Protocol: bsr.Protocol("TEST"),
|
|
Direction: bsr.Inbound,
|
|
Timestamp: bsr.NewTimestamp(now),
|
|
Type: bsr.ChunkHeader,
|
|
},
|
|
Compression: bsr.NoCompression,
|
|
Encryption: bsr.NoEncryption,
|
|
SessionId: "sess_123456789",
|
|
},
|
|
nil,
|
|
},
|
|
{
|
|
"valid-gzip-noencrpytion",
|
|
bsr.Protocol("TEST"),
|
|
bsr.Inbound,
|
|
bsr.NewTimestamp(now),
|
|
bsr.GzipCompression,
|
|
bsr.NoEncryption,
|
|
"sess_123456789",
|
|
&bsr.HeaderChunk{
|
|
BaseChunk: &bsr.BaseChunk{
|
|
Protocol: bsr.Protocol("TEST"),
|
|
Direction: bsr.Inbound,
|
|
Timestamp: bsr.NewTimestamp(now),
|
|
Type: bsr.ChunkHeader,
|
|
},
|
|
Compression: bsr.GzipCompression,
|
|
Encryption: bsr.NoEncryption,
|
|
SessionId: "sess_123456789",
|
|
},
|
|
nil,
|
|
},
|
|
{
|
|
"invalid-protocol",
|
|
bsr.Protocol("TEST_INVALID"),
|
|
bsr.Inbound,
|
|
bsr.NewTimestamp(now),
|
|
bsr.NoCompression,
|
|
bsr.NoEncryption,
|
|
"sess_123456789",
|
|
nil,
|
|
errors.New("bsr.NewBaseChunk: protocol name cannot be greater than 4 characters: invalid parameter"),
|
|
},
|
|
{
|
|
"invalid-direction",
|
|
bsr.Protocol("TEST"),
|
|
bsr.UnknownDirection,
|
|
bsr.NewTimestamp(now),
|
|
bsr.NoCompression,
|
|
bsr.NoEncryption,
|
|
"sess_123456789",
|
|
nil,
|
|
errors.New("bsr.NewBaseChunk: invalid direction: invalid parameter"),
|
|
},
|
|
{
|
|
"invalid-timestamp",
|
|
bsr.Protocol("TEST"),
|
|
bsr.Inbound,
|
|
nil,
|
|
bsr.NoCompression,
|
|
bsr.NoEncryption,
|
|
"sess_123456789",
|
|
nil,
|
|
errors.New("bsr.NewBaseChunk: timestamp must not be nil: invalid parameter"),
|
|
},
|
|
{
|
|
"invalid-compression",
|
|
bsr.Protocol("TEST"),
|
|
bsr.Inbound,
|
|
bsr.NewTimestamp(now),
|
|
bsr.Compression(255),
|
|
bsr.NoEncryption,
|
|
"sess_123456789",
|
|
nil,
|
|
errors.New("bsr.NewHeader: invalid compression: invalid parameter"),
|
|
},
|
|
{
|
|
"invalid-encryption",
|
|
bsr.Protocol("TEST"),
|
|
bsr.Inbound,
|
|
bsr.NewTimestamp(now),
|
|
bsr.NoCompression,
|
|
bsr.Encryption(255),
|
|
"sess_123456789",
|
|
nil,
|
|
errors.New("bsr.NewHeader: invalid encryption: invalid parameter"),
|
|
},
|
|
}
|
|
|
|
for _, tc := range cases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
got, err := bsr.NewHeader(ctx, tc.p, tc.d, tc.t, tc.c, tc.e, tc.sessionId)
|
|
if tc.wantErr != nil {
|
|
assert.EqualError(t, tc.wantErr, err.Error())
|
|
return
|
|
}
|
|
|
|
require.NoError(t, err)
|
|
assert.Equal(t, tc.want, got)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestHeaderMarshalData(t *testing.T) {
|
|
ctx := context.Background()
|
|
now := time.Now()
|
|
|
|
cases := []struct {
|
|
name string
|
|
h *bsr.HeaderChunk
|
|
want []byte
|
|
}{
|
|
{
|
|
"nocompression-noencrpytion",
|
|
&bsr.HeaderChunk{
|
|
BaseChunk: &bsr.BaseChunk{
|
|
Protocol: bsr.Protocol("TEST"),
|
|
Direction: bsr.Inbound,
|
|
Timestamp: bsr.NewTimestamp(now),
|
|
Type: bsr.ChunkHeader,
|
|
},
|
|
Compression: bsr.NoCompression,
|
|
Encryption: bsr.NoEncryption,
|
|
SessionId: "sess_123456789",
|
|
},
|
|
[]byte("\x00\x00sess_123456789"),
|
|
},
|
|
{
|
|
"gzip-noencrpytion",
|
|
&bsr.HeaderChunk{
|
|
BaseChunk: &bsr.BaseChunk{
|
|
Protocol: bsr.Protocol("TEST"),
|
|
Direction: bsr.Inbound,
|
|
Timestamp: bsr.NewTimestamp(now),
|
|
Type: bsr.ChunkHeader,
|
|
},
|
|
Compression: bsr.GzipCompression,
|
|
Encryption: bsr.NoEncryption,
|
|
SessionId: "sess_123456789",
|
|
},
|
|
[]byte("\x01\x00sess_123456789"),
|
|
},
|
|
}
|
|
for _, tc := range cases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
got, err := tc.h.MarshalData(ctx)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, tc.want, got)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestDecodeHeader(t *testing.T) {
|
|
ctx := context.Background()
|
|
|
|
ts := time.Date(2023, time.March, 16, 10, 47, 3, 14, time.UTC)
|
|
|
|
cases := []struct {
|
|
name string
|
|
bc *bsr.BaseChunk
|
|
encoded []byte
|
|
want bsr.Chunk
|
|
wantErr error
|
|
}{
|
|
{
|
|
"header-no-compression",
|
|
&bsr.BaseChunk{
|
|
Protocol: "TEST",
|
|
Direction: bsr.Inbound,
|
|
Timestamp: bsr.NewTimestamp(ts),
|
|
Type: bsr.ChunkHeader,
|
|
},
|
|
[]byte(
|
|
"" + // so everything else aligns better
|
|
"\x00" + // compression method
|
|
"\x00" + // encryption method
|
|
"sess_123456789" + // data
|
|
"",
|
|
),
|
|
&bsr.HeaderChunk{
|
|
BaseChunk: &bsr.BaseChunk{
|
|
Protocol: "TEST",
|
|
Direction: bsr.Inbound,
|
|
Timestamp: bsr.NewTimestamp(ts),
|
|
Type: bsr.ChunkHeader,
|
|
},
|
|
Compression: bsr.NoCompression,
|
|
Encryption: bsr.NoEncryption,
|
|
SessionId: "sess_123456789",
|
|
},
|
|
nil,
|
|
},
|
|
{
|
|
"header-gzip-compression",
|
|
&bsr.BaseChunk{
|
|
Protocol: "TEST",
|
|
Direction: bsr.Inbound,
|
|
Timestamp: bsr.NewTimestamp(ts),
|
|
Type: bsr.ChunkHeader,
|
|
},
|
|
[]byte(
|
|
"" + // so everything else aligns better
|
|
"\x01" + // compression method
|
|
"\x00" + // encryption method
|
|
"sess_123456789" + // data
|
|
"",
|
|
),
|
|
&bsr.HeaderChunk{
|
|
BaseChunk: &bsr.BaseChunk{
|
|
Protocol: "TEST",
|
|
Direction: bsr.Inbound,
|
|
Timestamp: bsr.NewTimestamp(ts),
|
|
Type: bsr.ChunkHeader,
|
|
},
|
|
Compression: bsr.GzipCompression,
|
|
Encryption: bsr.NoEncryption,
|
|
SessionId: "sess_123456789",
|
|
},
|
|
nil,
|
|
},
|
|
{
|
|
"header-wrong-type",
|
|
&bsr.BaseChunk{
|
|
Protocol: "TEST",
|
|
Direction: bsr.Inbound,
|
|
Timestamp: bsr.NewTimestamp(ts),
|
|
Type: "TEST",
|
|
},
|
|
[]byte(
|
|
"" + // so everything else aligns better
|
|
"\x01" + // compression method
|
|
"\x00" + // encryption method
|
|
"sess_123456789" + // data
|
|
"",
|
|
),
|
|
nil,
|
|
errors.New("bsr.DecodeHeader: invalid chunk type TEST"),
|
|
},
|
|
{
|
|
"header-nil-base-chunk",
|
|
nil,
|
|
[]byte(
|
|
"" + // so everything else aligns better
|
|
"\x01" + // compression method
|
|
"\x00" + // encryption method
|
|
"sess_123456789" + // data
|
|
"",
|
|
),
|
|
nil,
|
|
errors.New("bsr.DecodeHeader: nil base chunk: invalid parameter"),
|
|
},
|
|
{
|
|
"header-no-data",
|
|
&bsr.BaseChunk{
|
|
Protocol: "TEST",
|
|
Direction: bsr.Inbound,
|
|
Timestamp: bsr.NewTimestamp(ts),
|
|
Type: bsr.ChunkHeader,
|
|
},
|
|
nil,
|
|
nil,
|
|
errors.New("bsr.DecodeHeader: not enough data"),
|
|
},
|
|
{
|
|
"header-not-enough-data",
|
|
&bsr.BaseChunk{
|
|
Protocol: "TEST",
|
|
Direction: bsr.Inbound,
|
|
Timestamp: bsr.NewTimestamp(ts),
|
|
Type: bsr.ChunkHeader,
|
|
},
|
|
[]byte(
|
|
"" + // so everything else aligns better
|
|
"\x01" + // compression method
|
|
"",
|
|
),
|
|
nil,
|
|
errors.New("bsr.DecodeHeader: not enough data"),
|
|
},
|
|
}
|
|
|
|
for _, tc := range cases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
got, err := bsr.DecodeHeader(ctx, tc.bc, tc.encoded)
|
|
if tc.wantErr != nil {
|
|
require.EqualError(t, err, tc.wantErr.Error())
|
|
return
|
|
}
|
|
require.NoError(t, err)
|
|
assert.Equal(t, tc.want, got)
|
|
})
|
|
}
|
|
}
|