// Copyright 2015 Google Inc. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package marbl import ( "bufio" "encoding/binary" "fmt" "io" ) // Header is either an HTTP header or meta-data pertaining to the request or response. type Header struct { ID string MessageType MessageType Name string Value string } // String returns the contents of a Header frame in a format appropriate for debugging and runtime logging. func (hf Header) String() string { return fmt.Sprintf("ID=%s; Type=%d; Name=%s; Value=%s", hf.ID, hf.MessageType, hf.Name, hf.Value) } // FrameType returns HeaderFrame func (hf Header) FrameType() FrameType { return HeaderFrame } // Data is the payload (body) of the request or response. type Data struct { ID string MessageType MessageType Index uint32 Terminal bool Data []byte } // String returns the contents of a Data frame in a format appropriate for debugging and runtime logging. The // contents of the data content slice (df.Data) is not printed, instead the length of Data is printed. func (df Data) String() string { return fmt.Sprintf("ID=%s; Type=%d; Index=%d; Terminal=%t; Data length=%d", df.ID, df.MessageType, df.Index, df.Terminal, len(df.Data)) } // FrameType returns DataFrame func (df Data) FrameType() FrameType { return DataFrame } // Frame describes the interface for a frame (either Data or Header). type Frame interface { String() string FrameType() FrameType } // Reader wraps a buffered Reader that reads from the io.Reader and emits Frames. type Reader struct { r io.Reader } // NewReader returns a Reader initialized with a buffered reader. func NewReader(r io.Reader) *Reader { return &Reader{ r: bufio.NewReader(r), } } // ReadFrame reads from r, determines the FrameType, and returns either a Header or Data and an error. func (r *Reader) ReadFrame() (Frame, error) { fh := make([]byte, 10) if _, err := io.ReadFull(r.r, fh); err != nil { return nil, err } switch FrameType(fh[0]) { case HeaderFrame: hf := Header{ ID: string(fh[2:]), MessageType: MessageType(fh[1]), } lens := make([]byte, 8) if _, err := io.ReadFull(r.r, lens); err != nil { return nil, err } nl := binary.BigEndian.Uint32(lens[:4]) vl := binary.BigEndian.Uint32(lens[4:]) nv := make([]byte, int(nl+vl)) if _, err := io.ReadFull(r.r, nv); err != nil { return nil, err } hf.Name = string(nv[:nl]) hf.Value = string(nv[nl:]) return hf, nil case DataFrame: df := Data{ ID: string(fh[2:]), MessageType: MessageType(fh[1]), } // Reading 9 bytes: // 4 bytes index // 1 byte terminal // 4 bytes data length desc := make([]byte, 9) if _, err := io.ReadFull(r.r, desc); err != nil { return nil, err } df.Index = binary.BigEndian.Uint32(desc[:4]) df.Terminal = desc[4] == 1 dl := binary.BigEndian.Uint32(desc[5:]) data := make([]byte, int(dl)) if _, err := io.ReadFull(r.r, data); err != nil { return nil, err } df.Data = data return df, nil default: return nil, fmt.Errorf("marbl: unknown type of frame") } }