lib/connections: Refactor

1. Removes separate relay lists and relay clients/services, just makes it a listen address
2. Easier plugging-in of other transports
3. Allows "hot" disabling and enabling NAT services
4. Allows "hot" listen address changes
5. Changes listen address list with a preferable "default" value just like for discovery
6. Debounces global discovery announcements as external addresses change (which it might alot upon starting)
7. Stops this whole "pick other peers relay by latency". This information is no longer available,
   but I don't think it matters as most of the time other peer only has one relay.
8. Rename ListenAddress to ListenAddresses, as well as in javascript land.
9. Stop serializing deprecated values to JSON

GitHub-Pull-Request: https://github.com/syncthing/syncthing/pull/2982
This commit is contained in:
Audrius Butkevicius
2016-05-04 19:38:12 +00:00
committed by Jakob Borg
parent 09832abe50
commit 674fc566bb
45 changed files with 1683 additions and 1707 deletions

View File

@@ -78,8 +78,8 @@ func (m *cachingMux) Add(finder Finder, cacheTime, negCacheTime time.Duration, p
// Lookup attempts to resolve the device ID using any of the added Finders,
// while obeying the cache settings.
func (m *cachingMux) Lookup(deviceID protocol.DeviceID) (direct []string, relays []Relay, err error) {
var pdirect []prioritizedAddress
func (m *cachingMux) Lookup(deviceID protocol.DeviceID) (addresses []string, err error) {
var paddresses []prioritizedAddress
m.mut.RLock()
for i, finder := range m.finders {
@@ -90,10 +90,9 @@ func (m *cachingMux) Lookup(deviceID protocol.DeviceID) (direct []string, relays
// It's a positive, valid entry. Use it.
l.Debugln("cached discovery entry for", deviceID, "at", finder)
l.Debugln(" cache:", cacheEntry)
for _, addr := range cacheEntry.Direct {
pdirect = append(pdirect, prioritizedAddress{finder.priority, addr})
for _, addr := range cacheEntry.Addresses {
paddresses = append(paddresses, prioritizedAddress{finder.priority, addr})
}
relays = append(relays, cacheEntry.Relays...)
continue
}
@@ -109,19 +108,16 @@ func (m *cachingMux) Lookup(deviceID protocol.DeviceID) (direct []string, relays
}
// Perform the actual lookup and cache the result.
if td, tr, err := finder.Lookup(deviceID); err == nil {
if addrs, err := finder.Lookup(deviceID); err == nil {
l.Debugln("lookup for", deviceID, "at", finder)
l.Debugln(" direct:", td)
l.Debugln(" relays:", tr)
for _, addr := range td {
pdirect = append(pdirect, prioritizedAddress{finder.priority, addr})
l.Debugln(" addresses:", addrs)
for _, addr := range addrs {
paddresses = append(paddresses, prioritizedAddress{finder.priority, addr})
}
relays = append(relays, tr...)
m.caches[i].Set(deviceID, CacheEntry{
Direct: td,
Relays: tr,
when: time.Now(),
found: len(td)+len(tr) > 0,
Addresses: addrs,
when: time.Now(),
found: len(addrs) > 0,
})
} else {
// Lookup returned error, add a negative cache entry.
@@ -137,13 +133,11 @@ func (m *cachingMux) Lookup(deviceID protocol.DeviceID) (direct []string, relays
}
m.mut.RUnlock()
direct = uniqueSortedAddrs(pdirect)
relays = uniqueSortedRelays(relays)
addresses = uniqueSortedAddrs(paddresses)
l.Debugln("lookup results for", deviceID)
l.Debugln(" direct: ", direct)
l.Debugln(" relays: ", relays)
l.Debugln(" addresses: ", addresses)
return direct, relays, nil
return addresses, nil
}
func (m *cachingMux) String() string {
@@ -245,36 +239,6 @@ func uniqueSortedAddrs(ss []prioritizedAddress) []string {
return filtered
}
func uniqueSortedRelays(rs []Relay) []Relay {
m := make(map[string]Relay, len(rs))
for _, r := range rs {
m[r.URL] = r
}
var ur = make([]Relay, 0, len(m))
for _, r := range m {
ur = append(ur, r)
}
sort.Sort(relayList(ur))
return ur
}
type relayList []Relay
func (l relayList) Len() int {
return len(l)
}
func (l relayList) Swap(a, b int) {
l[a], l[b] = l[b], l[a]
}
func (l relayList) Less(a, b int) bool {
return l[a].URL < l[b].URL
}
type prioritizedAddressList []prioritizedAddress
func (l prioritizedAddressList) Len() int {

View File

@@ -15,13 +15,13 @@ import (
)
func TestCacheUnique(t *testing.T) {
direct0 := []string{"tcp://192.0.2.44:22000", "tcp://192.0.2.42:22000"} // prio 0
direct1 := []string{"tcp://192.0.2.43:22000", "tcp://192.0.2.42:22000"} // prio 1
addresses0 := []string{"tcp://192.0.2.44:22000", "tcp://192.0.2.42:22000"} // prio 0
addresses1 := []string{"tcp://192.0.2.43:22000", "tcp://192.0.2.42:22000"} // prio 1
// what we expect from just direct0
direct0Sorted := []string{"tcp://192.0.2.42:22000", "tcp://192.0.2.44:22000"}
// what we expect from just addresses0
addresses0Sorted := []string{"tcp://192.0.2.42:22000", "tcp://192.0.2.44:22000"}
// what we expect from direct0+direct1
// what we expect from addresses0+addresses1
totalSorted := []string{
// first prio 0, sorted
"tcp://192.0.2.42:22000", "tcp://192.0.2.44:22000",
@@ -30,8 +30,6 @@ func TestCacheUnique(t *testing.T) {
// no duplicate .42
}
relays := []Relay{{URL: "relay://192.0.2.44:443"}, {URL: "tcp://192.0.2.45:443"}}
c := NewCachingMux()
c.(*cachingMux).ServeBackground()
defer c.Stop()
@@ -39,45 +37,38 @@ func TestCacheUnique(t *testing.T) {
// Add a fake discovery service and verify we get it's answers through the
// cache.
f1 := &fakeDiscovery{direct0, relays}
f1 := &fakeDiscovery{addresses0}
c.Add(f1, time.Minute, 0, 0)
dir, rel, err := c.Lookup(protocol.LocalDeviceID)
addr, err := c.Lookup(protocol.LocalDeviceID)
if err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(dir, direct0Sorted) {
t.Errorf("Incorrect direct; %+v != %+v", dir, direct0Sorted)
}
if !reflect.DeepEqual(rel, relays) {
t.Errorf("Incorrect relays; %+v != %+v", rel, relays)
if !reflect.DeepEqual(addr, addresses0Sorted) {
t.Errorf("Incorrect addresses; %+v != %+v", addr, addresses0Sorted)
}
// Add one more that answers in the same way and check that we don't
// duplicate or otherwise mess up the responses now.
f2 := &fakeDiscovery{direct1, relays}
f2 := &fakeDiscovery{addresses1}
c.Add(f2, time.Minute, 0, 1)
dir, rel, err = c.Lookup(protocol.LocalDeviceID)
addr, err = c.Lookup(protocol.LocalDeviceID)
if err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(dir, totalSorted) {
t.Errorf("Incorrect direct; %+v != %+v", dir, totalSorted)
}
if !reflect.DeepEqual(rel, relays) {
t.Errorf("Incorrect relays; %+v != %+v", rel, relays)
if !reflect.DeepEqual(addr, totalSorted) {
t.Errorf("Incorrect addresses; %+v != %+v", addr, totalSorted)
}
}
type fakeDiscovery struct {
direct []string
relays []Relay
addresses []string
}
func (f *fakeDiscovery) Lookup(deviceID protocol.DeviceID) (direct []string, relays []Relay, err error) {
return f.direct, f.relays, nil
func (f *fakeDiscovery) Lookup(deviceID protocol.DeviceID) (addresses []string, err error) {
return f.addresses, nil
}
func (f *fakeDiscovery) Error() error {
@@ -126,10 +117,10 @@ type slowDiscovery struct {
started chan struct{}
}
func (f *slowDiscovery) Lookup(deviceID protocol.DeviceID) (direct []string, relays []Relay, err error) {
func (f *slowDiscovery) Lookup(deviceID protocol.DeviceID) (addresses []string, err error) {
close(f.started)
time.Sleep(f.delay)
return nil, nil, nil
return nil, nil
}
func (f *slowDiscovery) Error() error {

View File

@@ -15,15 +15,14 @@ import (
// A Finder provides lookup services of some kind.
type Finder interface {
Lookup(deviceID protocol.DeviceID) (direct []string, relays []Relay, err error)
Lookup(deviceID protocol.DeviceID) (address []string, err error)
Error() error
String() string
Cache() map[protocol.DeviceID]CacheEntry
}
type CacheEntry struct {
Direct []string `json:"direct"`
Relays []Relay `json:"relays"`
Addresses []string `json:"addresses"`
when time.Time // When did we get the result
found bool // Is it a success (cacheTime applies) or a failure (negCacheTime applies)?
validUntil time.Time // Validity time, overrides normal calculation
@@ -41,12 +40,6 @@ type FinderMux interface {
ChildStatus() map[string]error
}
// The RelayStatusProvider answers questions about current relay status.
type RelayStatusProvider interface {
Relays() []string
RelayStatus(uri string) (time.Duration, bool)
}
// The AddressLister answers questions about what addresses we are listening
// on.
type AddressLister interface {

View File

@@ -26,7 +26,6 @@ import (
type globalClient struct {
server string
addrList AddressLister
relayStat RelayStatusProvider
announceClient httpClient
queryClient httpClient
noAnnounce bool
@@ -46,8 +45,7 @@ const (
)
type announcement struct {
Direct []string `json:"direct"`
Relays []Relay `json:"relays"`
Addresses []string `json:"addresses"`
}
type serverOptions struct {
@@ -66,7 +64,7 @@ func (e lookupError) CacheFor() time.Duration {
return e.cacheFor
}
func NewGlobal(server string, cert tls.Certificate, addrList AddressLister, relayStat RelayStatusProvider) (FinderService, error) {
func NewGlobal(server string, cert tls.Certificate, addrList AddressLister) (FinderService, error) {
server, opts, err := parseOptions(server)
if err != nil {
return nil, err
@@ -117,7 +115,6 @@ func NewGlobal(server string, cert tls.Certificate, addrList AddressLister, rela
cl := &globalClient{
server: server,
addrList: addrList,
relayStat: relayStat,
announceClient: announceClient,
queryClient: queryClient,
noAnnounce: opts.noAnnounce,
@@ -128,12 +125,11 @@ func NewGlobal(server string, cert tls.Certificate, addrList AddressLister, rela
return cl, nil
}
// Lookup returns the list of addresses where the given device is available;
// direct, and via relays.
func (c *globalClient) Lookup(device protocol.DeviceID) (direct []string, relays []Relay, err error) {
// Lookup returns the list of addresses where the given device is available
func (c *globalClient) Lookup(device protocol.DeviceID) (addresses []string, err error) {
qURL, err := url.Parse(c.server)
if err != nil {
return nil, nil, err
return nil, err
}
q := qURL.Query()
@@ -143,7 +139,7 @@ func (c *globalClient) Lookup(device protocol.DeviceID) (direct []string, relays
resp, err := c.queryClient.Get(qURL.String())
if err != nil {
l.Debugln("globalClient.Lookup", qURL, err)
return nil, nil, err
return nil, err
}
if resp.StatusCode != 200 {
resp.Body.Close()
@@ -155,13 +151,13 @@ func (c *globalClient) Lookup(device protocol.DeviceID) (direct []string, relays
cacheFor: time.Duration(secs) * time.Second,
}
}
return nil, nil, err
return nil, err
}
var ann announcement
err = json.NewDecoder(resp.Body).Decode(&ann)
resp.Body.Close()
return ann.Direct, ann.Relays, err
return ann.Addresses, err
}
func (c *globalClient) String() string {
@@ -179,13 +175,15 @@ func (c *globalClient) Serve() {
timer := time.NewTimer(0)
defer timer.Stop()
eventSub := events.Default.Subscribe(events.ExternalPortMappingChanged | events.RelayStateChanged)
eventSub := events.Default.Subscribe(events.ListenAddressesChanged)
defer events.Default.Unsubscribe(eventSub)
for {
select {
case <-eventSub.C():
c.sendAnnouncement(timer)
// Defer announcement by 2 seconds, essentially debouncing
// if we have a stream of events incoming in quick succession.
timer.Reset(2 * time.Second)
case <-timer.C:
c.sendAnnouncement(timer)
@@ -200,22 +198,10 @@ func (c *globalClient) sendAnnouncement(timer *time.Timer) {
var ann announcement
if c.addrList != nil {
ann.Direct = c.addrList.ExternalAddresses()
ann.Addresses = c.addrList.ExternalAddresses()
}
if c.relayStat != nil {
for _, relay := range c.relayStat.Relays() {
latency, ok := c.relayStat.RelayStatus(relay)
if ok {
ann.Relays = append(ann.Relays, Relay{
URL: relay,
Latency: int32(latency / time.Millisecond),
})
}
}
}
if len(ann.Direct)+len(ann.Relays) == 0 {
if len(ann.Addresses) == 0 {
c.setError(errors.New("nothing to announce"))
l.Debugln("Nothing to announce")
timer.Reset(announceErrorRetryInterval)

View File

@@ -54,15 +54,15 @@ func TestGlobalOverHTTP(t *testing.T) {
// is only allowed in combination with the "insecure" and "noannounce"
// parameters.
if _, err := NewGlobal("http://192.0.2.42/", tls.Certificate{}, nil, nil); err == nil {
if _, err := NewGlobal("http://192.0.2.42/", tls.Certificate{}, nil); err == nil {
t.Fatal("http is not allowed without insecure and noannounce")
}
if _, err := NewGlobal("http://192.0.2.42/?insecure", tls.Certificate{}, nil, nil); err == nil {
if _, err := NewGlobal("http://192.0.2.42/?insecure", tls.Certificate{}, nil); err == nil {
t.Fatal("http is not allowed without noannounce")
}
if _, err := NewGlobal("http://192.0.2.42/?noannounce", tls.Certificate{}, nil, nil); err == nil {
if _, err := NewGlobal("http://192.0.2.42/?noannounce", tls.Certificate{}, nil); err == nil {
t.Fatal("http is not allowed without insecure")
}
@@ -80,30 +80,27 @@ func TestGlobalOverHTTP(t *testing.T) {
go http.Serve(list, mux)
// This should succeed
direct, relays, err := testLookup("http://" + list.Addr().String() + "?insecure&noannounce")
addresses, err := testLookup("http://" + list.Addr().String() + "?insecure&noannounce")
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if !testing.Short() {
// This should time out
_, _, err = testLookup("http://" + list.Addr().String() + "/block?insecure&noannounce")
_, err = testLookup("http://" + list.Addr().String() + "/block?insecure&noannounce")
if err == nil {
t.Fatalf("unexpected nil error, should have been a timeout")
}
}
// This should work again
_, _, err = testLookup("http://" + list.Addr().String() + "?insecure&noannounce")
_, err = testLookup("http://" + list.Addr().String() + "?insecure&noannounce")
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if len(direct) != 1 || direct[0] != "tcp://192.0.2.42::22000" {
t.Errorf("incorrect direct list: %+v", direct)
}
if len(relays) != 1 || relays[0] != (Relay{URL: "relay://192.0.2.43:443", Latency: 42}) {
t.Errorf("incorrect relays list: %+v", direct)
if len(addresses) != 1 || addresses[0] != "tcp://192.0.2.42::22000" {
t.Errorf("incorrect addresses list: %+v", addresses)
}
}
@@ -136,7 +133,7 @@ func TestGlobalOverHTTPS(t *testing.T) {
// here so we expect the lookup to fail.
url := "https://" + list.Addr().String()
if _, _, err := testLookup(url); err == nil {
if _, err := testLookup(url); err == nil {
t.Fatalf("unexpected nil error when we should have got a certificate error")
}
@@ -144,21 +141,18 @@ func TestGlobalOverHTTPS(t *testing.T) {
// be accepted.
url = "https://" + list.Addr().String() + "?insecure"
if direct, relays, err := testLookup(url); err != nil {
if addresses, err := testLookup(url); err != nil {
t.Fatalf("unexpected error: %v", err)
} else {
if len(direct) != 1 || direct[0] != "tcp://192.0.2.42::22000" {
t.Errorf("incorrect direct list: %+v", direct)
}
if len(relays) != 1 || relays[0] != (Relay{URL: "relay://192.0.2.43:443", Latency: 42}) {
t.Errorf("incorrect relays list: %+v", direct)
if len(addresses) != 1 || addresses[0] != "tcp://192.0.2.42::22000" {
t.Errorf("incorrect addresses list: %+v", addresses)
}
}
// With "id" set to something incorrect, the checks should fail again.
url = "https://" + list.Addr().String() + "?id=" + protocol.LocalDeviceID.String()
if _, _, err := testLookup(url); err == nil {
if _, err := testLookup(url); err == nil {
t.Fatalf("unexpected nil error for incorrect discovery server ID")
}
@@ -167,14 +161,11 @@ func TestGlobalOverHTTPS(t *testing.T) {
id := protocol.NewDeviceID(cert.Certificate[0])
url = "https://" + list.Addr().String() + "?id=" + id.String()
if direct, relays, err := testLookup(url); err != nil {
if addresses, err := testLookup(url); err != nil {
t.Fatalf("unexpected error: %v", err)
} else {
if len(direct) != 1 || direct[0] != "tcp://192.0.2.42::22000" {
t.Errorf("incorrect direct list: %+v", direct)
}
if len(relays) != 1 || relays[0] != (Relay{URL: "relay://192.0.2.43:443", Latency: 42}) {
t.Errorf("incorrect relays list: %+v", direct)
if len(addresses) != 1 || addresses[0] != "tcp://192.0.2.42::22000" {
t.Errorf("incorrect addresses list: %+v", addresses)
}
}
}
@@ -204,7 +195,7 @@ func TestGlobalAnnounce(t *testing.T) {
go http.Serve(list, mux)
url := "https://" + list.Addr().String() + "?insecure"
disco, err := NewGlobal(url, cert, new(fakeAddressLister), new(fakeRelayStatus))
disco, err := NewGlobal(url, cert, new(fakeAddressLister))
if err != nil {
t.Fatal(err)
}
@@ -223,17 +214,14 @@ func TestGlobalAnnounce(t *testing.T) {
}
if !strings.Contains(string(s.announce), "tcp://0.0.0.0:22000") {
t.Errorf("announce missing direct address: %s", s.announce)
}
if !strings.Contains(string(s.announce), "relay://192.0.2.42:443") {
t.Errorf("announce missing relay address: %s", s.announce)
t.Errorf("announce missing addresses address: %s", s.announce)
}
}
func testLookup(url string) ([]string, []Relay, error) {
disco, err := NewGlobal(url, tls.Certificate{}, nil, nil)
func testLookup(url string) ([]string, error) {
disco, err := NewGlobal(url, tls.Certificate{}, nil)
if err != nil {
return nil, nil, err
return nil, err
}
go disco.Serve()
defer disco.Stop()
@@ -256,7 +244,7 @@ func (s *fakeDiscoveryServer) handler(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(204)
} else {
w.Header().Set("Content-Type", "application/json")
w.Write([]byte(`{"direct":["tcp://192.0.2.42::22000"], "relays":[{"url": "relay://192.0.2.43:443", "latency": 42}]}`))
w.Write([]byte(`{"addresses":["tcp://192.0.2.42::22000"], "relays":[{"url": "relay://192.0.2.43:443", "latency": 42}]}`))
}
}
@@ -268,12 +256,3 @@ func (f *fakeAddressLister) ExternalAddresses() []string {
func (f *fakeAddressLister) AllAddresses() []string {
return []string{"tcp://0.0.0.0:22000", "tcp://192.168.0.1:22000"}
}
type fakeRelayStatus struct{}
func (f *fakeRelayStatus) Relays() []string {
return []string{"relay://192.0.2.42:443"}
}
func (f *fakeRelayStatus) RelayStatus(uri string) (time.Duration, bool) {
return 42 * time.Millisecond, true
}

View File

@@ -9,7 +9,6 @@ package discover
import (
"bytes"
"encoding/hex"
"errors"
"io"
"net"
"net/url"
@@ -24,10 +23,9 @@ import (
type localClient struct {
*suture.Supervisor
myID protocol.DeviceID
addrList AddressLister
relayStat RelayStatusProvider
name string
myID protocol.DeviceID
addrList AddressLister
name string
beacon beacon.Interface
localBcastStart time.Time
@@ -42,16 +40,11 @@ const (
CacheLifeTime = 3 * BroadcastInterval
)
var (
ErrIncorrectMagic = errors.New("incorrect magic number")
)
func NewLocal(id protocol.DeviceID, addr string, addrList AddressLister, relayStat RelayStatusProvider) (FinderService, error) {
func NewLocal(id protocol.DeviceID, addr string, addrList AddressLister) (FinderService, error) {
c := &localClient{
Supervisor: suture.NewSimple("local"),
myID: id,
addrList: addrList,
relayStat: relayStat,
localBcastTick: time.Tick(BroadcastInterval),
forcedBcastTick: make(chan time.Time),
localBcastStart: time.Now(),
@@ -94,13 +87,11 @@ func (c *localClient) startLocalIPv6Multicasts(localMCAddr string) {
go c.recvAnnouncements(c.beacon)
}
// Lookup returns a list of addresses the device is available at. Local
// discovery never returns relays.
func (c *localClient) Lookup(device protocol.DeviceID) (direct []string, relays []Relay, err error) {
// Lookup returns a list of addresses the device is available at.
func (c *localClient) Lookup(device protocol.DeviceID) (addresses []string, err error) {
if cache, ok := c.Get(device); ok {
if time.Since(cache.when) < CacheLifeTime {
direct = cache.Direct
relays = cache.Relays
addresses = cache.Addresses
}
}
@@ -123,25 +114,11 @@ func (c *localClient) announcementPkt() Announce {
})
}
var relays []Relay
if c.relayStat != nil {
for _, relay := range c.relayStat.Relays() {
latency, ok := c.relayStat.RelayStatus(relay)
if ok {
relays = append(relays, Relay{
URL: relay,
Latency: int32(latency / time.Millisecond),
})
}
}
}
return Announce{
Magic: AnnouncementMagic,
This: Device{
ID: c.myID[:],
Addresses: addrs,
Relays: relays,
},
}
}
@@ -171,6 +148,11 @@ func (c *localClient) recvAnnouncements(b beacon.Interface) {
continue
}
if pkt.Magic != AnnouncementMagic {
l.Debugf("discover: Incorrect magic from %s: %s != %s", addr, pkt.Magic, AnnouncementMagic)
continue
}
l.Debugf("discover: Received local announcement from %s for %s", addr, protocol.DeviceIDFromBytes(pkt.This.ID))
var newDevice bool
@@ -230,17 +212,15 @@ func (c *localClient) registerDevice(src net.Addr, device Device) bool {
}
c.Set(id, CacheEntry{
Direct: validAddresses,
Relays: device.Relays,
when: time.Now(),
found: true,
Addresses: validAddresses,
when: time.Now(),
found: true,
})
if isNewDevice {
events.Default.Log(events.DeviceDiscovered, map[string]interface{}{
"device": id.String(),
"addrs": validAddresses,
"relays": device.Relays,
})
}

View File

@@ -10,7 +10,7 @@
package discover
const (
AnnouncementMagic = 0x9D79BC40
AnnouncementMagic = 0x7D79BC40
)
type Announce struct {
@@ -22,14 +22,8 @@ type Announce struct {
type Device struct {
ID []byte // max:32
Addresses []Address // max:16
Relays []Relay // max:16
}
type Address struct {
URL string // max:2083
}
type Relay struct {
URL string `json:"url"` // max:2083
Latency int32 `json:"latency"`
}

View File

@@ -119,26 +119,18 @@ Device Structure:
\ Zero or more Address Structures \
/ /
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Number of Relays |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
/ /
\ Zero or more Relay Structures \
/ /
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
struct Device {
opaque ID<32>;
Address Addresses<16>;
Relay Relays<16>;
}
*/
func (o Device) XDRSize() int {
return 4 + len(o.ID) + xdr.Padding(len(o.ID)) +
4 + xdr.SizeOfSlice(o.Addresses) +
4 + xdr.SizeOfSlice(o.Relays)
4 + xdr.SizeOfSlice(o.Addresses)
}
func (o Device) MarshalXDR() ([]byte, error) {
@@ -169,15 +161,6 @@ func (o Device) MarshalXDRInto(m *xdr.Marshaller) error {
return err
}
}
if l := len(o.Relays); l > 16 {
return xdr.ElementSizeExceeded("Relays", l, 16)
}
m.MarshalUint32(uint32(len(o.Relays)))
for i := range o.Relays {
if err := o.Relays[i].MarshalXDRInto(m); err != nil {
return err
}
}
return m.Error
}
@@ -205,24 +188,6 @@ func (o *Device) UnmarshalXDRFrom(u *xdr.Unmarshaller) error {
(&o.Addresses[i]).UnmarshalXDRFrom(u)
}
}
_RelaysSize := int(u.UnmarshalUint32())
if _RelaysSize < 0 {
return xdr.ElementSizeExceeded("Relays", _RelaysSize, 16)
} else if _RelaysSize == 0 {
o.Relays = nil
} else {
if _RelaysSize > 16 {
return xdr.ElementSizeExceeded("Relays", _RelaysSize, 16)
}
if _RelaysSize <= len(o.Relays) {
o.Relays = o.Relays[:_RelaysSize]
} else {
o.Relays = make([]Relay, _RelaysSize)
}
for i := range o.Relays {
(&o.Relays[i]).UnmarshalXDRFrom(u)
}
}
return u.Error
}
@@ -279,62 +244,3 @@ func (o *Address) UnmarshalXDRFrom(u *xdr.Unmarshaller) error {
o.URL = u.UnmarshalStringMax(2083)
return u.Error
}
/*
Relay Structure:
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
/ /
\ URL (length + padded data) \
/ /
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Latency |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
struct Relay {
string URL<2083>;
int Latency;
}
*/
func (o Relay) XDRSize() int {
return 4 + len(o.URL) + xdr.Padding(len(o.URL)) + 4
}
func (o Relay) MarshalXDR() ([]byte, error) {
buf := make([]byte, o.XDRSize())
m := &xdr.Marshaller{Data: buf}
return buf, o.MarshalXDRInto(m)
}
func (o Relay) MustMarshalXDR() []byte {
bs, err := o.MarshalXDR()
if err != nil {
panic(err)
}
return bs
}
func (o Relay) MarshalXDRInto(m *xdr.Marshaller) error {
if l := len(o.URL); l > 2083 {
return xdr.ElementSizeExceeded("URL", l, 2083)
}
m.MarshalString(o.URL)
m.MarshalUint32(uint32(o.Latency))
return m.Error
}
func (o *Relay) UnmarshalXDR(bs []byte) error {
u := &xdr.Unmarshaller{Data: bs}
return o.UnmarshalXDRFrom(u)
}
func (o *Relay) UnmarshalXDRFrom(u *xdr.Unmarshaller) error {
o.URL = u.UnmarshalStringMax(2083)
o.Latency = int32(u.UnmarshalUint32())
return u.Error
}