mirror of https://github.com/grpc/grpc.git
commit
bf3c463cb4
9 changed files with 658 additions and 0 deletions
@ -0,0 +1,9 @@ |
|||||||
|
HTTP/2 Interop Tests |
||||||
|
==== |
||||||
|
|
||||||
|
This is a suite of tests that check a server to see if it plays nicely with other HTTP/2 clients. To run, just type: |
||||||
|
|
||||||
|
`go test -spec :1234` |
||||||
|
|
||||||
|
Where ":1234" is the ip:port of a running server. |
||||||
|
|
@ -0,0 +1,6 @@ |
|||||||
|
// http2interop project doc.go
|
||||||
|
|
||||||
|
/* |
||||||
|
http2interop document |
||||||
|
*/ |
||||||
|
package http2interop |
@ -0,0 +1,11 @@ |
|||||||
|
package http2interop |
||||||
|
|
||||||
|
import ( |
||||||
|
"io" |
||||||
|
) |
||||||
|
|
||||||
|
type Frame interface { |
||||||
|
GetHeader() *FrameHeader |
||||||
|
ParsePayload(io.Reader) error |
||||||
|
MarshalBinary() ([]byte, error) |
||||||
|
} |
@ -0,0 +1,109 @@ |
|||||||
|
package http2interop |
||||||
|
|
||||||
|
import ( |
||||||
|
"encoding/binary" |
||||||
|
"fmt" |
||||||
|
"io" |
||||||
|
) |
||||||
|
|
||||||
|
type FrameHeader struct { |
||||||
|
Length int |
||||||
|
Type FrameType |
||||||
|
Flags byte |
||||||
|
Reserved Reserved |
||||||
|
StreamID |
||||||
|
} |
||||||
|
|
||||||
|
type Reserved bool |
||||||
|
|
||||||
|
func (r Reserved) String() string { |
||||||
|
if r { |
||||||
|
return "R" |
||||||
|
} |
||||||
|
return "" |
||||||
|
} |
||||||
|
|
||||||
|
func (fh *FrameHeader) Parse(r io.Reader) error { |
||||||
|
buf := make([]byte, 9) |
||||||
|
if _, err := io.ReadFull(r, buf); err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
return fh.UnmarshalBinary(buf) |
||||||
|
} |
||||||
|
|
||||||
|
func (fh *FrameHeader) UnmarshalBinary(b []byte) error { |
||||||
|
if len(b) != 9 { |
||||||
|
return fmt.Errorf("Invalid frame header length %d", len(b)) |
||||||
|
} |
||||||
|
*fh = FrameHeader{ |
||||||
|
Length: int(b[0])<<16 | int(b[1])<<8 | int(b[2]), |
||||||
|
Type: FrameType(b[3]), |
||||||
|
Flags: b[4], |
||||||
|
Reserved: Reserved(b[5]>>7 == 1), |
||||||
|
StreamID: StreamID(binary.BigEndian.Uint32(b[5:9]) & 0x7fffffff), |
||||||
|
} |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
func (fh *FrameHeader) MarshalBinary() ([]byte, error) { |
||||||
|
buf := make([]byte, 9, 9+fh.Length) |
||||||
|
|
||||||
|
if fh.Length > 0xFFFFFF || fh.Length < 0 { |
||||||
|
return nil, fmt.Errorf("Invalid frame header length: %d", fh.Length) |
||||||
|
} |
||||||
|
if fh.StreamID < 0 { |
||||||
|
return nil, fmt.Errorf("Invalid Stream ID: %v", fh.StreamID) |
||||||
|
} |
||||||
|
|
||||||
|
buf[0], buf[1], buf[2] = byte(fh.Length>>16), byte(fh.Length>>8), byte(fh.Length) |
||||||
|
buf[3] = byte(fh.Type) |
||||||
|
buf[4] = fh.Flags |
||||||
|
binary.BigEndian.PutUint32(buf[5:], uint32(fh.StreamID)) |
||||||
|
|
||||||
|
return buf, nil |
||||||
|
} |
||||||
|
|
||||||
|
type StreamID int32 |
||||||
|
|
||||||
|
type FrameType byte |
||||||
|
|
||||||
|
func (ft FrameType) String() string { |
||||||
|
switch ft { |
||||||
|
case DataFrameType: |
||||||
|
return "DATA" |
||||||
|
case HeadersFrameType: |
||||||
|
return "HEADERS" |
||||||
|
case PriorityFrameType: |
||||||
|
return "PRIORITY" |
||||||
|
case ResetStreamFrameType: |
||||||
|
return "RST_STREAM" |
||||||
|
case SettingsFrameType: |
||||||
|
return "SETTINGS" |
||||||
|
case PushPromiseFrameType: |
||||||
|
return "PUSH_PROMISE" |
||||||
|
case PingFrameType: |
||||||
|
return "PING" |
||||||
|
case GoAwayFrameType: |
||||||
|
return "GOAWAY" |
||||||
|
case WindowUpdateFrameType: |
||||||
|
return "WINDOW_UPDATE" |
||||||
|
case ContinuationFrameType: |
||||||
|
return "CONTINUATION" |
||||||
|
default: |
||||||
|
return fmt.Sprintf("UNKNOWN(%d)", byte(ft)) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// Types
|
||||||
|
const ( |
||||||
|
DataFrameType FrameType = 0 |
||||||
|
HeadersFrameType FrameType = 1 |
||||||
|
PriorityFrameType FrameType = 2 |
||||||
|
ResetStreamFrameType FrameType = 3 |
||||||
|
SettingsFrameType FrameType = 4 |
||||||
|
PushPromiseFrameType FrameType = 5 |
||||||
|
PingFrameType FrameType = 6 |
||||||
|
GoAwayFrameType FrameType = 7 |
||||||
|
WindowUpdateFrameType FrameType = 8 |
||||||
|
ContinuationFrameType FrameType = 9 |
||||||
|
) |
@ -0,0 +1,245 @@ |
|||||||
|
package http2interop |
||||||
|
|
||||||
|
import ( |
||||||
|
"crypto/tls" |
||||||
|
"fmt" |
||||||
|
"io" |
||||||
|
"log" |
||||||
|
) |
||||||
|
|
||||||
|
const ( |
||||||
|
Preface = "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n" |
||||||
|
) |
||||||
|
|
||||||
|
func parseFrame(r io.Reader) (Frame, error) { |
||||||
|
fh := FrameHeader{} |
||||||
|
if err := fh.Parse(r); err != nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
var f Frame |
||||||
|
switch fh.Type { |
||||||
|
case PingFrameType: |
||||||
|
f = &PingFrame{ |
||||||
|
Header: fh, |
||||||
|
} |
||||||
|
case SettingsFrameType: |
||||||
|
f = &SettingsFrame{ |
||||||
|
Header: fh, |
||||||
|
} |
||||||
|
default: |
||||||
|
f = &UnknownFrame{ |
||||||
|
Header: fh, |
||||||
|
} |
||||||
|
} |
||||||
|
if err := f.ParsePayload(r); err != nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
|
||||||
|
return f, nil |
||||||
|
} |
||||||
|
|
||||||
|
func streamFrame(w io.Writer, f Frame) error { |
||||||
|
raw, err := f.MarshalBinary() |
||||||
|
if err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
if _, err := w.Write(raw); err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
func getHttp2Conn(addr string) (*tls.Conn, error) { |
||||||
|
config := &tls.Config{ |
||||||
|
InsecureSkipVerify: true, |
||||||
|
NextProtos: []string{"h2"}, |
||||||
|
} |
||||||
|
|
||||||
|
conn, err := tls.Dial("tcp", addr, config) |
||||||
|
if err != nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
|
||||||
|
return conn, nil |
||||||
|
} |
||||||
|
|
||||||
|
func testClientShortSettings(addr string, length int) error { |
||||||
|
c, err := getHttp2Conn(addr) |
||||||
|
if err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
defer c.Close() |
||||||
|
|
||||||
|
if _, err := c.Write([]byte(Preface)); err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
|
||||||
|
// Bad, settings, non multiple of 6
|
||||||
|
sf := &UnknownFrame{ |
||||||
|
Header: FrameHeader{ |
||||||
|
Type: SettingsFrameType, |
||||||
|
}, |
||||||
|
Data: make([]byte, length), |
||||||
|
} |
||||||
|
if err := streamFrame(c, sf); err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
|
||||||
|
for { |
||||||
|
frame, err := parseFrame(c) |
||||||
|
if err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
log.Println(frame) |
||||||
|
} |
||||||
|
|
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
func testClientPrefaceWithStreamId(addr string) error { |
||||||
|
c, err := getHttp2Conn(addr) |
||||||
|
if err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
defer c.Close() |
||||||
|
|
||||||
|
// Good so far
|
||||||
|
if _, err := c.Write([]byte(Preface)); err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
|
||||||
|
// Bad, settings do not have ids
|
||||||
|
sf := &SettingsFrame{ |
||||||
|
Header: FrameHeader{ |
||||||
|
StreamID: 1, |
||||||
|
}, |
||||||
|
} |
||||||
|
if err := streamFrame(c, sf); err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
|
||||||
|
for { |
||||||
|
frame, err := parseFrame(c) |
||||||
|
if err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
log.Println(frame) |
||||||
|
} |
||||||
|
|
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
func testUnknownFrameType(addr string) error { |
||||||
|
c, err := getHttp2Conn(addr) |
||||||
|
if err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
defer c.Close() |
||||||
|
|
||||||
|
if _, err := c.Write([]byte(Preface)); err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
|
||||||
|
// Send some settings, which are part of the client preface
|
||||||
|
sf := &SettingsFrame{} |
||||||
|
if err := streamFrame(c, sf); err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
|
||||||
|
// Write a bunch of invalid frame types.
|
||||||
|
for ft := ContinuationFrameType + 1; ft != 0; ft++ { |
||||||
|
fh := &UnknownFrame{ |
||||||
|
Header: FrameHeader{ |
||||||
|
Type: ft, |
||||||
|
}, |
||||||
|
} |
||||||
|
if err := streamFrame(c, fh); err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
pf := &PingFrame{ |
||||||
|
Data: []byte("01234567"), |
||||||
|
} |
||||||
|
if err := streamFrame(c, pf); err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
|
||||||
|
for { |
||||||
|
frame, err := parseFrame(c) |
||||||
|
if err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
if npf, ok := frame.(*PingFrame); !ok { |
||||||
|
continue |
||||||
|
} else { |
||||||
|
if string(npf.Data) != string(pf.Data) || npf.Header.Flags&PING_ACK == 0 { |
||||||
|
return fmt.Errorf("Bad ping %+v", *npf) |
||||||
|
} |
||||||
|
return nil |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
func testShortPreface(addr string, prefacePrefix string) error { |
||||||
|
c, err := getHttp2Conn(addr) |
||||||
|
if err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
defer c.Close() |
||||||
|
|
||||||
|
if _, err := c.Write([]byte(prefacePrefix)); err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
|
||||||
|
buf := make([]byte, 256) |
||||||
|
for ; err == nil; _, err = c.Read(buf) { |
||||||
|
} |
||||||
|
// TODO: maybe check for a GOAWAY?
|
||||||
|
return err |
||||||
|
} |
||||||
|
|
||||||
|
func testTLSMaxVersion(addr string, version uint16) error { |
||||||
|
config := &tls.Config{ |
||||||
|
InsecureSkipVerify: true, |
||||||
|
NextProtos: []string{"h2"}, |
||||||
|
MaxVersion: version, |
||||||
|
} |
||||||
|
conn, err := tls.Dial("tcp", addr, config) |
||||||
|
if err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
defer conn.Close() |
||||||
|
|
||||||
|
buf := make([]byte, 256) |
||||||
|
if n, err := conn.Read(buf); err != nil { |
||||||
|
if n != 0 { |
||||||
|
return fmt.Errorf("Expected no bytes to be read, but was %d", n) |
||||||
|
} |
||||||
|
return err |
||||||
|
} |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
func testTLSApplicationProtocol(addr string) error { |
||||||
|
config := &tls.Config{ |
||||||
|
InsecureSkipVerify: true, |
||||||
|
NextProtos: []string{"h2c"}, |
||||||
|
} |
||||||
|
conn, err := tls.Dial("tcp", addr, config) |
||||||
|
if err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
defer conn.Close() |
||||||
|
|
||||||
|
buf := make([]byte, 256) |
||||||
|
if n, err := conn.Read(buf); err != nil { |
||||||
|
if n != 0 { |
||||||
|
return fmt.Errorf("Expected no bytes to be read, but was %d", n) |
||||||
|
} |
||||||
|
return err |
||||||
|
} |
||||||
|
return nil |
||||||
|
} |
@ -0,0 +1,50 @@ |
|||||||
|
package http2interop |
||||||
|
|
||||||
|
import ( |
||||||
|
"crypto/tls" |
||||||
|
"flag" |
||||||
|
"io" |
||||||
|
"os" |
||||||
|
"testing" |
||||||
|
) |
||||||
|
|
||||||
|
var ( |
||||||
|
serverSpec = flag.String("spec", ":50051", "The server spec to test") |
||||||
|
) |
||||||
|
|
||||||
|
func TestShortPreface(t *testing.T) { |
||||||
|
for i := 0; i < len(Preface)-1; i++ { |
||||||
|
if err := testShortPreface(*serverSpec, Preface[:i]+"X"); err != io.EOF { |
||||||
|
t.Error("Expected an EOF but was", err) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func TestUnknownFrameType(t *testing.T) { |
||||||
|
if err := testUnknownFrameType(*serverSpec); err != nil { |
||||||
|
t.Fatal(err) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func TestTLSApplicationProtocol(t *testing.T) { |
||||||
|
if err := testTLSApplicationProtocol(*serverSpec); err != io.EOF { |
||||||
|
t.Fatal("Expected an EOF but was", err) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func TestTLSMaxVersion(t *testing.T) { |
||||||
|
if err := testTLSMaxVersion(*serverSpec, tls.VersionTLS11); err != io.EOF { |
||||||
|
t.Fatal("Expected an EOF but was", err) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func TestClientPrefaceWithStreamId(t *testing.T) { |
||||||
|
if err := testClientPrefaceWithStreamId(*serverSpec); err != io.EOF { |
||||||
|
t.Fatal("Expected an EOF but was", err) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func TestMain(m *testing.M) { |
||||||
|
flag.Parse() |
||||||
|
os.Exit(m.Run()) |
||||||
|
} |
@ -0,0 +1,65 @@ |
|||||||
|
package http2interop |
||||||
|
|
||||||
|
import ( |
||||||
|
"fmt" |
||||||
|
"io" |
||||||
|
) |
||||||
|
|
||||||
|
type PingFrame struct { |
||||||
|
Header FrameHeader |
||||||
|
Data []byte |
||||||
|
} |
||||||
|
|
||||||
|
const ( |
||||||
|
PING_ACK = 0x01 |
||||||
|
) |
||||||
|
|
||||||
|
func (f *PingFrame) GetHeader() *FrameHeader { |
||||||
|
return &f.Header |
||||||
|
} |
||||||
|
|
||||||
|
func (f *PingFrame) ParsePayload(r io.Reader) error { |
||||||
|
raw := make([]byte, f.Header.Length) |
||||||
|
if _, err := io.ReadFull(r, raw); err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
return f.UnmarshalPayload(raw) |
||||||
|
} |
||||||
|
|
||||||
|
func (f *PingFrame) UnmarshalPayload(raw []byte) error { |
||||||
|
if f.Header.Length != len(raw) { |
||||||
|
return fmt.Errorf("Invalid Payload length %d != %d", f.Header.Length, len(raw)) |
||||||
|
} |
||||||
|
if f.Header.Length != 8 { |
||||||
|
return fmt.Errorf("Invalid Payload length %d", f.Header.Length) |
||||||
|
} |
||||||
|
|
||||||
|
f.Data = []byte(string(raw)) |
||||||
|
|
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
func (f *PingFrame) MarshalPayload() ([]byte, error) { |
||||||
|
if len(f.Data) != 8 { |
||||||
|
return nil, fmt.Errorf("Invalid Payload length %d", len(f.Data)) |
||||||
|
} |
||||||
|
return []byte(string(f.Data)), nil |
||||||
|
} |
||||||
|
|
||||||
|
func (f *PingFrame) MarshalBinary() ([]byte, error) { |
||||||
|
payload, err := f.MarshalPayload() |
||||||
|
if err != nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
|
||||||
|
f.Header.Length = len(payload) |
||||||
|
f.Header.Type = PingFrameType |
||||||
|
header, err := f.Header.MarshalBinary() |
||||||
|
if err != nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
|
||||||
|
header = append(header, payload...) |
||||||
|
|
||||||
|
return header, nil |
||||||
|
} |
@ -0,0 +1,109 @@ |
|||||||
|
package http2interop |
||||||
|
|
||||||
|
import ( |
||||||
|
"encoding/binary" |
||||||
|
"fmt" |
||||||
|
"io" |
||||||
|
) |
||||||
|
|
||||||
|
const ( |
||||||
|
SETTINGS_ACK = 1 |
||||||
|
) |
||||||
|
|
||||||
|
type SettingsFrame struct { |
||||||
|
Header FrameHeader |
||||||
|
Params []SettingsParameter |
||||||
|
} |
||||||
|
|
||||||
|
type SettingsIdentifier uint16 |
||||||
|
|
||||||
|
const ( |
||||||
|
SettingsHeaderTableSize SettingsIdentifier = 1 |
||||||
|
SettingsEnablePush SettingsIdentifier = 2 |
||||||
|
SettingsMaxConcurrentStreams SettingsIdentifier = 3 |
||||||
|
SettingsInitialWindowSize SettingsIdentifier = 4 |
||||||
|
SettingsMaxFrameSize SettingsIdentifier = 5 |
||||||
|
SettingsMaxHeaderListSize SettingsIdentifier = 6 |
||||||
|
) |
||||||
|
|
||||||
|
func (si SettingsIdentifier) String() string { |
||||||
|
switch si { |
||||||
|
case SettingsHeaderTableSize: |
||||||
|
return "HEADER_TABLE_SIZE" |
||||||
|
case SettingsEnablePush: |
||||||
|
return "ENABLE_PUSH" |
||||||
|
case SettingsMaxConcurrentStreams: |
||||||
|
return "MAX_CONCURRENT_STREAMS" |
||||||
|
case SettingsInitialWindowSize: |
||||||
|
return "INITIAL_WINDOW_SIZE" |
||||||
|
case SettingsMaxFrameSize: |
||||||
|
return "MAX_FRAME_SIZE" |
||||||
|
case SettingsMaxHeaderListSize: |
||||||
|
return "MAX_HEADER_LIST_SIZE" |
||||||
|
default: |
||||||
|
return fmt.Sprintf("UNKNOWN(%d)", uint16(si)) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
type SettingsParameter struct { |
||||||
|
Identifier SettingsIdentifier |
||||||
|
Value uint32 |
||||||
|
} |
||||||
|
|
||||||
|
func (f *SettingsFrame) GetHeader() *FrameHeader { |
||||||
|
return &f.Header |
||||||
|
} |
||||||
|
|
||||||
|
func (f *SettingsFrame) ParsePayload(r io.Reader) error { |
||||||
|
raw := make([]byte, f.Header.Length) |
||||||
|
if _, err := io.ReadFull(r, raw); err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
return f.UnmarshalPayload(raw) |
||||||
|
} |
||||||
|
|
||||||
|
func (f *SettingsFrame) UnmarshalPayload(raw []byte) error { |
||||||
|
if f.Header.Length != len(raw) { |
||||||
|
return fmt.Errorf("Invalid Payload length %d != %d", f.Header.Length, len(raw)) |
||||||
|
} |
||||||
|
|
||||||
|
if f.Header.Length%6 != 0 { |
||||||
|
return fmt.Errorf("Invalid Payload length %d", f.Header.Length) |
||||||
|
} |
||||||
|
|
||||||
|
f.Params = make([]SettingsParameter, 0, f.Header.Length/6) |
||||||
|
for i := 0; i < len(raw); i += 6 { |
||||||
|
f.Params = append(f.Params, SettingsParameter{ |
||||||
|
Identifier: SettingsIdentifier(binary.BigEndian.Uint16(raw[i : i+2])), |
||||||
|
Value: binary.BigEndian.Uint32(raw[i+2 : i+6]), |
||||||
|
}) |
||||||
|
} |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
func (f *SettingsFrame) MarshalPayload() ([]byte, error) { |
||||||
|
raw := make([]byte, 0, len(f.Params)*6) |
||||||
|
for i, p := range f.Params { |
||||||
|
binary.BigEndian.PutUint16(raw[i*6:i*6+2], uint16(p.Identifier)) |
||||||
|
binary.BigEndian.PutUint32(raw[i*6+2:i*6+6], p.Value) |
||||||
|
} |
||||||
|
return raw, nil |
||||||
|
} |
||||||
|
|
||||||
|
func (f *SettingsFrame) MarshalBinary() ([]byte, error) { |
||||||
|
payload, err := f.MarshalPayload() |
||||||
|
if err != nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
|
||||||
|
f.Header.Length = len(payload) |
||||||
|
f.Header.Type = SettingsFrameType |
||||||
|
header, err := f.Header.MarshalBinary() |
||||||
|
if err != nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
|
||||||
|
header = append(header, payload...) |
||||||
|
|
||||||
|
return header, nil |
||||||
|
} |
@ -0,0 +1,54 @@ |
|||||||
|
package http2interop |
||||||
|
|
||||||
|
import ( |
||||||
|
"fmt" |
||||||
|
"io" |
||||||
|
) |
||||||
|
|
||||||
|
type UnknownFrame struct { |
||||||
|
Header FrameHeader |
||||||
|
Data []byte |
||||||
|
} |
||||||
|
|
||||||
|
func (f *UnknownFrame) GetHeader() *FrameHeader { |
||||||
|
return &f.Header |
||||||
|
} |
||||||
|
|
||||||
|
func (f *UnknownFrame) ParsePayload(r io.Reader) error { |
||||||
|
raw := make([]byte, f.Header.Length) |
||||||
|
if _, err := io.ReadFull(r, raw); err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
return f.UnmarshalPayload(raw) |
||||||
|
} |
||||||
|
|
||||||
|
func (f *UnknownFrame) UnmarshalPayload(raw []byte) error { |
||||||
|
if f.Header.Length != len(raw) { |
||||||
|
return fmt.Errorf("Invalid Payload length %d != %d", f.Header.Length, len(raw)) |
||||||
|
} |
||||||
|
|
||||||
|
f.Data = []byte(string(raw)) |
||||||
|
|
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
func (f *UnknownFrame) MarshalPayload() ([]byte, error) { |
||||||
|
return []byte(string(f.Data)), nil |
||||||
|
} |
||||||
|
|
||||||
|
func (f *UnknownFrame) MarshalBinary() ([]byte, error) { |
||||||
|
f.Header.Length = len(f.Data) |
||||||
|
buf, err := f.Header.MarshalBinary() |
||||||
|
if err != nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
|
||||||
|
payload, err := f.MarshalPayload() |
||||||
|
if err != nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
|
||||||
|
buf = append(buf, payload...) |
||||||
|
|
||||||
|
return buf, nil |
||||||
|
} |
Loading…
Reference in new issue