p2p: support protocol version negotiation
This commit is contained in:
		
							parent
							
								
									b0a5be4495
								
							
						
					
					
						commit
						d84638bd31
					
				
							
								
								
									
										11
									
								
								p2p/peer.go
									
									
									
									
									
								
							
							
						
						
									
										11
									
								
								p2p/peer.go
									
									
									
									
									
								
							| @ -249,15 +249,22 @@ func countMatchingProtocols(protocols []Protocol, caps []Cap) int { | |||||||
| 
 | 
 | ||||||
| // matchProtocols creates structures for matching named subprotocols.
 | // matchProtocols creates structures for matching named subprotocols.
 | ||||||
| func matchProtocols(protocols []Protocol, caps []Cap, rw MsgReadWriter) map[string]*protoRW { | func matchProtocols(protocols []Protocol, caps []Cap, rw MsgReadWriter) map[string]*protoRW { | ||||||
| 	sort.Sort(capsByName(caps)) | 	sort.Sort(capsByNameAndVersion(caps)) | ||||||
| 	offset := baseProtocolLength | 	offset := baseProtocolLength | ||||||
| 	result := make(map[string]*protoRW) | 	result := make(map[string]*protoRW) | ||||||
|  | 
 | ||||||
| outer: | outer: | ||||||
| 	for _, cap := range caps { | 	for _, cap := range caps { | ||||||
| 		for _, proto := range protocols { | 		for _, proto := range protocols { | ||||||
| 			if proto.Name == cap.Name && proto.Version == cap.Version && result[cap.Name] == nil { | 			if proto.Name == cap.Name && proto.Version == cap.Version { | ||||||
|  | 				// If an old protocol version matched, revert it
 | ||||||
|  | 				if old := result[cap.Name]; old != nil { | ||||||
|  | 					offset -= old.Length | ||||||
|  | 				} | ||||||
|  | 				// Assign the new match
 | ||||||
| 				result[cap.Name] = &protoRW{Protocol: proto, offset: offset, in: make(chan Msg), w: rw} | 				result[cap.Name] = &protoRW{Protocol: proto, offset: offset, in: make(chan Msg), w: rw} | ||||||
| 				offset += proto.Length | 				offset += proto.Length | ||||||
|  | 
 | ||||||
| 				continue outer | 				continue outer | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
|  | |||||||
| @ -196,3 +196,98 @@ func TestNewPeer(t *testing.T) { | |||||||
| 
 | 
 | ||||||
| 	p.Disconnect(DiscAlreadyConnected) // Should not hang
 | 	p.Disconnect(DiscAlreadyConnected) // Should not hang
 | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | func TestMatchProtocols(t *testing.T) { | ||||||
|  | 	tests := []struct { | ||||||
|  | 		Local  []Cap | ||||||
|  | 		Remote []Protocol | ||||||
|  | 		Match  map[string]protoRW | ||||||
|  | 	}{ | ||||||
|  | 		{ | ||||||
|  | 			// No remote protocols
 | ||||||
|  | 			Local: []Cap{{Name: "a"}}, | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			// No local capabilities
 | ||||||
|  | 			Remote: []Protocol{{Name: "a"}}, | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			// No mutual protocols
 | ||||||
|  | 			Local:  []Cap{{Name: "a"}}, | ||||||
|  | 			Remote: []Protocol{{Name: "b"}}, | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			// Some matches, some differences
 | ||||||
|  | 			Local:  []Cap{{Name: "local"}, {Name: "match1"}, {Name: "match2"}}, | ||||||
|  | 			Remote: []Protocol{{Name: "match1"}, {Name: "match2"}, {Name: "remote"}}, | ||||||
|  | 			Match:  map[string]protoRW{"match1": {Protocol: Protocol{Name: "match1"}}, "match2": {Protocol: Protocol{Name: "match2"}}}, | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			// Various alphabetical ordering
 | ||||||
|  | 			Local:  []Cap{{Name: "aa"}, {Name: "ab"}, {Name: "bb"}, {Name: "ba"}}, | ||||||
|  | 			Remote: []Protocol{{Name: "ba"}, {Name: "bb"}, {Name: "ab"}, {Name: "aa"}}, | ||||||
|  | 			Match:  map[string]protoRW{"aa": {Protocol: Protocol{Name: "aa"}}, "ab": {Protocol: Protocol{Name: "ab"}}, "ba": {Protocol: Protocol{Name: "ba"}}, "bb": {Protocol: Protocol{Name: "bb"}}}, | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			// No mutual versions
 | ||||||
|  | 			Local:  []Cap{{Version: 1}}, | ||||||
|  | 			Remote: []Protocol{{Version: 2}}, | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			// Multiple versions, single common
 | ||||||
|  | 			Local:  []Cap{{Version: 1}, {Version: 2}}, | ||||||
|  | 			Remote: []Protocol{{Version: 2}, {Version: 3}}, | ||||||
|  | 			Match:  map[string]protoRW{"": {Protocol: Protocol{Version: 2}}}, | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			// Multiple versions, multiple common
 | ||||||
|  | 			Local:  []Cap{{Version: 1}, {Version: 2}, {Version: 3}, {Version: 4}}, | ||||||
|  | 			Remote: []Protocol{{Version: 2}, {Version: 3}}, | ||||||
|  | 			Match:  map[string]protoRW{"": {Protocol: Protocol{Version: 3}}}, | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			// Various version orderings
 | ||||||
|  | 			Local:  []Cap{{Version: 4}, {Version: 1}, {Version: 3}, {Version: 2}}, | ||||||
|  | 			Remote: []Protocol{{Version: 2}, {Version: 3}, {Version: 1}}, | ||||||
|  | 			Match:  map[string]protoRW{"": {Protocol: Protocol{Version: 3}}}, | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			// Versions overriding sub-protocol lengths
 | ||||||
|  | 			Local:  []Cap{{Version: 1}, {Version: 2}, {Version: 3}, {Name: "a"}}, | ||||||
|  | 			Remote: []Protocol{{Version: 1, Length: 1}, {Version: 2, Length: 2}, {Version: 3, Length: 3}, {Name: "a"}}, | ||||||
|  | 			Match:  map[string]protoRW{"": {Protocol: Protocol{Version: 3}}, "a": {Protocol: Protocol{Name: "a"}, offset: 3}}, | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	for i, tt := range tests { | ||||||
|  | 		result := matchProtocols(tt.Remote, tt.Local, nil) | ||||||
|  | 		if len(result) != len(tt.Match) { | ||||||
|  | 			t.Errorf("test %d: negotiation mismatch: have %v, want %v", i, len(result), len(tt.Match)) | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  | 		// Make sure all negotiated protocols are needed and correct
 | ||||||
|  | 		for name, proto := range result { | ||||||
|  | 			match, ok := tt.Match[name] | ||||||
|  | 			if !ok { | ||||||
|  | 				t.Errorf("test %d, proto '%s': negotiated but shouldn't have", i, name) | ||||||
|  | 				continue | ||||||
|  | 			} | ||||||
|  | 			if proto.Name != match.Name { | ||||||
|  | 				t.Errorf("test %d, proto '%s': name mismatch: have %v, want %v", i, name, proto.Name, match.Name) | ||||||
|  | 			} | ||||||
|  | 			if proto.Version != match.Version { | ||||||
|  | 				t.Errorf("test %d, proto '%s': version mismatch: have %v, want %v", i, name, proto.Version, match.Version) | ||||||
|  | 			} | ||||||
|  | 			if proto.offset-baseProtocolLength != match.offset { | ||||||
|  | 				t.Errorf("test %d, proto '%s': offset mismatch: have %v, want %v", i, name, proto.offset-baseProtocolLength, match.offset) | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 		// Make sure no protocols missed negotiation
 | ||||||
|  | 		for name, _ := range tt.Match { | ||||||
|  | 			if _, ok := result[name]; !ok { | ||||||
|  | 				t.Errorf("test %d, proto '%s': not negotiated, should have", i, name) | ||||||
|  | 				continue | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | |||||||
| @ -43,8 +43,10 @@ func (cap Cap) String() string { | |||||||
| 	return fmt.Sprintf("%s/%d", cap.Name, cap.Version) | 	return fmt.Sprintf("%s/%d", cap.Name, cap.Version) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| type capsByName []Cap | type capsByNameAndVersion []Cap | ||||||
| 
 | 
 | ||||||
| func (cs capsByName) Len() int           { return len(cs) } | func (cs capsByNameAndVersion) Len() int      { return len(cs) } | ||||||
| func (cs capsByName) Less(i, j int) bool { return cs[i].Name < cs[j].Name } | func (cs capsByNameAndVersion) Swap(i, j int) { cs[i], cs[j] = cs[j], cs[i] } | ||||||
| func (cs capsByName) Swap(i, j int)      { cs[i], cs[j] = cs[j], cs[i] } | func (cs capsByNameAndVersion) Less(i, j int) bool { | ||||||
|  | 	return cs[i].Name < cs[j].Name || (cs[i].Name == cs[j].Name && cs[i].Version < cs[j].Version) | ||||||
|  | } | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user