github.com/jlmucb/cloudproxy@v0.0.0-20170830161738-b5aa0b619bc4/go/util/messagestream.go (about) 1 // Copyright (c) 2014, Kevin Walsh. All rights reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package util 16 17 import ( 18 "encoding/binary" 19 "errors" 20 "io" 21 "math" 22 23 "github.com/golang/glog" 24 "github.com/golang/protobuf/proto" 25 ) 26 27 // A MessageStream is an io.ReadWriteCloser that can also read and write strings 28 // and protobuf messages. Boundaries are preserved for strings and protobuf 29 // messages using a 32-bit (network byte order) length prefix before the 30 // contents of the string or marshalled protobuf message. MessageStream can also 31 // enforce an upper-limit on the size of received messages. 32 type MessageStream struct { 33 MaxMessageSize int // Negative means unlimited 34 io.ReadWriteCloser 35 } 36 37 // DefaultMaxMessageSize gives the default max for messages sent on a 38 // MessageStream. 39 const DefaultMaxMessageSize = 20 * 1024 * 1024 40 41 // ErrMessageTooLarge is the error message returned when a message larger than 42 // DefaultMaxMessageSize is sent on a MessageStream. 43 var ErrMessageTooLarge = errors.New("messagestream: message is too large") 44 45 // WriteString writes a 32-bit length followed by the string. 46 func (ms *MessageStream) WriteString(s string) (int, error) { 47 n := len(s) 48 if n > math.MaxUint32 { 49 return 0, ErrMessageTooLarge 50 } 51 var sizebytes [4]byte 52 binary.BigEndian.PutUint32(sizebytes[:], uint32(n)) 53 n, err := ms.Write(sizebytes[:]) 54 if err != nil { 55 return n, Logged(err) 56 } 57 m, err := ms.Write([]byte(s)) 58 return n + m, Logged(err) 59 } 60 61 // ReadString reads a 32-bit length followed by a string. 62 func (ms *MessageStream) ReadString() (string, error) { 63 var sizebytes [4]byte 64 _, err := io.ReadFull(ms, sizebytes[:]) 65 if err != nil { 66 return "", err 67 } 68 n := binary.BigEndian.Uint32(sizebytes[:]) 69 max := ms.MaxMessageSize 70 // We also check for int(n) to overflow so allocation below doesn't fail. 71 if int(n) < 0 || (max > 0 && int(n) > max) { 72 glog.Errorf("String on wire is too large: %d bytes\n", n) 73 return "", Logged(ErrMessageTooLarge) 74 } 75 strbytes := make([]byte, int(n)) 76 _, err = io.ReadFull(ms, strbytes) 77 if err != nil { 78 return "", Logged(err) 79 } 80 return string(strbytes), nil 81 } 82 83 // WriteMessage writes 32-bit length followed by a protobuf message. If m is 84 // nil, a blank message is written instead. 85 func (ms *MessageStream) WriteMessage(m proto.Message) (int, error) { 86 if m == nil { 87 return ms.WriteString("") 88 } 89 bytes, err := proto.Marshal(m) 90 if err != nil { 91 return 0, Logged(err) 92 } 93 return ms.WriteString(string(bytes)) 94 } 95 96 // ReadMessage reads a 32-bit length followed by a protobuf message. If m is 97 // nil, the incoming message is discarded. 98 func (ms *MessageStream) ReadMessage(m proto.Message) error { 99 s, err := ms.ReadString() 100 if err != nil { 101 return err 102 } 103 if m != nil { 104 err = proto.Unmarshal([]byte(s), m) 105 if err != nil { 106 return Logged(err) 107 } 108 } 109 return nil 110 } 111 112 // NewMessageStream creates a MessageStream for the given pipe with a reception 113 // limit of DefaultMaxMessageSize. 114 func NewMessageStream(pipe io.ReadWriteCloser) *MessageStream { 115 return &MessageStream{DefaultMaxMessageSize, pipe} 116 }