Skip to content

Commit a019191

Browse files
committed
Merge pull request #1341 from karalabe/proto-version-negotiation
p2p: support protocol version negotiation
2 parents b9ebdff + 216fc26 commit a019191

File tree

3 files changed

+110
-6
lines changed

3 files changed

+110
-6
lines changed

p2p/peer.go

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -249,15 +249,22 @@ func countMatchingProtocols(protocols []Protocol, caps []Cap) int {
249249

250250
// matchProtocols creates structures for matching named subprotocols.
251251
func matchProtocols(protocols []Protocol, caps []Cap, rw MsgReadWriter) map[string]*protoRW {
252-
sort.Sort(capsByName(caps))
252+
sort.Sort(capsByNameAndVersion(caps))
253253
offset := baseProtocolLength
254254
result := make(map[string]*protoRW)
255+
255256
outer:
256257
for _, cap := range caps {
257258
for _, proto := range protocols {
258-
if proto.Name == cap.Name && proto.Version == cap.Version && result[cap.Name] == nil {
259+
if proto.Name == cap.Name && proto.Version == cap.Version {
260+
// If an old protocol version matched, revert it
261+
if old := result[cap.Name]; old != nil {
262+
offset -= old.Length
263+
}
264+
// Assign the new match
259265
result[cap.Name] = &protoRW{Protocol: proto, offset: offset, in: make(chan Msg), w: rw}
260266
offset += proto.Length
267+
261268
continue outer
262269
}
263270
}

p2p/peer_test.go

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,3 +196,98 @@ func TestNewPeer(t *testing.T) {
196196

197197
p.Disconnect(DiscAlreadyConnected) // Should not hang
198198
}
199+
200+
func TestMatchProtocols(t *testing.T) {
201+
tests := []struct {
202+
Remote []Cap
203+
Local []Protocol
204+
Match map[string]protoRW
205+
}{
206+
{
207+
// No remote capabilities
208+
Local: []Protocol{{Name: "a"}},
209+
},
210+
{
211+
// No local protocols
212+
Remote: []Cap{{Name: "a"}},
213+
},
214+
{
215+
// No mutual protocols
216+
Remote: []Cap{{Name: "a"}},
217+
Local: []Protocol{{Name: "b"}},
218+
},
219+
{
220+
// Some matches, some differences
221+
Remote: []Cap{{Name: "local"}, {Name: "match1"}, {Name: "match2"}},
222+
Local: []Protocol{{Name: "match1"}, {Name: "match2"}, {Name: "remote"}},
223+
Match: map[string]protoRW{"match1": {Protocol: Protocol{Name: "match1"}}, "match2": {Protocol: Protocol{Name: "match2"}}},
224+
},
225+
{
226+
// Various alphabetical ordering
227+
Remote: []Cap{{Name: "aa"}, {Name: "ab"}, {Name: "bb"}, {Name: "ba"}},
228+
Local: []Protocol{{Name: "ba"}, {Name: "bb"}, {Name: "ab"}, {Name: "aa"}},
229+
Match: map[string]protoRW{"aa": {Protocol: Protocol{Name: "aa"}}, "ab": {Protocol: Protocol{Name: "ab"}}, "ba": {Protocol: Protocol{Name: "ba"}}, "bb": {Protocol: Protocol{Name: "bb"}}},
230+
},
231+
{
232+
// No mutual versions
233+
Remote: []Cap{{Version: 1}},
234+
Local: []Protocol{{Version: 2}},
235+
},
236+
{
237+
// Multiple versions, single common
238+
Remote: []Cap{{Version: 1}, {Version: 2}},
239+
Local: []Protocol{{Version: 2}, {Version: 3}},
240+
Match: map[string]protoRW{"": {Protocol: Protocol{Version: 2}}},
241+
},
242+
{
243+
// Multiple versions, multiple common
244+
Remote: []Cap{{Version: 1}, {Version: 2}, {Version: 3}, {Version: 4}},
245+
Local: []Protocol{{Version: 2}, {Version: 3}},
246+
Match: map[string]protoRW{"": {Protocol: Protocol{Version: 3}}},
247+
},
248+
{
249+
// Various version orderings
250+
Remote: []Cap{{Version: 4}, {Version: 1}, {Version: 3}, {Version: 2}},
251+
Local: []Protocol{{Version: 2}, {Version: 3}, {Version: 1}},
252+
Match: map[string]protoRW{"": {Protocol: Protocol{Version: 3}}},
253+
},
254+
{
255+
// Versions overriding sub-protocol lengths
256+
Remote: []Cap{{Version: 1}, {Version: 2}, {Version: 3}, {Name: "a"}},
257+
Local: []Protocol{{Version: 1, Length: 1}, {Version: 2, Length: 2}, {Version: 3, Length: 3}, {Name: "a"}},
258+
Match: map[string]protoRW{"": {Protocol: Protocol{Version: 3}}, "a": {Protocol: Protocol{Name: "a"}, offset: 3}},
259+
},
260+
}
261+
262+
for i, tt := range tests {
263+
result := matchProtocols(tt.Local, tt.Remote, nil)
264+
if len(result) != len(tt.Match) {
265+
t.Errorf("test %d: negotiation mismatch: have %v, want %v", i, len(result), len(tt.Match))
266+
continue
267+
}
268+
// Make sure all negotiated protocols are needed and correct
269+
for name, proto := range result {
270+
match, ok := tt.Match[name]
271+
if !ok {
272+
t.Errorf("test %d, proto '%s': negotiated but shouldn't have", i, name)
273+
continue
274+
}
275+
if proto.Name != match.Name {
276+
t.Errorf("test %d, proto '%s': name mismatch: have %v, want %v", i, name, proto.Name, match.Name)
277+
}
278+
if proto.Version != match.Version {
279+
t.Errorf("test %d, proto '%s': version mismatch: have %v, want %v", i, name, proto.Version, match.Version)
280+
}
281+
if proto.offset-baseProtocolLength != match.offset {
282+
t.Errorf("test %d, proto '%s': offset mismatch: have %v, want %v", i, name, proto.offset-baseProtocolLength, match.offset)
283+
}
284+
}
285+
// Make sure no protocols missed negotiation
286+
for name, _ := range tt.Match {
287+
if _, ok := result[name]; !ok {
288+
t.Errorf("test %d, proto '%s': not negotiated, should have", i, name)
289+
continue
290+
}
291+
}
292+
}
293+
}

p2p/protocol.go

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,10 @@ func (cap Cap) String() string {
4343
return fmt.Sprintf("%s/%d", cap.Name, cap.Version)
4444
}
4545

46-
type capsByName []Cap
46+
type capsByNameAndVersion []Cap
4747

48-
func (cs capsByName) Len() int { return len(cs) }
49-
func (cs capsByName) Less(i, j int) bool { return cs[i].Name < cs[j].Name }
50-
func (cs capsByName) Swap(i, j int) { cs[i], cs[j] = cs[j], cs[i] }
48+
func (cs capsByNameAndVersion) Len() int { return len(cs) }
49+
func (cs capsByNameAndVersion) Swap(i, j int) { cs[i], cs[j] = cs[j], cs[i] }
50+
func (cs capsByNameAndVersion) Less(i, j int) bool {
51+
return cs[i].Name < cs[j].Name || (cs[i].Name == cs[j].Name && cs[i].Version < cs[j].Version)
52+
}

0 commit comments

Comments
 (0)