forked from cerc-io/ipld-eth-server
update dependencies to work with update eth-block-extractor
This commit is contained in:
+32
@@ -0,0 +1,32 @@
|
||||
os:
|
||||
- linux
|
||||
|
||||
language: go
|
||||
|
||||
go:
|
||||
- 1.11.x
|
||||
|
||||
env:
|
||||
global:
|
||||
- GOTFLAGS="-race"
|
||||
matrix:
|
||||
- BUILD_DEPTYPE=gx
|
||||
- BUILD_DEPTYPE=gomod
|
||||
|
||||
|
||||
# disable travis install
|
||||
install:
|
||||
- true
|
||||
|
||||
script:
|
||||
- bash <(curl -s https://raw.githubusercontent.com/ipfs/ci-helpers/master/travis-ci/run-standard-tests.sh)
|
||||
|
||||
|
||||
cache:
|
||||
directories:
|
||||
- $GOPATH/src/gx
|
||||
- $GOPATH/pkg/mod
|
||||
- /home/travis/.cache/go-build
|
||||
|
||||
notifications:
|
||||
email: false
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016 Jeromy Johnson
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
export IPFS_API ?= v04x.ipfs.io
|
||||
|
||||
gx:
|
||||
go get github.com/whyrusleeping/gx
|
||||
go get github.com/whyrusleeping/gx-go
|
||||
|
||||
deps: gx
|
||||
gx --verbose install --global
|
||||
gx-go rewrite
|
||||
|
||||
publish:
|
||||
gx-go rewrite --undo
|
||||
|
||||
+153
@@ -0,0 +1,153 @@
|
||||
# go-multistream
|
||||
|
||||
[](http://ipn.io)
|
||||
[](https://github.com/multiformats/multiformats)
|
||||
[](https://webchat.freenode.net/?channels=%23ipfs)
|
||||
[](https://github.com/RichardLitt/standard-readme)
|
||||
[](https://godoc.org/github.com/multiformats/go-multistream)
|
||||
[](https://travis-ci.org/multiformats/go-multistream)
|
||||
[](https://codecov.io/github/multiformats/go-multistream?branch=master)
|
||||
|
||||
> an implementation of the multistream protocol in go
|
||||
|
||||
This package implements a simple stream router for the multistream-select protocol.
|
||||
The protocol is defined [here](https://github.com/multiformats/multistream-select).
|
||||
|
||||
## Table of Contents
|
||||
|
||||
|
||||
- [Install](#install)
|
||||
- [Usage](#usage)
|
||||
- [Maintainers](#maintainers)
|
||||
- [Contribute](#contribute)
|
||||
- [License](#license)
|
||||
|
||||
## Install
|
||||
|
||||
`go-multistream` is a standard Go module which can be installed with:
|
||||
|
||||
```sh
|
||||
go get github.com/multiformats/go-multistream
|
||||
```
|
||||
|
||||
Note that `go-multistream` is packaged with Gx, so it is recommended to use Gx to install and use it (see Usage section).
|
||||
|
||||
|
||||
## Usage
|
||||
|
||||
### Using Gx and Gx-go
|
||||
|
||||
This module is packaged with [Gx](https://github.com/whyrusleeping/gx). In order to use it in your own project do:
|
||||
|
||||
```sh
|
||||
go get -u github.com/whyrusleeping/gx
|
||||
go get -u github.com/whyrusleeping/gx-go
|
||||
cd <your-project-repository>
|
||||
gx init
|
||||
gx import github.com/multiformats/go-multistream
|
||||
gx install --global
|
||||
gx-go --rewrite
|
||||
```
|
||||
|
||||
Please check [Gx](https://github.com/whyrusleeping/gx) and [Gx-go](https://github.com/whyrusleeping/gx-go) documentation for more information.
|
||||
|
||||
### Example
|
||||
|
||||
|
||||
This example shows how to use a multistream muxer. A muxer uses user-added handlers to handle different "protocols". The first step when interacting with a connection handler by the muxer is to select the protocol (the example uses `SelectProtoOrFail`). This will then let the muxer use the right handler.
|
||||
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
|
||||
ms "github.com/multiformats/go-multistream"
|
||||
)
|
||||
|
||||
// This example creates a multistream muxer, adds handlers for the protocols
|
||||
// "/cats" and "/dogs" and exposes it on a localhost:8765. It then opens connections
|
||||
// to that port, selects the protocols and tests that the handlers are working.
|
||||
func main() {
|
||||
mux := ms.NewMultistreamMuxer()
|
||||
mux.AddHandler("/cats", func(proto string, rwc io.ReadWriteCloser) error {
|
||||
fmt.Fprintln(rwc, proto, ": HELLO I LIKE CATS")
|
||||
return rwc.Close()
|
||||
})
|
||||
mux.AddHandler("/dogs", func(proto string, rwc io.ReadWriteCloser) error {
|
||||
fmt.Fprintln(rwc, proto, ": HELLO I LIKE DOGS")
|
||||
return rwc.Close()
|
||||
})
|
||||
|
||||
list, err := net.Listen("tcp", ":8765")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
go func() {
|
||||
for {
|
||||
con, err := list.Accept()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
go mux.Handle(con)
|
||||
}
|
||||
}()
|
||||
|
||||
// The Muxer is ready, let's test it
|
||||
conn, err := net.Dial("tcp", ":8765")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Create a new multistream to talk to the muxer
|
||||
// which will negotiate that we want to talk with /cats
|
||||
mstream := ms.NewMSSelect(conn, "/cats")
|
||||
cats, err := ioutil.ReadAll(mstream)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Printf("%s", cats)
|
||||
mstream.Close()
|
||||
|
||||
// A different way of talking to the muxer
|
||||
// is to manually selecting the protocol ourselves
|
||||
conn, err = net.Dial("tcp", ":8765")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer conn.Close()
|
||||
err = ms.SelectProtoOrFail("/dogs", conn)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
dogs, err := ioutil.ReadAll(conn)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Printf("%s", dogs)
|
||||
conn.Close()
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## Maintainers
|
||||
|
||||
Captain: [@whyrusleeping](https://github.com/whyrusleeping).
|
||||
|
||||
## Contribute
|
||||
|
||||
Contributions welcome. Please check out [the issues](https://github.com/multiformats/go-multistream/issues).
|
||||
|
||||
Check out our [contributing document](https://github.com/multiformats/multiformats/blob/master/contributing.md) for more information on how we work, and about contributing in general. Please be aware that all interactions related to multiformats are subject to the IPFS [Code of Conduct](https://github.com/ipfs/community/blob/master/code-of-conduct.md).
|
||||
|
||||
Small note: If editing the README, please conform to the [standard-readme](https://github.com/RichardLitt/standard-readme) specification.
|
||||
|
||||
## License
|
||||
|
||||
[MIT](LICENSE) © 2016 Jeromy Johnson
|
||||
+87
@@ -0,0 +1,87 @@
|
||||
package multistream
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
)
|
||||
|
||||
// ErrNotSupported is the error returned when the muxer does not support
|
||||
// the protocol specified for the handshake.
|
||||
var ErrNotSupported = errors.New("protocol not supported")
|
||||
|
||||
// SelectProtoOrFail performs the initial multistream handshake
|
||||
// to inform the muxer of the protocol that will be used to communicate
|
||||
// on this ReadWriteCloser. It returns an error if, for example,
|
||||
// the muxer does not know how to handle this protocol.
|
||||
func SelectProtoOrFail(proto string, rwc io.ReadWriteCloser) error {
|
||||
err := handshake(rwc)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return trySelect(proto, rwc)
|
||||
}
|
||||
|
||||
// SelectOneOf will perform handshakes with the protocols on the given slice
|
||||
// until it finds one which is supported by the muxer.
|
||||
func SelectOneOf(protos []string, rwc io.ReadWriteCloser) (string, error) {
|
||||
err := handshake(rwc)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
for _, p := range protos {
|
||||
err := trySelect(p, rwc)
|
||||
switch err {
|
||||
case nil:
|
||||
return p, nil
|
||||
case ErrNotSupported:
|
||||
default:
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
return "", ErrNotSupported
|
||||
}
|
||||
|
||||
func handshake(rwc io.ReadWriteCloser) error {
|
||||
errCh := make(chan error, 1)
|
||||
go func() {
|
||||
errCh <- delimWriteBuffered(rwc, []byte(ProtocolID))
|
||||
}()
|
||||
|
||||
tok, readErr := ReadNextToken(rwc)
|
||||
writeErr := <-errCh
|
||||
|
||||
if writeErr != nil {
|
||||
return writeErr
|
||||
}
|
||||
if readErr != nil {
|
||||
return readErr
|
||||
}
|
||||
|
||||
if tok != ProtocolID {
|
||||
return errors.New("received mismatch in protocol id")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func trySelect(proto string, rwc io.ReadWriteCloser) error {
|
||||
err := delimWriteBuffered(rwc, []byte(proto))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tok, err := ReadNextToken(rwc)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
switch tok {
|
||||
case proto:
|
||||
return nil
|
||||
case "na":
|
||||
return ErrNotSupported
|
||||
default:
|
||||
return errors.New("unrecognized response: " + tok)
|
||||
}
|
||||
}
|
||||
+1
@@ -0,0 +1 @@
|
||||
module github.com/multiformats/go-multistream
|
||||
+142
@@ -0,0 +1,142 @@
|
||||
package multistream
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"io"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// Multistream represents in essense a ReadWriteCloser, or a single
|
||||
// communication wire which supports multiple streams on it. Each
|
||||
// stream is identified by a protocol tag.
|
||||
type Multistream interface {
|
||||
io.ReadWriteCloser
|
||||
}
|
||||
|
||||
// NewMSSelect returns a new Multistream which is able to perform
|
||||
// protocol selection with a MultistreamMuxer.
|
||||
func NewMSSelect(c io.ReadWriteCloser, proto string) Multistream {
|
||||
return &lazyClientConn{
|
||||
protos: []string{ProtocolID, proto},
|
||||
con: c,
|
||||
}
|
||||
}
|
||||
|
||||
// NewMultistream returns a multistream for the given protocol. This will not
|
||||
// perform any protocol selection. If you are using a MultistreamMuxer, use
|
||||
// NewMSSelect.
|
||||
func NewMultistream(c io.ReadWriteCloser, proto string) Multistream {
|
||||
return &lazyClientConn{
|
||||
protos: []string{proto},
|
||||
con: c,
|
||||
}
|
||||
}
|
||||
|
||||
// lazyClientConn is a ReadWriteCloser adapter that lazily negotiates a protocol
|
||||
// using multistream-select on first use.
|
||||
//
|
||||
// It *does not* block writes waiting for the other end to respond. Instead, it
|
||||
// simply assumes the negotiation went successfully and starts writing data.
|
||||
// See: https://github.com/multiformats/go-multistream/issues/20
|
||||
type lazyClientConn struct {
|
||||
// Used to ensure we only trigger the write half of the handshake once.
|
||||
rhandshakeOnce sync.Once
|
||||
rerr error
|
||||
|
||||
// Used to ensure we only trigger the read half of the handshake once.
|
||||
whandshakeOnce sync.Once
|
||||
werr error
|
||||
|
||||
// The sequence of protocols to negotiate.
|
||||
protos []string
|
||||
|
||||
// The inner connection.
|
||||
con io.ReadWriteCloser
|
||||
}
|
||||
|
||||
// Read reads data from the io.ReadWriteCloser.
|
||||
//
|
||||
// If the protocol hasn't yet been negotiated, this method triggers the write
|
||||
// half of the handshake and then waits for the read half to complete.
|
||||
//
|
||||
// It returns an error if the read half of the handshake fails.
|
||||
func (l *lazyClientConn) Read(b []byte) (int, error) {
|
||||
l.rhandshakeOnce.Do(func() {
|
||||
go l.whandshakeOnce.Do(l.doWriteHandshake)
|
||||
l.doReadHandshake()
|
||||
})
|
||||
if l.rerr != nil {
|
||||
return 0, l.rerr
|
||||
}
|
||||
if len(b) == 0 {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
return l.con.Read(b)
|
||||
}
|
||||
|
||||
func (l *lazyClientConn) doReadHandshake() {
|
||||
for _, proto := range l.protos {
|
||||
// read protocol
|
||||
tok, err := ReadNextToken(l.con)
|
||||
if err != nil {
|
||||
l.rerr = err
|
||||
return
|
||||
}
|
||||
|
||||
if tok != proto {
|
||||
l.rerr = fmt.Errorf("protocol mismatch in lazy handshake ( %s != %s )", tok, proto)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (l *lazyClientConn) doWriteHandshake() {
|
||||
l.doWriteHandshakeWithData(nil)
|
||||
}
|
||||
|
||||
// Perform the write handshake but *also* write some extra data.
|
||||
func (l *lazyClientConn) doWriteHandshakeWithData(extra []byte) int {
|
||||
buf := bufio.NewWriter(l.con)
|
||||
for _, proto := range l.protos {
|
||||
l.werr = delimWrite(buf, []byte(proto))
|
||||
if l.werr != nil {
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
n := 0
|
||||
if len(extra) > 0 {
|
||||
n, l.werr = buf.Write(extra)
|
||||
if l.werr != nil {
|
||||
return n
|
||||
}
|
||||
}
|
||||
l.werr = buf.Flush()
|
||||
return n
|
||||
}
|
||||
|
||||
// Write writes the given buffer to the underlying connection.
|
||||
//
|
||||
// If the protocol has not yet been negotiated, write waits for the write half
|
||||
// of the handshake to complete triggers (but does not wait for) the read half.
|
||||
//
|
||||
// Write *also* ignores errors from the read half of the handshake (in case the
|
||||
// stream is actually write only).
|
||||
func (l *lazyClientConn) Write(b []byte) (int, error) {
|
||||
n := 0
|
||||
l.whandshakeOnce.Do(func() {
|
||||
go l.rhandshakeOnce.Do(l.doReadHandshake)
|
||||
n = l.doWriteHandshakeWithData(b)
|
||||
})
|
||||
if l.werr != nil || n > 0 {
|
||||
return n, l.werr
|
||||
}
|
||||
return l.con.Write(b)
|
||||
}
|
||||
|
||||
// Close closes the underlying io.ReadWriteCloser
|
||||
func (l *lazyClientConn) Close() error {
|
||||
return l.con.Close()
|
||||
}
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
package multistream
|
||||
|
||||
import (
|
||||
"io"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// lazyServerConn is an io.ReadWriteCloser adapter used for negotiating inbound
|
||||
// streams (see NegotiateLazy).
|
||||
//
|
||||
// This is "lazy" because it doesn't wait for the write half to succeed before
|
||||
// allowing us to read from the stream.
|
||||
type lazyServerConn struct {
|
||||
waitForHandshake sync.Once
|
||||
werr error
|
||||
|
||||
con io.ReadWriteCloser
|
||||
}
|
||||
|
||||
func (l *lazyServerConn) Write(b []byte) (int, error) {
|
||||
l.waitForHandshake.Do(func() { panic("didn't initiate handshake") })
|
||||
if l.werr != nil {
|
||||
return 0, l.werr
|
||||
}
|
||||
return l.con.Write(b)
|
||||
}
|
||||
|
||||
func (l *lazyServerConn) Read(b []byte) (int, error) {
|
||||
if len(b) == 0 {
|
||||
return 0, nil
|
||||
}
|
||||
return l.con.Read(b)
|
||||
}
|
||||
|
||||
func (l *lazyServerConn) Close() error {
|
||||
return l.con.Close()
|
||||
}
|
||||
+440
@@ -0,0 +1,440 @@
|
||||
// Package multistream implements a simple stream router for the
|
||||
// multistream-select protocoli. The protocol is defined at
|
||||
// https://github.com/multiformats/multistream-select
|
||||
package multistream
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"io"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// ErrTooLarge is an error to signal that an incoming message was too large
|
||||
var ErrTooLarge = errors.New("incoming message was too large")
|
||||
|
||||
// ProtocolID identifies the multistream protocol itself and makes sure
|
||||
// the multistream muxers on both sides of a channel can work with each other.
|
||||
const ProtocolID = "/multistream/1.0.0"
|
||||
|
||||
// HandlerFunc is a user-provided function used by the MultistreamMuxer to
|
||||
// handle a protocol/stream.
|
||||
type HandlerFunc func(protocol string, rwc io.ReadWriteCloser) error
|
||||
|
||||
// Handler is a wrapper to HandlerFunc which attaches a name (protocol) and a
|
||||
// match function which can optionally be used to select a handler by other
|
||||
// means than the name.
|
||||
type Handler struct {
|
||||
MatchFunc func(string) bool
|
||||
Handle HandlerFunc
|
||||
AddName string
|
||||
}
|
||||
|
||||
// MultistreamMuxer is a muxer for multistream. Depending on the stream
|
||||
// protocol tag it will select the right handler and hand the stream off to it.
|
||||
type MultistreamMuxer struct {
|
||||
handlerlock sync.Mutex
|
||||
handlers []Handler
|
||||
}
|
||||
|
||||
// NewMultistreamMuxer creates a muxer.
|
||||
func NewMultistreamMuxer() *MultistreamMuxer {
|
||||
return new(MultistreamMuxer)
|
||||
}
|
||||
|
||||
func writeUvarint(w io.Writer, i uint64) error {
|
||||
varintbuf := make([]byte, 16)
|
||||
n := binary.PutUvarint(varintbuf, i)
|
||||
_, err := w.Write(varintbuf[:n])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func delimWriteBuffered(w io.Writer, mes []byte) error {
|
||||
bw := bufio.NewWriter(w)
|
||||
err := delimWrite(bw, mes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return bw.Flush()
|
||||
}
|
||||
|
||||
func delimWrite(w io.Writer, mes []byte) error {
|
||||
err := writeUvarint(w, uint64(len(mes)+1))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = w.Write(mes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = w.Write([]byte{'\n'})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Ls is a Multistream muxer command which returns the list of handler names
|
||||
// available on a muxer.
|
||||
func Ls(rw io.ReadWriter) ([]string, error) {
|
||||
err := delimWriteBuffered(rw, []byte("ls"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
n, err := binary.ReadUvarint(&byteReader{rw})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var out []string
|
||||
for i := uint64(0); i < n; i++ {
|
||||
val, err := lpReadBuf(rw)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out = append(out, string(val))
|
||||
}
|
||||
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func fulltextMatch(s string) func(string) bool {
|
||||
return func(a string) bool {
|
||||
return a == s
|
||||
}
|
||||
}
|
||||
|
||||
// AddHandler attaches a new protocol handler to the muxer.
|
||||
func (msm *MultistreamMuxer) AddHandler(protocol string, handler HandlerFunc) {
|
||||
msm.AddHandlerWithFunc(protocol, fulltextMatch(protocol), handler)
|
||||
}
|
||||
|
||||
// AddHandlerWithFunc attaches a new protocol handler to the muxer with a match.
|
||||
// If the match function returns true for a given protocol tag, the protocol
|
||||
// will be selected even if the handler name and protocol tags are different.
|
||||
func (msm *MultistreamMuxer) AddHandlerWithFunc(protocol string, match func(string) bool, handler HandlerFunc) {
|
||||
msm.handlerlock.Lock()
|
||||
msm.removeHandler(protocol)
|
||||
msm.handlers = append(msm.handlers, Handler{
|
||||
MatchFunc: match,
|
||||
Handle: handler,
|
||||
AddName: protocol,
|
||||
})
|
||||
msm.handlerlock.Unlock()
|
||||
}
|
||||
|
||||
// RemoveHandler removes the handler with the given name from the muxer.
|
||||
func (msm *MultistreamMuxer) RemoveHandler(protocol string) {
|
||||
msm.handlerlock.Lock()
|
||||
defer msm.handlerlock.Unlock()
|
||||
|
||||
msm.removeHandler(protocol)
|
||||
}
|
||||
|
||||
func (msm *MultistreamMuxer) removeHandler(protocol string) {
|
||||
for i, h := range msm.handlers {
|
||||
if h.AddName == protocol {
|
||||
msm.handlers = append(msm.handlers[:i], msm.handlers[i+1:]...)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Protocols returns the list of handler-names added to this this muxer.
|
||||
func (msm *MultistreamMuxer) Protocols() []string {
|
||||
var out []string
|
||||
msm.handlerlock.Lock()
|
||||
for _, h := range msm.handlers {
|
||||
out = append(out, h.AddName)
|
||||
}
|
||||
msm.handlerlock.Unlock()
|
||||
return out
|
||||
}
|
||||
|
||||
// ErrIncorrectVersion is an error reported when the muxer protocol negotiation
|
||||
// fails because of a ProtocolID mismatch.
|
||||
var ErrIncorrectVersion = errors.New("client connected with incorrect version")
|
||||
|
||||
func (msm *MultistreamMuxer) findHandler(proto string) *Handler {
|
||||
msm.handlerlock.Lock()
|
||||
defer msm.handlerlock.Unlock()
|
||||
|
||||
for _, h := range msm.handlers {
|
||||
if h.MatchFunc(proto) {
|
||||
return &h
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// NegotiateLazy performs protocol selection and returns
|
||||
// a multistream, the protocol used, the handler and an error. It is lazy
|
||||
// because the write-handshake is performed on a subroutine, allowing this
|
||||
// to return before that handshake is completed.
|
||||
func (msm *MultistreamMuxer) NegotiateLazy(rwc io.ReadWriteCloser) (Multistream, string, HandlerFunc, error) {
|
||||
pval := make(chan string, 1)
|
||||
writeErr := make(chan error, 1)
|
||||
defer close(pval)
|
||||
|
||||
lzc := &lazyServerConn{
|
||||
con: rwc,
|
||||
}
|
||||
|
||||
started := make(chan struct{})
|
||||
go lzc.waitForHandshake.Do(func() {
|
||||
close(started)
|
||||
|
||||
defer close(writeErr)
|
||||
|
||||
if err := delimWriteBuffered(rwc, []byte(ProtocolID)); err != nil {
|
||||
lzc.werr = err
|
||||
writeErr <- err
|
||||
return
|
||||
}
|
||||
|
||||
for proto := range pval {
|
||||
if err := delimWriteBuffered(rwc, []byte(proto)); err != nil {
|
||||
lzc.werr = err
|
||||
writeErr <- err
|
||||
return
|
||||
}
|
||||
}
|
||||
})
|
||||
<-started
|
||||
|
||||
line, err := ReadNextToken(rwc)
|
||||
if err != nil {
|
||||
return nil, "", nil, err
|
||||
}
|
||||
|
||||
if line != ProtocolID {
|
||||
rwc.Close()
|
||||
return nil, "", nil, ErrIncorrectVersion
|
||||
}
|
||||
|
||||
loop:
|
||||
for {
|
||||
// Now read and respond to commands until they send a valid protocol id
|
||||
tok, err := ReadNextToken(rwc)
|
||||
if err != nil {
|
||||
rwc.Close()
|
||||
return nil, "", nil, err
|
||||
}
|
||||
|
||||
switch tok {
|
||||
case "ls":
|
||||
select {
|
||||
case pval <- "ls":
|
||||
case err := <-writeErr:
|
||||
rwc.Close()
|
||||
return nil, "", nil, err
|
||||
}
|
||||
default:
|
||||
h := msm.findHandler(tok)
|
||||
if h == nil {
|
||||
select {
|
||||
case pval <- "na":
|
||||
case err := <-writeErr:
|
||||
rwc.Close()
|
||||
return nil, "", nil, err
|
||||
}
|
||||
continue loop
|
||||
}
|
||||
|
||||
select {
|
||||
case pval <- tok:
|
||||
case <-writeErr:
|
||||
// explicitly ignore this error. It will be returned to any
|
||||
// writers and if we don't plan on writing anything, we still
|
||||
// want to complete the handshake
|
||||
}
|
||||
|
||||
// hand off processing to the sub-protocol handler
|
||||
return lzc, tok, h.Handle, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Negotiate performs protocol selection and returns the protocol name and
|
||||
// the matching handler function for it (or an error).
|
||||
func (msm *MultistreamMuxer) Negotiate(rwc io.ReadWriteCloser) (string, HandlerFunc, error) {
|
||||
// Send our protocol ID
|
||||
err := delimWriteBuffered(rwc, []byte(ProtocolID))
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
|
||||
line, err := ReadNextToken(rwc)
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
|
||||
if line != ProtocolID {
|
||||
rwc.Close()
|
||||
return "", nil, ErrIncorrectVersion
|
||||
}
|
||||
|
||||
loop:
|
||||
for {
|
||||
// Now read and respond to commands until they send a valid protocol id
|
||||
tok, err := ReadNextToken(rwc)
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
|
||||
switch tok {
|
||||
case "ls":
|
||||
err := msm.Ls(rwc)
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
default:
|
||||
h := msm.findHandler(tok)
|
||||
if h == nil {
|
||||
err := delimWriteBuffered(rwc, []byte("na"))
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
continue loop
|
||||
}
|
||||
|
||||
err := delimWriteBuffered(rwc, []byte(tok))
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
|
||||
// hand off processing to the sub-protocol handler
|
||||
return tok, h.Handle, nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Ls implements the "ls" command which writes the list of
|
||||
// supported protocols to the given Writer.
|
||||
func (msm *MultistreamMuxer) Ls(w io.Writer) error {
|
||||
buf := new(bytes.Buffer)
|
||||
msm.handlerlock.Lock()
|
||||
err := writeUvarint(buf, uint64(len(msm.handlers)))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, h := range msm.handlers {
|
||||
err := delimWrite(buf, []byte(h.AddName))
|
||||
if err != nil {
|
||||
msm.handlerlock.Unlock()
|
||||
return err
|
||||
}
|
||||
}
|
||||
msm.handlerlock.Unlock()
|
||||
ll := make([]byte, 16)
|
||||
nw := binary.PutUvarint(ll, uint64(buf.Len()))
|
||||
|
||||
r := io.MultiReader(bytes.NewReader(ll[:nw]), buf)
|
||||
|
||||
_, err = io.Copy(w, r)
|
||||
return err
|
||||
}
|
||||
|
||||
// Handle performs protocol negotiation on a ReadWriteCloser
|
||||
// (i.e. a connection). It will find a matching handler for the
|
||||
// incoming protocol and pass the ReadWriteCloser to it.
|
||||
func (msm *MultistreamMuxer) Handle(rwc io.ReadWriteCloser) error {
|
||||
p, h, err := msm.Negotiate(rwc)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return h(p, rwc)
|
||||
}
|
||||
|
||||
// ReadNextToken extracts a token from a ReadWriter. It is used during
|
||||
// protocol negotiation and returns a string.
|
||||
func ReadNextToken(rw io.ReadWriter) (string, error) {
|
||||
tok, err := ReadNextTokenBytes(rw)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return string(tok), nil
|
||||
}
|
||||
|
||||
// ReadNextTokenBytes extracts a token from a ReadWriter. It is used
|
||||
// during protocol negotiation and returns a byte slice.
|
||||
func ReadNextTokenBytes(rw io.ReadWriter) ([]byte, error) {
|
||||
data, err := lpReadBuf(rw)
|
||||
switch err {
|
||||
case nil:
|
||||
return data, nil
|
||||
case ErrTooLarge:
|
||||
err := delimWriteBuffered(rw, []byte("messages over 64k are not allowed"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return nil, ErrTooLarge
|
||||
default:
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
func lpReadBuf(r io.Reader) ([]byte, error) {
|
||||
br, ok := r.(io.ByteReader)
|
||||
if !ok {
|
||||
br = &byteReader{r}
|
||||
}
|
||||
|
||||
length, err := binary.ReadUvarint(br)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if length > 64*1024 {
|
||||
return nil, ErrTooLarge
|
||||
}
|
||||
|
||||
buf := make([]byte, length)
|
||||
_, err = io.ReadFull(r, buf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(buf) == 0 || buf[length-1] != '\n' {
|
||||
return nil, errors.New("message did not have trailing newline")
|
||||
}
|
||||
|
||||
// slice off the trailing newline
|
||||
buf = buf[:length-1]
|
||||
|
||||
return buf, nil
|
||||
|
||||
}
|
||||
|
||||
// byteReader implements the ByteReader interface that ReadUVarint requires
|
||||
type byteReader struct {
|
||||
io.Reader
|
||||
}
|
||||
|
||||
func (br *byteReader) ReadByte() (byte, error) {
|
||||
var b [1]byte
|
||||
n, err := br.Read(b[:])
|
||||
if n == 1 {
|
||||
return b[0], nil
|
||||
}
|
||||
if err == nil {
|
||||
if n != 0 {
|
||||
panic("read more bytes than buffer size")
|
||||
}
|
||||
err = io.ErrNoProgress
|
||||
}
|
||||
return 0, err
|
||||
}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"author": "whyrusleeping",
|
||||
"bugs": {
|
||||
"url": "https://github.com/multiformats/go-multistream"
|
||||
},
|
||||
"gx": {
|
||||
"dvcsimport": "github.com/multiformats/go-multistream"
|
||||
},
|
||||
"gxVersion": "0.7.0",
|
||||
"language": "go",
|
||||
"license": "",
|
||||
"name": "go-multistream",
|
||||
"releaseCmd": "git commit -a -m \"gx publish $VERSION\"",
|
||||
"version": "0.3.9"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user