Skip to content

Commit b8f1996

Browse files
authored
Merge pull request #15 from multiformats/docs-improvements
README/docs: Fix and improve example. Add Gx instructions. Golint.
2 parents 661a0b9 + 6ef027f commit b8f1996

File tree

4 files changed

+136
-11
lines changed

4 files changed

+136
-11
lines changed

README.md

Lines changed: 80 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
[![](https://img.shields.io/badge/project-multiformats-blue.svg?style=flat-square)](https://github.com/multiformats/multiformats)
55
[![](https://img.shields.io/badge/freenode-%23ipfs-blue.svg?style=flat-square)](https://webchat.freenode.net/?channels=%23ipfs)
66
[![](https://img.shields.io/badge/readme%20style-standard-brightgreen.svg?style=flat-square)](https://github.com/RichardLitt/standard-readme)
7+
[![GoDoc](https://godoc.org/github.com/multiformats/go-multistream?status.svg)](https://godoc.org/github.com/multiformats/go-multistream)
78
[![Travis CI](https://img.shields.io/travis/multiformats/go-multistream.svg?style=flat-square&branch=master)](https://travis-ci.org/multiformats/go-multistream)
89
[![codecov.io](https://img.shields.io/codecov/c/github/multiformats/go-multistream.svg?style=flat-square&branch=master)](https://codecov.io/github/multiformats/go-multistream?branch=master)
910

@@ -23,30 +24,62 @@ The protocol is defined [here](https://github.com/multiformats/multistream-selec
2324

2425
## Install
2526

27+
`go-multistream` is a standard Go module which can be installed with:
28+
2629
```sh
2730
go get github.com/multiformats/go-multistream
2831
```
2932

33+
Note that `go-multistream` is packaged with Gx, so it is recommended to use Gx to install and use it (see Usage section).
34+
35+
3036
## Usage
3137

38+
### Using Gx and Gx-go
39+
40+
This module is packaged with [Gx](https://github.com/whyrusleeping/gx). In order to use it in your own project do:
41+
42+
```sh
43+
go get -u github.com/whyrusleeping/gx
44+
go get -u github.com/whyrusleeping/gx-go
45+
cd <your-project-repository>
46+
gx init
47+
gx import github.com/multiformats/go-multistream
48+
gx install --global
49+
gx-go --rewrite
50+
```
51+
52+
Please check [Gx](https://github.com/whyrusleeping/gx) and [Gx-go](https://github.com/whyrusleeping/gx-go) documentation for more information.
53+
54+
### Example
55+
56+
57+
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.
58+
59+
3260
```go
3361
package main
3462

3563
import (
3664
"fmt"
37-
ms "github.com/multiformats/go-multistream"
3865
"io"
66+
"io/ioutil"
3967
"net"
68+
69+
ms "github.com/multiformats/go-multistream"
4070
)
4171

72+
// This example creates a multistream muxer, adds handlers for the protocols
73+
// "/cats" and "/docs" and exposes it on a localhost:8765. It then opens connections
74+
// to that port, selects the protocols and tests that the handlers are working.
4275
func main() {
4376
mux := ms.NewMultistreamMuxer()
44-
mux.AddHandler("/cats", func(rwc io.ReadWriteCloser) error {
45-
fmt.Fprintln(rwc, "HELLO I LIKE CATS")
77+
mux.AddHandler("/cats", func(proto string, rwc io.ReadWriteCloser) error {
78+
fmt.Fprintln(rwc, proto, ": HELLO I LIKE CATS")
4679
return rwc.Close()
4780
})
48-
mux.AddHandler("/dogs", func(rwc io.ReadWriteCloser) error {
49-
fmt.Fprintln(rwc, "HELLO I LIKE DOGS")
81+
mux.AddHandler("/dogs", func(proto string, rwc io.ReadWriteCloser) error {
82+
fmt.Fprintln(rwc, proto, ": HELLO I LIKE DOGS")
5083
return rwc.Close()
5184
})
5285

@@ -55,17 +88,54 @@ func main() {
5588
panic(err)
5689
}
5790

58-
for {
59-
con, err := list.Accept()
60-
if err != nil {
61-
panic(err)
91+
go func() {
92+
for {
93+
con, err := list.Accept()
94+
if err != nil {
95+
panic(err)
96+
}
97+
98+
go mux.Handle(con)
6299
}
100+
}()
63101

64-
go mux.Handle(con)
102+
// The Muxer is ready, let's test it
103+
conn, err := net.Dial("tcp", ":8765")
104+
if err != nil {
105+
panic(err)
65106
}
107+
108+
// Create a new multistream to talk to the muxer
109+
// which will negotiate that we want to talk with /cats
110+
mstream := ms.NewMSSelect(conn, "/cats")
111+
cats, err := ioutil.ReadAll(mstream)
112+
if err != nil {
113+
panic(err)
114+
}
115+
fmt.Printf("%s", cats)
116+
mstream.Close()
117+
118+
// A different way of talking to the muxer
119+
// is to manually selecting the protocol ourselves
120+
conn, err = net.Dial("tcp", ":8765")
121+
if err != nil {
122+
panic(err)
123+
}
124+
defer conn.Close()
125+
err = ms.SelectProtoOrFail("/dogs", conn)
126+
if err != nil {
127+
panic(err)
128+
}
129+
dogs, err := ioutil.ReadAll(conn)
130+
if err != nil {
131+
panic(err)
132+
}
133+
fmt.Printf("%s", dogs)
134+
conn.Close()
66135
}
67136
```
68137

138+
69139
## Maintainers
70140

71141
Captain: [@whyrusleeping](https://github.com/whyrusleeping).

client.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,14 @@ import (
55
"io"
66
)
77

8+
// ErrNotSupported is the error returned when the muxer does not support
9+
// the protocol specified for the handshake.
810
var ErrNotSupported = errors.New("protocol not supported")
911

12+
// SelectProtoOrFail performs the initial multistream handshake
13+
// to inform the muxer of the protocol that will be used to communicate
14+
// on this ReadWriteCloser. It returns an error if, for example,
15+
// the muxer does not know how to handle this protocol.
1016
func SelectProtoOrFail(proto string, rwc io.ReadWriteCloser) error {
1117
err := handshake(rwc)
1218
if err != nil {
@@ -16,6 +22,8 @@ func SelectProtoOrFail(proto string, rwc io.ReadWriteCloser) error {
1622
return trySelect(proto, rwc)
1723
}
1824

25+
// SelectOneOf will perform handshakes with the protocols on the given slice
26+
// until it finds one which is supported by the muxer.
1927
func SelectOneOf(protos []string, rwc io.ReadWriteCloser) (string, error) {
2028
err := handshake(rwc)
2129
if err != nil {

lazy.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,25 @@ import (
77
"sync"
88
)
99

10+
// Multistream represents in essense a ReadWriteCloser, or a single
11+
// communication wire which supports multiple streams on it. Each
12+
// stream is identified by a protocol tag.
1013
type Multistream interface {
1114
io.ReadWriteCloser
1215
}
1316

17+
// NewMSSelect returns a new Multistream which is able to perform
18+
// protocol selection with a MultistreamMuxer.
1419
func NewMSSelect(c io.ReadWriteCloser, proto string) Multistream {
1520
return &lazyConn{
1621
protos: []string{ProtocolID, proto},
1722
con: c,
1823
}
1924
}
2025

26+
// NewMultistream returns a multistream for the given protocol. This will not
27+
// perform any protocol selection. If you are using a MultistreamMuxer, use
28+
// NewMSSelect.
2129
func NewMultistream(c io.ReadWriteCloser, proto string) Multistream {
2230
return &lazyConn{
2331
protos: []string{proto},

multistream.go

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
// Package multistream implements a simple stream router for the
2+
// multistream-select protocoli. The protocol is defined at
3+
// https://github.com/multiformats/multistream-select
14
package multistream
25

36
import (
@@ -9,23 +12,34 @@ import (
912
"sync"
1013
)
1114

15+
// ErrTooLarge is an error to signal that an incoming message was too large
1216
var ErrTooLarge = errors.New("incoming message was too large")
1317

18+
// ProtocolID identifies the multistream protocol itself and makes sure
19+
// the multistream muxers on both sides of a channel can work with each other.
1420
const ProtocolID = "/multistream/1.0.0"
1521

16-
type HandlerFunc func(string, io.ReadWriteCloser) error
22+
// HandlerFunc is a user-provided function used by the MultistreamMuxer to
23+
// handle a protocol/stream.
24+
type HandlerFunc func(protocol string, rwc io.ReadWriteCloser) error
1725

26+
// Handler is a wrapper to HandlerFunc which attaches a name (protocol) and a
27+
// match function which can optionally be used to select a handler by other
28+
// means than the name.
1829
type Handler struct {
1930
MatchFunc func(string) bool
2031
Handle HandlerFunc
2132
AddName string
2233
}
2334

35+
// MultistreamMuxer is a muxer for multistream. Depending on the stream
36+
// protocol tag it will select the right handler and hand the stream off to it.
2437
type MultistreamMuxer struct {
2538
handlerlock sync.Mutex
2639
handlers []Handler
2740
}
2841

42+
// NewMultistreamMuxer creates a muxer.
2943
func NewMultistreamMuxer() *MultistreamMuxer {
3044
return new(MultistreamMuxer)
3145
}
@@ -68,6 +82,8 @@ func delimWrite(w io.Writer, mes []byte) error {
6882
return nil
6983
}
7084

85+
// Ls is a Multistream muxer command which returns the list of handler names
86+
// available on a muxer.
7187
func Ls(rw io.ReadWriter) ([]string, error) {
7288
err := delimWriteBuffered(rw, []byte("ls"))
7389
if err != nil {
@@ -97,10 +113,14 @@ func fulltextMatch(s string) func(string) bool {
97113
}
98114
}
99115

116+
// AddHandler attaches a new protocol handler to the muxer.
100117
func (msm *MultistreamMuxer) AddHandler(protocol string, handler HandlerFunc) {
101118
msm.AddHandlerWithFunc(protocol, fulltextMatch(protocol), handler)
102119
}
103120

121+
// AddHandlerWithFunc attaches a new protocol handler to the muxer with a match.
122+
// If the match function returns true for a given protocol tag, the protocol
123+
// will be selected even if the handler name and protocol tags are different.
104124
func (msm *MultistreamMuxer) AddHandlerWithFunc(protocol string, match func(string) bool, handler HandlerFunc) {
105125
msm.handlerlock.Lock()
106126
msm.removeHandler(protocol)
@@ -112,6 +132,7 @@ func (msm *MultistreamMuxer) AddHandlerWithFunc(protocol string, match func(stri
112132
msm.handlerlock.Unlock()
113133
}
114134

135+
// RemoveHandler removes the handler with the given name from the muxer.
115136
func (msm *MultistreamMuxer) RemoveHandler(protocol string) {
116137
msm.handlerlock.Lock()
117138
defer msm.handlerlock.Unlock()
@@ -128,6 +149,7 @@ func (msm *MultistreamMuxer) removeHandler(protocol string) {
128149
}
129150
}
130151

152+
// Protocols returns the list of handler-names added to this this muxer.
131153
func (msm *MultistreamMuxer) Protocols() []string {
132154
var out []string
133155
msm.handlerlock.Lock()
@@ -138,6 +160,8 @@ func (msm *MultistreamMuxer) Protocols() []string {
138160
return out
139161
}
140162

163+
// ErrIncorrectVersion is an error reported when the muxer protocol negotiation
164+
// fails because of a ProtocolID mismatch.
141165
var ErrIncorrectVersion = errors.New("client connected with incorrect version")
142166

143167
func (msm *MultistreamMuxer) findHandler(proto string) *Handler {
@@ -153,6 +177,10 @@ func (msm *MultistreamMuxer) findHandler(proto string) *Handler {
153177
return nil
154178
}
155179

180+
// NegotiateLazy performs protocol selection and returns
181+
// a multistream, the protocol used, the handler and an error. It is lazy
182+
// because the write-handshake is performed on a subroutine, allowing this
183+
// to return before that handshake is completed.
156184
func (msm *MultistreamMuxer) NegotiateLazy(rwc io.ReadWriteCloser) (Multistream, string, HandlerFunc, error) {
157185
pval := make(chan string, 1)
158186
writeErr := make(chan error, 1)
@@ -239,6 +267,8 @@ loop:
239267
}
240268
}
241269

270+
// Negotiate performs protocol selection and returns the protocol name and
271+
// the matching handler function for it (or an error).
242272
func (msm *MultistreamMuxer) Negotiate(rwc io.ReadWriteCloser) (string, HandlerFunc, error) {
243273
// Send our protocol ID
244274
err := delimWriteBuffered(rwc, []byte(ProtocolID))
@@ -292,6 +322,8 @@ loop:
292322

293323
}
294324

325+
// Ls implements the "ls" command which writes the list of
326+
// supported protocols to the given Writer.
295327
func (msm *MultistreamMuxer) Ls(w io.Writer) error {
296328
buf := new(bytes.Buffer)
297329
msm.handlerlock.Lock()
@@ -317,6 +349,9 @@ func (msm *MultistreamMuxer) Ls(w io.Writer) error {
317349
return err
318350
}
319351

352+
// Handle performs protocol negotiation on a ReadWriteCloser
353+
// (i.e. a connection). It will find a matching handler for the
354+
// incoming protocol and pass the ReadWriteCloser to it.
320355
func (msm *MultistreamMuxer) Handle(rwc io.ReadWriteCloser) error {
321356
p, h, err := msm.Negotiate(rwc)
322357
if err != nil {
@@ -325,6 +360,8 @@ func (msm *MultistreamMuxer) Handle(rwc io.ReadWriteCloser) error {
325360
return h(p, rwc)
326361
}
327362

363+
// ReadNextToken extracts a token from a ReadWriter. It is used during
364+
// protocol negotiation and returns a string.
328365
func ReadNextToken(rw io.ReadWriter) (string, error) {
329366
tok, err := ReadNextTokenBytes(rw)
330367
if err != nil {
@@ -334,6 +371,8 @@ func ReadNextToken(rw io.ReadWriter) (string, error) {
334371
return string(tok), nil
335372
}
336373

374+
// ReadNextTokenBytes extracts a token from a ReadWriter. It is used
375+
// during protocol negotiation and returns a byte slice.
337376
func ReadNextTokenBytes(rw io.ReadWriter) ([]byte, error) {
338377
data, err := lpReadBuf(rw)
339378
switch err {

0 commit comments

Comments
 (0)