From d71c16683531167230c5f6c4ce60bbe2e9f2b093 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Wed, 10 Sep 2014 19:38:21 -0700 Subject: [PATCH 01/28] organize imports --- bitswap/bitswap.go | 10 +++++----- bitswap/ledger.go | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/bitswap/bitswap.go b/bitswap/bitswap.go index 22f97514c..0484065a6 100644 --- a/bitswap/bitswap.go +++ b/bitswap/bitswap.go @@ -1,17 +1,17 @@ package bitswap import ( - "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto" + "time" + + proto "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto" + ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/datastore.go" + blocks "github.com/jbenet/go-ipfs/blocks" peer "github.com/jbenet/go-ipfs/peer" routing "github.com/jbenet/go-ipfs/routing" dht "github.com/jbenet/go-ipfs/routing/dht" swarm "github.com/jbenet/go-ipfs/swarm" u "github.com/jbenet/go-ipfs/util" - - ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/datastore.go" - - "time" ) // PartnerWantListMax is the bound for the number of keys we'll store per diff --git a/bitswap/ledger.go b/bitswap/ledger.go index a0f23b8d4..2a197f057 100644 --- a/bitswap/ledger.go +++ b/bitswap/ledger.go @@ -1,10 +1,10 @@ package bitswap import ( + "time" + peer "github.com/jbenet/go-ipfs/peer" u "github.com/jbenet/go-ipfs/util" - - "time" ) // Ledger stores the data exchange relationship between two peers. From a910d6878d5c466b733fff6f8c749248ddc6c891 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Wed, 10 Sep 2014 19:43:23 -0700 Subject: [PATCH 02/28] privatize getLedger method --- bitswap/bitswap.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/bitswap/bitswap.go b/bitswap/bitswap.go index 0484065a6..a80895462 100644 --- a/bitswap/bitswap.go +++ b/bitswap/bitswap.go @@ -192,7 +192,7 @@ func (bs *BitSwap) handleMessages() { // and then if we do, check the ledger for whether or not we should send it. func (bs *BitSwap) peerWantsBlock(p *peer.Peer, want string) { u.DOut("peer [%s] wants block [%s]\n", p.ID.Pretty(), u.Key(want).Pretty()) - ledg := bs.GetLedger(p) + ledg := bs.getLedger(p) dsk := ds.NewKey(want) blk_i, err := bs.datastore.Get(dsk) @@ -239,11 +239,11 @@ func (bs *BitSwap) blockReceive(p *peer.Peer, blk *blocks.Block) { } bs.listener.Respond(string(blk.Key()), mes) - ledger := bs.GetLedger(p) + ledger := bs.getLedger(p) ledger.ReceivedBytes(len(blk.Data)) } -func (bs *BitSwap) GetLedger(p *peer.Peer) *Ledger { +func (bs *BitSwap) getLedger(p *peer.Peer) *Ledger { l, ok := bs.partners[p.Key()] if ok { return l From 182604c61f74104e713a7cab985ecc9021961641 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Wed, 10 Sep 2014 19:47:40 -0700 Subject: [PATCH 03/28] substitute with LedgerMap type --- bitswap/bitswap.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bitswap/bitswap.go b/bitswap/bitswap.go index a80895462..d118c3687 100644 --- a/bitswap/bitswap.go +++ b/bitswap/bitswap.go @@ -44,7 +44,7 @@ type BitSwap struct { // The Ledger has the peer.ID, and the peer connection works through net. // Ledgers of known relationships (active or inactive) stored in datastore. // Changes to the Ledger should be committed to the datastore. - partners map[u.Key]*Ledger + partners LedgerMap // haveList is the set of keys we have values for. a map for fast lookups. // haveList KeySet -- not needed. all values in datastore? From 99db07c3a4fa77a367431333680fc671950105e1 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Wed, 10 Sep 2014 20:23:39 -0700 Subject: [PATCH 04/28] hide ledger wantlist manipulation --- bitswap/bitswap.go | 15 +++++++-------- bitswap/ledger.go | 10 ++++++++++ 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/bitswap/bitswap.go b/bitswap/bitswap.go index d118c3687..a2012c3de 100644 --- a/bitswap/bitswap.go +++ b/bitswap/bitswap.go @@ -137,7 +137,7 @@ func (bs *BitSwap) getBlock(k u.Key, p *peer.Peer, timeout time.Duration) ([]byt func (bs *BitSwap) HaveBlock(blk *blocks.Block) error { go func() { for _, ledger := range bs.partners { - if _, ok := ledger.WantList[blk.Key()]; ok { + if ledger.WantListContains(blk.Key()) { //send block to node if ledger.ShouldSend() { bs.SendBlock(ledger.Partner, blk) @@ -192,14 +192,13 @@ func (bs *BitSwap) handleMessages() { // and then if we do, check the ledger for whether or not we should send it. func (bs *BitSwap) peerWantsBlock(p *peer.Peer, want string) { u.DOut("peer [%s] wants block [%s]\n", p.ID.Pretty(), u.Key(want).Pretty()) - ledg := bs.getLedger(p) + ledger := bs.getLedger(p) dsk := ds.NewKey(want) blk_i, err := bs.datastore.Get(dsk) if err != nil { if err == ds.ErrNotFound { - // TODO: this needs to be different. We need timeouts. - ledg.WantList[u.Key(want)] = struct{}{} + ledger.Wants(u.Key(want)) } u.PErr("datastore get error: %v\n", err) return @@ -211,7 +210,7 @@ func (bs *BitSwap) peerWantsBlock(p *peer.Peer, want string) { return } - if ledg.ShouldSend() { + if ledger.ShouldSend() { u.DOut("Sending block to peer.\n") bblk, err := blocks.NewBlock(blk) if err != nil { @@ -219,7 +218,7 @@ func (bs *BitSwap) peerWantsBlock(p *peer.Peer, want string) { return } bs.SendBlock(p, bblk) - ledg.SentBytes(len(blk)) + ledger.SentBytes(len(blk)) } else { u.DOut("Decided not to send block.") } @@ -276,7 +275,7 @@ func (bs *BitSwap) Halt() { func (bs *BitSwap) SetStrategy(sf StrategyFunc) { bs.strategy = sf - for _, ledg := range bs.partners { - ledg.Strategy = sf + for _, ledger := range bs.partners { + ledger.Strategy = sf } } diff --git a/bitswap/ledger.go b/bitswap/ledger.go index 2a197f057..2e99d2ec7 100644 --- a/bitswap/ledger.go +++ b/bitswap/ledger.go @@ -49,3 +49,13 @@ func (l *Ledger) ReceivedBytes(n int) { l.LastExchange = time.Now() l.Accounting.BytesRecv += uint64(n) } + +// TODO: this needs to be different. We need timeouts. +func (l *Ledger) Wants(k u.Key) { + l.WantList[k] = struct{}{} +} + +func (l *Ledger) WantListContains(k u.Key) bool { + _, ok := l.WantList[k] + return ok +} From 7bae74287242b64b8d7ac61f299ca2d3387e4713 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Wed, 10 Sep 2014 20:30:08 -0700 Subject: [PATCH 05/28] privatize fields in ledger --- bitswap/ledger.go | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/bitswap/ledger.go b/bitswap/ledger.go index 2e99d2ec7..b8b58e5a6 100644 --- a/bitswap/ledger.go +++ b/bitswap/ledger.go @@ -16,17 +16,17 @@ type Ledger struct { // Accounting tracks bytes sent and recieved. Accounting debtRatio - // FirstExchnage is the time of the first data exchange. - FirstExchange time.Time + // firstExchnage is the time of the first data exchange. + firstExchange time.Time - // LastExchange is the time of the last data exchange. - LastExchange time.Time + // lastExchange is the time of the last data exchange. + lastExchange time.Time - // Number of exchanges with this peer - ExchangeCount uint64 + // exchangeCount is the number of exchanges with this peer + exchangeCount uint64 - // WantList is a (bounded, small) set of keys that Partner desires. - WantList KeySet + // wantList is a (bounded, small) set of keys that Partner desires. + wantList KeySet Strategy StrategyFunc } @@ -39,23 +39,23 @@ func (l *Ledger) ShouldSend() bool { } func (l *Ledger) SentBytes(n int) { - l.ExchangeCount++ - l.LastExchange = time.Now() + l.exchangeCount++ + l.lastExchange = time.Now() l.Accounting.BytesSent += uint64(n) } func (l *Ledger) ReceivedBytes(n int) { - l.ExchangeCount++ - l.LastExchange = time.Now() + l.exchangeCount++ + l.lastExchange = time.Now() l.Accounting.BytesRecv += uint64(n) } // TODO: this needs to be different. We need timeouts. func (l *Ledger) Wants(k u.Key) { - l.WantList[k] = struct{}{} + l.wantList[k] = struct{}{} } func (l *Ledger) WantListContains(k u.Key) bool { - _, ok := l.WantList[k] + _, ok := l.wantList[k] return ok } From ad30333581e9050f70c64b81cff0047671949ce4 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Thu, 11 Sep 2014 01:02:52 -0700 Subject: [PATCH 06/28] fix(bitswap:ledger) race conditions https://github.com/jbenet/go-ipfs/issues/39 --- bitswap/ledger.go | 23 +++++++++++++++++++++++ bitswap/ledger_test.go | 23 +++++++++++++++++++++++ 2 files changed, 46 insertions(+) create mode 100644 bitswap/ledger_test.go diff --git a/bitswap/ledger.go b/bitswap/ledger.go index b8b58e5a6..6ddc0a711 100644 --- a/bitswap/ledger.go +++ b/bitswap/ledger.go @@ -1,6 +1,7 @@ package bitswap import ( + "sync" "time" peer "github.com/jbenet/go-ipfs/peer" @@ -9,6 +10,7 @@ import ( // Ledger stores the data exchange relationship between two peers. type Ledger struct { + lock sync.RWMutex // Partner is the remote Peer. Partner *peer.Peer @@ -35,16 +37,25 @@ type Ledger struct { type LedgerMap map[u.Key]*Ledger func (l *Ledger) ShouldSend() bool { + l.lock.Lock() + defer l.lock.Unlock() + return l.Strategy(l) } func (l *Ledger) SentBytes(n int) { + l.lock.Lock() + defer l.lock.Unlock() + l.exchangeCount++ l.lastExchange = time.Now() l.Accounting.BytesSent += uint64(n) } func (l *Ledger) ReceivedBytes(n int) { + l.lock.Lock() + defer l.lock.Unlock() + l.exchangeCount++ l.lastExchange = time.Now() l.Accounting.BytesRecv += uint64(n) @@ -52,10 +63,22 @@ func (l *Ledger) ReceivedBytes(n int) { // TODO: this needs to be different. We need timeouts. func (l *Ledger) Wants(k u.Key) { + l.lock.Lock() + defer l.lock.Unlock() + l.wantList[k] = struct{}{} } func (l *Ledger) WantListContains(k u.Key) bool { + l.lock.RLock() + defer l.lock.RUnlock() + _, ok := l.wantList[k] return ok } + +func (l *Ledger) ExchangeCount() uint64 { + l.lock.RLock() + defer l.lock.RUnlock() + return l.exchangeCount +} diff --git a/bitswap/ledger_test.go b/bitswap/ledger_test.go new file mode 100644 index 000000000..d651d485f --- /dev/null +++ b/bitswap/ledger_test.go @@ -0,0 +1,23 @@ +package bitswap + +import ( + "sync" + "testing" +) + +func TestRaceConditions(t *testing.T) { + const numberOfExpectedExchanges = 10000 + l := new(Ledger) + var wg sync.WaitGroup + for i := 0; i < numberOfExpectedExchanges; i++ { + wg.Add(1) + go func() { + defer wg.Done() + l.ReceivedBytes(1) + }() + } + wg.Wait() + if l.ExchangeCount() != numberOfExpectedExchanges { + t.Fail() + } +} From 421fe5bf76078e8fc666dce926e3c10baa44dbd6 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Thu, 11 Sep 2014 03:09:36 -0700 Subject: [PATCH 07/28] refactor(bitswap:message) add wrapper for proto --- bitswap/bitswap.go | 21 +++++------- bitswap/message.go | 38 +++++++++++++++++++++ bitswap/message_test.go | 75 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 122 insertions(+), 12 deletions(-) create mode 100644 bitswap/message.go create mode 100644 bitswap/message_test.go diff --git a/bitswap/bitswap.go b/bitswap/bitswap.go index 22f97514c..05be4ce0a 100644 --- a/bitswap/bitswap.go +++ b/bitswap/bitswap.go @@ -115,13 +115,12 @@ func (bs *BitSwap) GetBlock(k u.Key, timeout time.Duration) ( func (bs *BitSwap) getBlock(k u.Key, p *peer.Peer, timeout time.Duration) ([]byte, error) { u.DOut("[%s] getBlock '%s' from [%s]\n", bs.peer.ID.Pretty(), k.Pretty(), p.ID.Pretty()) - pmes := new(PBMessage) - pmes.Wantlist = []string{string(k)} + message := newMessage() + message.AppendWanted(k) after := time.After(timeout) resp := bs.listener.Listen(string(k), 1, timeout) - smes := swarm.NewMessage(p, pmes) - bs.meschan.Outgoing <- smes + bs.meschan.Outgoing <- message.ToSwarm(p) select { case resp_mes := <-resp: @@ -149,11 +148,9 @@ func (bs *BitSwap) HaveBlock(blk *blocks.Block) error { } func (bs *BitSwap) SendBlock(p *peer.Peer, b *blocks.Block) { - pmes := new(PBMessage) - pmes.Blocks = [][]byte{b.Data} - - swarm_mes := swarm.NewMessage(p, pmes) - bs.meschan.Outgoing <- swarm_mes + message := newMessage() + message.AppendBlock(b) + bs.meschan.Outgoing <- message.ToSwarm(p) } func (bs *BitSwap) handleMessages() { @@ -257,14 +254,14 @@ func (bs *BitSwap) GetLedger(p *peer.Peer) *Ledger { } func (bs *BitSwap) SendWantList(wl KeySet) error { - pmes := new(PBMessage) + message := newMessage() for k, _ := range wl { - pmes.Wantlist = append(pmes.Wantlist, string(k)) + message.AppendWanted(k) } // Lets just ping everybody all at once for _, ledger := range bs.partners { - bs.meschan.Outgoing <- swarm.NewMessage(ledger.Partner, pmes) + bs.meschan.Outgoing <- message.ToSwarm(ledger.Partner) } return nil diff --git a/bitswap/message.go b/bitswap/message.go new file mode 100644 index 000000000..94bb82ef8 --- /dev/null +++ b/bitswap/message.go @@ -0,0 +1,38 @@ +package bitswap + +import ( + blocks "github.com/jbenet/go-ipfs/blocks" + peer "github.com/jbenet/go-ipfs/peer" + swarm "github.com/jbenet/go-ipfs/swarm" + u "github.com/jbenet/go-ipfs/util" +) + +// message wraps a proto message for convenience +type message struct { + pb PBMessage +} + +func newMessageFromProto(pb PBMessage) *message { + return &message{pb: pb} +} + +func newMessage() *message { + return new(message) +} + +func (m *message) AppendWanted(k u.Key) { + m.pb.Wantlist = append(m.pb.Wantlist, string(k)) +} + +func (m *message) AppendBlock(b *blocks.Block) { + m.pb.Blocks = append(m.pb.Blocks, b.Data) +} + +func (m *message) ToProto() *PBMessage { + cp := m.pb + return &cp +} + +func (m *message) ToSwarm(p *peer.Peer) *swarm.Message { + return swarm.NewMessage(p, m.ToProto()) +} diff --git a/bitswap/message_test.go b/bitswap/message_test.go new file mode 100644 index 000000000..bc52b5aa9 --- /dev/null +++ b/bitswap/message_test.go @@ -0,0 +1,75 @@ +package bitswap + +import ( + "bytes" + "testing" + + blocks "github.com/jbenet/go-ipfs/blocks" + u "github.com/jbenet/go-ipfs/util" +) + +func TestAppendWanted(t *testing.T) { + const str = "foo" + m := newMessage() + m.AppendWanted(u.Key(str)) + + if !contains(m.ToProto().GetWantlist(), str) { + t.Fail() + } +} + +func TestNewMessageFromProto(t *testing.T) { + const str = "a_key" + protoMessage := new(PBMessage) + protoMessage.Wantlist = []string{string(str)} + if !contains(protoMessage.Wantlist, str) { + t.Fail() + } + m := newMessageFromProto(*protoMessage) + if !contains(m.ToProto().GetWantlist(), str) { + t.Fail() + } +} + +func TestAppendBlock(t *testing.T) { + + strs := make([]string, 2) + strs = append(strs, "Celeritas") + strs = append(strs, "Incendia") + + m := newMessage() + for _, str := range strs { + block, err := blocks.NewBlock([]byte(str)) + if err != nil { + t.Fail() + } + m.AppendBlock(block) + } + + // assert strings are in proto message + for _, blockbytes := range m.ToProto().GetBlocks() { + s := bytes.NewBuffer(blockbytes).String() + if !contains(strs, s) { + t.Fail() + } + } +} + +func TestCopyProtoByValue(t *testing.T) { + const str = "foo" + m := newMessage() + protoBeforeAppend := m.ToProto() + m.AppendWanted(u.Key(str)) + if contains(protoBeforeAppend.GetWantlist(), str) { + t.Fail() + } +} + +func contains(s []string, x string) bool { + for _, a := range s { + if a == x { + return true + } + } + return false +} From 7f39299033dcdbefb4d6fdd743aaa84dd1abc5dd Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Thu, 11 Sep 2014 11:13:07 -0700 Subject: [PATCH 08/28] chore(deps:go-multiaddr) update to b90678896b52c procedure is to execute ``` cd my/github.com/jbenet/go-multiaddr git pull cd my/github.com/jbenet/go-ipfs godep update github.com/jbenet/go-multiaddr ``` --- Godeps/Godeps.json | 4 ++-- .../src/github.com/jbenet/go-multiaddr/codec.go | 16 ++++++++++------ .../src/github.com/jbenet/go-multiaddr/index.go | 15 ++++++++++++--- .../jbenet/go-multiaddr/multiaddr_test.go | 4 ++-- .../github.com/jbenet/go-multiaddr/protocols.go | 5 ++++- 5 files changed, 30 insertions(+), 14 deletions(-) diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json index 0fb6cb109..08444fe3e 100644 --- a/Godeps/Godeps.json +++ b/Godeps/Godeps.json @@ -55,8 +55,8 @@ }, { "ImportPath": "github.com/jbenet/go-multiaddr", - "Comment": "0.1.0-1-g99196c0", - "Rev": "99196c0d231f83eea7f6e47cf59cbb5a0b86b358" + "Comment": "0.1.2", + "Rev": "b90678896b52c3e5a4c8176805c6facc3fe3eb82" }, { "ImportPath": "github.com/jbenet/go-multihash", diff --git a/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/codec.go b/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/codec.go index ca0400a99..527f0cb58 100644 --- a/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/codec.go +++ b/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/codec.go @@ -8,10 +8,14 @@ import ( "strings" ) -func StringToBytes(s string) ([]byte, error) { +func stringToBytes(s string) ([]byte, error) { b := []byte{} sp := strings.Split(s, "/") + if sp[0] != "" { + return nil, fmt.Errorf("invalid multiaddr, must begin with /") + } + // consume first empty elem sp = sp[1:] @@ -22,7 +26,7 @@ func StringToBytes(s string) ([]byte, error) { } b = append(b, byte(p.Code)) - a := AddressStringToBytes(p, sp[1]) + a := addressStringToBytes(p, sp[1]) b = append(b, a...) sp = sp[2:] @@ -30,7 +34,7 @@ func StringToBytes(s string) ([]byte, error) { return b, nil } -func BytesToString(b []byte) (ret string, err error) { +func bytesToString(b []byte) (ret string, err error) { // panic handler, in case we try accessing bytes incorrectly. defer func() { if e := recover(); e != nil { @@ -49,7 +53,7 @@ func BytesToString(b []byte) (ret string, err error) { s = strings.Join([]string{s, "/", p.Name}, "") b = b[1:] - a := AddressBytesToString(p, b[:(p.Size/8)]) + a := addressBytesToString(p, b[:(p.Size/8)]) if len(a) > 0 { s = strings.Join([]string{s, "/", a}, "") } @@ -59,7 +63,7 @@ func BytesToString(b []byte) (ret string, err error) { return s, nil } -func AddressStringToBytes(p *Protocol, s string) []byte { +func addressStringToBytes(p *Protocol, s string) []byte { switch p.Code { // ipv4,6 @@ -79,7 +83,7 @@ func AddressStringToBytes(p *Protocol, s string) []byte { return []byte{} } -func AddressBytesToString(p *Protocol, b []byte) string { +func addressBytesToString(p *Protocol, b []byte) string { switch p.Code { // ipv4,6 diff --git a/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/index.go b/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/index.go index 413a971f5..df22012e9 100644 --- a/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/index.go +++ b/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/index.go @@ -5,22 +5,26 @@ import ( "strings" ) +// Multiaddr is the data structure representing a multiaddr type Multiaddr struct { Bytes []byte } +// NewMultiaddr parses and validates an input string, returning a *Multiaddr func NewMultiaddr(s string) (*Multiaddr, error) { - b, err := StringToBytes(s) + b, err := stringToBytes(s) if err != nil { return nil, err } return &Multiaddr{Bytes: b}, nil } +// String returns the string representation of a Multiaddr func (m *Multiaddr) String() (string, error) { - return BytesToString(m.Bytes) + return bytesToString(m.Bytes) } +// Protocols returns the list of protocols this Multiaddr has. func (m *Multiaddr) Protocols() (ret []*Protocol, err error) { // panic handler, in case we try accessing bytes incorrectly. @@ -44,12 +48,14 @@ func (m *Multiaddr) Protocols() (ret []*Protocol, err error) { return ps, nil } +// Encapsulate wraps a given Multiaddr, returning the resulting joined Multiaddr func (m *Multiaddr) Encapsulate(o *Multiaddr) *Multiaddr { b := make([]byte, len(m.Bytes)+len(o.Bytes)) b = append(m.Bytes, o.Bytes...) return &Multiaddr{Bytes: b} } +// Decapsulate unwraps Multiaddr up until the given Multiaddr is found. func (m *Multiaddr) Decapsulate(o *Multiaddr) (*Multiaddr, error) { s1, err := m.String() if err != nil { @@ -68,9 +74,10 @@ func (m *Multiaddr) Decapsulate(o *Multiaddr) (*Multiaddr, error) { return NewMultiaddr(s1[:i]) } +// DialArgs is a convenience function returning arguments for use in net.Dial func (m *Multiaddr) DialArgs() (string, string, error) { if !m.IsThinWaist() { - return "", "", fmt.Errorf("%s is not a 'thin waist' address.", m) + return "", "", fmt.Errorf("%s is not a 'thin waist' address", m) } str, err := m.String() @@ -84,6 +91,8 @@ func (m *Multiaddr) DialArgs() (string, string, error) { return network, host, nil } +// IsThinWaist returns whether this multiaddr includes "Thin Waist" Protocols. +// This means: /{IP4, IP6}/{TCP, UDP} func (m *Multiaddr) IsThinWaist() bool { p, err := m.Protocols() if err != nil { diff --git a/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/multiaddr_test.go b/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/multiaddr_test.go index 976e09556..65cb97219 100644 --- a/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/multiaddr_test.go +++ b/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/multiaddr_test.go @@ -14,7 +14,7 @@ func TestStringToBytes(t *testing.T) { t.Error("failed to decode hex", h) } - b2, err := StringToBytes(s) + b2, err := stringToBytes(s) if err != nil { t.Error("failed to convert", s) } @@ -35,7 +35,7 @@ func TestBytesToString(t *testing.T) { t.Error("failed to decode hex", h) } - s2, err := BytesToString(b) + s2, err := bytesToString(b) if err != nil { t.Error("failed to convert", b) } diff --git a/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/protocols.go b/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/protocols.go index e08d01f07..ed4d29f13 100644 --- a/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/protocols.go +++ b/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/protocols.go @@ -1,5 +1,6 @@ package multiaddr +// Protocol is a Multiaddr protocol description structure. type Protocol struct { Code int Size int @@ -10,7 +11,6 @@ type Protocol struct { // 1. avoid parsing the csv // 2. ensuring errors in the csv don't screw up code. // 3. changing a number has to happen in two places. - const ( P_IP4 = 4 P_TCP = 6 @@ -20,6 +20,7 @@ const ( P_SCTP = 132 ) +// Protocols is the list of multiaddr protocols supported by this module. var Protocols = []*Protocol{ &Protocol{P_IP4, 32, "ip4"}, &Protocol{P_TCP, 16, "tcp"}, @@ -32,6 +33,7 @@ var Protocols = []*Protocol{ // {443, 0, "https"}, } +// ProtocolWithName returns the Protocol description with given string name. func ProtocolWithName(s string) *Protocol { for _, p := range Protocols { if p.Name == s { @@ -41,6 +43,7 @@ func ProtocolWithName(s string) *Protocol { return nil } +// ProtocolWithCode returns the Protocol description with given protocol code. func ProtocolWithCode(c int) *Protocol { for _, p := range Protocols { if p.Code == c { From b88ee2870ac3e64a3f537b5716aae11ee27195eb Mon Sep 17 00:00:00 2001 From: Siraj Ravel Date: Thu, 11 Sep 2014 12:25:52 -0700 Subject: [PATCH 09/28] golint cleanup --- cmd/ipfs/init.go | 2 +- identify/identify.go | 10 +++++----- identify/identify_test.go | 4 ++-- routing/dht/dht_test.go | 4 ++-- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/cmd/ipfs/init.go b/cmd/ipfs/init.go index 77f3fb9f0..dffb08efc 100644 --- a/cmd/ipfs/init.go +++ b/cmd/ipfs/init.go @@ -68,7 +68,7 @@ func initCmd(c *commander.Command, inp []string) error { } cfg.Identity.PrivKey = base64.StdEncoding.EncodeToString(skbytes) - id, err := identify.IdFromPubKey(pk) + id, err := identify.IDFromPubKey(pk) if err != nil { return err } diff --git a/identify/identify.go b/identify/identify.go index 72555144c..23171aaff 100644 --- a/identify/identify.go +++ b/identify/identify.go @@ -1,4 +1,4 @@ -// The identify package handles how peers identify with eachother upon +// Package identify handles how peers identify with eachother upon // connection to the network package identify @@ -31,7 +31,7 @@ var SupportedHashes = "SHA256,SHA512,SHA1" // ErrUnsupportedKeyType is returned when a private key cast/type switch fails. var ErrUnsupportedKeyType = errors.New("unsupported key type") -// Perform initial communication with this peer to share node ID's and +// Performs initial communication with this peer to share node ID's and // initiate communication. (secureIn, secureOut, error) func Handshake(self, remote *peer.Peer, in, out chan []byte) (chan []byte, chan []byte, error) { // Generate and send Hello packet. @@ -74,7 +74,7 @@ func Handshake(self, remote *peer.Peer, in, out chan []byte) (chan []byte, chan return nil, nil, err } - remote.ID, err = IdFromPubKey(remote.PubKey) + remote.ID, err = IDFromPubKey(remote.PubKey) if err != nil { return nil, nil, err } @@ -238,8 +238,8 @@ func secureOutProxy(out, secureOut chan []byte, hashType string, mIV, mCKey, mMK out <- buff } } - -func IdFromPubKey(pk ci.PubKey) (peer.ID, error) { +// IDFromPubKey returns Nodes ID given its public key +func IDFromPubKey(pk ci.PubKey) (peer.ID, error) { b, err := pk.Bytes() if err != nil { return nil, err diff --git a/identify/identify_test.go b/identify/identify_test.go index 6ac33039f..3d529f3e4 100644 --- a/identify/identify_test.go +++ b/identify/identify_test.go @@ -20,7 +20,7 @@ func TestHandshake(t *testing.T) { cha := make(chan []byte, 5) chb := make(chan []byte, 5) - ida, err := IdFromPubKey(pka) + ida, err := IDFromPubKey(pka) if err != nil { t.Fatal(err) } @@ -30,7 +30,7 @@ func TestHandshake(t *testing.T) { PrivKey: ska, } - idb, err := IdFromPubKey(pkb) + idb, err := IDFromPubKey(pkb) if err != nil { t.Fatal(err) } diff --git a/routing/dht/dht_test.go b/routing/dht/dht_test.go index 9e14987d8..3b8f7f7d1 100644 --- a/routing/dht/dht_test.go +++ b/routing/dht/dht_test.go @@ -35,7 +35,7 @@ func setupDHTS(n int, t *testing.T) ([]*ma.Multiaddr, []*peer.Peer, []*IpfsDHT) } p.PubKey = pk p.PrivKey = sk - id, err := identify.IdFromPubKey(pk) + id, err := identify.IDFromPubKey(pk) if err != nil { panic(err) } @@ -67,7 +67,7 @@ func makePeer(addr *ma.Multiaddr) *peer.Peer { } p.PrivKey = sk p.PubKey = pk - id, err := identify.IdFromPubKey(pk) + id, err := identify.IDFromPubKey(pk) if err != nil { panic(err) } From 61586864f11e07339b64914c51287623394323ee Mon Sep 17 00:00:00 2001 From: Siraj Ravel Date: Thu, 11 Sep 2014 12:30:59 -0700 Subject: [PATCH 10/28] fmt previous --- identify/identify.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/identify/identify.go b/identify/identify.go index 23171aaff..13d1181b5 100644 --- a/identify/identify.go +++ b/identify/identify.go @@ -238,7 +238,8 @@ func secureOutProxy(out, secureOut chan []byte, hashType string, mIV, mCKey, mMK out <- buff } } -// IDFromPubKey returns Nodes ID given its public key + +// IDFromPubKey returns Nodes ID given its public key func IDFromPubKey(pk ci.PubKey) (peer.ID, error) { b, err := pk.Bytes() if err != nil { From 02deb3cccb37155a83080992b79306959d374499 Mon Sep 17 00:00:00 2001 From: Siraj Ravel Date: Thu, 11 Sep 2014 13:00:56 -0700 Subject: [PATCH 11/28] last golint --- importer/splitting.go | 23 ++++++++++++----------- merkledag/dagreader.go | 2 +- peer/peer.go | 2 +- 3 files changed, 14 insertions(+), 13 deletions(-) diff --git a/importer/splitting.go b/importer/splitting.go index 1832743d8..001408e82 100644 --- a/importer/splitting.go +++ b/importer/splitting.go @@ -34,12 +34,13 @@ func SplitterBySize(n int) BlockSplitter { } // TODO: this should take a reader, not a byte array. what if we're splitting a 3TB file? +//Rabin Fingerprinting for file chunking func Rabin(b []byte) [][]byte { var out [][]byte windowsize := uint64(48) - chunk_max := 1024 * 16 - min_blk_size := 2048 - blk_beg_i := 0 + chunkMax := 1024 * 16 + minBlkSize := 2048 + blkBegI := 0 prime := uint64(61) var poly uint64 @@ -63,21 +64,21 @@ func Rabin(b []byte) [][]byte { poly = (poly * prime) + cur curchecksum -= (uint64(b[i-1]) * prime) - if i-blk_beg_i >= chunk_max { + if i-blkgBegI >= chunkMax { // push block - out = append(out, b[blk_beg_i:i]) - blk_beg_i = i + out = append(out, b[blkgBegI:i]) + blkgBegI = i } // first 13 bits of polynomial are 0 - if poly%8192 == 0 && i-blk_beg_i >= min_blk_size { + if poly%8192 == 0 && i-blkgBegI >= minBlkSize { // push block - out = append(out, b[blk_beg_i:i]) - blk_beg_i = i + out = append(out, b[blkgBegI:i]) + blkgBegI = i } } - if i > blk_beg_i { - out = append(out, b[blk_beg_i:]) + if i > blkgBegI { + out = append(out, b[blkgBegI:]) } return out } diff --git a/merkledag/dagreader.go b/merkledag/dagreader.go index 0aa0d2606..967ec63a4 100644 --- a/merkledag/dagreader.go +++ b/merkledag/dagreader.go @@ -9,7 +9,7 @@ import ( u "github.com/jbenet/go-ipfs/util" ) -var ErrIsDir = errors.New("this dag node is a directory.") +var ErrIsDir = errors.New("this dag node is a directory") // DagReader provides a way to easily read the data contained in a dag. type DagReader struct { diff --git a/peer/peer.go b/peer/peer.go index 1d270450d..870170c4b 100644 --- a/peer/peer.go +++ b/peer/peer.go @@ -16,7 +16,7 @@ import ( // ID is a byte slice representing the identity of a peer. type ID mh.Multihash -// Utililty function for comparing two peer ID's +// Equal is utililty function for comparing two peer ID's func (id ID) Equal(other ID) bool { return bytes.Equal(id, other) } From 5059654ec4465977590f9c8310bf96187e5dfdf8 Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Thu, 11 Sep 2014 13:05:59 -0700 Subject: [PATCH 12/28] config: Broke out config directory into its own const --- config/config.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/config/config.go b/config/config.go index b80ff1a2c..76088da1f 100644 --- a/config/config.go +++ b/config/config.go @@ -34,12 +34,13 @@ type Config struct { Peers []*SavedPeer } -var DefaultConfigFilePath = "~/.go-ipfs/config" -var DefaultConfigFile = `{ +const DefaultPathRoot = "~/.go-ipfs" +const DefaultConfigFilePath = DefaultPathRoot + "/config" +const DefaultConfigFile = `{ "identity": {}, "datastore": { "type": "leveldb", - "path": "~/.go-ipfs/datastore" + "path": "` + DefaultPathRoot + `/datastore" } } ` From f1e088508a4ffc19f50dab86f0208c38318b5e2e Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Thu, 11 Sep 2014 13:06:51 -0700 Subject: [PATCH 13/28] config: Made WriteConfigFile create config parent directory if it doesn't exist --- config/serialize.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/config/serialize.go b/config/serialize.go index bf495a260..647e19e33 100644 --- a/config/serialize.go +++ b/config/serialize.go @@ -22,6 +22,11 @@ func ReadConfigFile(filename string, cfg interface{}) error { // WriteConfigFile writes the config from `cfg` into `filename`. func WriteConfigFile(filename string, cfg interface{}) error { + err := os.MkdirAll(filepath.Dir(filename), 0775) + if err != nil { + return err + } + f, err := os.Create(filename) if err != nil { return err From fbec97982f4df17d233a02090597c491032868c9 Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Thu, 11 Sep 2014 13:07:38 -0700 Subject: [PATCH 14/28] cmd/init: Fixed init checking if config exists --- cmd/ipfs/init.go | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/cmd/ipfs/init.go b/cmd/ipfs/init.go index 77f3fb9f0..839ee2c9d 100644 --- a/cmd/ipfs/init.go +++ b/cmd/ipfs/init.go @@ -32,9 +32,13 @@ func init() { } func initCmd(c *commander.Command, inp []string) error { - _, err := os.Lstat(config.DefaultConfigFilePath) + filename, err := config.Filename(config.DefaultConfigFilePath) + if err != nil { + return errors.New("Couldn't get home directory path") + } + fi, err := os.Lstat(filename) force := c.Flag.Lookup("f").Value.Get().(bool) - if err != nil && !force { + if fi != nil || (err != nil && !os.IsNotExist(err)) && !force { return errors.New("ipfs configuration file already exists!\nReinitializing would overwrite your keys.\n(use -f to force overwrite)") } cfg := new(config.Config) From 7b32c11347c7018a7f2ca39d5d362065b8927194 Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Thu, 11 Sep 2014 17:22:44 -0700 Subject: [PATCH 15/28] core: Refactored node initialization --- core/core.go | 59 +++++++++++++++++++++++++++++++--------------------- 1 file changed, 35 insertions(+), 24 deletions(-) diff --git a/core/core.go b/core/core.go index b369f2ffa..4e06621f5 100644 --- a/core/core.go +++ b/core/core.go @@ -69,13 +69,32 @@ func NewIpfsNode(cfg *config.Config, online bool) (*IpfsNode, error) { return nil, err } - var swap *bitswap.BitSwap + local, err := initIdentity(cfg) + if err != nil { + return nil, err + } + + var ( + net *swarm.Swarm + // TODO: refactor so we can use IpfsRouting interface instead of being DHT-specific + route* dht.IpfsDHT + swap *bitswap.BitSwap + ) + if online { - swap, err = loadBitswap(cfg, d) + net = swarm.NewSwarm(local) + err = net.Listen() if err != nil { return nil, err } + + route = dht.NewDHT(local, net, d) + route.Start() + + swap = bitswap.NewBitSwap(local, net, d, route) swap.SetStrategy(bitswap.YesManStrategy) + + go initConnections(cfg, route) } bs, err := bserv.NewBlockService(d, swap) @@ -85,19 +104,24 @@ func NewIpfsNode(cfg *config.Config, online bool) (*IpfsNode, error) { dag := &merkledag.DAGService{Blocks: bs} - n := &IpfsNode{ + return &IpfsNode{ Config: cfg, PeerMap: &peer.Map{}, Datastore: d, Blocks: bs, DAG: dag, Resolver: &path.Resolver{DAG: dag}, - } - - return n, nil + BitSwap: swap, + Identity: local, + Routing: route, + }, nil } -func loadBitswap(cfg *config.Config, d ds.Datastore) (*bitswap.BitSwap, error) { +func initIdentity(cfg *config.Config) (*peer.Peer, error) { + if len(cfg.Identity.PeerID) == 0 { + return nil, errors.New("No peer ID in config! (was ipfs init run?)") + } + maddr, err := ma.NewMultiaddr(cfg.Identity.Address) if err != nil { return nil, err @@ -113,26 +137,15 @@ func loadBitswap(cfg *config.Config, d ds.Datastore) (*bitswap.BitSwap, error) { return nil, err } - local := &peer.Peer{ + return &peer.Peer{ ID: peer.ID(b58.Decode(cfg.Identity.PeerID)), Addresses: []*ma.Multiaddr{maddr}, PrivKey: sk, PubKey: sk.GetPublic(), - } - - if len(local.ID) == 0 { - return nil, errors.New("No peer ID in config! (was ipfs init run?)") - } - - net := swarm.NewSwarm(local) - err = net.Listen() - if err != nil { - return nil, err - } - - route := dht.NewDHT(local, net, d) - route.Start() + }, nil +} +func initConnections(cfg *config.Config, route *dht.IpfsDHT) { for _, p := range cfg.Peers { maddr, err := ma.NewMultiaddr(p.Address) if err != nil { @@ -145,8 +158,6 @@ func loadBitswap(cfg *config.Config, d ds.Datastore) (*bitswap.BitSwap, error) { u.PErr("Bootstrapping error: %v\n", err) } } - - return bitswap.NewBitSwap(local, net, d, route), nil } func (n *IpfsNode) PinDagNode(nd *merkledag.Node) error { From b63e10f3b5d72d415329e49757cdbd8ce54669c8 Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Thu, 11 Sep 2014 17:48:05 -0700 Subject: [PATCH 16/28] core: Don't require address for node initialization --- core/core.go | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/core/core.go b/core/core.go index 4e06621f5..beb6ddd5a 100644 --- a/core/core.go +++ b/core/core.go @@ -122,9 +122,15 @@ func initIdentity(cfg *config.Config) (*peer.Peer, error) { return nil, errors.New("No peer ID in config! (was ipfs init run?)") } - maddr, err := ma.NewMultiaddr(cfg.Identity.Address) - if err != nil { - return nil, err + // address is optional + var addresses []*ma.Multiaddr + if len(cfg.Identity.Address) > 0 { + maddr, err := ma.NewMultiaddr(cfg.Identity.Address) + if err != nil { + return nil, err + } + + addresses = []*ma.Multiaddr{ maddr } } skb, err := base64.StdEncoding.DecodeString(cfg.Identity.PrivKey) @@ -139,7 +145,7 @@ func initIdentity(cfg *config.Config) (*peer.Peer, error) { return &peer.Peer{ ID: peer.ID(b58.Decode(cfg.Identity.PeerID)), - Addresses: []*ma.Multiaddr{maddr}, + Addresses: addresses, PrivKey: sk, PubKey: sk.GetPublic(), }, nil From 3ae37b29bde242c5bf87deb33b3a6dedd45b8b41 Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Thu, 11 Sep 2014 17:50:05 -0700 Subject: [PATCH 17/28] core: Show error when config identity is not set --- core/core.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/core/core.go b/core/core.go index beb6ddd5a..0a9db055a 100644 --- a/core/core.go +++ b/core/core.go @@ -118,6 +118,10 @@ func NewIpfsNode(cfg *config.Config, online bool) (*IpfsNode, error) { } func initIdentity(cfg *config.Config) (*peer.Peer, error) { + if cfg.Identity == nil { + return nil, errors.New("Identity was not set in config (was ipfs init run?)") + } + if len(cfg.Identity.PeerID) == 0 { return nil, errors.New("No peer ID in config! (was ipfs init run?)") } From 50d318c0bed3ee2a1b54f5a9b81b7ddd00d6f6da Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Thu, 11 Sep 2014 17:52:21 -0700 Subject: [PATCH 18/28] core: Updated basic initialization tests --- core/core_test.go | 28 +++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/core/core_test.go b/core/core_test.go index f2de5f016..c3c7474c2 100644 --- a/core/core_test.go +++ b/core/core_test.go @@ -6,17 +6,35 @@ import ( config "github.com/jbenet/go-ipfs/config" ) -func TestDatastores(t *testing.T) { +func TestInitialization(t *testing.T) { + id := &config.Identity { + PeerID: "QmNgdzLieYi8tgfo2WfTUzNVH5hQK9oAYGVf6dxN12NrHt", + Address: "/ip4/127.0.0.1/tcp/8000", + PrivKey: "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", + } good := []*config.Config{ - &config.Config{Datastore: &config.Datastore{Type: "memory"}}, - &config.Config{Datastore: &config.Datastore{Type: "leveldb", Path: ".testdb"}}, + &config.Config { + Identity: id, + Datastore: &config.Datastore{ + Type: "memory", + }, + }, + + &config.Config { + Identity: id, + Datastore: &config.Datastore { + Type: "leveldb", + Path: ".testdb", + }, + }, } bad := []*config.Config{ - &config.Config{Datastore: &config.Datastore{}}, - &config.Config{Datastore: &config.Datastore{Type: "badtype"}}, + &config.Config{Identity: id, Datastore: &config.Datastore{}}, + &config.Config{Identity: id, Datastore: &config.Datastore{Type: "badtype"}}, &config.Config{}, + &config.Config{Datastore: &config.Datastore{Type: "memory"}}, nil, } From a5c0f67c8c3b6f639a539b5e5f8850a7a7e89ecf Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Fri, 12 Sep 2014 04:20:46 -0700 Subject: [PATCH 19/28] fix(config) failing test also replace *Datastore with Datastore --- cmd/ipfs/init.go | 2 +- config/config.go | 2 +- config/config_test.go | 11 ++++++++--- core/core_test.go | 18 +++++++++--------- core/datastore.go | 7 ++++--- 5 files changed, 23 insertions(+), 17 deletions(-) diff --git a/cmd/ipfs/init.go b/cmd/ipfs/init.go index 839ee2c9d..88a8c1ba0 100644 --- a/cmd/ipfs/init.go +++ b/cmd/ipfs/init.go @@ -43,7 +43,7 @@ func initCmd(c *commander.Command, inp []string) error { } cfg := new(config.Config) - cfg.Datastore = new(config.Datastore) + cfg.Datastore = config.Datastore{} dspath, err := u.TildeExpansion("~/.go-ipfs/datastore") if err != nil { return err diff --git a/config/config.go b/config/config.go index 76088da1f..25f40ec9e 100644 --- a/config/config.go +++ b/config/config.go @@ -30,7 +30,7 @@ type SavedPeer struct { // Config is used to load IPFS config files. type Config struct { Identity *Identity - Datastore *Datastore + Datastore Datastore Peers []*SavedPeer } diff --git a/config/config_test.go b/config/config_test.go index ffc7ef7af..edbc1c764 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -6,12 +6,17 @@ import ( ) func TestConfig(t *testing.T) { - - cfg, err := Load(".ipfsconfig") + const filename = ".ipfsconfig" + cfgWritten := new(Config) + err := WriteConfigFile(filename, cfgWritten) + if err != nil { + t.Error(err) + } + cfgRead, err := Load(filename) if err != nil { t.Error(err) return } - fmt.Printf(cfg.Datastore.Path) + fmt.Printf(cfgRead.Datastore.Path) } diff --git a/core/core_test.go b/core/core_test.go index c3c7474c2..c6695eb6b 100644 --- a/core/core_test.go +++ b/core/core_test.go @@ -7,23 +7,23 @@ import ( ) func TestInitialization(t *testing.T) { - id := &config.Identity { - PeerID: "QmNgdzLieYi8tgfo2WfTUzNVH5hQK9oAYGVf6dxN12NrHt", + id := &config.Identity{ + PeerID: "QmNgdzLieYi8tgfo2WfTUzNVH5hQK9oAYGVf6dxN12NrHt", Address: "/ip4/127.0.0.1/tcp/8000", PrivKey: "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", } good := []*config.Config{ - &config.Config { + &config.Config{ Identity: id, - Datastore: &config.Datastore{ + Datastore: config.Datastore{ Type: "memory", }, }, - &config.Config { + &config.Config{ Identity: id, - Datastore: &config.Datastore { + Datastore: config.Datastore{ Type: "leveldb", Path: ".testdb", }, @@ -31,10 +31,10 @@ func TestInitialization(t *testing.T) { } bad := []*config.Config{ - &config.Config{Identity: id, Datastore: &config.Datastore{}}, - &config.Config{Identity: id, Datastore: &config.Datastore{Type: "badtype"}}, + &config.Config{Identity: id, Datastore: config.Datastore{}}, + &config.Config{Identity: id, Datastore: config.Datastore{Type: "badtype"}}, &config.Config{}, - &config.Config{Datastore: &config.Datastore{Type: "memory"}}, + &config.Config{Datastore: config.Datastore{Type: "memory"}}, nil, } diff --git a/core/datastore.go b/core/datastore.go index 5395e74a4..9105adaab 100644 --- a/core/datastore.go +++ b/core/datastore.go @@ -2,13 +2,14 @@ package core import ( "fmt" + ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/datastore.go" lds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/datastore.go/leveldb" config "github.com/jbenet/go-ipfs/config" ) -func makeDatastore(cfg *config.Datastore) (ds.Datastore, error) { - if cfg == nil || len(cfg.Type) == 0 { +func makeDatastore(cfg config.Datastore) (ds.Datastore, error) { + if len(cfg.Type) == 0 { return nil, fmt.Errorf("config datastore.type required") } @@ -22,7 +23,7 @@ func makeDatastore(cfg *config.Datastore) (ds.Datastore, error) { return nil, fmt.Errorf("Unknown datastore type: %s", cfg.Type) } -func makeLevelDBDatastore(cfg *config.Datastore) (ds.Datastore, error) { +func makeLevelDBDatastore(cfg config.Datastore) (ds.Datastore, error) { if len(cfg.Path) == 0 { return nil, fmt.Errorf("config datastore.path required for leveldb") } From 2e512e81c92b44ba6a4a86b36b9de8dd35f7dc53 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Fri, 12 Sep 2014 04:36:07 -0700 Subject: [PATCH 20/28] test(config) add path assertion --- config/config_test.go | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/config/config_test.go b/config/config_test.go index edbc1c764..c891d6c51 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -1,13 +1,14 @@ package config import ( - "fmt" "testing" ) func TestConfig(t *testing.T) { const filename = ".ipfsconfig" + const dsPath = "/path/to/datastore" cfgWritten := new(Config) + cfgWritten.Datastore.Path = dsPath err := WriteConfigFile(filename, cfgWritten) if err != nil { t.Error(err) @@ -17,6 +18,7 @@ func TestConfig(t *testing.T) { t.Error(err) return } - - fmt.Printf(cfgRead.Datastore.Path) + if cfgWritten.Datastore.Path != cfgRead.Datastore.Path { + t.Fail() + } } From 94b8f1d22bea077c2dda2b2013c9bd8100b08725 Mon Sep 17 00:00:00 2001 From: Siraj Ravel Date: Fri, 12 Sep 2014 09:30:47 -0700 Subject: [PATCH 21/28] build fixed --- importer/splitting.go | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/importer/splitting.go b/importer/splitting.go index 001408e82..d2690c784 100644 --- a/importer/splitting.go +++ b/importer/splitting.go @@ -64,21 +64,21 @@ func Rabin(b []byte) [][]byte { poly = (poly * prime) + cur curchecksum -= (uint64(b[i-1]) * prime) - if i-blkgBegI >= chunkMax { + if i-blkBegI >= chunkMax { // push block - out = append(out, b[blkgBegI:i]) - blkgBegI = i + out = append(out, b[blkBegI:i]) + blkBegI = i } // first 13 bits of polynomial are 0 - if poly%8192 == 0 && i-blkgBegI >= minBlkSize { + if poly%8192 == 0 && i-blkBegI >= minBlkSize { // push block - out = append(out, b[blkgBegI:i]) - blkgBegI = i + out = append(out, b[blkBegI:i]) + blkBegI = i } } - if i > blkgBegI { - out = append(out, b[blkgBegI:]) + if i > blkBegI { + out = append(out, b[blkBegI:]) } return out } From b249ebe84e5a929e2b399944fde327b3445d8588 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Fri, 12 Sep 2014 22:40:13 -0700 Subject: [PATCH 22/28] fix(identify) error handling * handle subroutine errors * further specify the type of channel --- identify/identify.go | 34 +++++++++++++++++++++++++++------- 1 file changed, 27 insertions(+), 7 deletions(-) diff --git a/identify/identify.go b/identify/identify.go index 13d1181b5..b6b896780 100644 --- a/identify/identify.go +++ b/identify/identify.go @@ -33,11 +33,14 @@ var ErrUnsupportedKeyType = errors.New("unsupported key type") // Performs initial communication with this peer to share node ID's and // initiate communication. (secureIn, secureOut, error) -func Handshake(self, remote *peer.Peer, in, out chan []byte) (chan []byte, chan []byte, error) { +func Handshake(self, remote *peer.Peer, in <-chan []byte, out chan<- []byte) (<-chan []byte, chan<- []byte, error) { // Generate and send Hello packet. // Hello = (rand, PublicKey, Supported) nonce := make([]byte, 16) - rand.Read(nonce) + _, err := rand.Read(nonce) + if err != nil { + return nil, nil, err + } hello := new(Hello) @@ -95,6 +98,9 @@ func Handshake(self, remote *peer.Peer, in, out chan []byte) (chan []byte, chan } epubkey, done, err := ci.GenerateEKeyPair(exchange) // Generate EphemeralPubKey + if err != nil { + return nil, nil, err + } var handshake bytes.Buffer // Gather corpus to sign. handshake.Write(encoded) @@ -110,6 +116,9 @@ func Handshake(self, remote *peer.Peer, in, out chan []byte) (chan []byte, chan } exEncoded, err := proto.Marshal(exPacket) + if err != nil { + return nil, nil, err + } out <- exEncoded @@ -124,9 +133,18 @@ func Handshake(self, remote *peer.Peer, in, out chan []byte) (chan []byte, chan } var theirHandshake bytes.Buffer - theirHandshake.Write(resp) - theirHandshake.Write(encoded) - theirHandshake.Write(exchangeResp.GetEpubkey()) + _, err = theirHandshake.Write(resp) + if err != nil { + return nil, nil, err + } + _, err = theirHandshake.Write(encoded) + if err != nil { + return nil, nil, err + } + _, err = theirHandshake.Write(exchangeResp.GetEpubkey()) + if err != nil { + return nil, nil, err + } ok, err := remote.PubKey.Verify(theirHandshake.Bytes(), exchangeResp.GetSignature()) if err != nil { @@ -176,7 +194,7 @@ func makeMac(hashType string, key []byte) (hash.Hash, int) { } } -func secureInProxy(in, secureIn chan []byte, hashType string, tIV, tCKey, tMKey []byte) { +func secureInProxy(in <-chan []byte, secureIn chan<- []byte, hashType string, tIV, tCKey, tMKey []byte) { theirBlock, _ := aes.NewCipher(tCKey) theirCipher := cipher.NewCTR(theirBlock, tIV) @@ -185,6 +203,7 @@ func secureInProxy(in, secureIn chan []byte, hashType string, tIV, tCKey, tMKey for { data, ok := <-in if !ok { + close(secureIn) return } @@ -211,7 +230,7 @@ func secureInProxy(in, secureIn chan []byte, hashType string, tIV, tCKey, tMKey } } -func secureOutProxy(out, secureOut chan []byte, hashType string, mIV, mCKey, mMKey []byte) { +func secureOutProxy(out chan<- []byte, secureOut <-chan []byte, hashType string, mIV, mCKey, mMKey []byte) { myBlock, _ := aes.NewCipher(mCKey) myCipher := cipher.NewCTR(myBlock, mIV) @@ -220,6 +239,7 @@ func secureOutProxy(out, secureOut chan []byte, hashType string, mIV, mCKey, mMK for { data, ok := <-secureOut if !ok { + close(out) return } From 7bf9ff617daf25c01d0e042555d99378e6def159 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Fri, 12 Sep 2014 22:54:14 -0700 Subject: [PATCH 23/28] refactor(swarm) specify direction of secure chans --- swarm/conn.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/swarm/conn.go b/swarm/conn.go index 1f2df1658..468e86cd2 100644 --- a/swarm/conn.go +++ b/swarm/conn.go @@ -25,8 +25,8 @@ type Conn struct { Closed chan bool Outgoing *msgio.Chan Incoming *msgio.Chan - secIn chan []byte - secOut chan []byte + secIn <-chan []byte + secOut chan<- []byte } // ConnMap maps Keys (Peer.IDs) to Connections. From c236f8cb4fe7df6760bcd2d82382382c715ecda5 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Sat, 13 Sep 2014 01:29:19 -0700 Subject: [PATCH 24/28] refactor(identify) extract hello --- identify/identify.go | 29 +++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/identify/identify.go b/identify/identify.go index b6b896780..bb11686e2 100644 --- a/identify/identify.go +++ b/identify/identify.go @@ -31,22 +31,20 @@ var SupportedHashes = "SHA256,SHA512,SHA1" // ErrUnsupportedKeyType is returned when a private key cast/type switch fails. var ErrUnsupportedKeyType = errors.New("unsupported key type") -// Performs initial communication with this peer to share node ID's and -// initiate communication. (secureIn, secureOut, error) -func Handshake(self, remote *peer.Peer, in <-chan []byte, out chan<- []byte) (<-chan []byte, chan<- []byte, error) { +func genRandHello(self *peer.Peer) (*Hello, error) { + hello := new(Hello) + // Generate and send Hello packet. // Hello = (rand, PublicKey, Supported) nonce := make([]byte, 16) _, err := rand.Read(nonce) if err != nil { - return nil, nil, err + return nil, err } - hello := new(Hello) - myPubKey, err := self.PubKey.Bytes() if err != nil { - return nil, nil, err + return nil, err } hello.Rand = nonce @@ -54,8 +52,18 @@ func Handshake(self, remote *peer.Peer, in <-chan []byte, out chan<- []byte) (<- hello.Exchanges = &SupportedExchanges hello.Ciphers = &SupportedCiphers hello.Hashes = &SupportedHashes + return hello, nil +} - encoded, err := proto.Marshal(hello) +// Performs initial communication with this peer to share node ID's and +// initiate communication. (secureIn, secureOut, error) +func Handshake(self, remote *peer.Peer, in <-chan []byte, out chan<- []byte) (<-chan []byte, chan<- []byte, error) { + h, err := genRandHello(self) + if err != nil { + return nil, nil, err + } + + encoded, err := proto.Marshal(h) if err != nil { return nil, nil, err } @@ -160,6 +168,11 @@ func Handshake(self, remote *peer.Peer, in <-chan []byte, out chan<- []byte) (<- return nil, nil, err } + myPubKey, err := self.PubKey.Bytes() + if err != nil { + return nil, nil, err + } + cmp := bytes.Compare(myPubKey, helloResp.GetPubkey()) mIV, tIV, mCKey, tCKey, mMKey, tMKey := ci.KeyStretcher(cmp, cipherType, hashType, secret) From 1386205f7ad6f645f7cb78a33dc03cd4916b3813 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Sat, 13 Sep 2014 01:34:26 -0700 Subject: [PATCH 25/28] chore(identify) move private func to bottom of file --- identify/identify.go | 48 ++++++++++++++++++++++---------------------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/identify/identify.go b/identify/identify.go index bb11686e2..01e629baf 100644 --- a/identify/identify.go +++ b/identify/identify.go @@ -31,30 +31,6 @@ var SupportedHashes = "SHA256,SHA512,SHA1" // ErrUnsupportedKeyType is returned when a private key cast/type switch fails. var ErrUnsupportedKeyType = errors.New("unsupported key type") -func genRandHello(self *peer.Peer) (*Hello, error) { - hello := new(Hello) - - // Generate and send Hello packet. - // Hello = (rand, PublicKey, Supported) - nonce := make([]byte, 16) - _, err := rand.Read(nonce) - if err != nil { - return nil, err - } - - myPubKey, err := self.PubKey.Bytes() - if err != nil { - return nil, err - } - - hello.Rand = nonce - hello.Pubkey = myPubKey - hello.Exchanges = &SupportedExchanges - hello.Ciphers = &SupportedCiphers - hello.Hashes = &SupportedHashes - return hello, nil -} - // Performs initial communication with this peer to share node ID's and // initiate communication. (secureIn, secureOut, error) func Handshake(self, remote *peer.Peer, in <-chan []byte, out chan<- []byte) (<-chan []byte, chan<- []byte, error) { @@ -322,3 +298,27 @@ func selectBest(myPrefs, theirPrefs string) (string, error) { return "", errors.New("No algorithms in common!") } + +func genRandHello(self *peer.Peer) (*Hello, error) { + hello := new(Hello) + + // Generate and send Hello packet. + // Hello = (rand, PublicKey, Supported) + nonce := make([]byte, 16) + _, err := rand.Read(nonce) + if err != nil { + return nil, err + } + + myPubKey, err := self.PubKey.Bytes() + if err != nil { + return nil, err + } + + hello.Rand = nonce + hello.Pubkey = myPubKey + hello.Exchanges = &SupportedExchanges + hello.Ciphers = &SupportedCiphers + hello.Hashes = &SupportedHashes + return hello, nil +} From 6f6d79d6992a00895e11558f89f6f0966d65788d Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Sat, 13 Sep 2014 01:42:59 -0700 Subject: [PATCH 26/28] refactor(identify) extract proto encoding --- identify/identify.go | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/identify/identify.go b/identify/identify.go index 01e629baf..205e56733 100644 --- a/identify/identify.go +++ b/identify/identify.go @@ -34,17 +34,14 @@ var ErrUnsupportedKeyType = errors.New("unsupported key type") // Performs initial communication with this peer to share node ID's and // initiate communication. (secureIn, secureOut, error) func Handshake(self, remote *peer.Peer, in <-chan []byte, out chan<- []byte) (<-chan []byte, chan<- []byte, error) { - h, err := genRandHello(self) + + encodedHello, err := encodedProtoHello(self) if err != nil { return nil, nil, err } - encoded, err := proto.Marshal(h) - if err != nil { - return nil, nil, err - } - - out <- encoded + // TODO(brian): select on |ctx| + out <- encodedHello // Parse their Hello packet and generate an Exchange packet. // Exchange = (EphemeralPubKey, Signature) @@ -322,3 +319,12 @@ func genRandHello(self *peer.Peer) (*Hello, error) { hello.Hashes = &SupportedHashes return hello, nil } + +func encodedProtoHello(self *peer.Peer) ([]byte, error) { + h, err := genRandHello(self) + if err != nil { + return nil, err + } + + return proto.Marshal(h) +} From f743c7ab86e6688ae955952c3bc1c2c1e7ceae26 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Sat, 13 Sep 2014 02:03:52 -0700 Subject: [PATCH 27/28] refactor(identify) pass context to handshake --- Godeps/Godeps.json | 5 + .../p/go.net/context/context.go | 431 ++++++++++++++ .../p/go.net/context/context_test.go | 553 ++++++++++++++++++ .../p/go.net/context/withtimeout_test.go | 26 + identify/identify.go | 8 +- identify/identify_test.go | 7 +- swarm/swarm.go | 9 +- 7 files changed, 1032 insertions(+), 7 deletions(-) create mode 100644 Godeps/_workspace/src/code.google.com/p/go.net/context/context.go create mode 100644 Godeps/_workspace/src/code.google.com/p/go.net/context/context_test.go create mode 100644 Godeps/_workspace/src/code.google.com/p/go.net/context/withtimeout_test.go diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json index 08444fe3e..5e346f47f 100644 --- a/Godeps/Godeps.json +++ b/Godeps/Godeps.json @@ -19,6 +19,11 @@ "Comment": "null-219", "Rev": "00a7d3b31bbab5795b4a51933c04fc2768242970" }, + { + "ImportPath": "code.google.com/p/go.net/context", + "Comment": "null-144", + "Rev": "ad01a6fcc8a19d3a4478c836895ffe883bd2ceab" + }, { "ImportPath": "code.google.com/p/gogoprotobuf/proto", "Rev": "6c980277330804e94257ac7ef70a3adbe1641059" diff --git a/Godeps/_workspace/src/code.google.com/p/go.net/context/context.go b/Godeps/_workspace/src/code.google.com/p/go.net/context/context.go new file mode 100644 index 000000000..e3c5345d7 --- /dev/null +++ b/Godeps/_workspace/src/code.google.com/p/go.net/context/context.go @@ -0,0 +1,431 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package context defines the Context type, which carries deadlines, +// cancelation signals, and other request-scoped values across API boundaries +// and between processes. +// +// Incoming requests to a server should create a Context, and outgoing calls to +// servers should accept a Context. The chain of function calls between must +// propagate the Context, optionally replacing it with a modified copy created +// using WithDeadline, WithTimeout, WithCancel, or WithValue. +// +// Programs that use Contexts should follow these rules to keep interfaces +// consistent across packages and enable static analysis tools to check context +// propagation: +// +// Do not store Contexts inside a struct type; instead, pass a Context +// explicitly to each function that needs it. The Context should be the first +// parameter, typically named ctx: +// +// func DoSomething(ctx context.Context, arg Arg) error { +// // ... use ctx ... +// } +// +// Do not pass a nil Context, even if a function permits it. Pass context.TODO +// if you are unsure about which Context to use. +// +// Use context Values only for request-scoped data that transits processes and +// APIs, not for passing optional parameters to functions. +// +// The same Context may be passed to functions running in different goroutines; +// Contexts are safe for simultaneous use by multiple goroutines. +// +// See http://blog.golang.org/context for example code for a server that uses +// Contexts. +package context + +import ( + "errors" + "fmt" + "sync" + "time" +) + +// A Context carries a deadline, a cancelation signal, and other values across +// API boundaries. +// +// Context's methods may be called by multiple goroutines simultaneously. +type Context interface { + // Deadline returns the time when work done on behalf of this context + // should be canceled. Deadline returns ok==false when no deadline is + // set. Successive calls to Deadline return the same results. + Deadline() (deadline time.Time, ok bool) + + // Done returns a channel that's closed when work done on behalf of this + // context should be canceled. Done may return nil if this context can + // never be canceled. Successive calls to Done return the same value. + // + // WithCancel arranges for Done to be closed when cancel is called; + // WithDeadline arranges for Done to be closed when the deadline + // expires; WithTimeout arranges for Done to be closed when the timeout + // elapses. + // + // Done is provided for use in select statements: + // + // // DoSomething calls DoSomethingSlow and returns as soon as + // // it returns or ctx.Done is closed. + // func DoSomething(ctx context.Context) (Result, error) { + // c := make(chan Result, 1) + // go func() { c <- DoSomethingSlow(ctx) }() + // select { + // case res := <-c: + // return res, nil + // case <-ctx.Done(): + // return nil, ctx.Err() + // } + // } + // + // See http://blog.golang.org/pipelines for more examples of how to use + // a Done channel for cancelation. + Done() <-chan struct{} + + // Err returns a non-nil error value after Done is closed. Err returns + // Canceled if the context was canceled or DeadlineExceeded if the + // context's deadline passed. No other values for Err are defined. + // After Done is closed, successive calls to Err return the same value. + Err() error + + // Value returns the value associated with this context for key, or nil + // if no value is associated with key. Successive calls to Value with + // the same key returns the same result. + // + // Use context values only for request-scoped data that transits + // processes and API boundaries, not for passing optional parameters to + // functions. + // + // A key identifies a specific value in a Context. Functions that wish + // to store values in Context typically allocate a key in a global + // variable then use that key as the argument to context.WithValue and + // Context.Value. A key can be any type that supports equality; + // packages should define keys as an unexported type to avoid + // collisions. + // + // Packages that define a Context key should provide type-safe accessors + // for the values stores using that key: + // + // // Package user defines a User type that's stored in Contexts. + // package user + // + // import "code.google.com/p/go.net/context" + // + // // User is the type of value stored in the Contexts. + // type User struct {...} + // + // // key is an unexported type for keys defined in this package. + // // This prevents collisions with keys defined in other packages. + // type key int + // + // // userKey is the key for user.User values in Contexts. It is + // // unexported; clients use user.NewContext and user.FromContext + // // instead of using this key directly. + // var userKey key = 0 + // + // // NewContext returns a new Context that carries value u. + // func NewContext(ctx context.Context, u *User) context.Context { + // return context.WithValue(userKey, u) + // } + // + // // FromContext returns the User value stored in ctx, if any. + // func FromContext(ctx context.Context) (*User, bool) { + // u, ok := ctx.Value(userKey).(*User) + // return u, ok + // } + Value(key interface{}) interface{} +} + +// Canceled is the error returned by Context.Err when the context is canceled. +var Canceled = errors.New("context canceled") + +// DeadlineExceeded is the error returned by Context.Err when the context's +// deadline passes. +var DeadlineExceeded = errors.New("context deadline exceeded") + +// An emptyCtx is never canceled, has no values, and has no deadline. +type emptyCtx int + +func (emptyCtx) Deadline() (deadline time.Time, ok bool) { + return +} + +func (emptyCtx) Done() <-chan struct{} { + return nil +} + +func (emptyCtx) Err() error { + return nil +} + +func (emptyCtx) Value(key interface{}) interface{} { + return nil +} + +func (n emptyCtx) String() string { + switch n { + case background: + return "context.Background" + case todo: + return "context.TODO" + } + return "unknown empty Context" +} + +const ( + background emptyCtx = 1 + todo emptyCtx = 2 +) + +// Background returns a non-nil, empty Context. It is never canceled, has no +// values, and has no deadline. It is typically used by the main function, +// initialization, and tests, and as the top-level Context for incoming +// requests. +func Background() Context { + return background +} + +// TODO returns a non-nil, empty Context. Code should use context.TODO when +// it's unclear which Context to use or it's is not yet available (because the +// surrounding function has not yet been extended to accept a Context +// parameter). TODO is recognized by static analysis tools that determine +// whether Contexts are propagated correctly in a program. +func TODO() Context { + return todo +} + +// A CancelFunc tells an operation to abandon its work. +// A CancelFunc does not wait for the work to stop. +// After the first call, subsequent calls to a CancelFunc do nothing. +type CancelFunc func() + +// WithCancel returns a copy of parent with a new Done channel. The returned +// context's Done channel is closed when the returned cancel function is called +// or when the parent context's Done channel is closed, whichever happens first. +func WithCancel(parent Context) (ctx Context, cancel CancelFunc) { + c := newCancelCtx(parent) + propagateCancel(parent, &c) + return &c, func() { c.cancel(true, Canceled) } +} + +// newCancelCtx returns an initialized cancelCtx. +func newCancelCtx(parent Context) cancelCtx { + return cancelCtx{ + Context: parent, + done: make(chan struct{}), + } +} + +// propagateCancel arranges for child to be canceled when parent is. +func propagateCancel(parent Context, child canceler) { + if parent.Done() == nil { + return // parent is never canceled + } + if p, ok := parentCancelCtx(parent); ok { + p.mu.Lock() + if p.err != nil { + // parent has already been canceled + child.cancel(false, p.err) + } else { + if p.children == nil { + p.children = make(map[canceler]bool) + } + p.children[child] = true + } + p.mu.Unlock() + } else { + go func() { + select { + case <-parent.Done(): + child.cancel(false, parent.Err()) + case <-child.Done(): + } + }() + } +} + +// parentCancelCtx follows a chain of parent references until it finds a +// *cancelCtx. This function understands how each of the concrete types in this +// package represents its parent. +func parentCancelCtx(parent Context) (*cancelCtx, bool) { + for { + switch c := parent.(type) { + case *cancelCtx: + return c, true + case *timerCtx: + return &c.cancelCtx, true + case *valueCtx: + parent = c.Context + default: + return nil, false + } + } +} + +// A canceler is a context type that can be canceled directly. The +// implementations are *cancelCtx and *timerCtx. +type canceler interface { + cancel(removeFromParent bool, err error) + Done() <-chan struct{} +} + +// A cancelCtx can be canceled. When canceled, it also cancels any children +// that implement canceler. +type cancelCtx struct { + Context + + done chan struct{} // closed by the first cancel call. + + mu sync.Mutex + children map[canceler]bool // set to nil by the first cancel call + err error // set to non-nil by the first cancel call +} + +func (c *cancelCtx) Done() <-chan struct{} { + return c.done +} + +func (c *cancelCtx) Err() error { + c.mu.Lock() + defer c.mu.Unlock() + return c.err +} + +func (c *cancelCtx) String() string { + return fmt.Sprintf("%v.WithCancel", c.Context) +} + +// cancel closes c.done, cancels each of c's children, and, if +// removeFromParent is true, removes c from its parent's children. +func (c *cancelCtx) cancel(removeFromParent bool, err error) { + if err == nil { + panic("context: internal error: missing cancel error") + } + c.mu.Lock() + if c.err != nil { + c.mu.Unlock() + return // already canceled + } + c.err = err + close(c.done) + for child := range c.children { + // NOTE: acquiring the child's lock while holding parent's lock. + child.cancel(false, err) + } + c.children = nil + c.mu.Unlock() + + if removeFromParent { + if p, ok := parentCancelCtx(c.Context); ok { + p.mu.Lock() + if p.children != nil { + delete(p.children, c) + } + p.mu.Unlock() + } + } +} + +// WithDeadline returns a copy of the parent context with the deadline adjusted +// to be no later than d. If the parent's deadline is already earlier than d, +// WithDeadline(parent, d) is semantically equivalent to parent. The returned +// context's Done channel is closed when the deadline expires, when the returned +// cancel function is called, or when the parent context's Done channel is +// closed, whichever happens first. +// +// Canceling this context releases resources associated with the deadline +// timer, so code should call cancel as soon as the operations running in this +// Context complete. +func WithDeadline(parent Context, deadline time.Time) (Context, CancelFunc) { + if cur, ok := parent.Deadline(); ok && cur.Before(deadline) { + // The current deadline is already sooner than the new one. + return WithCancel(parent) + } + c := &timerCtx{ + cancelCtx: newCancelCtx(parent), + deadline: deadline, + } + propagateCancel(parent, c) + d := deadline.Sub(time.Now()) + if d <= 0 { + c.cancel(true, DeadlineExceeded) // deadline has already passed + return c, func() { c.cancel(true, Canceled) } + } + c.mu.Lock() + defer c.mu.Unlock() + if c.err == nil { + c.timer = time.AfterFunc(d, func() { + c.cancel(true, DeadlineExceeded) + }) + } + return c, func() { c.cancel(true, Canceled) } +} + +// A timerCtx carries a timer and a deadline. It embeds a cancelCtx to +// implement Done and Err. It implements cancel by stopping its timer then +// delegating to cancelCtx.cancel. +type timerCtx struct { + cancelCtx + timer *time.Timer // Under cancelCtx.mu. + + deadline time.Time +} + +func (c *timerCtx) Deadline() (deadline time.Time, ok bool) { + return c.deadline, true +} + +func (c *timerCtx) String() string { + return fmt.Sprintf("%v.WithDeadline(%s [%s])", c.cancelCtx.Context, c.deadline, c.deadline.Sub(time.Now())) +} + +func (c *timerCtx) cancel(removeFromParent bool, err error) { + c.cancelCtx.cancel(removeFromParent, err) + c.mu.Lock() + if c.timer != nil { + c.timer.Stop() + c.timer = nil + } + c.mu.Unlock() +} + +// WithTimeout returns WithDeadline(parent, time.Now().Add(timeout)). +// +// Canceling this context releases resources associated with the deadline +// timer, so code should call cancel as soon as the operations running in this +// Context complete: +// +// func slowOperationWithTimeout(ctx context.Context) (Result, error) { +// ctx, cancel := context.WithTimeout(ctx, 100*time.Millisecond) +// defer cancel() // releases resources if slowOperation completes before timeout elapses +// return slowOperation(ctx) +// } +func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc) { + return WithDeadline(parent, time.Now().Add(timeout)) +} + +// WithValue returns a copy of parent in which the value associated with key is +// val. +// +// Use context Values only for request-scoped data that transits processes and +// APIs, not for passing optional parameters to functions. +func WithValue(parent Context, key interface{}, val interface{}) Context { + return &valueCtx{parent, key, val} +} + +// A valueCtx carries a key-value pair. It implements Value for that key and +// delegates all other calls to the embedded Context. +type valueCtx struct { + Context + key, val interface{} +} + +func (c *valueCtx) String() string { + return fmt.Sprintf("%v.WithValue(%#v, %#v)", c.Context, c.key, c.val) +} + +func (c *valueCtx) Value(key interface{}) interface{} { + if c.key == key { + return c.val + } + return c.Context.Value(key) +} diff --git a/Godeps/_workspace/src/code.google.com/p/go.net/context/context_test.go b/Godeps/_workspace/src/code.google.com/p/go.net/context/context_test.go new file mode 100644 index 000000000..c1a4de5ff --- /dev/null +++ b/Godeps/_workspace/src/code.google.com/p/go.net/context/context_test.go @@ -0,0 +1,553 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package context + +import ( + "fmt" + "math/rand" + "runtime" + "strings" + "sync" + "testing" + "time" +) + +// otherContext is a Context that's not one of the types defined in context.go. +// This lets us test code paths that differ based on the underlying type of the +// Context. +type otherContext struct { + Context +} + +func TestBackground(t *testing.T) { + c := Background() + if c == nil { + t.Fatalf("Background returned nil") + } + select { + case x := <-c.Done(): + t.Errorf("<-c.Done() == %v want nothing (it should block)", x) + default: + } + if got, want := fmt.Sprint(c), "context.Background"; got != want { + t.Errorf("Background().String() = %q want %q", got, want) + } +} + +func TestTODO(t *testing.T) { + c := TODO() + if c == nil { + t.Fatalf("TODO returned nil") + } + select { + case x := <-c.Done(): + t.Errorf("<-c.Done() == %v want nothing (it should block)", x) + default: + } + if got, want := fmt.Sprint(c), "context.TODO"; got != want { + t.Errorf("TODO().String() = %q want %q", got, want) + } +} + +func TestWithCancel(t *testing.T) { + c1, cancel := WithCancel(Background()) + + if got, want := fmt.Sprint(c1), "context.Background.WithCancel"; got != want { + t.Errorf("c1.String() = %q want %q", got, want) + } + + o := otherContext{c1} + c2, _ := WithCancel(o) + contexts := []Context{c1, o, c2} + + for i, c := range contexts { + if d := c.Done(); d == nil { + t.Errorf("c[%d].Done() == %v want non-nil", i, d) + } + if e := c.Err(); e != nil { + t.Errorf("c[%d].Err() == %v want nil", i, e) + } + + select { + case x := <-c.Done(): + t.Errorf("<-c.Done() == %v want nothing (it should block)", x) + default: + } + } + + cancel() + time.Sleep(100 * time.Millisecond) // let cancelation propagate + + for i, c := range contexts { + select { + case <-c.Done(): + default: + t.Errorf("<-c[%d].Done() blocked, but shouldn't have", i) + } + if e := c.Err(); e != Canceled { + t.Errorf("c[%d].Err() == %v want %v", i, e, Canceled) + } + } +} + +func TestParentFinishesChild(t *testing.T) { + // Context tree: + // parent -> cancelChild + // parent -> valueChild -> timerChild + parent, cancel := WithCancel(Background()) + cancelChild, stop := WithCancel(parent) + defer stop() + valueChild := WithValue(parent, "key", "value") + timerChild, stop := WithTimeout(valueChild, 10000*time.Hour) + defer stop() + + select { + case x := <-parent.Done(): + t.Errorf("<-parent.Done() == %v want nothing (it should block)", x) + case x := <-cancelChild.Done(): + t.Errorf("<-cancelChild.Done() == %v want nothing (it should block)", x) + case x := <-timerChild.Done(): + t.Errorf("<-timerChild.Done() == %v want nothing (it should block)", x) + case x := <-valueChild.Done(): + t.Errorf("<-valueChild.Done() == %v want nothing (it should block)", x) + default: + } + + // The parent's children should contain the two cancelable children. + pc := parent.(*cancelCtx) + cc := cancelChild.(*cancelCtx) + tc := timerChild.(*timerCtx) + pc.mu.Lock() + if len(pc.children) != 2 || !pc.children[cc] || !pc.children[tc] { + t.Errorf("bad linkage: pc.children = %v, want %v and %v", + pc.children, cc, tc) + } + pc.mu.Unlock() + + if p, ok := parentCancelCtx(cc.Context); !ok || p != pc { + t.Errorf("bad linkage: parentCancelCtx(cancelChild.Context) = %v, %v want %v, true", p, ok, pc) + } + if p, ok := parentCancelCtx(tc.Context); !ok || p != pc { + t.Errorf("bad linkage: parentCancelCtx(timerChild.Context) = %v, %v want %v, true", p, ok, pc) + } + + cancel() + + pc.mu.Lock() + if len(pc.children) != 0 { + t.Errorf("pc.cancel didn't clear pc.children = %v", pc.children) + } + pc.mu.Unlock() + + // parent and children should all be finished. + check := func(ctx Context, name string) { + select { + case <-ctx.Done(): + default: + t.Errorf("<-%s.Done() blocked, but shouldn't have", name) + } + if e := ctx.Err(); e != Canceled { + t.Errorf("%s.Err() == %v want %v", name, e, Canceled) + } + } + check(parent, "parent") + check(cancelChild, "cancelChild") + check(valueChild, "valueChild") + check(timerChild, "timerChild") + + // WithCancel should return a canceled context on a canceled parent. + precanceledChild := WithValue(parent, "key", "value") + select { + case <-precanceledChild.Done(): + default: + t.Errorf("<-precanceledChild.Done() blocked, but shouldn't have") + } + if e := precanceledChild.Err(); e != Canceled { + t.Errorf("precanceledChild.Err() == %v want %v", e, Canceled) + } +} + +func TestChildFinishesFirst(t *testing.T) { + cancelable, stop := WithCancel(Background()) + defer stop() + for _, parent := range []Context{Background(), cancelable} { + child, cancel := WithCancel(parent) + + select { + case x := <-parent.Done(): + t.Errorf("<-parent.Done() == %v want nothing (it should block)", x) + case x := <-child.Done(): + t.Errorf("<-child.Done() == %v want nothing (it should block)", x) + default: + } + + cc := child.(*cancelCtx) + pc, pcok := parent.(*cancelCtx) // pcok == false when parent == Background() + if p, ok := parentCancelCtx(cc.Context); ok != pcok || (ok && pc != p) { + t.Errorf("bad linkage: parentCancelCtx(cc.Context) = %v, %v want %v, %v", p, ok, pc, pcok) + } + + if pcok { + pc.mu.Lock() + if len(pc.children) != 1 || !pc.children[cc] { + t.Errorf("bad linkage: pc.children = %v, cc = %v", pc.children, cc) + } + pc.mu.Unlock() + } + + cancel() + + if pcok { + pc.mu.Lock() + if len(pc.children) != 0 { + t.Errorf("child's cancel didn't remove self from pc.children = %v", pc.children) + } + pc.mu.Unlock() + } + + // child should be finished. + select { + case <-child.Done(): + default: + t.Errorf("<-child.Done() blocked, but shouldn't have") + } + if e := child.Err(); e != Canceled { + t.Errorf("child.Err() == %v want %v", e, Canceled) + } + + // parent should not be finished. + select { + case x := <-parent.Done(): + t.Errorf("<-parent.Done() == %v want nothing (it should block)", x) + default: + } + if e := parent.Err(); e != nil { + t.Errorf("parent.Err() == %v want nil", e) + } + } +} + +func testDeadline(c Context, wait time.Duration, t *testing.T) { + select { + case <-time.After(wait): + t.Fatalf("context should have timed out") + case <-c.Done(): + } + if e := c.Err(); e != DeadlineExceeded { + t.Errorf("c.Err() == %v want %v", e, DeadlineExceeded) + } +} + +func TestDeadline(t *testing.T) { + c, _ := WithDeadline(Background(), time.Now().Add(100*time.Millisecond)) + if got, prefix := fmt.Sprint(c), "context.Background.WithDeadline("; !strings.HasPrefix(got, prefix) { + t.Errorf("c.String() = %q want prefix %q", got, prefix) + } + testDeadline(c, 200*time.Millisecond, t) + + c, _ = WithDeadline(Background(), time.Now().Add(100*time.Millisecond)) + o := otherContext{c} + testDeadline(o, 200*time.Millisecond, t) + + c, _ = WithDeadline(Background(), time.Now().Add(100*time.Millisecond)) + o = otherContext{c} + c, _ = WithDeadline(o, time.Now().Add(300*time.Millisecond)) + testDeadline(c, 200*time.Millisecond, t) +} + +func TestTimeout(t *testing.T) { + c, _ := WithTimeout(Background(), 100*time.Millisecond) + if got, prefix := fmt.Sprint(c), "context.Background.WithDeadline("; !strings.HasPrefix(got, prefix) { + t.Errorf("c.String() = %q want prefix %q", got, prefix) + } + testDeadline(c, 200*time.Millisecond, t) + + c, _ = WithTimeout(Background(), 100*time.Millisecond) + o := otherContext{c} + testDeadline(o, 200*time.Millisecond, t) + + c, _ = WithTimeout(Background(), 100*time.Millisecond) + o = otherContext{c} + c, _ = WithTimeout(o, 300*time.Millisecond) + testDeadline(c, 200*time.Millisecond, t) +} + +func TestCanceledTimeout(t *testing.T) { + c, _ := WithTimeout(Background(), 200*time.Millisecond) + o := otherContext{c} + c, cancel := WithTimeout(o, 400*time.Millisecond) + cancel() + time.Sleep(100 * time.Millisecond) // let cancelation propagate + select { + case <-c.Done(): + default: + t.Errorf("<-c.Done() blocked, but shouldn't have") + } + if e := c.Err(); e != Canceled { + t.Errorf("c.Err() == %v want %v", e, Canceled) + } +} + +type key1 int +type key2 int + +var k1 = key1(1) +var k2 = key2(1) // same int as k1, different type +var k3 = key2(3) // same type as k2, different int + +func TestValues(t *testing.T) { + check := func(c Context, nm, v1, v2, v3 string) { + if v, ok := c.Value(k1).(string); ok == (len(v1) == 0) || v != v1 { + t.Errorf(`%s.Value(k1).(string) = %q, %t want %q, %t`, nm, v, ok, v1, len(v1) != 0) + } + if v, ok := c.Value(k2).(string); ok == (len(v2) == 0) || v != v2 { + t.Errorf(`%s.Value(k2).(string) = %q, %t want %q, %t`, nm, v, ok, v2, len(v2) != 0) + } + if v, ok := c.Value(k3).(string); ok == (len(v3) == 0) || v != v3 { + t.Errorf(`%s.Value(k3).(string) = %q, %t want %q, %t`, nm, v, ok, v3, len(v3) != 0) + } + } + + c0 := Background() + check(c0, "c0", "", "", "") + + c1 := WithValue(Background(), k1, "c1k1") + check(c1, "c1", "c1k1", "", "") + + if got, want := fmt.Sprint(c1), `context.Background.WithValue(1, "c1k1")`; got != want { + t.Errorf("c.String() = %q want %q", got, want) + } + + c2 := WithValue(c1, k2, "c2k2") + check(c2, "c2", "c1k1", "c2k2", "") + + c3 := WithValue(c2, k3, "c3k3") + check(c3, "c2", "c1k1", "c2k2", "c3k3") + + c4 := WithValue(c3, k1, nil) + check(c4, "c4", "", "c2k2", "c3k3") + + o0 := otherContext{Background()} + check(o0, "o0", "", "", "") + + o1 := otherContext{WithValue(Background(), k1, "c1k1")} + check(o1, "o1", "c1k1", "", "") + + o2 := WithValue(o1, k2, "o2k2") + check(o2, "o2", "c1k1", "o2k2", "") + + o3 := otherContext{c4} + check(o3, "o3", "", "c2k2", "c3k3") + + o4 := WithValue(o3, k3, nil) + check(o4, "o4", "", "c2k2", "") +} + +func TestAllocs(t *testing.T) { + bg := Background() + for _, test := range []struct { + desc string + f func() + limit float64 + gccgoLimit float64 + }{ + { + desc: "Background()", + f: func() { Background() }, + limit: 0, + gccgoLimit: 0, + }, + { + desc: fmt.Sprintf("WithValue(bg, %v, nil)", k1), + f: func() { + c := WithValue(bg, k1, nil) + c.Value(k1) + }, + limit: 1, + gccgoLimit: 3, + }, + { + desc: "WithTimeout(bg, 15*time.Millisecond)", + f: func() { + c, _ := WithTimeout(bg, 15*time.Millisecond) + <-c.Done() + }, + limit: 8, + gccgoLimit: 13, + }, + { + desc: "WithCancel(bg)", + f: func() { + c, cancel := WithCancel(bg) + cancel() + <-c.Done() + }, + limit: 5, + gccgoLimit: 8, + }, + { + desc: "WithTimeout(bg, 100*time.Millisecond)", + f: func() { + c, cancel := WithTimeout(bg, 100*time.Millisecond) + cancel() + <-c.Done() + }, + limit: 8, + gccgoLimit: 25, + }, + } { + limit := test.limit + if runtime.Compiler == "gccgo" { + // gccgo does not yet do escape analysis. + // TOOD(iant): Remove this when gccgo does do escape analysis. + limit = test.gccgoLimit + } + if n := testing.AllocsPerRun(100, test.f); n > limit { + t.Errorf("%s allocs = %f want %d", test.desc, n, int(limit)) + } + } +} + +func TestSimultaneousCancels(t *testing.T) { + root, cancel := WithCancel(Background()) + m := map[Context]CancelFunc{root: cancel} + q := []Context{root} + // Create a tree of contexts. + for len(q) != 0 && len(m) < 100 { + parent := q[0] + q = q[1:] + for i := 0; i < 4; i++ { + ctx, cancel := WithCancel(parent) + m[ctx] = cancel + q = append(q, ctx) + } + } + // Start all the cancels in a random order. + var wg sync.WaitGroup + wg.Add(len(m)) + for _, cancel := range m { + go func(cancel CancelFunc) { + cancel() + wg.Done() + }(cancel) + } + // Wait on all the contexts in a random order. + for ctx := range m { + select { + case <-ctx.Done(): + case <-time.After(1 * time.Second): + buf := make([]byte, 10<<10) + n := runtime.Stack(buf, true) + t.Fatalf("timed out waiting for <-ctx.Done(); stacks:\n%s", buf[:n]) + } + } + // Wait for all the cancel functions to return. + done := make(chan struct{}) + go func() { + wg.Wait() + close(done) + }() + select { + case <-done: + case <-time.After(1 * time.Second): + buf := make([]byte, 10<<10) + n := runtime.Stack(buf, true) + t.Fatalf("timed out waiting for cancel functions; stacks:\n%s", buf[:n]) + } +} + +func TestInterlockedCancels(t *testing.T) { + parent, cancelParent := WithCancel(Background()) + child, cancelChild := WithCancel(parent) + go func() { + parent.Done() + cancelChild() + }() + cancelParent() + select { + case <-child.Done(): + case <-time.After(1 * time.Second): + buf := make([]byte, 10<<10) + n := runtime.Stack(buf, true) + t.Fatalf("timed out waiting for child.Done(); stacks:\n%s", buf[:n]) + } +} + +func TestLayersCancel(t *testing.T) { + testLayers(t, time.Now().UnixNano(), false) +} + +func TestLayersTimeout(t *testing.T) { + testLayers(t, time.Now().UnixNano(), true) +} + +func testLayers(t *testing.T, seed int64, testTimeout bool) { + rand.Seed(seed) + errorf := func(format string, a ...interface{}) { + t.Errorf(fmt.Sprintf("seed=%d: %s", seed, format), a...) + } + const ( + timeout = 200 * time.Millisecond + minLayers = 30 + ) + type value int + var ( + vals []*value + cancels []CancelFunc + numTimers int + ctx = Background() + ) + for i := 0; i < minLayers || numTimers == 0 || len(cancels) == 0 || len(vals) == 0; i++ { + switch rand.Intn(3) { + case 0: + v := new(value) + ctx = WithValue(ctx, v, v) + vals = append(vals, v) + case 1: + var cancel CancelFunc + ctx, cancel = WithCancel(ctx) + cancels = append(cancels, cancel) + case 2: + var cancel CancelFunc + ctx, cancel = WithTimeout(ctx, timeout) + cancels = append(cancels, cancel) + numTimers++ + } + } + checkValues := func(when string) { + for _, key := range vals { + if val := ctx.Value(key).(*value); key != val { + errorf("%s: ctx.Value(%p) = %p want %p", when, key, val, key) + } + } + } + select { + case <-ctx.Done(): + errorf("ctx should not be canceled yet") + default: + } + if s, prefix := fmt.Sprint(ctx), "context.Background."; !strings.HasPrefix(s, prefix) { + t.Errorf("ctx.String() = %q want prefix %q", s, prefix) + } + t.Log(ctx) + checkValues("before cancel") + if testTimeout { + select { + case <-ctx.Done(): + case <-time.After(timeout + timeout/10): + errorf("ctx should have timed out") + } + checkValues("after timeout") + } else { + cancel := cancels[rand.Intn(len(cancels))] + cancel() + select { + case <-ctx.Done(): + default: + errorf("ctx should be canceled") + } + checkValues("after cancel") + } +} diff --git a/Godeps/_workspace/src/code.google.com/p/go.net/context/withtimeout_test.go b/Godeps/_workspace/src/code.google.com/p/go.net/context/withtimeout_test.go new file mode 100644 index 000000000..64854d81b --- /dev/null +++ b/Godeps/_workspace/src/code.google.com/p/go.net/context/withtimeout_test.go @@ -0,0 +1,26 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package context_test + +import ( + "fmt" + "time" + + "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" +) + +func ExampleWithTimeout() { + // Pass a context with a timeout to tell a blocking function that it + // should abandon its work after the timeout elapses. + ctx, _ := context.WithTimeout(context.Background(), 100*time.Millisecond) + select { + case <-time.After(200 * time.Millisecond): + fmt.Println("overslept") + case <-ctx.Done(): + fmt.Println(ctx.Err()) // prints "context deadline exceeded" + } + // Output: + // context deadline exceeded +} diff --git a/identify/identify.go b/identify/identify.go index 205e56733..315bb4899 100644 --- a/identify/identify.go +++ b/identify/identify.go @@ -16,6 +16,8 @@ import ( "crypto/sha512" "hash" + context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" + proto "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto" ci "github.com/jbenet/go-ipfs/crypto" peer "github.com/jbenet/go-ipfs/peer" @@ -33,7 +35,7 @@ var ErrUnsupportedKeyType = errors.New("unsupported key type") // Performs initial communication with this peer to share node ID's and // initiate communication. (secureIn, secureOut, error) -func Handshake(self, remote *peer.Peer, in <-chan []byte, out chan<- []byte) (<-chan []byte, chan<- []byte, error) { +func Handshake(ctx context.Context, self, remote *peer.Peer, in <-chan []byte, out chan<- []byte) (<-chan []byte, chan<- []byte, error) { encodedHello, err := encodedProtoHello(self) if err != nil { @@ -84,7 +86,7 @@ func Handshake(self, remote *peer.Peer, in <-chan []byte, out chan<- []byte) (<- } var handshake bytes.Buffer // Gather corpus to sign. - handshake.Write(encoded) + handshake.Write(encodedHello) handshake.Write(resp) handshake.Write(epubkey) @@ -118,7 +120,7 @@ func Handshake(self, remote *peer.Peer, in <-chan []byte, out chan<- []byte) (<- if err != nil { return nil, nil, err } - _, err = theirHandshake.Write(encoded) + _, err = theirHandshake.Write(encodedHello) if err != nil { return nil, nil, err } diff --git a/identify/identify_test.go b/identify/identify_test.go index 3d529f3e4..05b927e36 100644 --- a/identify/identify_test.go +++ b/identify/identify_test.go @@ -3,6 +3,8 @@ package identify import ( "testing" + context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" + ci "github.com/jbenet/go-ipfs/crypto" "github.com/jbenet/go-ipfs/peer" ) @@ -40,14 +42,15 @@ func TestHandshake(t *testing.T) { PrivKey: skb, } + ctx := context.Background() go func() { - _, _, err := Handshake(pa, pb, cha, chb) + _, _, err := Handshake(ctx, pa, pb, cha, chb) if err != nil { t.Fatal(err) } }() - _, _, err = Handshake(pb, pa, chb, cha) + _, _, err = Handshake(ctx, pb, pa, chb, cha) if err != nil { t.Fatal(err) } diff --git a/swarm/swarm.go b/swarm/swarm.go index 389453703..5b2d7407c 100644 --- a/swarm/swarm.go +++ b/swarm/swarm.go @@ -6,6 +6,8 @@ import ( "net" "sync" + context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" + proto "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto" ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr" ident "github.com/jbenet/go-ipfs/identify" @@ -162,6 +164,7 @@ func (s *Swarm) connListen(maddr *ma.Multiaddr) error { } // Handle getting ID from this peer and adding it into the map +// TODO(brian): impl a cancellable context func (s *Swarm) handleNewConn(nconn net.Conn) { p := new(peer.Peer) @@ -172,7 +175,7 @@ func (s *Swarm) handleNewConn(nconn net.Conn) { } newConnChans(conn) - sin, sout, err := ident.Handshake(s.local, p, conn.Incoming.MsgChan, conn.Outgoing.MsgChan) + sin, sout, err := ident.Handshake(context.Background(), s.local, p, conn.Incoming.MsgChan, conn.Outgoing.MsgChan) if err != nil { u.PErr("%v\n", err.Error()) conn.Close() @@ -426,8 +429,10 @@ func (s *Swarm) GetConnection(id peer.ID, addr *ma.Multiaddr) (*peer.Peer, error } // Handle performing a handshake on a new connection and ensuring proper forward communication +// TODO(brian): impl a cancellable context func (s *Swarm) handleDialedCon(conn *Conn) error { - sin, sout, err := ident.Handshake(s.local, conn.Peer, conn.Incoming.MsgChan, conn.Outgoing.MsgChan) + + sin, sout, err := ident.Handshake(context.Background(), s.local, conn.Peer, conn.Incoming.MsgChan, conn.Outgoing.MsgChan) if err != nil { return err } From 24b7703f2f66b2861dec5c8488ef55f8b4b20541 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Sat, 13 Sep 2014 12:17:04 -0700 Subject: [PATCH 28/28] refactor(identify) keep only minor fixes --- Godeps/Godeps.json | 5 - .../p/go.net/context/context.go | 431 -------------- .../p/go.net/context/context_test.go | 553 ------------------ .../p/go.net/context/withtimeout_test.go | 26 - identify/identify.go | 73 +-- identify/identify_test.go | 7 +- swarm/swarm.go | 9 +- 7 files changed, 30 insertions(+), 1074 deletions(-) delete mode 100644 Godeps/_workspace/src/code.google.com/p/go.net/context/context.go delete mode 100644 Godeps/_workspace/src/code.google.com/p/go.net/context/context_test.go delete mode 100644 Godeps/_workspace/src/code.google.com/p/go.net/context/withtimeout_test.go diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json index 5e346f47f..08444fe3e 100644 --- a/Godeps/Godeps.json +++ b/Godeps/Godeps.json @@ -19,11 +19,6 @@ "Comment": "null-219", "Rev": "00a7d3b31bbab5795b4a51933c04fc2768242970" }, - { - "ImportPath": "code.google.com/p/go.net/context", - "Comment": "null-144", - "Rev": "ad01a6fcc8a19d3a4478c836895ffe883bd2ceab" - }, { "ImportPath": "code.google.com/p/gogoprotobuf/proto", "Rev": "6c980277330804e94257ac7ef70a3adbe1641059" diff --git a/Godeps/_workspace/src/code.google.com/p/go.net/context/context.go b/Godeps/_workspace/src/code.google.com/p/go.net/context/context.go deleted file mode 100644 index e3c5345d7..000000000 --- a/Godeps/_workspace/src/code.google.com/p/go.net/context/context.go +++ /dev/null @@ -1,431 +0,0 @@ -// Copyright 2014 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Package context defines the Context type, which carries deadlines, -// cancelation signals, and other request-scoped values across API boundaries -// and between processes. -// -// Incoming requests to a server should create a Context, and outgoing calls to -// servers should accept a Context. The chain of function calls between must -// propagate the Context, optionally replacing it with a modified copy created -// using WithDeadline, WithTimeout, WithCancel, or WithValue. -// -// Programs that use Contexts should follow these rules to keep interfaces -// consistent across packages and enable static analysis tools to check context -// propagation: -// -// Do not store Contexts inside a struct type; instead, pass a Context -// explicitly to each function that needs it. The Context should be the first -// parameter, typically named ctx: -// -// func DoSomething(ctx context.Context, arg Arg) error { -// // ... use ctx ... -// } -// -// Do not pass a nil Context, even if a function permits it. Pass context.TODO -// if you are unsure about which Context to use. -// -// Use context Values only for request-scoped data that transits processes and -// APIs, not for passing optional parameters to functions. -// -// The same Context may be passed to functions running in different goroutines; -// Contexts are safe for simultaneous use by multiple goroutines. -// -// See http://blog.golang.org/context for example code for a server that uses -// Contexts. -package context - -import ( - "errors" - "fmt" - "sync" - "time" -) - -// A Context carries a deadline, a cancelation signal, and other values across -// API boundaries. -// -// Context's methods may be called by multiple goroutines simultaneously. -type Context interface { - // Deadline returns the time when work done on behalf of this context - // should be canceled. Deadline returns ok==false when no deadline is - // set. Successive calls to Deadline return the same results. - Deadline() (deadline time.Time, ok bool) - - // Done returns a channel that's closed when work done on behalf of this - // context should be canceled. Done may return nil if this context can - // never be canceled. Successive calls to Done return the same value. - // - // WithCancel arranges for Done to be closed when cancel is called; - // WithDeadline arranges for Done to be closed when the deadline - // expires; WithTimeout arranges for Done to be closed when the timeout - // elapses. - // - // Done is provided for use in select statements: - // - // // DoSomething calls DoSomethingSlow and returns as soon as - // // it returns or ctx.Done is closed. - // func DoSomething(ctx context.Context) (Result, error) { - // c := make(chan Result, 1) - // go func() { c <- DoSomethingSlow(ctx) }() - // select { - // case res := <-c: - // return res, nil - // case <-ctx.Done(): - // return nil, ctx.Err() - // } - // } - // - // See http://blog.golang.org/pipelines for more examples of how to use - // a Done channel for cancelation. - Done() <-chan struct{} - - // Err returns a non-nil error value after Done is closed. Err returns - // Canceled if the context was canceled or DeadlineExceeded if the - // context's deadline passed. No other values for Err are defined. - // After Done is closed, successive calls to Err return the same value. - Err() error - - // Value returns the value associated with this context for key, or nil - // if no value is associated with key. Successive calls to Value with - // the same key returns the same result. - // - // Use context values only for request-scoped data that transits - // processes and API boundaries, not for passing optional parameters to - // functions. - // - // A key identifies a specific value in a Context. Functions that wish - // to store values in Context typically allocate a key in a global - // variable then use that key as the argument to context.WithValue and - // Context.Value. A key can be any type that supports equality; - // packages should define keys as an unexported type to avoid - // collisions. - // - // Packages that define a Context key should provide type-safe accessors - // for the values stores using that key: - // - // // Package user defines a User type that's stored in Contexts. - // package user - // - // import "code.google.com/p/go.net/context" - // - // // User is the type of value stored in the Contexts. - // type User struct {...} - // - // // key is an unexported type for keys defined in this package. - // // This prevents collisions with keys defined in other packages. - // type key int - // - // // userKey is the key for user.User values in Contexts. It is - // // unexported; clients use user.NewContext and user.FromContext - // // instead of using this key directly. - // var userKey key = 0 - // - // // NewContext returns a new Context that carries value u. - // func NewContext(ctx context.Context, u *User) context.Context { - // return context.WithValue(userKey, u) - // } - // - // // FromContext returns the User value stored in ctx, if any. - // func FromContext(ctx context.Context) (*User, bool) { - // u, ok := ctx.Value(userKey).(*User) - // return u, ok - // } - Value(key interface{}) interface{} -} - -// Canceled is the error returned by Context.Err when the context is canceled. -var Canceled = errors.New("context canceled") - -// DeadlineExceeded is the error returned by Context.Err when the context's -// deadline passes. -var DeadlineExceeded = errors.New("context deadline exceeded") - -// An emptyCtx is never canceled, has no values, and has no deadline. -type emptyCtx int - -func (emptyCtx) Deadline() (deadline time.Time, ok bool) { - return -} - -func (emptyCtx) Done() <-chan struct{} { - return nil -} - -func (emptyCtx) Err() error { - return nil -} - -func (emptyCtx) Value(key interface{}) interface{} { - return nil -} - -func (n emptyCtx) String() string { - switch n { - case background: - return "context.Background" - case todo: - return "context.TODO" - } - return "unknown empty Context" -} - -const ( - background emptyCtx = 1 - todo emptyCtx = 2 -) - -// Background returns a non-nil, empty Context. It is never canceled, has no -// values, and has no deadline. It is typically used by the main function, -// initialization, and tests, and as the top-level Context for incoming -// requests. -func Background() Context { - return background -} - -// TODO returns a non-nil, empty Context. Code should use context.TODO when -// it's unclear which Context to use or it's is not yet available (because the -// surrounding function has not yet been extended to accept a Context -// parameter). TODO is recognized by static analysis tools that determine -// whether Contexts are propagated correctly in a program. -func TODO() Context { - return todo -} - -// A CancelFunc tells an operation to abandon its work. -// A CancelFunc does not wait for the work to stop. -// After the first call, subsequent calls to a CancelFunc do nothing. -type CancelFunc func() - -// WithCancel returns a copy of parent with a new Done channel. The returned -// context's Done channel is closed when the returned cancel function is called -// or when the parent context's Done channel is closed, whichever happens first. -func WithCancel(parent Context) (ctx Context, cancel CancelFunc) { - c := newCancelCtx(parent) - propagateCancel(parent, &c) - return &c, func() { c.cancel(true, Canceled) } -} - -// newCancelCtx returns an initialized cancelCtx. -func newCancelCtx(parent Context) cancelCtx { - return cancelCtx{ - Context: parent, - done: make(chan struct{}), - } -} - -// propagateCancel arranges for child to be canceled when parent is. -func propagateCancel(parent Context, child canceler) { - if parent.Done() == nil { - return // parent is never canceled - } - if p, ok := parentCancelCtx(parent); ok { - p.mu.Lock() - if p.err != nil { - // parent has already been canceled - child.cancel(false, p.err) - } else { - if p.children == nil { - p.children = make(map[canceler]bool) - } - p.children[child] = true - } - p.mu.Unlock() - } else { - go func() { - select { - case <-parent.Done(): - child.cancel(false, parent.Err()) - case <-child.Done(): - } - }() - } -} - -// parentCancelCtx follows a chain of parent references until it finds a -// *cancelCtx. This function understands how each of the concrete types in this -// package represents its parent. -func parentCancelCtx(parent Context) (*cancelCtx, bool) { - for { - switch c := parent.(type) { - case *cancelCtx: - return c, true - case *timerCtx: - return &c.cancelCtx, true - case *valueCtx: - parent = c.Context - default: - return nil, false - } - } -} - -// A canceler is a context type that can be canceled directly. The -// implementations are *cancelCtx and *timerCtx. -type canceler interface { - cancel(removeFromParent bool, err error) - Done() <-chan struct{} -} - -// A cancelCtx can be canceled. When canceled, it also cancels any children -// that implement canceler. -type cancelCtx struct { - Context - - done chan struct{} // closed by the first cancel call. - - mu sync.Mutex - children map[canceler]bool // set to nil by the first cancel call - err error // set to non-nil by the first cancel call -} - -func (c *cancelCtx) Done() <-chan struct{} { - return c.done -} - -func (c *cancelCtx) Err() error { - c.mu.Lock() - defer c.mu.Unlock() - return c.err -} - -func (c *cancelCtx) String() string { - return fmt.Sprintf("%v.WithCancel", c.Context) -} - -// cancel closes c.done, cancels each of c's children, and, if -// removeFromParent is true, removes c from its parent's children. -func (c *cancelCtx) cancel(removeFromParent bool, err error) { - if err == nil { - panic("context: internal error: missing cancel error") - } - c.mu.Lock() - if c.err != nil { - c.mu.Unlock() - return // already canceled - } - c.err = err - close(c.done) - for child := range c.children { - // NOTE: acquiring the child's lock while holding parent's lock. - child.cancel(false, err) - } - c.children = nil - c.mu.Unlock() - - if removeFromParent { - if p, ok := parentCancelCtx(c.Context); ok { - p.mu.Lock() - if p.children != nil { - delete(p.children, c) - } - p.mu.Unlock() - } - } -} - -// WithDeadline returns a copy of the parent context with the deadline adjusted -// to be no later than d. If the parent's deadline is already earlier than d, -// WithDeadline(parent, d) is semantically equivalent to parent. The returned -// context's Done channel is closed when the deadline expires, when the returned -// cancel function is called, or when the parent context's Done channel is -// closed, whichever happens first. -// -// Canceling this context releases resources associated with the deadline -// timer, so code should call cancel as soon as the operations running in this -// Context complete. -func WithDeadline(parent Context, deadline time.Time) (Context, CancelFunc) { - if cur, ok := parent.Deadline(); ok && cur.Before(deadline) { - // The current deadline is already sooner than the new one. - return WithCancel(parent) - } - c := &timerCtx{ - cancelCtx: newCancelCtx(parent), - deadline: deadline, - } - propagateCancel(parent, c) - d := deadline.Sub(time.Now()) - if d <= 0 { - c.cancel(true, DeadlineExceeded) // deadline has already passed - return c, func() { c.cancel(true, Canceled) } - } - c.mu.Lock() - defer c.mu.Unlock() - if c.err == nil { - c.timer = time.AfterFunc(d, func() { - c.cancel(true, DeadlineExceeded) - }) - } - return c, func() { c.cancel(true, Canceled) } -} - -// A timerCtx carries a timer and a deadline. It embeds a cancelCtx to -// implement Done and Err. It implements cancel by stopping its timer then -// delegating to cancelCtx.cancel. -type timerCtx struct { - cancelCtx - timer *time.Timer // Under cancelCtx.mu. - - deadline time.Time -} - -func (c *timerCtx) Deadline() (deadline time.Time, ok bool) { - return c.deadline, true -} - -func (c *timerCtx) String() string { - return fmt.Sprintf("%v.WithDeadline(%s [%s])", c.cancelCtx.Context, c.deadline, c.deadline.Sub(time.Now())) -} - -func (c *timerCtx) cancel(removeFromParent bool, err error) { - c.cancelCtx.cancel(removeFromParent, err) - c.mu.Lock() - if c.timer != nil { - c.timer.Stop() - c.timer = nil - } - c.mu.Unlock() -} - -// WithTimeout returns WithDeadline(parent, time.Now().Add(timeout)). -// -// Canceling this context releases resources associated with the deadline -// timer, so code should call cancel as soon as the operations running in this -// Context complete: -// -// func slowOperationWithTimeout(ctx context.Context) (Result, error) { -// ctx, cancel := context.WithTimeout(ctx, 100*time.Millisecond) -// defer cancel() // releases resources if slowOperation completes before timeout elapses -// return slowOperation(ctx) -// } -func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc) { - return WithDeadline(parent, time.Now().Add(timeout)) -} - -// WithValue returns a copy of parent in which the value associated with key is -// val. -// -// Use context Values only for request-scoped data that transits processes and -// APIs, not for passing optional parameters to functions. -func WithValue(parent Context, key interface{}, val interface{}) Context { - return &valueCtx{parent, key, val} -} - -// A valueCtx carries a key-value pair. It implements Value for that key and -// delegates all other calls to the embedded Context. -type valueCtx struct { - Context - key, val interface{} -} - -func (c *valueCtx) String() string { - return fmt.Sprintf("%v.WithValue(%#v, %#v)", c.Context, c.key, c.val) -} - -func (c *valueCtx) Value(key interface{}) interface{} { - if c.key == key { - return c.val - } - return c.Context.Value(key) -} diff --git a/Godeps/_workspace/src/code.google.com/p/go.net/context/context_test.go b/Godeps/_workspace/src/code.google.com/p/go.net/context/context_test.go deleted file mode 100644 index c1a4de5ff..000000000 --- a/Godeps/_workspace/src/code.google.com/p/go.net/context/context_test.go +++ /dev/null @@ -1,553 +0,0 @@ -// Copyright 2014 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package context - -import ( - "fmt" - "math/rand" - "runtime" - "strings" - "sync" - "testing" - "time" -) - -// otherContext is a Context that's not one of the types defined in context.go. -// This lets us test code paths that differ based on the underlying type of the -// Context. -type otherContext struct { - Context -} - -func TestBackground(t *testing.T) { - c := Background() - if c == nil { - t.Fatalf("Background returned nil") - } - select { - case x := <-c.Done(): - t.Errorf("<-c.Done() == %v want nothing (it should block)", x) - default: - } - if got, want := fmt.Sprint(c), "context.Background"; got != want { - t.Errorf("Background().String() = %q want %q", got, want) - } -} - -func TestTODO(t *testing.T) { - c := TODO() - if c == nil { - t.Fatalf("TODO returned nil") - } - select { - case x := <-c.Done(): - t.Errorf("<-c.Done() == %v want nothing (it should block)", x) - default: - } - if got, want := fmt.Sprint(c), "context.TODO"; got != want { - t.Errorf("TODO().String() = %q want %q", got, want) - } -} - -func TestWithCancel(t *testing.T) { - c1, cancel := WithCancel(Background()) - - if got, want := fmt.Sprint(c1), "context.Background.WithCancel"; got != want { - t.Errorf("c1.String() = %q want %q", got, want) - } - - o := otherContext{c1} - c2, _ := WithCancel(o) - contexts := []Context{c1, o, c2} - - for i, c := range contexts { - if d := c.Done(); d == nil { - t.Errorf("c[%d].Done() == %v want non-nil", i, d) - } - if e := c.Err(); e != nil { - t.Errorf("c[%d].Err() == %v want nil", i, e) - } - - select { - case x := <-c.Done(): - t.Errorf("<-c.Done() == %v want nothing (it should block)", x) - default: - } - } - - cancel() - time.Sleep(100 * time.Millisecond) // let cancelation propagate - - for i, c := range contexts { - select { - case <-c.Done(): - default: - t.Errorf("<-c[%d].Done() blocked, but shouldn't have", i) - } - if e := c.Err(); e != Canceled { - t.Errorf("c[%d].Err() == %v want %v", i, e, Canceled) - } - } -} - -func TestParentFinishesChild(t *testing.T) { - // Context tree: - // parent -> cancelChild - // parent -> valueChild -> timerChild - parent, cancel := WithCancel(Background()) - cancelChild, stop := WithCancel(parent) - defer stop() - valueChild := WithValue(parent, "key", "value") - timerChild, stop := WithTimeout(valueChild, 10000*time.Hour) - defer stop() - - select { - case x := <-parent.Done(): - t.Errorf("<-parent.Done() == %v want nothing (it should block)", x) - case x := <-cancelChild.Done(): - t.Errorf("<-cancelChild.Done() == %v want nothing (it should block)", x) - case x := <-timerChild.Done(): - t.Errorf("<-timerChild.Done() == %v want nothing (it should block)", x) - case x := <-valueChild.Done(): - t.Errorf("<-valueChild.Done() == %v want nothing (it should block)", x) - default: - } - - // The parent's children should contain the two cancelable children. - pc := parent.(*cancelCtx) - cc := cancelChild.(*cancelCtx) - tc := timerChild.(*timerCtx) - pc.mu.Lock() - if len(pc.children) != 2 || !pc.children[cc] || !pc.children[tc] { - t.Errorf("bad linkage: pc.children = %v, want %v and %v", - pc.children, cc, tc) - } - pc.mu.Unlock() - - if p, ok := parentCancelCtx(cc.Context); !ok || p != pc { - t.Errorf("bad linkage: parentCancelCtx(cancelChild.Context) = %v, %v want %v, true", p, ok, pc) - } - if p, ok := parentCancelCtx(tc.Context); !ok || p != pc { - t.Errorf("bad linkage: parentCancelCtx(timerChild.Context) = %v, %v want %v, true", p, ok, pc) - } - - cancel() - - pc.mu.Lock() - if len(pc.children) != 0 { - t.Errorf("pc.cancel didn't clear pc.children = %v", pc.children) - } - pc.mu.Unlock() - - // parent and children should all be finished. - check := func(ctx Context, name string) { - select { - case <-ctx.Done(): - default: - t.Errorf("<-%s.Done() blocked, but shouldn't have", name) - } - if e := ctx.Err(); e != Canceled { - t.Errorf("%s.Err() == %v want %v", name, e, Canceled) - } - } - check(parent, "parent") - check(cancelChild, "cancelChild") - check(valueChild, "valueChild") - check(timerChild, "timerChild") - - // WithCancel should return a canceled context on a canceled parent. - precanceledChild := WithValue(parent, "key", "value") - select { - case <-precanceledChild.Done(): - default: - t.Errorf("<-precanceledChild.Done() blocked, but shouldn't have") - } - if e := precanceledChild.Err(); e != Canceled { - t.Errorf("precanceledChild.Err() == %v want %v", e, Canceled) - } -} - -func TestChildFinishesFirst(t *testing.T) { - cancelable, stop := WithCancel(Background()) - defer stop() - for _, parent := range []Context{Background(), cancelable} { - child, cancel := WithCancel(parent) - - select { - case x := <-parent.Done(): - t.Errorf("<-parent.Done() == %v want nothing (it should block)", x) - case x := <-child.Done(): - t.Errorf("<-child.Done() == %v want nothing (it should block)", x) - default: - } - - cc := child.(*cancelCtx) - pc, pcok := parent.(*cancelCtx) // pcok == false when parent == Background() - if p, ok := parentCancelCtx(cc.Context); ok != pcok || (ok && pc != p) { - t.Errorf("bad linkage: parentCancelCtx(cc.Context) = %v, %v want %v, %v", p, ok, pc, pcok) - } - - if pcok { - pc.mu.Lock() - if len(pc.children) != 1 || !pc.children[cc] { - t.Errorf("bad linkage: pc.children = %v, cc = %v", pc.children, cc) - } - pc.mu.Unlock() - } - - cancel() - - if pcok { - pc.mu.Lock() - if len(pc.children) != 0 { - t.Errorf("child's cancel didn't remove self from pc.children = %v", pc.children) - } - pc.mu.Unlock() - } - - // child should be finished. - select { - case <-child.Done(): - default: - t.Errorf("<-child.Done() blocked, but shouldn't have") - } - if e := child.Err(); e != Canceled { - t.Errorf("child.Err() == %v want %v", e, Canceled) - } - - // parent should not be finished. - select { - case x := <-parent.Done(): - t.Errorf("<-parent.Done() == %v want nothing (it should block)", x) - default: - } - if e := parent.Err(); e != nil { - t.Errorf("parent.Err() == %v want nil", e) - } - } -} - -func testDeadline(c Context, wait time.Duration, t *testing.T) { - select { - case <-time.After(wait): - t.Fatalf("context should have timed out") - case <-c.Done(): - } - if e := c.Err(); e != DeadlineExceeded { - t.Errorf("c.Err() == %v want %v", e, DeadlineExceeded) - } -} - -func TestDeadline(t *testing.T) { - c, _ := WithDeadline(Background(), time.Now().Add(100*time.Millisecond)) - if got, prefix := fmt.Sprint(c), "context.Background.WithDeadline("; !strings.HasPrefix(got, prefix) { - t.Errorf("c.String() = %q want prefix %q", got, prefix) - } - testDeadline(c, 200*time.Millisecond, t) - - c, _ = WithDeadline(Background(), time.Now().Add(100*time.Millisecond)) - o := otherContext{c} - testDeadline(o, 200*time.Millisecond, t) - - c, _ = WithDeadline(Background(), time.Now().Add(100*time.Millisecond)) - o = otherContext{c} - c, _ = WithDeadline(o, time.Now().Add(300*time.Millisecond)) - testDeadline(c, 200*time.Millisecond, t) -} - -func TestTimeout(t *testing.T) { - c, _ := WithTimeout(Background(), 100*time.Millisecond) - if got, prefix := fmt.Sprint(c), "context.Background.WithDeadline("; !strings.HasPrefix(got, prefix) { - t.Errorf("c.String() = %q want prefix %q", got, prefix) - } - testDeadline(c, 200*time.Millisecond, t) - - c, _ = WithTimeout(Background(), 100*time.Millisecond) - o := otherContext{c} - testDeadline(o, 200*time.Millisecond, t) - - c, _ = WithTimeout(Background(), 100*time.Millisecond) - o = otherContext{c} - c, _ = WithTimeout(o, 300*time.Millisecond) - testDeadline(c, 200*time.Millisecond, t) -} - -func TestCanceledTimeout(t *testing.T) { - c, _ := WithTimeout(Background(), 200*time.Millisecond) - o := otherContext{c} - c, cancel := WithTimeout(o, 400*time.Millisecond) - cancel() - time.Sleep(100 * time.Millisecond) // let cancelation propagate - select { - case <-c.Done(): - default: - t.Errorf("<-c.Done() blocked, but shouldn't have") - } - if e := c.Err(); e != Canceled { - t.Errorf("c.Err() == %v want %v", e, Canceled) - } -} - -type key1 int -type key2 int - -var k1 = key1(1) -var k2 = key2(1) // same int as k1, different type -var k3 = key2(3) // same type as k2, different int - -func TestValues(t *testing.T) { - check := func(c Context, nm, v1, v2, v3 string) { - if v, ok := c.Value(k1).(string); ok == (len(v1) == 0) || v != v1 { - t.Errorf(`%s.Value(k1).(string) = %q, %t want %q, %t`, nm, v, ok, v1, len(v1) != 0) - } - if v, ok := c.Value(k2).(string); ok == (len(v2) == 0) || v != v2 { - t.Errorf(`%s.Value(k2).(string) = %q, %t want %q, %t`, nm, v, ok, v2, len(v2) != 0) - } - if v, ok := c.Value(k3).(string); ok == (len(v3) == 0) || v != v3 { - t.Errorf(`%s.Value(k3).(string) = %q, %t want %q, %t`, nm, v, ok, v3, len(v3) != 0) - } - } - - c0 := Background() - check(c0, "c0", "", "", "") - - c1 := WithValue(Background(), k1, "c1k1") - check(c1, "c1", "c1k1", "", "") - - if got, want := fmt.Sprint(c1), `context.Background.WithValue(1, "c1k1")`; got != want { - t.Errorf("c.String() = %q want %q", got, want) - } - - c2 := WithValue(c1, k2, "c2k2") - check(c2, "c2", "c1k1", "c2k2", "") - - c3 := WithValue(c2, k3, "c3k3") - check(c3, "c2", "c1k1", "c2k2", "c3k3") - - c4 := WithValue(c3, k1, nil) - check(c4, "c4", "", "c2k2", "c3k3") - - o0 := otherContext{Background()} - check(o0, "o0", "", "", "") - - o1 := otherContext{WithValue(Background(), k1, "c1k1")} - check(o1, "o1", "c1k1", "", "") - - o2 := WithValue(o1, k2, "o2k2") - check(o2, "o2", "c1k1", "o2k2", "") - - o3 := otherContext{c4} - check(o3, "o3", "", "c2k2", "c3k3") - - o4 := WithValue(o3, k3, nil) - check(o4, "o4", "", "c2k2", "") -} - -func TestAllocs(t *testing.T) { - bg := Background() - for _, test := range []struct { - desc string - f func() - limit float64 - gccgoLimit float64 - }{ - { - desc: "Background()", - f: func() { Background() }, - limit: 0, - gccgoLimit: 0, - }, - { - desc: fmt.Sprintf("WithValue(bg, %v, nil)", k1), - f: func() { - c := WithValue(bg, k1, nil) - c.Value(k1) - }, - limit: 1, - gccgoLimit: 3, - }, - { - desc: "WithTimeout(bg, 15*time.Millisecond)", - f: func() { - c, _ := WithTimeout(bg, 15*time.Millisecond) - <-c.Done() - }, - limit: 8, - gccgoLimit: 13, - }, - { - desc: "WithCancel(bg)", - f: func() { - c, cancel := WithCancel(bg) - cancel() - <-c.Done() - }, - limit: 5, - gccgoLimit: 8, - }, - { - desc: "WithTimeout(bg, 100*time.Millisecond)", - f: func() { - c, cancel := WithTimeout(bg, 100*time.Millisecond) - cancel() - <-c.Done() - }, - limit: 8, - gccgoLimit: 25, - }, - } { - limit := test.limit - if runtime.Compiler == "gccgo" { - // gccgo does not yet do escape analysis. - // TOOD(iant): Remove this when gccgo does do escape analysis. - limit = test.gccgoLimit - } - if n := testing.AllocsPerRun(100, test.f); n > limit { - t.Errorf("%s allocs = %f want %d", test.desc, n, int(limit)) - } - } -} - -func TestSimultaneousCancels(t *testing.T) { - root, cancel := WithCancel(Background()) - m := map[Context]CancelFunc{root: cancel} - q := []Context{root} - // Create a tree of contexts. - for len(q) != 0 && len(m) < 100 { - parent := q[0] - q = q[1:] - for i := 0; i < 4; i++ { - ctx, cancel := WithCancel(parent) - m[ctx] = cancel - q = append(q, ctx) - } - } - // Start all the cancels in a random order. - var wg sync.WaitGroup - wg.Add(len(m)) - for _, cancel := range m { - go func(cancel CancelFunc) { - cancel() - wg.Done() - }(cancel) - } - // Wait on all the contexts in a random order. - for ctx := range m { - select { - case <-ctx.Done(): - case <-time.After(1 * time.Second): - buf := make([]byte, 10<<10) - n := runtime.Stack(buf, true) - t.Fatalf("timed out waiting for <-ctx.Done(); stacks:\n%s", buf[:n]) - } - } - // Wait for all the cancel functions to return. - done := make(chan struct{}) - go func() { - wg.Wait() - close(done) - }() - select { - case <-done: - case <-time.After(1 * time.Second): - buf := make([]byte, 10<<10) - n := runtime.Stack(buf, true) - t.Fatalf("timed out waiting for cancel functions; stacks:\n%s", buf[:n]) - } -} - -func TestInterlockedCancels(t *testing.T) { - parent, cancelParent := WithCancel(Background()) - child, cancelChild := WithCancel(parent) - go func() { - parent.Done() - cancelChild() - }() - cancelParent() - select { - case <-child.Done(): - case <-time.After(1 * time.Second): - buf := make([]byte, 10<<10) - n := runtime.Stack(buf, true) - t.Fatalf("timed out waiting for child.Done(); stacks:\n%s", buf[:n]) - } -} - -func TestLayersCancel(t *testing.T) { - testLayers(t, time.Now().UnixNano(), false) -} - -func TestLayersTimeout(t *testing.T) { - testLayers(t, time.Now().UnixNano(), true) -} - -func testLayers(t *testing.T, seed int64, testTimeout bool) { - rand.Seed(seed) - errorf := func(format string, a ...interface{}) { - t.Errorf(fmt.Sprintf("seed=%d: %s", seed, format), a...) - } - const ( - timeout = 200 * time.Millisecond - minLayers = 30 - ) - type value int - var ( - vals []*value - cancels []CancelFunc - numTimers int - ctx = Background() - ) - for i := 0; i < minLayers || numTimers == 0 || len(cancels) == 0 || len(vals) == 0; i++ { - switch rand.Intn(3) { - case 0: - v := new(value) - ctx = WithValue(ctx, v, v) - vals = append(vals, v) - case 1: - var cancel CancelFunc - ctx, cancel = WithCancel(ctx) - cancels = append(cancels, cancel) - case 2: - var cancel CancelFunc - ctx, cancel = WithTimeout(ctx, timeout) - cancels = append(cancels, cancel) - numTimers++ - } - } - checkValues := func(when string) { - for _, key := range vals { - if val := ctx.Value(key).(*value); key != val { - errorf("%s: ctx.Value(%p) = %p want %p", when, key, val, key) - } - } - } - select { - case <-ctx.Done(): - errorf("ctx should not be canceled yet") - default: - } - if s, prefix := fmt.Sprint(ctx), "context.Background."; !strings.HasPrefix(s, prefix) { - t.Errorf("ctx.String() = %q want prefix %q", s, prefix) - } - t.Log(ctx) - checkValues("before cancel") - if testTimeout { - select { - case <-ctx.Done(): - case <-time.After(timeout + timeout/10): - errorf("ctx should have timed out") - } - checkValues("after timeout") - } else { - cancel := cancels[rand.Intn(len(cancels))] - cancel() - select { - case <-ctx.Done(): - default: - errorf("ctx should be canceled") - } - checkValues("after cancel") - } -} diff --git a/Godeps/_workspace/src/code.google.com/p/go.net/context/withtimeout_test.go b/Godeps/_workspace/src/code.google.com/p/go.net/context/withtimeout_test.go deleted file mode 100644 index 64854d81b..000000000 --- a/Godeps/_workspace/src/code.google.com/p/go.net/context/withtimeout_test.go +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright 2014 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package context_test - -import ( - "fmt" - "time" - - "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" -) - -func ExampleWithTimeout() { - // Pass a context with a timeout to tell a blocking function that it - // should abandon its work after the timeout elapses. - ctx, _ := context.WithTimeout(context.Background(), 100*time.Millisecond) - select { - case <-time.After(200 * time.Millisecond): - fmt.Println("overslept") - case <-ctx.Done(): - fmt.Println(ctx.Err()) // prints "context deadline exceeded" - } - // Output: - // context deadline exceeded -} diff --git a/identify/identify.go b/identify/identify.go index 315bb4899..b6b896780 100644 --- a/identify/identify.go +++ b/identify/identify.go @@ -16,8 +16,6 @@ import ( "crypto/sha512" "hash" - context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" - proto "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto" ci "github.com/jbenet/go-ipfs/crypto" peer "github.com/jbenet/go-ipfs/peer" @@ -35,15 +33,34 @@ var ErrUnsupportedKeyType = errors.New("unsupported key type") // Performs initial communication with this peer to share node ID's and // initiate communication. (secureIn, secureOut, error) -func Handshake(ctx context.Context, self, remote *peer.Peer, in <-chan []byte, out chan<- []byte) (<-chan []byte, chan<- []byte, error) { - - encodedHello, err := encodedProtoHello(self) +func Handshake(self, remote *peer.Peer, in <-chan []byte, out chan<- []byte) (<-chan []byte, chan<- []byte, error) { + // Generate and send Hello packet. + // Hello = (rand, PublicKey, Supported) + nonce := make([]byte, 16) + _, err := rand.Read(nonce) if err != nil { return nil, nil, err } - // TODO(brian): select on |ctx| - out <- encodedHello + hello := new(Hello) + + myPubKey, err := self.PubKey.Bytes() + if err != nil { + return nil, nil, err + } + + hello.Rand = nonce + hello.Pubkey = myPubKey + hello.Exchanges = &SupportedExchanges + hello.Ciphers = &SupportedCiphers + hello.Hashes = &SupportedHashes + + encoded, err := proto.Marshal(hello) + if err != nil { + return nil, nil, err + } + + out <- encoded // Parse their Hello packet and generate an Exchange packet. // Exchange = (EphemeralPubKey, Signature) @@ -86,7 +103,7 @@ func Handshake(ctx context.Context, self, remote *peer.Peer, in <-chan []byte, o } var handshake bytes.Buffer // Gather corpus to sign. - handshake.Write(encodedHello) + handshake.Write(encoded) handshake.Write(resp) handshake.Write(epubkey) @@ -120,7 +137,7 @@ func Handshake(ctx context.Context, self, remote *peer.Peer, in <-chan []byte, o if err != nil { return nil, nil, err } - _, err = theirHandshake.Write(encodedHello) + _, err = theirHandshake.Write(encoded) if err != nil { return nil, nil, err } @@ -143,11 +160,6 @@ func Handshake(ctx context.Context, self, remote *peer.Peer, in <-chan []byte, o return nil, nil, err } - myPubKey, err := self.PubKey.Bytes() - if err != nil { - return nil, nil, err - } - cmp := bytes.Compare(myPubKey, helloResp.GetPubkey()) mIV, tIV, mCKey, tCKey, mMKey, tMKey := ci.KeyStretcher(cmp, cipherType, hashType, secret) @@ -297,36 +309,3 @@ func selectBest(myPrefs, theirPrefs string) (string, error) { return "", errors.New("No algorithms in common!") } - -func genRandHello(self *peer.Peer) (*Hello, error) { - hello := new(Hello) - - // Generate and send Hello packet. - // Hello = (rand, PublicKey, Supported) - nonce := make([]byte, 16) - _, err := rand.Read(nonce) - if err != nil { - return nil, err - } - - myPubKey, err := self.PubKey.Bytes() - if err != nil { - return nil, err - } - - hello.Rand = nonce - hello.Pubkey = myPubKey - hello.Exchanges = &SupportedExchanges - hello.Ciphers = &SupportedCiphers - hello.Hashes = &SupportedHashes - return hello, nil -} - -func encodedProtoHello(self *peer.Peer) ([]byte, error) { - h, err := genRandHello(self) - if err != nil { - return nil, err - } - - return proto.Marshal(h) -} diff --git a/identify/identify_test.go b/identify/identify_test.go index 05b927e36..3d529f3e4 100644 --- a/identify/identify_test.go +++ b/identify/identify_test.go @@ -3,8 +3,6 @@ package identify import ( "testing" - context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" - ci "github.com/jbenet/go-ipfs/crypto" "github.com/jbenet/go-ipfs/peer" ) @@ -42,15 +40,14 @@ func TestHandshake(t *testing.T) { PrivKey: skb, } - ctx := context.Background() go func() { - _, _, err := Handshake(ctx, pa, pb, cha, chb) + _, _, err := Handshake(pa, pb, cha, chb) if err != nil { t.Fatal(err) } }() - _, _, err = Handshake(ctx, pb, pa, chb, cha) + _, _, err = Handshake(pb, pa, chb, cha) if err != nil { t.Fatal(err) } diff --git a/swarm/swarm.go b/swarm/swarm.go index 5b2d7407c..389453703 100644 --- a/swarm/swarm.go +++ b/swarm/swarm.go @@ -6,8 +6,6 @@ import ( "net" "sync" - context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" - proto "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto" ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr" ident "github.com/jbenet/go-ipfs/identify" @@ -164,7 +162,6 @@ func (s *Swarm) connListen(maddr *ma.Multiaddr) error { } // Handle getting ID from this peer and adding it into the map -// TODO(brian): impl a cancellable context func (s *Swarm) handleNewConn(nconn net.Conn) { p := new(peer.Peer) @@ -175,7 +172,7 @@ func (s *Swarm) handleNewConn(nconn net.Conn) { } newConnChans(conn) - sin, sout, err := ident.Handshake(context.Background(), s.local, p, conn.Incoming.MsgChan, conn.Outgoing.MsgChan) + sin, sout, err := ident.Handshake(s.local, p, conn.Incoming.MsgChan, conn.Outgoing.MsgChan) if err != nil { u.PErr("%v\n", err.Error()) conn.Close() @@ -429,10 +426,8 @@ func (s *Swarm) GetConnection(id peer.ID, addr *ma.Multiaddr) (*peer.Peer, error } // Handle performing a handshake on a new connection and ensuring proper forward communication -// TODO(brian): impl a cancellable context func (s *Swarm) handleDialedCon(conn *Conn) error { - - sin, sout, err := ident.Handshake(context.Background(), s.local, conn.Peer, conn.Incoming.MsgChan, conn.Outgoing.MsgChan) + sin, sout, err := ident.Handshake(s.local, conn.Peer, conn.Incoming.MsgChan, conn.Outgoing.MsgChan) if err != nil { return err }