Ensure backwards compatibility before modifying protocol

This change makes sure that things work smoothly when "we" are a newer
version than our peer and have more fields in our messages than they do.
Missing fields will be left at zero/nil.

(The other side will ignore our extra fields, for the same effect.)
This commit is contained in:
Jakob Borg 2015-01-08 14:21:58 +01:00
parent 2a58ca7697
commit ce3e6e084c
3 changed files with 31 additions and 1 deletions

2
Godeps/Godeps.json generated
View File

@ -23,7 +23,7 @@
}, },
{ {
"ImportPath": "github.com/calmh/xdr", "ImportPath": "github.com/calmh/xdr",
"Rev": "45c46b7db7ff83b8b9ee09bbd95f36ab50043ece" "Rev": "214788d8fedfc310c18eca9ed12be408a5054cd5"
}, },
{ {
"ImportPath": "github.com/juju/ratelimit", "ImportPath": "github.com/juju/ratelimit",

View File

@ -154,6 +154,10 @@ func (e XDRError) Error() string {
return "xdr " + e.op + ": " + e.err.Error() return "xdr " + e.op + ": " + e.err.Error()
} }
func (e XDRError) IsEOF() bool {
return e.err == io.EOF
}
func (r *Reader) Error() error { func (r *Reader) Error() error {
if r.err == nil { if r.err == nil {
return nil return nil

View File

@ -133,6 +133,10 @@ type encodable interface {
AppendXDR([]byte) ([]byte, error) AppendXDR([]byte) ([]byte, error)
} }
type isEofer interface {
IsEOF() bool
}
const ( const (
pingTimeout = 30 * time.Second pingTimeout = 30 * time.Second
pingIdleTime = 60 * time.Second pingIdleTime = 60 * time.Second
@ -376,20 +380,36 @@ func (c *rawConnection) readMessage() (hdr header, msg encodable, err error) {
} }
} }
// We check each returned error for the XDRError.IsEOF() method.
// IsEOF()==true here means that the message contained fewer fields than
// expected. It does not signify an EOF on the socket, because we've
// successfully read a size value and that many bytes already. New fields
// we expected but the other peer didn't send should be interpreted as
// zero/nil, and if that's not valid we'll verify it somewhere else.
switch hdr.msgType { switch hdr.msgType {
case messageTypeIndex, messageTypeIndexUpdate: case messageTypeIndex, messageTypeIndexUpdate:
var idx IndexMessage var idx IndexMessage
err = idx.UnmarshalXDR(msgBuf) err = idx.UnmarshalXDR(msgBuf)
if xdrErr, ok := err.(isEofer); ok && xdrErr.IsEOF() {
err = nil
}
msg = idx msg = idx
case messageTypeRequest: case messageTypeRequest:
var req RequestMessage var req RequestMessage
err = req.UnmarshalXDR(msgBuf) err = req.UnmarshalXDR(msgBuf)
if xdrErr, ok := err.(isEofer); ok && xdrErr.IsEOF() {
err = nil
}
msg = req msg = req
case messageTypeResponse: case messageTypeResponse:
var resp ResponseMessage var resp ResponseMessage
err = resp.UnmarshalXDR(msgBuf) err = resp.UnmarshalXDR(msgBuf)
if xdrErr, ok := err.(isEofer); ok && xdrErr.IsEOF() {
err = nil
}
msg = resp msg = resp
case messageTypePing, messageTypePong: case messageTypePing, messageTypePong:
@ -398,11 +418,17 @@ func (c *rawConnection) readMessage() (hdr header, msg encodable, err error) {
case messageTypeClusterConfig: case messageTypeClusterConfig:
var cc ClusterConfigMessage var cc ClusterConfigMessage
err = cc.UnmarshalXDR(msgBuf) err = cc.UnmarshalXDR(msgBuf)
if xdrErr, ok := err.(isEofer); ok && xdrErr.IsEOF() {
err = nil
}
msg = cc msg = cc
case messageTypeClose: case messageTypeClose:
var cm CloseMessage var cm CloseMessage
err = cm.UnmarshalXDR(msgBuf) err = cm.UnmarshalXDR(msgBuf)
if xdrErr, ok := err.(isEofer); ok && xdrErr.IsEOF() {
err = nil
}
msg = cm msg = cm
default: default: