Commit 461faf4a authored by Steven Allen's avatar Steven Allen Committed by GitHub
Browse files

Merge pull request #230 from libp2p/feat/update-stream-muxer

update go-stream-muxer
parents 958008b5 664afd4c
4.5.5: QmXZyBQMkqSYigxhJResC6fLWDGFhbphK67eZoqMDUvBmK 5.0.0: QmTykKqiBX2QyDjrJDKX6qzwU2ybMtWqaLET23mgyDSojx
...@@ -134,8 +134,12 @@ func main() { ...@@ -134,8 +134,12 @@ func main() {
// a user-defined protocol name. // a user-defined protocol name.
ha.SetStreamHandler("/echo/1.0.0", func(s net.Stream) { ha.SetStreamHandler("/echo/1.0.0", func(s net.Stream) {
log.Println("Got a new stream!") log.Println("Got a new stream!")
defer s.Close() if err := doEcho(s); err != nil {
doEcho(s) log.Println(err)
s.Reset()
} else {
s.Close()
}
}) })
if *target == "" { if *target == "" {
...@@ -194,18 +198,14 @@ func main() { ...@@ -194,18 +198,14 @@ func main() {
} }
// doEcho reads a line of data a stream and writes it back // doEcho reads a line of data a stream and writes it back
func doEcho(s net.Stream) { func doEcho(s net.Stream) error {
buf := bufio.NewReader(s) buf := bufio.NewReader(s)
str, err := buf.ReadString('\n') str, err := buf.ReadString('\n')
if err != nil { if err != nil {
log.Println(err) return err
return
} }
log.Printf("read: %s\n", str) log.Printf("read: %s\n", str)
_, err = s.Write([]byte(str)) _, err = s.Write([]byte(str))
if err != nil { return err
log.Println(err)
return
}
} }
...@@ -109,6 +109,7 @@ func streamHandler(stream inet.Stream) { ...@@ -109,6 +109,7 @@ func streamHandler(stream inet.Stream) {
// Read the HTTP request from the buffer // Read the HTTP request from the buffer
req, err := http.ReadRequest(buf) req, err := http.ReadRequest(buf)
if err != nil { if err != nil {
stream.Reset()
log.Println(err) log.Println(err)
return return
} }
...@@ -132,6 +133,7 @@ func streamHandler(stream inet.Stream) { ...@@ -132,6 +133,7 @@ func streamHandler(stream inet.Stream) {
fmt.Printf("Making request to %s\n", req.URL) fmt.Printf("Making request to %s\n", req.URL)
resp, err := http.DefaultTransport.RoundTrip(outreq) resp, err := http.DefaultTransport.RoundTrip(outreq)
if err != nil { if err != nil {
stream.Reset()
log.Println(err) log.Println(err)
return return
} }
...@@ -176,6 +178,7 @@ func (p *ProxyService) ServeHTTP(w http.ResponseWriter, r *http.Request) { ...@@ -176,6 +178,7 @@ func (p *ProxyService) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// r.Write() writes the HTTP request to the stream. // r.Write() writes the HTTP request to the stream.
err = r.Write(stream) err = r.Write(stream)
if err != nil { if err != nil {
stream.Reset()
log.Println(err) log.Println(err)
http.Error(w, err.Error(), http.StatusServiceUnavailable) http.Error(w, err.Error(), http.StatusServiceUnavailable)
return return
...@@ -186,6 +189,7 @@ func (p *ProxyService) ServeHTTP(w http.ResponseWriter, r *http.Request) { ...@@ -186,6 +189,7 @@ func (p *ProxyService) ServeHTTP(w http.ResponseWriter, r *http.Request) {
buf := bufio.NewReader(stream) buf := bufio.NewReader(stream)
resp, err := http.ReadResponse(buf, r) resp, err := http.ReadResponse(buf, r)
if err != nil { if err != nil {
stream.Reset()
log.Println(err) log.Println(err)
http.Error(w, err.Error(), http.StatusServiceUnavailable) http.Error(w, err.Error(), http.StatusServiceUnavailable)
return return
......
...@@ -240,7 +240,7 @@ func (h *BasicHost) newStreamHandler(s inet.Stream) { ...@@ -240,7 +240,7 @@ func (h *BasicHost) newStreamHandler(s inet.Stream) {
if h.negtimeout > 0 { if h.negtimeout > 0 {
if err := s.SetDeadline(time.Now().Add(h.negtimeout)); err != nil { if err := s.SetDeadline(time.Now().Add(h.negtimeout)); err != nil {
log.Error("setting stream deadline: ", err) log.Error("setting stream deadline: ", err)
s.Close() s.Reset()
return return
} }
} }
...@@ -257,7 +257,7 @@ func (h *BasicHost) newStreamHandler(s inet.Stream) { ...@@ -257,7 +257,7 @@ func (h *BasicHost) newStreamHandler(s inet.Stream) {
} else { } else {
log.Warning("protocol mux failed: %s (took %s)", err, took) log.Warning("protocol mux failed: %s (took %s)", err, took)
} }
s.Close() s.Reset()
return return
} }
...@@ -269,7 +269,7 @@ func (h *BasicHost) newStreamHandler(s inet.Stream) { ...@@ -269,7 +269,7 @@ func (h *BasicHost) newStreamHandler(s inet.Stream) {
if h.negtimeout > 0 { if h.negtimeout > 0 {
if err := s.SetDeadline(time.Time{}); err != nil { if err := s.SetDeadline(time.Time{}); err != nil {
log.Error("resetting stream deadline: ", err) log.Error("resetting stream deadline: ", err)
s.Close() s.Reset()
return return
} }
} }
...@@ -364,7 +364,7 @@ func (h *BasicHost) NewStream(ctx context.Context, p peer.ID, pids ...protocol.I ...@@ -364,7 +364,7 @@ func (h *BasicHost) NewStream(ctx context.Context, p peer.ID, pids ...protocol.I
selected, err := msmux.SelectOneOf(protoStrs, s) selected, err := msmux.SelectOneOf(protoStrs, s)
if err != nil { if err != nil {
s.Close() s.Reset()
return nil, err return nil, err
} }
selpid := protocol.ID(selected) selpid := protocol.ID(selected)
......
...@@ -180,7 +180,7 @@ func TestHostProtoMismatch(t *testing.T) { ...@@ -180,7 +180,7 @@ func TestHostProtoMismatch(t *testing.T) {
h1.SetStreamHandler("/super", func(s inet.Stream) { h1.SetStreamHandler("/super", func(s inet.Stream) {
t.Error("shouldnt get here") t.Error("shouldnt get here")
s.Close() s.Reset()
}) })
_, err := h2.NewStream(ctx, h1.ID(), "/foo", "/bar", "/baz/1.0.0") _, err := h2.NewStream(ctx, h1.ID(), "/foo", "/bar", "/baz/1.0.0")
......
...@@ -54,7 +54,7 @@ func (c *conn) Close() error { ...@@ -54,7 +54,7 @@ func (c *conn) Close() error {
func (c *conn) teardown() error { func (c *conn) teardown() error {
for _, s := range c.allStreams() { for _, s := range c.allStreams() {
s.Close() s.Reset()
} }
c.net.removeConn(c) c.net.removeConn(c)
c.net.notifyAll(func(n inet.Notifiee) { c.net.notifyAll(func(n inet.Notifiee) {
......
...@@ -2,7 +2,7 @@ package mocknet ...@@ -2,7 +2,7 @@ package mocknet
import ( import (
// "fmt" // "fmt"
"net" "io"
"sync" "sync"
"time" "time"
...@@ -45,11 +45,12 @@ func (l *link) newConnPair(dialer *peernet) (*conn, *conn) { ...@@ -45,11 +45,12 @@ func (l *link) newConnPair(dialer *peernet) (*conn, *conn) {
} }
func (l *link) newStreamPair() (*stream, *stream) { func (l *link) newStreamPair() (*stream, *stream) {
a, b := net.Pipe() ra, wb := io.Pipe()
rb, wa := io.Pipe()
s1 := NewStream(a) sa := NewStream(wa, ra)
s2 := NewStream(b) sb := NewStream(wb, rb)
return s1, s2 return sa, sb
} }
func (l *link) Networks() []inet.Network { func (l *link) Networks() []inet.Network {
......
...@@ -2,38 +2,52 @@ package mocknet ...@@ -2,38 +2,52 @@ package mocknet
import ( import (
"bytes" "bytes"
"errors"
"io" "io"
"net" "net"
"time" "time"
process "github.com/jbenet/goprocess"
inet "github.com/libp2p/go-libp2p-net" inet "github.com/libp2p/go-libp2p-net"
protocol "github.com/libp2p/go-libp2p-protocol" protocol "github.com/libp2p/go-libp2p-protocol"
) )
// stream implements inet.Stream // stream implements inet.Stream
type stream struct { type stream struct {
Pipe net.Conn write *io.PipeWriter
read *io.PipeReader
conn *conn conn *conn
toDeliver chan *transportObject toDeliver chan *transportObject
proc process.Process control chan int
state int
closed chan struct{}
protocol protocol.ID protocol protocol.ID
} }
var ErrReset error = errors.New("stream reset")
var ErrClosed error = errors.New("stream closed")
const (
stateOpen = iota
stateClose
stateReset
)
type transportObject struct { type transportObject struct {
msg []byte msg []byte
arrivalTime time.Time arrivalTime time.Time
} }
func NewStream(p net.Conn) *stream { func NewStream(w *io.PipeWriter, r *io.PipeReader) *stream {
s := &stream{ s := &stream{
Pipe: p, read: r,
write: w,
control: make(chan int),
closed: make(chan struct{}),
toDeliver: make(chan *transportObject), toDeliver: make(chan *transportObject),
} }
s.proc = process.WithTeardown(s.teardown) go s.transport()
s.proc.Go(s.transport)
return s return s
} }
...@@ -43,8 +57,13 @@ func (s *stream) Write(p []byte) (n int, err error) { ...@@ -43,8 +57,13 @@ func (s *stream) Write(p []byte) (n int, err error) {
delay := l.GetLatency() + l.RateLimit(len(p)) delay := l.GetLatency() + l.RateLimit(len(p))
t := time.Now().Add(delay) t := time.Now().Add(delay)
select { select {
case <-s.proc.Closing(): // bail out if we're closing. case <-s.closed: // bail out if we're closing.
return 0, io.ErrClosedPipe switch s.state {
case stateReset:
return 0, ErrReset
case stateClose:
return 0, ErrClosed
}
case s.toDeliver <- &transportObject{msg: p, arrivalTime: t}: case s.toDeliver <- &transportObject{msg: p, arrivalTime: t}:
} }
return len(p), nil return len(p), nil
...@@ -59,21 +78,46 @@ func (s *stream) SetProtocol(proto protocol.ID) { ...@@ -59,21 +78,46 @@ func (s *stream) SetProtocol(proto protocol.ID) {
} }
func (s *stream) Close() error { func (s *stream) Close() error {
return s.proc.Close() select {
case s.control <- stateClose:
case <-s.closed:
}
<-s.closed
if s.state == stateReset {
return nil
} else {
return ErrClosed
}
} }
// teardown shuts down the stream. it is called by s.proc.Close() func (s *stream) Reset() error {
// after all the children of this s.proc (i.e. transport's proc) // Cancel any pending reads.
// are done. s.write.Close()
func (s *stream) teardown() error {
// at this point, no streams are writing.
select {
case s.control <- stateReset:
case <-s.closed:
}
<-s.closed
if s.state == stateReset {
return nil
} else {
return ErrClosed
}
}
func (s *stream) teardown() {
s.write.Close()
// at this point, no streams are writing.
s.conn.removeStream(s) s.conn.removeStream(s)
s.Pipe.Close()
// Mark as closed.
close(s.closed)
s.conn.net.notifyAll(func(n inet.Notifiee) { s.conn.net.notifyAll(func(n inet.Notifiee) {
n.ClosedStream(s.conn.net, s) n.ClosedStream(s.conn.net, s)
}) })
return nil
} }
func (s *stream) Conn() inet.Conn { func (s *stream) Conn() inet.Conn {
...@@ -81,33 +125,44 @@ func (s *stream) Conn() inet.Conn { ...@@ -81,33 +125,44 @@ func (s *stream) Conn() inet.Conn {
} }
func (s *stream) SetDeadline(t time.Time) error { func (s *stream) SetDeadline(t time.Time) error {
return s.Pipe.SetDeadline(t) return &net.OpError{Op: "set", Net: "pipe", Source: nil, Addr: nil, Err: errors.New("deadline not supported")}
} }
func (s *stream) SetReadDeadline(t time.Time) error { func (p *stream) SetReadDeadline(t time.Time) error {
return s.Pipe.SetReadDeadline(t) return &net.OpError{Op: "set", Net: "pipe", Source: nil, Addr: nil, Err: errors.New("deadline not supported")}
} }
func (s *stream) SetWriteDeadline(t time.Time) error { func (p *stream) SetWriteDeadline(t time.Time) error {
return s.Pipe.SetWriteDeadline(t) return &net.OpError{Op: "set", Net: "pipe", Source: nil, Addr: nil, Err: errors.New("deadline not supported")}
} }
func (s *stream) Read(b []byte) (int, error) { func (s *stream) Read(b []byte) (int, error) {
return s.Pipe.Read(b) return s.read.Read(b)
} }
// transport will grab message arrival times, wait until that time, and // transport will grab message arrival times, wait until that time, and
// then write the message out when it is scheduled to arrive // then write the message out when it is scheduled to arrive
func (s *stream) transport(proc process.Process) { func (s *stream) transport() {
defer s.teardown()
bufsize := 256 bufsize := 256
buf := new(bytes.Buffer) buf := new(bytes.Buffer)
ticker := time.NewTicker(time.Millisecond * 4) timer := time.NewTimer(0)
if !timer.Stop() {
select {
case <-timer.C:
default:
}
}
// cleanup
defer timer.Stop()
// writeBuf writes the contents of buf through to the s.Writer. // writeBuf writes the contents of buf through to the s.Writer.
// done only when arrival time makes sense. // done only when arrival time makes sense.
drainBuf := func() { drainBuf := func() {
if buf.Len() > 0 { if buf.Len() > 0 {
_, err := s.Pipe.Write(buf.Bytes()) _, err := s.write.Write(buf.Bytes())
if err != nil { if err != nil {
return return
} }
...@@ -121,44 +176,63 @@ func (s *stream) transport(proc process.Process) { ...@@ -121,44 +176,63 @@ func (s *stream) transport(proc process.Process) {
deliverOrWait := func(o *transportObject) { deliverOrWait := func(o *transportObject) {
buffered := len(o.msg) + buf.Len() buffered := len(o.msg) + buf.Len()
now := time.Now() // Yes, we can end up extending a timer multiple times if we
if now.Before(o.arrivalTime) { // keep on making small writes but that shouldn't be too much of an
if buffered < bufsize { // issue. Fixing that would be painful.
buf.Write(o.msg) if !timer.Stop() {
return // FIXME: So, we *shouldn't* need to do this but we hang
// here if we don't... Go bug?
select {
case <-timer.C:
default:
} }
}
// we do not buffer + return here, instead hanging the delay := o.arrivalTime.Sub(time.Now())
// call (i.e. not accepting any more transportObjects) if delay >= 0 {
// so that we apply back-pressure to the sender. timer.Reset(delay)
// this sleep should wake up same time as ticker. } else {
time.Sleep(o.arrivalTime.Sub(now)) timer.Reset(0)
} }
// ok, we waited our due time. now rite the buf + msg. if buffered >= bufsize {
select {
// drainBuf first, before we write this message. case <-timer.C:
drainBuf() case s.state = <-s.control:
return
// write this message. }
_, err := s.Pipe.Write(o.msg) drainBuf()
if err != nil { // write this message.
log.Error("mock_stream", err) _, err := s.write.Write(o.msg)
if err != nil {
log.Error("mock_stream", err)
}
} else {
buf.Write(o.msg)
} }
} }
for { for {
select { switch s.state {
case <-proc.Closing(): case stateClose:
return // bail out of here. drainBuf()
return
case stateReset:
s.read.CloseWithError(ErrReset)
return
default:
panic("invalid state")
case stateOpen:
}
select {
case s.state = <-s.control:
continue
case o, ok := <-s.toDeliver: case o, ok := <-s.toDeliver:
if !ok { if !ok {
return return
} }
deliverOrWait(o) deliverOrWait(o)
case <-timer.C: // ok, due to write it out.
case <-ticker.C: // ok, due to write it out.
drainBuf() drainBuf()
} }
} }
......
...@@ -33,33 +33,42 @@ func NewPingService(h host.Host) *PingService { ...@@ -33,33 +33,42 @@ func NewPingService(h host.Host) *PingService {
} }
func (p *PingService) PingHandler(s inet.Stream) { func (p *PingService) PingHandler(s inet.Stream) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
buf := make([]byte, PingSize) buf := make([]byte, PingSize)
errCh := make(chan error, 1)
defer close(errCh)
timer := time.NewTimer(pingTimeout) timer := time.NewTimer(pingTimeout)
defer timer.Stop() defer timer.Stop()
go func() { go func() {
select { select {
case <-timer.C: case <-timer.C:
case <-ctx.Done(): log.Debug("ping timeout")
s.Reset()
case err, ok := <-errCh:
if ok {
log.Debug(err)
if err == io.EOF {
s.Close()
} else {
s.Reset()
}
} else {
log.Error("ping loop failed without error")
}
} }
s.Close()
}() }()
for { for {
_, err := io.ReadFull(s, buf) _, err := io.ReadFull(s, buf)
if err != nil { if err != nil {
log.Debug(err) errCh <- err
return return
} }
_, err = s.Write(buf) _, err = s.Write(buf)
if err != nil { if err != nil {
log.Debug(err) errCh <- err
return return
} }
...@@ -84,6 +93,7 @@ func (ps *PingService) Ping(ctx context.Context, p peer.ID) (<-chan time.Duratio ...@@ -84,6 +93,7 @@ func (ps *PingService) Ping(ctx context.Context, p peer.ID) (<-chan time.Duratio
default: default:
t, err := ping(s) t, err := ping(s)
if err != nil { if err != nil {
s.Reset()
log.Debugf("ping error: %s", err) log.Debugf("ping error: %s", err)
return return
} }
......
...@@ -31,8 +31,12 @@ func EchoStreamHandler(stream inet.Stream) { ...@@ -31,8 +31,12 @@ func EchoStreamHandler(stream inet.Stream) {
c := stream.Conn() c := stream.Conn()
log.Debugf("%s echoing %s", c.LocalPeer(), c.RemotePeer()) log.Debugf("%s echoing %s", c.LocalPeer(), c.RemotePeer())
go func() { go func() {
defer stream.Close() _, err := io.Copy(stream, stream)
io.Copy(stream, stream) if err == nil {
stream.Close()
} else {
stream.Reset()
}
}() }()
} }
......
...@@ -109,9 +109,9 @@ ...@@ -109,9 +109,9 @@
"version": "0.0.0" "version": "0.0.0"
}, },
{ {
"hash": "QmVNPgPmEG4QKaDKkxMPKY34Z53n8efzv1sEh4NTsdhto7", "hash": "QmTMNkpso2WRMevXC8ZxgyBhJvoEHvk24SNeUr9Mf9UM1a",
"name": "go-peerstream", "name": "go-peerstream",
"version": "1.7.0" "version": "2.0.2"
}, },
{ {
"author": "whyrusleeping", "author": "whyrusleeping",
...@@ -151,9 +151,9 @@ ...@@ -151,9 +151,9 @@
}, },
{ {
"author": "whyrusleeping", "author": "whyrusleeping",
"hash": "QmdQcv14hCd41WEzNA4avJohJR5sdPqVgFtXZtDz6MTCKx", "hash": "QmbrUTiVDSK3WGePN18qVjpGYmvXQt6YVPyyGoXWx593uq",
"name": "go-tcp-transport", "name": "go-tcp-transport",
"version": "1.2.2" "version": "1.2.3"
}, },
{ {
"author": "whyrusleeping", "author": "whyrusleeping",
...@@ -181,21 +181,21 @@ ...@@ -181,21 +181,21 @@
}, },
{ {
"author": "whyrusleeping", "author": "whyrusleeping",
"hash": "QmX49btJy5UQuHYmWxrNTmpcnUE5a4upGS6xPYH7mPE46D", "hash": "QmTi4629yyHJ8qW9sXFjvxJpYcN499tHhERLZYdUqwRU9i",
"name": "go-libp2p-conn", "name": "go-libp2p-conn",
"version": "1.6.12" "version": "1.6.13"
}, },
{ {
"author": "whyrusleeping", "author": "whyrusleeping",
"hash": "QmahYsGWry85Y7WUe2SX5G4JkH2zifEQAUtJVLZ24aC9DF", "hash": "QmNa31VPzC561NWwRsJLE7nGYZYuuD2QfpK2b1q9BK54J1",
"name": "go-libp2p-net", "name": "go-libp2p-net",
"version": "1.6.12" "version": "2.0.0"
}, },
{ {
"author": "whyrusleeping", "author": "whyrusleeping",
"hash": "QmVjRAPfRtResCMCE4eBqr4Beoa6A89P1YweG9wUS6RqUL", "hash": "QmQbh3Rb7KM37As3vkHYnEFnzkVXNCP8EYGtHz6g2fXk14",
"name": "go-libp2p-metrics", "name": "go-libp2p-metrics",
"version": "1.6.10" "version": "2.0.0"
}, },
{ {
"author": "whyrusleeping", "author": "whyrusleeping",
...@@ -205,15 +205,15 @@ ...@@ -205,15 +205,15 @@
}, },
{ {
"author": "whyrusleeping", "author": "whyrusleeping",
"hash": "QmUwW8jMQDxXhLD2j4EfWqLEMX3MsvyWcWGvJPVDh1aTmu", "hash": "QmaSxYRuMq4pkpBBG2CYaRrPx2z7NmMVEs34b9g61biQA6",
"name": "go-libp2p-host", "name": "go-libp2p-host",
"version": "1.3.19" "version": "2.0.0"
}, },
{ {
"author": "whyrusleeping", "author": "whyrusleeping",
"hash": "QmQUmDr1DMDDy6KMSsJuyV9nVD7dJZ9iWxXESQWPvte2NP", "hash": "QmW97nvnsknsoN8NUz8CUF5hVVaicLgNaEb6EZMk3oB943",
"name": "go-libp2p-swarm", "name": "go-libp2p-swarm",
"version": "1.7.7" "version": "2.0.2"
}, },
{ {
"author": "whyrusleeping", "author": "whyrusleeping",
...@@ -223,15 +223,15 @@ ...@@ -223,15 +223,15 @@
}, },
{ {
"author": "whyrusleeping", "author": "whyrusleeping",
"hash": "QmQ1bJEsmdEiGfTQRoj6CsshWmAKduAEDEbwzbvk5QT5Ui", "hash": "QmP4cEjmvf8tC6ykxKXrvmYLo8vqtGsgduMatjbAKnBzv8",
"name": "go-libp2p-netutil", "name": "go-libp2p-netutil",
"version": "0.2.25" "version": "0.3.1"
}, },
{ {
"author": "whyrusleeping", "author": "whyrusleeping",
"hash": "QmSmgF5Nnmf1Ygkv96xCmUdPk4QPx3JotTA7sqwXpoxCV2", "hash": "QmPZRCaYeNLMo5GfcRS2rv9ZxVuXXt6MFg9dWLmgsdXKCw",
"name": "go-libp2p-blankhost", "name": "go-libp2p-blankhost",
"version": "0.1.18" "version": "0.2.0"
}, },
{ {
"author": "whyrusleeping", "author": "whyrusleeping",
...@@ -241,9 +241,9 @@ ...@@ -241,9 +241,9 @@
}, },
{ {
"author": "whyrusleeping", "author": "whyrusleeping",
"hash": "Qmbn7RYyWzBVXiUp9jZ1dA4VADHy9DtS7iZLwfhEUQvm3U", "hash": "QmfTJ3UpS5ycNX7uQvPUSSRjGxk9EhUG7SyCstX6tCoNXS",
"name": "go-smux-yamux", "name": "go-smux-yamux",
"version": "1.2.0" "version": "2.0.0"
}, },
{ {
"author": "whyrusleeping", "author": "whyrusleeping",
...@@ -253,15 +253,15 @@ ...@@ -253,15 +253,15 @@
}, },
{ {
"author": "whyrusleeping", "author": "whyrusleeping",
"hash": "QmRVYfZ7tWNHPBzWiG6KWGzvT2hcGems8srihsQE29x1U5", "hash": "QmVniQJkdzLZaZwzwMdd3dJTvWiJ1DQEkreVy6hs6h7Vk5",
"name": "go-smux-multistream", "name": "go-smux-multistream",
"version": "1.5.5" "version": "2.0.0"
}, },
{ {
"author": "whyrusleeping", "author": "whyrusleeping",
"hash": "QmTuwwqGf4NH2Jj3opKtaKx45ge4RiXSCtvUkb7a4gk2ua", "hash": "QmYUpfXEBqLdtiSUDzzc8hLfcELPHiPtANF12EpEX1WCVB",
"name": "go-libp2p-connmgr", "name": "go-libp2p-connmgr",
"version": "0.1.3" "version": "0.2.0"
}, },
{ {
"author": "whyrusleeping", "author": "whyrusleeping",
...@@ -271,9 +271,9 @@ ...@@ -271,9 +271,9 @@
}, },
{ {
"author": "vyzo", "author": "vyzo",
"hash": "QmYkTCcfrPdR5QMasnhh3FVRVNEKzH3YsvuBPpB4YPgwWC", "hash": "QmVXc7cgEkxWDELn9sGV9r1HbqfQR9YCUmbsrkp1rcXSjn",
"name": "go-libp2p-circuit", "name": "go-libp2p-circuit",
"version": "1.1.8" "version": "2.0.1"
}, },
{ {
"author": "lgierth", "author": "lgierth",
...@@ -287,6 +287,6 @@ ...@@ -287,6 +287,6 @@
"license": "MIT", "license": "MIT",
"name": "go-libp2p", "name": "go-libp2p",
"releaseCmd": "git commit -a -m \"gx publish $VERSION\"", "releaseCmd": "git commit -a -m \"gx publish $VERSION\"",
"version": "4.5.5" "version": "5.0.0"
} }
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment