Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in / Register
Toggle navigation
Menu
Open sidebar
adam.huang
go-libp2p
Commits
51fd99e3
Commit
51fd99e3
authored
Nov 11, 2015
by
Jeromy
Browse files
extract from 0.4.0
parent
5a0162c7
Changes
125
Hide whitespace changes
Inline
Side-by-side
p2p/net/mock/ratelimiter.go
View file @
51fd99e3
package
mocknet
import
(
"sync"
"time"
)
// A ratelimiter is used by a link to determine how long to wait before sending
// data given a bandwidth cap.
type
ratelimiter
struct
{
lock
sync
.
Mutex
bandwidth
float64
// bytes per nanosecond
allowance
float64
// in bytes
maxAllowance
float64
// in bytes
...
...
@@ -29,6 +31,8 @@ func NewRatelimiter(bandwidth float64) *ratelimiter {
// Changes bandwidth of a ratelimiter and resets its allowance
func
(
r
*
ratelimiter
)
UpdateBandwidth
(
bandwidth
float64
)
{
r
.
lock
.
Lock
()
defer
r
.
lock
.
Unlock
()
// Convert bandwidth from bytes/second to bytes/nanosecond
b
:=
bandwidth
/
float64
(
time
.
Second
)
r
.
bandwidth
=
b
...
...
@@ -40,6 +44,8 @@ func (r *ratelimiter) UpdateBandwidth(bandwidth float64) {
// Returns how long to wait before sending data with length 'dataSize' bytes
func
(
r
*
ratelimiter
)
Limit
(
dataSize
int
)
time
.
Duration
{
r
.
lock
.
Lock
()
defer
r
.
lock
.
Unlock
()
// update time
var
duration
time
.
Duration
=
time
.
Duration
(
0
)
if
r
.
bandwidth
==
0
{
...
...
p2p/net/swarm/addr/addr.go
View file @
51fd99e3
...
...
@@ -3,14 +3,14 @@ package addrutil
import
(
"fmt"
logging
"
github.com/ipfs/go-ipfs/vendor/go-log-v1.0.0
"
logging
"
QmWRypnfEwrgH4k93KEHN5hng7VjKYkWmzDYRuTZeh2Mgh/go-log
"
ma
"github.com/
ipfs/go-ipfs/Godeps/_workspace/src/github.com/
jbenet/go-multiaddr"
manet
"github.com/
ipfs/go-ipfs/Godeps/_workspace/src/github.com/
jbenet/go-multiaddr-net"
context
"
github.com/ipfs/go-ipfs/Godeps/_workspace/src/
golang.org/x/net/context"
ma
"github.com/jbenet/go-multiaddr"
manet
"github.com/jbenet/go-multiaddr-net"
context
"golang.org/x/net/context"
)
var
log
=
logging
.
Logger
(
"p2p/net/swarm/addr"
)
var
log
=
logging
.
Logger
(
"
github.com/ipfs/go-libp2p/
p2p/net/swarm/addr"
)
// SupportedTransportStrings is the list of supported transports for the swarm.
// These are strings of encapsulated multiaddr protocols. E.g.:
...
...
p2p/net/swarm/addr/addr_test.go
View file @
51fd99e3
...
...
@@ -3,8 +3,8 @@ package addrutil
import
(
"testing"
ma
"github.com/
ipfs/go-ipfs/Godeps/_workspace/src/github.com/
jbenet/go-multiaddr"
manet
"github.com/
ipfs/go-ipfs/Godeps/_workspace/src/github.com/
jbenet/go-multiaddr-net"
ma
"github.com/jbenet/go-multiaddr"
manet
"github.com/jbenet/go-multiaddr-net"
)
func
newMultiaddr
(
t
*
testing
.
T
,
s
string
)
ma
.
Multiaddr
{
...
...
p2p/net/swarm/dial_test.go
View file @
51fd99e3
...
...
@@ -2,6 +2,7 @@ package swarm
import
(
"net"
"sort"
"sync"
"testing"
"time"
...
...
@@ -9,12 +10,12 @@ import (
addrutil
"github.com/ipfs/go-libp2p/p2p/net/swarm/addr"
peer
"github.com/ipfs/go-libp2p/p2p/peer"
testutil
"
github.com/ipfs/go-ipfs/
util/testutil"
ci
"
github.com/ipfs/go-ipfs/
util/testutil/ci"
testutil
"util/testutil"
ci
"util/testutil/ci"
ma
"github.com/
ipfs/go-ipfs/Godeps/_workspace/src/github.com/
jbenet/go-multiaddr"
manet
"github.com/
ipfs/go-ipfs/Godeps/_workspace/src/github.com/
jbenet/go-multiaddr-net"
context
"
github.com/ipfs/go-ipfs/Godeps/_workspace/src/
golang.org/x/net/context"
ma
"github.com/jbenet/go-multiaddr"
manet
"github.com/jbenet/go-multiaddr-net"
context
"golang.org/x/net/context"
)
func
acceptAndHang
(
l
net
.
Listener
)
{
...
...
@@ -419,18 +420,18 @@ func TestDialBackoffClears(t *testing.T) {
}
s1
.
peers
.
AddAddrs
(
s2
.
local
,
ifaceAddrs1
,
peer
.
PermanentAddrTTL
)
before
=
time
.
Now
()
if
_
,
err
:=
s1
.
Dial
(
ctx
,
s2
.
local
);
err
==
nil
{
t
.
Fatal
(
"should have failed to dial backed off peer"
)
}
time
.
Sleep
(
baseBackoffTime
)
if
c
,
err
:=
s1
.
Dial
(
ctx
,
s2
.
local
);
err
!=
nil
{
t
.
Fatal
(
err
)
}
else
{
c
.
Close
()
t
.
Log
(
"correctly connected"
)
}
duration
=
time
.
Now
()
.
Sub
(
before
)
if
duration
>=
dt
{
// t.Error("took too long", duration, dt)
}
if
s1
.
backf
.
Backoff
(
s2
.
local
)
{
t
.
Error
(
"s2 should no longer be on backoff"
)
...
...
@@ -438,3 +439,38 @@ func TestDialBackoffClears(t *testing.T) {
t
.
Log
(
"correctly cleared backoff"
)
}
}
func
mkAddr
(
t
*
testing
.
T
,
s
string
)
ma
.
Multiaddr
{
a
,
err
:=
ma
.
NewMultiaddr
(
s
)
if
err
!=
nil
{
t
.
Fatal
(
err
)
}
return
a
}
func
TestAddressSorting
(
t
*
testing
.
T
)
{
u1
:=
mkAddr
(
t
,
"/ip4/152.12.23.53/udp/1234/utp"
)
u2l
:=
mkAddr
(
t
,
"/ip4/127.0.0.1/udp/1234/utp"
)
local
:=
mkAddr
(
t
,
"/ip4/127.0.0.1/tcp/1234"
)
norm
:=
mkAddr
(
t
,
"/ip4/6.5.4.3/tcp/1234"
)
l
:=
AddrList
{
local
,
u1
,
u2l
,
norm
}
sort
.
Sort
(
l
)
if
!
l
[
0
]
.
Equal
(
u2l
)
{
t
.
Fatal
(
"expected utp local addr to be sorted first: "
,
l
[
0
])
}
if
!
l
[
1
]
.
Equal
(
u1
)
{
t
.
Fatal
(
"expected utp addr to be sorted second"
)
}
if
!
l
[
2
]
.
Equal
(
local
)
{
t
.
Fatal
(
"expected tcp localhost addr thid"
)
}
if
!
l
[
3
]
.
Equal
(
norm
)
{
t
.
Fatal
(
"expected normal addr last"
)
}
}
p2p/net/swarm/peers_test.go
View file @
51fd99e3
...
...
@@ -5,8 +5,8 @@ import (
peer
"github.com/ipfs/go-libp2p/p2p/peer"
ma
"github.com/
ipfs/go-ipfs/Godeps/_workspace/src/github.com/
jbenet/go-multiaddr"
context
"
github.com/ipfs/go-ipfs/Godeps/_workspace/src/
golang.org/x/net/context"
ma
"github.com/jbenet/go-multiaddr"
context
"golang.org/x/net/context"
)
func
TestPeers
(
t
*
testing
.
T
)
{
...
...
p2p/net/swarm/simul_test.go
View file @
51fd99e3
package
swarm
import
(
"runtime"
"sync"
"testing"
"time"
ci
"github.com/ipfs/go-ipfs/util/testutil/ci"
peer
"github.com/ipfs/go-libp2p/p2p/peer"
ci
"util/testutil/ci"
ma
"github.com/
ipfs/go-ipfs/Godeps/_workspace/src/github.com/
jbenet/go-multiaddr"
context
"
github.com/ipfs/go-ipfs/Godeps/_workspace/src/
golang.org/x/net/context"
ma
"github.com/jbenet/go-multiaddr"
context
"golang.org/x/net/context"
)
func
TestSimultOpen
(
t
*
testing
.
T
)
{
...
...
@@ -49,7 +50,8 @@ func TestSimultOpenMany(t *testing.T) {
addrs
:=
20
rounds
:=
10
if
ci
.
IsRunning
()
{
if
ci
.
IsRunning
()
||
runtime
.
GOOS
==
"darwin"
{
// osx has a limit of 256 file descriptors
addrs
=
10
rounds
=
5
}
...
...
p2p/net/swarm/swarm.go
View file @
51fd99e3
...
...
@@ -7,22 +7,26 @@ import (
"sync"
"time"
logging
"github.com/ipfs/go-ipfs/vendor/go-log-v1.0.0"
metrics
"github.com/ipfs/go-libp2p/p2p/metrics"
mconn
"github.com/ipfs/go-libp2p/p2p/metrics/conn"
inet
"github.com/ipfs/go-libp2p/p2p/net"
conn
"github.com/ipfs/go-libp2p/p2p/net/conn"
filter
"github.com/ipfs/go-libp2p/p2p/net/filter"
addrutil
"github.com/ipfs/go-libp2p/p2p/net/swarm/addr"
transport
"github.com/ipfs/go-libp2p/p2p/net/transport"
peer
"github.com/ipfs/go-libp2p/p2p/peer"
metrics
"github.com/ipfs/go-libp2p/util/metrics"
ma
"github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr"
ps
"github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-peerstream"
pst
"github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-stream-muxer"
psy
"github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-stream-muxer/yamux"
"github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess"
goprocessctx
"github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess/context"
prom
"github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus"
mafilter
"github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/whyrusleeping/multiaddr-filter"
context
"github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context"
ma
"github.com/jbenet/go-multiaddr"
ps
"github.com/jbenet/go-peerstream"
pst
"github.com/jbenet/go-stream-muxer"
psmss
"github.com/jbenet/go-stream-muxer/multistream"
"github.com/jbenet/goprocess"
goprocessctx
"github.com/jbenet/goprocess/context"
prom
"github.com/prometheus/client_golang/prometheus"
mafilter
"github.com/whyrusleeping/multiaddr-filter"
context
"golang.org/x/net/context"
logging
"QmWRypnfEwrgH4k93KEHN5hng7VjKYkWmzDYRuTZeh2Mgh/go-log"
)
var
log
=
logging
.
Logger
(
"swarm2"
)
...
...
@@ -37,9 +41,7 @@ var peersTotal = prom.NewGaugeVec(prom.GaugeOpts{
},
[]
string
{
"peer_id"
})
func
init
()
{
tpt
:=
*
psy
.
DefaultTransport
tpt
.
MaxStreamWindowSize
=
512
*
1024
PSTransport
=
&
tpt
PSTransport
=
psmss
.
NewTransport
()
}
// Swarm is a connection muxer, allowing connections to other peers to
...
...
@@ -58,12 +60,19 @@ type Swarm struct {
backf
dialbackoff
dialT
time
.
Duration
// mainly for tests
dialer
*
conn
.
Dialer
notifmu
sync
.
RWMutex
notifs
map
[
inet
.
Notifiee
]
ps
.
Notifiee
transports
[]
transport
.
Transport
// filters for addresses that shouldnt be dialed
Filters
*
filter
.
Filters
// file descriptor rate limited
fdRateLimit
chan
struct
{}
proc
goprocess
.
Process
ctx
context
.
Context
bwc
metrics
.
Reporter
...
...
@@ -78,15 +87,22 @@ func NewSwarm(ctx context.Context, listenAddrs []ma.Multiaddr,
return
nil
,
err
}
wrap
:=
func
(
c
transport
.
Conn
)
transport
.
Conn
{
return
mconn
.
WrapConn
(
bwc
,
c
)
}
s
:=
&
Swarm
{
swarm
:
ps
.
NewSwarm
(
PSTransport
),
local
:
local
,
peers
:
peers
,
ctx
:
ctx
,
dialT
:
DialTimeout
,
notifs
:
make
(
map
[
inet
.
Notifiee
]
ps
.
Notifiee
),
bwc
:
bwc
,
Filters
:
filter
.
NewFilters
(),
swarm
:
ps
.
NewSwarm
(
PSTransport
),
local
:
local
,
peers
:
peers
,
ctx
:
ctx
,
dialT
:
DialTimeout
,
notifs
:
make
(
map
[
inet
.
Notifiee
]
ps
.
Notifiee
),
transports
:
[]
transport
.
Transport
{
transport
.
NewTCPTransport
()},
bwc
:
bwc
,
fdRateLimit
:
make
(
chan
struct
{},
concurrentFdDials
),
Filters
:
filter
.
NewFilters
(),
dialer
:
conn
.
NewDialer
(
local
,
peers
.
PrivKey
(
local
),
wrap
),
}
// configure Swarm
...
...
@@ -97,7 +113,12 @@ func NewSwarm(ctx context.Context, listenAddrs []ma.Multiaddr,
prom
.
MustRegisterOrGet
(
peersTotal
)
s
.
Notify
((
*
metricsNotifiee
)(
s
))
return
s
,
s
.
listen
(
listenAddrs
)
err
=
s
.
setupInterfaces
(
listenAddrs
)
if
err
!=
nil
{
return
nil
,
err
}
return
s
,
nil
}
func
(
s
*
Swarm
)
teardown
()
error
{
...
...
@@ -130,7 +151,7 @@ func (s *Swarm) Listen(addrs ...ma.Multiaddr) error {
return
err
}
return
s
.
listen
(
addrs
)
return
s
.
setupInterfaces
(
addrs
)
}
// Process returns the Process of the swarm
...
...
p2p/net/swarm/swarm_addr.go
View file @
51fd99e3
...
...
@@ -4,7 +4,7 @@ import (
conn
"github.com/ipfs/go-libp2p/p2p/net/conn"
addrutil
"github.com/ipfs/go-libp2p/p2p/net/swarm/addr"
ma
"github.com/
ipfs/go-ipfs/Godeps/_workspace/src/github.com/
jbenet/go-multiaddr"
ma
"github.com/jbenet/go-multiaddr"
)
// ListenAddresses returns a list of addresses at which this swarm listens.
...
...
p2p/net/swarm/swarm_addr_test.go
View file @
51fd99e3
...
...
@@ -3,13 +3,13 @@ package swarm
import
(
"testing"
testutil
"github.com/ipfs/go-
ipfs/util/testutil
"
metrics
"github.com/ipfs/go-
libp2p/p2p/metrics
"
addrutil
"github.com/ipfs/go-libp2p/p2p/net/swarm/addr"
peer
"github.com/ipfs/go-libp2p/p2p/peer"
metrics
"github.com/ipfs/go-libp2p/util/metrics
"
testutil
"util/testutil
"
ma
"github.com/
ipfs/go-ipfs/Godeps/_workspace/src/github.com/
jbenet/go-multiaddr"
context
"
github.com/ipfs/go-ipfs/Godeps/_workspace/src/
golang.org/x/net/context"
ma
"github.com/jbenet/go-multiaddr"
context
"golang.org/x/net/context"
)
func
TestFilterAddrs
(
t
*
testing
.
T
)
{
...
...
p2p/net/swarm/swarm_conn.go
View file @
51fd99e3
...
...
@@ -8,9 +8,9 @@ import (
conn
"github.com/ipfs/go-libp2p/p2p/net/conn"
peer
"github.com/ipfs/go-libp2p/p2p/peer"
ma
"github.com/
ipfs/go-ipfs/Godeps/_workspace/src/github.com/
jbenet/go-multiaddr"
ps
"github.com/
ipfs/go-ipfs/Godeps/_workspace/src/github.com/
jbenet/go-peerstream"
context
"
github.com/ipfs/go-ipfs/Godeps/_workspace/src/
golang.org/x/net/context"
ma
"github.com/jbenet/go-multiaddr"
ps
"github.com/jbenet/go-peerstream"
context
"golang.org/x/net/context"
)
// a Conn is a simple wrapper around a ps.Conn that also exposes
...
...
p2p/net/swarm/swarm_dial.go
View file @
51fd99e3
package
swarm
import
(
"bytes"
"errors"
"fmt"
"math/rand"
"net"
"sort"
"sync"
"time"
"github.com/jbenet/go-multiaddr-net"
conn
"github.com/ipfs/go-libp2p/p2p/net/conn"
addrutil
"github.com/ipfs/go-libp2p/p2p/net/swarm/addr"
peer
"github.com/ipfs/go-libp2p/p2p/peer"
lgbl
"github.com/ipfs/go-libp2p/util/eventlog/loggables"
mconn
"github.com/ipfs/go-libp2p/util/metrics/conn"
ma
"github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr"
manet
"github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr-net"
process
"github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess"
processctx
"github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess/context"
ratelimit
"github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess/ratelimit"
context
"github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context"
lgbl
"util/eventlog/loggables"
ma
"github.com/jbenet/go-multiaddr"
context
"golang.org/x/net/context"
)
// Diagram of dial sync:
...
...
@@ -44,6 +40,9 @@ var (
// add loop back in Dial(.)
const
dialAttempts
=
1
// number of concurrent outbound dials over transports that consume file descriptors
const
concurrentFdDials
=
160
// DialTimeout is the amount of time each dial attempt has. We can think about making
// this larger down the road, or putting more granular timeouts (i.e. within each
// subcomponent of Dial)
...
...
@@ -115,6 +114,7 @@ func (ds *dialsync) Unlock(dst peer.ID) {
if
!
found
{
panic
(
"called dialDone with no ongoing dials to peer: "
+
dst
.
Pretty
())
}
delete
(
ds
.
ongoing
,
dst
)
// remove ongoing dial
close
(
wait
)
// release everyone else
ds
.
lock
.
Unlock
()
...
...
@@ -145,44 +145,71 @@ func (ds *dialsync) Unlock(dst peer.ID) {
// dialbackoff.Clear(p)
// }
//
type
dialbackoff
struct
{
entries
map
[
peer
.
ID
]
struct
{}
entries
map
[
peer
.
ID
]
*
backoffPeer
lock
sync
.
RWMutex
}
type
backoffPeer
struct
{
tries
int
until
time
.
Time
}
func
(
db
*
dialbackoff
)
init
()
{
if
db
.
entries
==
nil
{
db
.
entries
=
make
(
map
[
peer
.
ID
]
struct
{}
)
db
.
entries
=
make
(
map
[
peer
.
ID
]
*
backoffPeer
)
}
}
// Backoff returns whether the client should backoff from dialing
// pee
e
r p
func
(
db
*
dialbackoff
)
Backoff
(
p
peer
.
ID
)
bool
{
// peer p
func
(
db
*
dialbackoff
)
Backoff
(
p
peer
.
ID
)
(
backoff
bool
)
{
db
.
lock
.
Lock
()
defer
db
.
lock
.
Unlock
()
db
.
init
()
_
,
found
:=
db
.
entries
[
p
]
db
.
lock
.
Unlock
()
return
found
bp
,
found
:=
db
.
entries
[
p
]
if
found
&&
time
.
Now
()
.
Before
(
bp
.
until
)
{
return
true
}
return
false
}
const
baseBackoffTime
=
time
.
Second
*
5
const
maxBackoffTime
=
time
.
Minute
*
5
// AddBackoff lets other nodes know that we've entered backoff with
// peer p, so dialers should not wait unnecessarily. We still will
// attempt to dial with one goroutine, in case we get through.
func
(
db
*
dialbackoff
)
AddBackoff
(
p
peer
.
ID
)
{
db
.
lock
.
Lock
()
defer
db
.
lock
.
Unlock
()
db
.
init
()
db
.
entries
[
p
]
=
struct
{}{}
db
.
lock
.
Unlock
()
bp
,
ok
:=
db
.
entries
[
p
]
if
!
ok
{
db
.
entries
[
p
]
=
&
backoffPeer
{
tries
:
1
,
until
:
time
.
Now
()
.
Add
(
baseBackoffTime
),
}
return
}
expTimeAdd
:=
time
.
Second
*
time
.
Duration
(
bp
.
tries
*
bp
.
tries
)
if
expTimeAdd
>
maxBackoffTime
{
expTimeAdd
=
maxBackoffTime
}
bp
.
until
=
time
.
Now
()
.
Add
(
baseBackoffTime
+
expTimeAdd
)
bp
.
tries
++
}
// Clear removes a backoff record. Clients should call this after a
// successful Dial.
func
(
db
*
dialbackoff
)
Clear
(
p
peer
.
ID
)
{
db
.
lock
.
Lock
()
defer
db
.
lock
.
Unlock
()
db
.
init
()
delete
(
db
.
entries
,
p
)
db
.
lock
.
Unlock
()
}
// Dial connects to a peer.
...
...
@@ -225,14 +252,20 @@ func (s *Swarm) gatedDialAttempt(ctx context.Context, p peer.ID) (*Conn, error)
// check if there's an ongoing dial to this peer
if
ok
,
wait
:=
s
.
dsync
.
Lock
(
p
);
ok
{
defer
s
.
dsync
.
Unlock
(
p
)
// if this peer has been backed off, lets get out of here
if
s
.
backf
.
Backoff
(
p
)
{
log
.
Event
(
ctx
,
"swarmDialBackoff"
,
logdial
)
return
nil
,
ErrDialBackoff
}
// ok, we have been charged to dial! let's do it.
// if it succeeds, dial will add the conn to the swarm itself.
defer
log
.
EventBegin
(
ctx
,
"swarmDialAttemptStart"
,
logdial
)
.
Done
()
ctxT
,
cancel
:=
context
.
WithTimeout
(
ctx
,
s
.
dialT
)
conn
,
err
:=
s
.
dial
(
ctxT
,
p
)
cancel
()
s
.
dsync
.
Unlock
(
p
)
log
.
Debugf
(
"dial end %s"
,
conn
)
if
err
!=
nil
{
log
.
Event
(
ctx
,
"swarmDialBackoffAdd"
,
logdial
)
...
...
@@ -287,14 +320,6 @@ func (s *Swarm) dial(ctx context.Context, p peer.ID) (*Conn, error) {
log
.
Debug
(
"Dial not given PrivateKey, so WILL NOT SECURE conn."
)
}
// get our own addrs. try dialing out from our listener addresses (reusing ports)
// Note that using our peerstore's addresses here is incorrect, as that would
// include observed addresses. TODO: make peerstore's address book smarter.
localAddrs
:=
s
.
ListenAddresses
()
if
len
(
localAddrs
)
==
0
{
log
.
Debug
(
"Dialing out with no local addresses."
)
}
// get remote peer addrs
remoteAddrs
:=
s
.
peers
.
Addrs
(
p
)
// make sure we can use the addresses.
...
...
@@ -319,23 +344,8 @@ func (s *Swarm) dial(ctx context.Context, p peer.ID) (*Conn, error) {
return
nil
,
err
}
// open connection to peer
d
:=
&
conn
.
Dialer
{
Dialer
:
manet
.
Dialer
{
Dialer
:
net
.
Dialer
{
Timeout
:
s
.
dialT
,
},
},
LocalPeer
:
s
.
local
,
LocalAddrs
:
localAddrs
,
PrivateKey
:
sk
,
Wrapper
:
func
(
c
manet
.
Conn
)
manet
.
Conn
{
return
mconn
.
WrapConn
(
s
.
bwc
,
c
)
},
}
// try to get a connection to any addr
connC
,
err
:=
s
.
dialAddrs
(
ctx
,
d
,
p
,
remoteAddrs
)
connC
,
err
:=
s
.
dialAddrs
(
ctx
,
p
,
remoteAddrs
)
if
err
!=
nil
{
logdial
[
"error"
]
=
err
return
nil
,
err
...
...
@@ -355,7 +365,10 @@ func (s *Swarm) dial(ctx context.Context, p peer.ID) (*Conn, error) {
return
swarmC
,
nil
}
func
(
s
*
Swarm
)
dialAddrs
(
ctx
context
.
Context
,
d
*
conn
.
Dialer
,
p
peer
.
ID
,
remoteAddrs
[]
ma
.
Multiaddr
)
(
conn
.
Conn
,
error
)
{
func
(
s
*
Swarm
)
dialAddrs
(
ctx
context
.
Context
,
p
peer
.
ID
,
remoteAddrs
[]
ma
.
Multiaddr
)
(
conn
.
Conn
,
error
)
{
// sort addresses so preferred addresses are dialed sooner
sort
.
Sort
(
AddrList
(
remoteAddrs
))
// try to connect to one of the peer's known addresses.
// we dial concurrently to each of the addresses, which:
...
...
@@ -367,78 +380,89 @@ func (s *Swarm) dialAddrs(ctx context.Context, d *conn.Dialer, p peer.ID, remote
ctx
,
cancel
:=
context
.
WithCancel
(
ctx
)
defer
cancel
()
// cancel work when we exit func
foundConn
:=
make
(
chan
struct
{})
conns
:=
make
(
chan
conn
.
Conn
,
len
(
remoteAddrs
))
conns
:=
make
(
chan
conn
.
Conn
)
errs
:=
make
(
chan
error
,
len
(
remoteAddrs
))
// dialSingleAddr is used in the rate-limited async thing below.
dialSingleAddr
:=
func
(
addr
ma
.
Multiaddr
)
{
connC
,
err
:=
s
.
dialAddr
(
ctx
,
d
,
p
,
addr
)
// rebind chans in scope so we can nil them out easily
connsout
:=
conns
errsout
:=
errs
connC
,
err
:=
s
.
dialAddr
(
ctx
,
p
,
addr
)
if
err
!=
nil
{
connsout
=
nil
}
else
if
connC
==
nil
{
// NOTE: this really should never happen
log
.
Errorf
(
"failed to dial %s %s and got no error!"
,
p
,
addr
)
err
=
fmt
.
Errorf
(
"failed to dial %s %s"
,
p
,
addr
)
connsout
=
nil
}
else
{
errsout
=
nil
}
// check parent still wants our results
select
{
case
<-
foundConn
:
case
<-
ctx
.
Done
()
:
if
connC
!=
nil
{
connC
.
Close
()
}
return
default
:
}
if
err
!=
nil
{
errs
<-
err
}
else
if
connC
==
nil
{
errs
<-
fmt
.
Errorf
(
"failed to dial %s %s"
,
p
,
addr
)
}
else
{
conns
<-
connC
case
errsout
<-
err
:
case
connsout
<-
connC
:
}
}
// this whole thing is in a goroutine so we can use foundConn
// to end early.
go
func
()
{
// rate limiting just in case. at most 10 addrs at once.
limiter
:=
ratelimit
.
NewRateLimiter
(
process
.
Background
(),
10
)
limiter
.
Go
(
func
(
worker
process
.
Process
)
{
//
permute addrs so we try different sets first each time
.
for
_
,
i
:=
range
rand
.
Perm
(
len
(
remoteAddrs
))
{
select
{
case
<-
foundConn
:
// if one of them succeeded already
break
case
<-
worker
.
Closing
()
:
// our context was cancelled
break
default
:
}
workerAddr
:=
remoteAddrs
[
i
]
// shadow variable to avoid race
limiter
.
LimitedGo
(
func
(
worker
process
.
Process
)
{
dialSingleAddr
(
workerAddr
)
})
limiter
:=
make
(
chan
struct
{},
8
)
for
_
,
addr
:=
range
remoteAddrs
{
// returns whatever ratelimiting is acceptable for workerAddr.
//
may not rate limit at all
.
rl
:=
s
.
addrDialRateLimit
(
addr
)
select
{
case
<-
ctx
.
Done
()
:
// our context was cancelled
return
case
rl
<-
struct
{}{}
:
// take the token, move on
}
select
{
case
<-
ctx
.
Done
()
:
// our context was cancelled
return
case
limiter
<-
struct
{}{}
:
// take the token, move on
}
})
processctx
.
CloseAfterContext
(
limiter
,
ctx
)
go
func
(
rlc
<-
chan
struct
{},
a
ma
.
Multiaddr
)
{
dialSingleAddr
(
a
)
<-
limiter
<-
rlc
}(
rl
,
addr
)
}
}()
// wair fo
t
the results.
// wair fo
r
the results.
exitErr
:=
fmt
.
Errorf
(
"failed to dial %s"
,
p
)
for
i
:=
0
;
i
<
len
(
remoteAddrs
);
i
++
{
for
range
remoteAddrs
{
select
{
case
exitErr
=
<-
errs
:
//
log
.
Debug
(
"dial error: "
,
exitErr
)
case
connC
:=
<-
conns
:
// take the first + return asap
close
(
foundConn
)
return
connC
,
nil
case
<-
ctx
.
Done
()
:
// break out and return error
break
}
}
return
nil
,
exitErr
}
func
(
s
*
Swarm
)
dialAddr
(
ctx
context
.
Context
,
d
*
conn
.
Dialer
,
p
peer
.
ID
,
addr
ma
.
Multiaddr
)
(
conn
.
Conn
,
error
)
{
func
(
s
*
Swarm
)
dialAddr
(
ctx
context
.
Context
,
p
peer
.
ID
,
addr
ma
.
Multiaddr
)
(
conn
.
Conn
,
error
)
{
log
.
Debugf
(
"%s swarm dialing %s %s"
,
s
.
local
,
p
,
addr
)
connC
,
err
:=
d
.
Dial
(
ctx
,
addr
,
p
)
connC
,
err
:=
s
.
dialer
.
Dial
(
ctx
,
addr
,
p
)
if
err
!=
nil
{
return
nil
,
fmt
.
Errorf
(
"%s --> %s dial attempt failed: %s"
,
s
.
local
,
p
,
err
)
}
...
...
@@ -491,3 +515,72 @@ func dialConnSetup(ctx context.Context, s *Swarm, connC conn.Conn) (*Conn, error
return
swarmC
,
err
}
// addrDialRateLimit returns a ratelimiting channel for dialing transport
// addrs like a. for example, tcp is fd-ratelimited. utp is not ratelimited.
func
(
s
*
Swarm
)
addrDialRateLimit
(
a
ma
.
Multiaddr
)
chan
struct
{}
{
if
isFDCostlyTransport
(
a
)
{
return
s
.
fdRateLimit
}
// do not rate limit it at all
return
make
(
chan
struct
{},
1
)
}
func
isFDCostlyTransport
(
a
ma
.
Multiaddr
)
bool
{
return
isTCPMultiaddr
(
a
)
}
func
isTCPMultiaddr
(
a
ma
.
Multiaddr
)
bool
{
p
:=
a
.
Protocols
()
return
len
(
p
)
==
2
&&
(
p
[
0
]
.
Name
==
"ip4"
||
p
[
0
]
.
Name
==
"ip6"
)
&&
p
[
1
]
.
Name
==
"tcp"
}
type
AddrList
[]
ma
.
Multiaddr
func
(
al
AddrList
)
Len
()
int
{
return
len
(
al
)
}
func
(
al
AddrList
)
Swap
(
i
,
j
int
)
{
al
[
i
],
al
[
j
]
=
al
[
j
],
al
[
i
]
}
func
(
al
AddrList
)
Less
(
i
,
j
int
)
bool
{
a
:=
al
[
i
]
b
:=
al
[
j
]
// dial localhost addresses next, they should fail immediately
lba
:=
manet
.
IsIPLoopback
(
a
)
lbb
:=
manet
.
IsIPLoopback
(
b
)
if
lba
{
if
!
lbb
{
return
true
}
}
// dial utp and similar 'non-fd-consuming' addresses first
fda
:=
isFDCostlyTransport
(
a
)
fdb
:=
isFDCostlyTransport
(
b
)
if
!
fda
{
if
fdb
{
return
true
}
// if neither consume fd's, assume equal ordering
return
false
}
// if 'b' doesnt take a file descriptor
if
!
fdb
{
return
false
}
// if 'b' is loopback and both take file descriptors
if
lbb
{
return
false
}
// for the rest, just sort by bytes
return
bytes
.
Compare
(
a
.
Bytes
(),
b
.
Bytes
())
>
0
}
p2p/net/swarm/swarm_listen.go
View file @
51fd99e3
...
...
@@ -3,68 +3,81 @@ package swarm
import
(
"fmt"
mconn
"github.com/ipfs/go-libp2p/p2p/metrics/conn"
inet
"github.com/ipfs/go-libp2p/p2p/net"
conn
"github.com/ipfs/go-libp2p/p2p/net/conn"
addrutil
"github.com/ipfs/go-libp2p/p2p/net/swarm/addr"
lgbl
"github.com/ipfs/go-libp2p/util/eventlog/loggables"
mconn
"github.com/ipfs/go-libp2p/util/metrics/conn"
ma
"github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr"
manet
"github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr-net"
ps
"github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-peerstream"
context
"github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context"
multierr
"github.com/ipfs/go-ipfs/thirdparty/multierr"
transport
"github.com/ipfs/go-libp2p/p2p/net/transport"
lgbl
"util/eventlog/loggables"
ma
"github.com/jbenet/go-multiaddr"
ps
"github.com/jbenet/go-peerstream"
context
"golang.org/x/net/context"
)
// Open listeners for each network the swarm should listen on
func
(
s
*
Swarm
)
listen
(
addrs
[]
ma
.
Multiaddr
)
error
{
// Open listeners and reuse-dialers for the given addresses
func
(
s
*
Swarm
)
setupInterfaces
(
addrs
[]
ma
.
Multiaddr
)
error
{
errs
:=
make
([]
error
,
len
(
addrs
))
var
succeeded
int
for
i
,
a
:=
range
addrs
{
tpt
:=
s
.
transportForAddr
(
a
)
if
tpt
==
nil
{
errs
[
i
]
=
fmt
.
Errorf
(
"no transport for address: %s"
,
a
)
continue
}
for
_
,
addr
:=
range
addrs
{
if
!
addrutil
.
AddrUsable
(
addr
,
true
)
{
return
fmt
.
Errorf
(
"cannot use addr: %s"
,
addr
)
d
,
err
:=
tpt
.
Dialer
(
a
,
transport
.
TimeoutOpt
(
DialTimeout
),
transport
.
ReusePorts
)
if
err
!=
nil
{
errs
[
i
]
=
err
continue
}
}
retErr
:=
multierr
.
New
(
)
s
.
dialer
.
AddDialer
(
d
)
// listen on every address
for
i
,
addr
:=
range
addrs
{
err
:=
s
.
setupListener
(
addr
)
list
,
err
:=
tpt
.
Listen
(
a
)
if
err
!=
nil
{
if
retErr
.
Errors
==
nil
{
retErr
.
Errors
=
make
([]
error
,
len
(
addrs
))
}
retErr
.
Errors
[
i
]
=
err
log
.
Debugf
(
"Failed to listen on: %s - %s"
,
addr
,
err
)
errs
[
i
]
=
err
continue
}
err
=
s
.
addListener
(
list
)
if
err
!=
nil
{
errs
[
i
]
=
err
continue
}
succeeded
++
}
if
retErr
.
Errors
!=
nil
{
return
retErr
for
i
,
e
:=
range
errs
{
if
e
!=
nil
{
log
.
Warning
(
"listen on %s failed: %s"
,
addrs
[
i
],
errs
[
i
])
}
}
if
succeeded
==
0
&&
len
(
addrs
)
>
0
{
return
fmt
.
Errorf
(
"failed to listen on any addresses: %s"
,
errs
)
}
return
nil
}
// Listen for new connections on the given multiaddr
func
(
s
*
Swarm
)
setupListener
(
maddr
ma
.
Multiaddr
)
error
{
func
(
s
*
Swarm
)
transportForAddr
(
a
ma
.
Multiaddr
)
transport
.
Transport
{
for
_
,
t
:=
range
s
.
transports
{
if
t
.
Matches
(
a
)
{
return
t
}
}
return
nil
}
// TODO rethink how this has to work. (jbenet)
//
// resolved, err := resolveUnspecifiedAddresses([]ma.Multiaddr{maddr})
// if err != nil {
// return err
// }
// for _, a := range resolved {
// s.peers.AddAddr(s.local, a)
// }
func
(
s
*
Swarm
)
addListener
(
tptlist
transport
.
Listener
)
error
{
sk
:=
s
.
peers
.
PrivKey
(
s
.
local
)
if
sk
==
nil
{
// may be fine for sk to be nil, just log a warning.
log
.
Warning
(
"Listener not given PrivateKey, so WILL NOT SECURE conns."
)
}
log
.
Debugf
(
"Swarm Listening at %s"
,
maddr
)
list
,
err
:=
conn
.
Listen
(
s
.
Context
(),
maddr
,
s
.
local
,
sk
)
list
,
err
:=
conn
.
WrapTransport
Listen
er
(
s
.
Context
(),
tptlist
,
s
.
local
,
sk
)
if
err
!=
nil
{
return
err
}
...
...
@@ -72,11 +85,15 @@ func (s *Swarm) setupListener(maddr ma.Multiaddr) error {
list
.
SetAddrFilters
(
s
.
Filters
)
if
cw
,
ok
:=
list
.
(
conn
.
ListenerConnWrapper
);
ok
{
cw
.
SetConnWrapper
(
func
(
c
manet
.
Conn
)
mane
t
.
Conn
{
cw
.
SetConnWrapper
(
func
(
c
transport
.
Conn
)
transpor
t
.
Conn
{
return
mconn
.
WrapConn
(
s
.
bwc
,
c
)
})
}
return
s
.
addConnListener
(
list
)
}
func
(
s
*
Swarm
)
addConnListener
(
list
conn
.
Listener
)
error
{
// AddListener to the peerstream Listener. this will begin accepting connections
// and streams!
sl
,
err
:=
s
.
swarm
.
AddListener
(
list
)
...
...
@@ -85,6 +102,8 @@ func (s *Swarm) setupListener(maddr ma.Multiaddr) error {
}
log
.
Debugf
(
"Swarm Listeners at %s"
,
s
.
ListenAddresses
())
maddr
:=
list
.
Multiaddr
()
// signal to our notifiees on successful conn.
s
.
notifyAll
(
func
(
n
inet
.
Notifiee
)
{
n
.
Listen
((
*
Network
)(
s
),
maddr
)
...
...
@@ -107,7 +126,7 @@ func (s *Swarm) setupListener(maddr ma.Multiaddr) error {
if
!
more
{
return
}
log
.
Warning
f
(
"swarm listener accept error: %s"
,
err
)
log
.
Error
f
(
"swarm listener accept error: %s"
,
err
)
case
<-
ctx
.
Done
()
:
return
}
...
...
@@ -138,5 +157,8 @@ func (s *Swarm) connHandler(c *ps.Conn) *Conn {
return
nil
}
// if a peer dials us, remove from dial backoff.
s
.
backf
.
Clear
(
sc
.
RemotePeer
())
return
sc
}
p2p/net/swarm/swarm_net.go
View file @
51fd99e3
...
...
@@ -5,12 +5,12 @@ import (
peer
"github.com/ipfs/go-libp2p/p2p/peer"
metrics
"github.com/ipfs/go-libp2p/p2p/metrics"
inet
"github.com/ipfs/go-libp2p/p2p/net"
metrics
"github.com/ipfs/go-libp2p/util/metrics"
ma
"github.com/
ipfs/go-ipfs/Godeps/_workspace/src/github.com/
jbenet/go-multiaddr"
"github.com/
ipfs/go-ipfs/Godeps/_workspace/src/github.com/
jbenet/goprocess"
context
"
github.com/ipfs/go-ipfs/Godeps/_workspace/src/
golang.org/x/net/context"
ma
"github.com/jbenet/go-multiaddr"
"github.com/jbenet/goprocess"
context
"golang.org/x/net/context"
)
// Network implements the inet.Network interface.
...
...
p2p/net/swarm/swarm_net_test.go
View file @
51fd99e3
...
...
@@ -5,7 +5,7 @@ import (
"testing"
"time"
context
"
github.com/ipfs/go-ipfs/Godeps/_workspace/src/
golang.org/x/net/context"
context
"golang.org/x/net/context"
inet
"github.com/ipfs/go-libp2p/p2p/net"
testutil
"github.com/ipfs/go-libp2p/p2p/test/util"
)
...
...
p2p/net/swarm/swarm_notif_test.go
View file @
51fd99e3
...
...
@@ -4,8 +4,8 @@ import (
"testing"
"time"
ma
"github.com/
ipfs/go-ipfs/Godeps/_workspace/src/github.com/
jbenet/go-multiaddr"
context
"
github.com/ipfs/go-ipfs/Godeps/_workspace/src/
golang.org/x/net/context"
ma
"github.com/jbenet/go-multiaddr"
context
"golang.org/x/net/context"
inet
"github.com/ipfs/go-libp2p/p2p/net"
peer
"github.com/ipfs/go-libp2p/p2p/peer"
...
...
p2p/net/swarm/swarm_stream.go
View file @
51fd99e3
...
...
@@ -3,7 +3,7 @@ package swarm
import
(
inet
"github.com/ipfs/go-libp2p/p2p/net"
ps
"github.com/
ipfs/go-ipfs/Godeps/_workspace/src/github.com/
jbenet/go-peerstream"
ps
"github.com/jbenet/go-peerstream"
)
// a Stream is a wrapper around a ps.Stream that exposes a way to get
...
...
p2p/net/swarm/swarm_test.go
View file @
51fd99e3
...
...
@@ -9,13 +9,13 @@ import (
"testing"
"time"
testutil
"github.com/ipfs/go-
ipfs/util/testutil
"
metrics
"github.com/ipfs/go-
libp2p/p2p/metrics
"
inet
"github.com/ipfs/go-libp2p/p2p/net"
peer
"github.com/ipfs/go-libp2p/p2p/peer"
metrics
"github.com/ipfs/go-libp2p/util/metrics
"
testutil
"util/testutil
"
ma
"github.com/
ipfs/go-ipfs/Godeps/_workspace/src/github.com/
jbenet/go-multiaddr"
context
"
github.com/ipfs/go-ipfs/Godeps/_workspace/src/
golang.org/x/net/context"
ma
"github.com/jbenet/go-multiaddr"
context
"golang.org/x/net/context"
)
func
EchoStreamHandler
(
stream
inet
.
Stream
)
{
...
...
@@ -237,6 +237,15 @@ func TestSwarm(t *testing.T) {
SubtestSwarm
(
t
,
swarms
,
msgs
)
}
func
TestBasicSwarm
(
t
*
testing
.
T
)
{
// t.Skip("skipping for another test")
t
.
Parallel
()
msgs
:=
1
swarms
:=
2
SubtestSwarm
(
t
,
swarms
,
msgs
)
}
func
TestConnHandler
(
t
*
testing
.
T
)
{
// t.Skip("skipping for another test")
t
.
Parallel
()
...
...
p2p/net/
conn
/reuseport.go
→
p2p/net/
transport
/reuseport.go
View file @
51fd99e3
package
conn
package
transport
import
(
"net"
"os"
"strings"
"syscall"
reuseport
"github.com/
ipfs/go-ipfs/Godeps/_workspace/src/github.com/
jbenet/go-reuseport"
reuseport
"github.com/jbenet/go-reuseport"
)
// envReuseport is the env variable name used to turn off reuse port.
...
...
@@ -30,6 +32,34 @@ func init() {
//
// If this becomes a sought after feature, we could add this to the config.
// In the end, reuseport is a stop-gap.
func
r
euseportIsAvailable
()
bool
{
func
R
euseportIsAvailable
()
bool
{
return
envReuseportVal
&&
reuseport
.
Available
()
}
// ReuseErrShouldRetry diagnoses whether to retry after a reuse error.
// if we failed to bind, we should retry. if bind worked and this is a
// real dial error (remote end didnt answer) then we should not retry.
func
ReuseErrShouldRetry
(
err
error
)
bool
{
if
err
==
nil
{
return
false
// hey, it worked! no need to retry.
}
// if it's a network timeout error, it's a legitimate failure.
if
nerr
,
ok
:=
err
.
(
net
.
Error
);
ok
&&
nerr
.
Timeout
()
{
return
false
}
errno
,
ok
:=
err
.
(
syscall
.
Errno
)
if
!
ok
{
// not an errno? who knows what this is. retry.
return
true
}
switch
errno
{
case
syscall
.
EADDRINUSE
,
syscall
.
EADDRNOTAVAIL
:
return
true
// failure to bind. retry.
case
syscall
.
ECONNREFUSED
:
return
false
// real dial error
default
:
return
true
// optimistically default to retry.
}
}
p2p/net/transport/tcp.go
0 → 100644
View file @
51fd99e3
package
transport
import
(
"fmt"
"net"
"sync"
"time"
ma
"github.com/jbenet/go-multiaddr"
manet
"github.com/jbenet/go-multiaddr-net"
reuseport
"github.com/jbenet/go-reuseport"
context
"golang.org/x/net/context"
lgbl
"util/eventlog/loggables"
)
type
TcpTransport
struct
{
dlock
sync
.
Mutex
dialers
map
[
string
]
Dialer
llock
sync
.
Mutex
listeners
map
[
string
]
Listener
}
func
NewTCPTransport
()
*
TcpTransport
{
return
&
TcpTransport
{
dialers
:
make
(
map
[
string
]
Dialer
),
listeners
:
make
(
map
[
string
]
Listener
),
}
}
func
(
t
*
TcpTransport
)
Dialer
(
laddr
ma
.
Multiaddr
,
opts
...
DialOpt
)
(
Dialer
,
error
)
{
t
.
dlock
.
Lock
()
defer
t
.
dlock
.
Unlock
()
s
:=
laddr
.
String
()
d
,
found
:=
t
.
dialers
[
s
]
if
found
{
return
d
,
nil
}
var
base
manet
.
Dialer
var
doReuse
bool
for
_
,
o
:=
range
opts
{
switch
o
:=
o
.
(
type
)
{
case
TimeoutOpt
:
base
.
Timeout
=
time
.
Duration
(
o
)
case
ReuseportOpt
:
doReuse
=
bool
(
o
)
default
:
return
nil
,
fmt
.
Errorf
(
"unrecognized option: %#v"
,
o
)
}
}
tcpd
,
err
:=
t
.
newTcpDialer
(
base
,
laddr
,
doReuse
)
if
err
!=
nil
{
return
nil
,
err
}
t
.
dialers
[
s
]
=
tcpd
return
tcpd
,
nil
}
func
(
t
*
TcpTransport
)
Listen
(
laddr
ma
.
Multiaddr
)
(
Listener
,
error
)
{
t
.
llock
.
Lock
()
defer
t
.
llock
.
Unlock
()
s
:=
laddr
.
String
()
l
,
found
:=
t
.
listeners
[
s
]
if
found
{
return
l
,
nil
}
list
,
err
:=
manetListen
(
laddr
)
if
err
!=
nil
{
return
nil
,
err
}
tlist
:=
&
tcpListener
{
list
:
list
,
transport
:
t
,
}
t
.
listeners
[
s
]
=
tlist
return
tlist
,
nil
}
func
manetListen
(
addr
ma
.
Multiaddr
)
(
manet
.
Listener
,
error
)
{
network
,
naddr
,
err
:=
manet
.
DialArgs
(
addr
)
if
err
!=
nil
{
return
nil
,
err
}
if
ReuseportIsAvailable
()
{
nl
,
err
:=
reuseport
.
Listen
(
network
,
naddr
)
if
err
==
nil
{
// hey, it worked!
return
manet
.
WrapNetListener
(
nl
)
}
// reuseport is available, but we failed to listen. log debug, and retry normally.
log
.
Debugf
(
"reuseport available, but failed to listen: %s %s, %s"
,
network
,
naddr
,
err
)
}
// either reuseport not available, or it failed. try normally.
return
manet
.
Listen
(
addr
)
}
func
(
t
*
TcpTransport
)
Matches
(
a
ma
.
Multiaddr
)
bool
{
return
IsTcpMultiaddr
(
a
)
}
type
tcpDialer
struct
{
laddr
ma
.
Multiaddr
doReuse
bool
rd
reuseport
.
Dialer
madialer
manet
.
Dialer
transport
Transport
}
func
(
t
*
TcpTransport
)
newTcpDialer
(
base
manet
.
Dialer
,
laddr
ma
.
Multiaddr
,
doReuse
bool
)
(
*
tcpDialer
,
error
)
{
// get the local net.Addr manually
la
,
err
:=
manet
.
ToNetAddr
(
laddr
)
if
err
!=
nil
{
return
nil
,
err
// something wrong with laddr.
}
if
doReuse
&&
ReuseportIsAvailable
()
{
rd
:=
reuseport
.
Dialer
{
D
:
net
.
Dialer
{
LocalAddr
:
la
,
Timeout
:
base
.
Timeout
,
},
}
return
&
tcpDialer
{
doReuse
:
true
,
laddr
:
laddr
,
rd
:
rd
,
madialer
:
base
,
transport
:
t
,
},
nil
}
return
&
tcpDialer
{
doReuse
:
false
,
laddr
:
laddr
,
madialer
:
base
,
transport
:
t
,
},
nil
}
func
(
d
*
tcpDialer
)
Dial
(
raddr
ma
.
Multiaddr
)
(
Conn
,
error
)
{
var
c
manet
.
Conn
var
err
error
if
d
.
doReuse
{
c
,
err
=
d
.
reuseDial
(
raddr
)
}
else
{
c
,
err
=
d
.
madialer
.
Dial
(
raddr
)
}
if
err
!=
nil
{
return
nil
,
err
}
return
&
connWrap
{
Conn
:
c
,
transport
:
d
.
transport
,
},
nil
}
func
(
d
*
tcpDialer
)
reuseDial
(
raddr
ma
.
Multiaddr
)
(
manet
.
Conn
,
error
)
{
logdial
:=
lgbl
.
Dial
(
"conn"
,
""
,
""
,
d
.
laddr
,
raddr
)
rpev
:=
log
.
EventBegin
(
context
.
TODO
(),
"tptDialReusePort"
,
logdial
)
network
,
netraddr
,
err
:=
manet
.
DialArgs
(
raddr
)
if
err
!=
nil
{
return
nil
,
err
}
con
,
err
:=
d
.
rd
.
Dial
(
network
,
netraddr
)
if
err
==
nil
{
logdial
[
"reuseport"
]
=
"success"
rpev
.
Done
()
return
manet
.
WrapNetConn
(
con
)
}
if
!
ReuseErrShouldRetry
(
err
)
{
logdial
[
"reuseport"
]
=
"failure"
logdial
[
"error"
]
=
err
rpev
.
Done
()
return
nil
,
err
}
logdial
[
"reuseport"
]
=
"retry"
logdial
[
"error"
]
=
err
rpev
.
Done
()
return
d
.
madialer
.
Dial
(
raddr
)
}
func
(
d
*
tcpDialer
)
Matches
(
a
ma
.
Multiaddr
)
bool
{
return
IsTcpMultiaddr
(
a
)
}
type
tcpListener
struct
{
list
manet
.
Listener
transport
Transport
}
func
(
d
*
tcpListener
)
Accept
()
(
Conn
,
error
)
{
c
,
err
:=
d
.
list
.
Accept
()
if
err
!=
nil
{
return
nil
,
err
}
return
&
connWrap
{
Conn
:
c
,
transport
:
d
.
transport
,
},
nil
}
func
(
d
*
tcpListener
)
Addr
()
net
.
Addr
{
return
d
.
list
.
Addr
()
}
func
(
t
*
tcpListener
)
Multiaddr
()
ma
.
Multiaddr
{
return
t
.
list
.
Multiaddr
()
}
func
(
t
*
tcpListener
)
NetListener
()
net
.
Listener
{
return
t
.
list
.
NetListener
()
}
func
(
d
*
tcpListener
)
Close
()
error
{
return
d
.
list
.
Close
()
}
p2p/net/transport/transport.go
0 → 100644
View file @
51fd99e3
package
transport
import
(
"net"
"time"
logging
"QmWRypnfEwrgH4k93KEHN5hng7VjKYkWmzDYRuTZeh2Mgh/go-log"
ma
"github.com/jbenet/go-multiaddr"
manet
"github.com/jbenet/go-multiaddr-net"
)
var
log
=
logging
.
Logger
(
"transport"
)
type
Conn
interface
{
manet
.
Conn
Transport
()
Transport
}
type
Transport
interface
{
Dialer
(
laddr
ma
.
Multiaddr
,
opts
...
DialOpt
)
(
Dialer
,
error
)
Listen
(
laddr
ma
.
Multiaddr
)
(
Listener
,
error
)
Matches
(
ma
.
Multiaddr
)
bool
}
type
Dialer
interface
{
Dial
(
raddr
ma
.
Multiaddr
)
(
Conn
,
error
)
Matches
(
ma
.
Multiaddr
)
bool
}
type
Listener
interface
{
Accept
()
(
Conn
,
error
)
Close
()
error
Addr
()
net
.
Addr
Multiaddr
()
ma
.
Multiaddr
}
type
connWrap
struct
{
manet
.
Conn
transport
Transport
}
func
(
cw
*
connWrap
)
Transport
()
Transport
{
return
cw
.
transport
}
type
DialOpt
interface
{}
type
TimeoutOpt
time
.
Duration
type
ReuseportOpt
bool
var
ReusePorts
ReuseportOpt
=
true
func
IsTcpMultiaddr
(
a
ma
.
Multiaddr
)
bool
{
p
:=
a
.
Protocols
()
return
len
(
p
)
==
2
&&
(
p
[
0
]
.
Name
==
"ip4"
||
p
[
0
]
.
Name
==
"ip6"
)
&&
p
[
1
]
.
Name
==
"tcp"
}
func
IsUtpMultiaddr
(
a
ma
.
Multiaddr
)
bool
{
p
:=
a
.
Protocols
()
return
len
(
p
)
==
3
&&
p
[
2
]
.
Name
==
"utp"
}
Prev
1
2
3
4
5
6
7
Next
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment