)
type httpResponse struct {
- FailureReason string `bencode:"failure reason"`
- Interval int32 `bencode:"interval"`
- TrackerId string `bencode:"tracker id"`
- Complete int32 `bencode:"complete"`
- Incomplete int32 `bencode:"incomplete"`
- Peers interface{} `bencode:"peers"`
+ FailureReason string `bencode:"failure reason"`
+ Interval int32 `bencode:"interval"`
+ TrackerId string `bencode:"tracker id"`
+ Complete int32 `bencode:"complete"`
+ Incomplete int32 `bencode:"incomplete"`
+ Peers Peers `bencode:"peers"`
+ // BEP 7
+ Peers6 krpc.CompactIPv6NodeAddrs `bencode:"peers6"`
}
-func (r *httpResponse) UnmarshalPeers() (ret []Peer, err error) {
- switch v := r.Peers.(type) {
+type Peers []Peer
+
+func (me *Peers) UnmarshalBencode(b []byte) (err error) {
+ var _v interface{}
+ err = bencode.Unmarshal(b, &_v)
+ if err != nil {
+ return
+ }
+ switch v := _v.(type) {
case string:
- var cps krpc.CompactIPv4NodeAddrs
- err = cps.UnmarshalBinary([]byte(v))
+ vars.Add("http responses with string peers", 1)
+ var cnas krpc.CompactIPv4NodeAddrs
+ err = cnas.UnmarshalBinary([]byte(v))
if err != nil {
return
}
- ret = make([]Peer, 0, len(cps))
- for _, cp := range cps {
- ret = append(ret, Peer{
+ for _, cp := range cnas {
+ *me = append(*me, Peer{
IP: cp.IP[:],
Port: int(cp.Port),
})
}
return
case []interface{}:
+ vars.Add("http responses with list peers", 1)
for _, i := range v {
var p Peer
p.fromDictInterface(i.(map[string]interface{}))
- ret = append(ret, p)
+ *me = append(*me, p)
}
return
default:
- err = fmt.Errorf("unsupported peers value type: %T", r.Peers)
+ vars.Add("http responses with unhandled peers type", 1)
+ err = fmt.Errorf("unsupported type: %T", _v)
return
}
}
err = errors.New(trackerResponse.FailureReason)
return
}
+ vars.Add("successful http announces", 1)
ret.Interval = trackerResponse.Interval
ret.Leechers = trackerResponse.Incomplete
ret.Seeders = trackerResponse.Complete
- ret.Peers, err = trackerResponse.UnmarshalPeers()
+ if len(trackerResponse.Peers) != 0 {
+ vars.Add("http responses with nonempty peers key", 1)
+ }
+ ret.Peers = trackerResponse.Peers
+ if len(trackerResponse.Peers6) != 0 {
+ vars.Add("http responses with nonempty peers6 key", 1)
+ }
+ for _, na := range trackerResponse.Peers6 {
+ ret.Peers = append(ret.Peers, Peer{
+ IP: na.IP,
+ Port: na.Port,
+ })
+ }
return
}
func TestUnmarshalHTTPResponsePeerDicts(t *testing.T) {
var hr httpResponse
- require.NoError(t, bencode.Unmarshal([]byte("d5:peersl"+
- "d2:ip7:1.2.3.47:peer id20:thisisthe20bytepeeri4:porti9999ee"+
- "d7:peer id20:thisisthe20bytepeeri2:ip39:2001:0db8:85a3:0000:0000:8a2e:0370:73344:porti9998ee"+
- "ee"), &hr))
- ps, err := hr.UnmarshalPeers()
- require.NoError(t, err)
- require.Len(t, ps, 2)
- assert.Equal(t, []byte("thisisthe20bytepeeri"), ps[0].ID)
- assert.EqualValues(t, 9999, ps[0].Port)
- assert.EqualValues(t, 9998, ps[1].Port)
- assert.NotNil(t, ps[0].IP)
- assert.NotNil(t, ps[1].IP)
+ require.NoError(t, bencode.Unmarshal(
+ []byte("d5:peersl"+
+ "d2:ip7:1.2.3.47:peer id20:thisisthe20bytepeeri4:porti9999ee"+
+ "d7:peer id20:thisisthe20bytepeeri2:ip39:2001:0db8:85a3:0000:0000:8a2e:0370:73344:porti9998ee"+
+ "e"+
+ "6:peers618:123412341234123456"+
+ "e"),
+ &hr))
+
+ require.Len(t, hr.Peers, 2)
+ assert.Equal(t, []byte("thisisthe20bytepeeri"), hr.Peers[0].ID)
+ assert.EqualValues(t, 9999, hr.Peers[0].Port)
+ assert.EqualValues(t, 9998, hr.Peers[1].Port)
+ assert.NotNil(t, hr.Peers[0].IP)
+ assert.NotNil(t, hr.Peers[1].IP)
+
+ assert.Len(t, hr.Peers6, 1)
+ assert.EqualValues(t, "1234123412341234", hr.Peers6[0].IP)
+ assert.EqualValues(t, 0x3536, hr.Peers6[0].Port)
+}
+
+func TestUnmarshalHttpResponseNoPeers(t *testing.T) {
+ var hr httpResponse
+ require.NoError(t, bencode.Unmarshal(
+ []byte("d6:peers618:123412341234123456e"),
+ &hr,
+ ))
+ require.Len(t, hr.Peers, 0)
+ assert.Len(t, hr.Peers6, 1)
+}
+
+func TestUnmarshalHttpResponsePeers6NotCompact(t *testing.T) {
+ var hr httpResponse
+ require.Error(t, bencode.Unmarshal(
+ []byte("d6:peers6lee"),
+ &hr,
+ ))
}