Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

better user-agent handling #702

Merged
merged 5 commits into from
Aug 28, 2019
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ os:
language: go

go:
- 1.11.x
- 1.12.x

env:
global:
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,9 @@ libp2p is the product of a long, and arduous quest of understanding -- a deep di

## Usage

This repository (`go-libp2p`) serves as the entrypoint to the universe of modules that compose the Go implementation of the libp2p stack.
This repository (`go-libp2p`) serves as the entrypoint to the universe of modules that compose the Go implementation of the libp2p stack. Libp2p requires go 1.12+.

We mainly use [Go modules](https://github.com/golang/go/wiki/Modules) for our dependency and release management (and thus require go >= 1.11). In order to get the best developer experience, we recommend you do too. Otherwise, you may ocassionally encounter a breaking build as you'll be running off master (which, by definition, is not guaranteed to be stable).
We mainly use [Go modules](https://github.com/golang/go/wiki/Modules) for our dependency and release management (and thus require go >= 1.12+). In order to get the best developer experience, we recommend you do too. Otherwise, you may ocassionally encounter a breaking build as you'll be running off master (which, by definition, is not guaranteed to be stable).

You can start using go-libp2p in your Go application simply by adding imports from our repos, e.g.:

Expand Down
3 changes: 3 additions & 0 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ type RoutingC func(host.Host) (routing.PeerRouting, error)
// This is *not* a stable interface. Use the options defined in the root
// package.
type Config struct {
UserAgent string

PeerKey crypto.PrivKey

Transports []TptC
Expand Down Expand Up @@ -120,6 +122,7 @@ func (cfg *Config) NewNode(ctx context.Context) (host.Host, error) {
AddrsFactory: cfg.AddrsFactory,
NATManager: cfg.NATManager,
EnablePing: !cfg.DisablePing,
UserAgent: cfg.UserAgent,
})

if err != nil {
Expand Down
8 changes: 8 additions & 0 deletions options.go
Original file line number Diff line number Diff line change
Expand Up @@ -319,3 +319,11 @@ var NoTransports = func(cfg *Config) error {
cfg.Transports = []config.TptC{}
return nil
}

// UserAgent sets the libp2p user-agent sent along with the identify protocol
func UserAgent(userAgent string) Option {
return func(cfg *Config) error {
cfg.UserAgent = userAgent
return nil
}
}
19 changes: 9 additions & 10 deletions p2p/host/basic/basic_host.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,10 +100,6 @@ type HostOpts struct {
// If below 0, timeouts on streams will be deactivated.
NegotiationTimeout time.Duration

// IdentifyService holds an implementation of the /ipfs/id/ protocol.
// If omitted, a new *identify.IDService will be used.
IdentifyService *identify.IDService

// AddrsFactory holds a function which can be used to override or filter the result of Addrs.
// If omitted, there's no override or filtering, and the results of Addrs and AllAddrs are the same.
AddrsFactory AddrsFactory
Expand All @@ -121,6 +117,9 @@ type HostOpts struct {

// EnablePing indicates whether to instantiate the ping service
EnablePing bool

// UserAgent sets the user-agent for the host. Defaults to ClientVersion.
UserAgent string
}

// NewHost constructs a new *BasicHost and activates it by attaching its stream and connection handlers to the given inet.Network.
Expand Down Expand Up @@ -154,12 +153,12 @@ func NewHost(ctx context.Context, net network.Network, opts *HostOpts) (*BasicHo
h.mux = opts.MultistreamMuxer
}

if opts.IdentifyService != nil {
h.ids = opts.IdentifyService
} else {
// we can't set this as a default above because it depends on the *BasicHost.
h.ids = identify.NewIDService(goprocessctx.WithProcessClosing(ctx, h.proc), h)
}
// we can't set this as a default above because it depends on the *BasicHost.
h.ids = identify.NewIDService(
goprocessctx.WithProcessClosing(ctx, h.proc),
h,
identify.UserAgent(opts.UserAgent),
)

if uint64(opts.NegotiationTimeout) != 0 {
h.negtimeout = opts.NegotiationTimeout
Expand Down
43 changes: 38 additions & 5 deletions p2p/protocol/identify/id.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ package identify

import (
"context"
"fmt"
"runtime/debug"
"sync"
"time"

Expand Down Expand Up @@ -31,9 +33,27 @@ const ID = "/ipfs/id/1.0.0"

// LibP2PVersion holds the current protocol version for a client running this code
// TODO(jbenet): fix the versioning mess.
// XXX: Don't change this till 2020. You'll break all go-ipfs versions prior to
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should make this configurable too. Many projects using libp2p don't care about compatibility with IPFS and their networks are deliberately segregated.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See #714

// 0.4.17 which asserted an exact version match.
const LibP2PVersion = "ipfs/0.1.0"

var ClientVersion = "go-libp2p/3.3.4"
// ClientVersion is the default user agent.
//
// Deprecated: Set this with the UserAgent option.
var ClientVersion = "github.com/libp2p/go-libp2p"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let's just call this go-libp2p.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure but users using go mod will still use their full paths by default. E.g., github.com/libp2p/go-libp2p-examples@v0.1.1.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh ok, nevermind then.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will increase the size of the identify messages by 15 bytes. It's ok but worth noting.


func init() {
bi, ok := debug.ReadBuildInfo()
if !ok {
return
}
version := bi.Main.Version
if version == "(devel)" {
ClientVersion = bi.Main.Path
Copy link
Contributor

@vyzo vyzo Aug 13, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let's keep the devel designator somewhere in the reported agent.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Anything built manually (i.e., built from a manually checked-out source tree) will have a (devel) version. It doesn't mean it's actually a development version.

To get a proper version, one would have to build with go get github.com/libp2p/go-libp2p-examples@latest (i.e., build with go mod).

Users that care can override this.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we suffix with @unknown?

} else {
ClientVersion = fmt.Sprintf("%s@%s", bi.Main.Path, bi.Main.Version)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the path/version of the main module (application), not libp2p itself.

}
}

// transientTTL is a short ttl for invalidated previously connected addrs
const transientTTL = 10 * time.Second
Expand All @@ -47,7 +67,8 @@ const transientTTL = 10 * time.Second
// * Our IPFS Agent Version
// * Our public Listen Addresses
type IDService struct {
Host host.Host
Host host.Host
UserAgent string
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Keeping this exported could lead to races if the user modifies the value at runtime. Not a biggie, but just pointing it out.


ctx context.Context

Expand All @@ -70,9 +91,21 @@ type IDService struct {

// NewIDService constructs a new *IDService and activates it by
// attaching its stream handler to the given host.Host.
func NewIDService(ctx context.Context, h host.Host) *IDService {
func NewIDService(ctx context.Context, h host.Host, opts ...Option) *IDService {
var cfg config
for _, opt := range opts {
opt(&cfg)
}

userAgent := ClientVersion
if cfg.userAgent != "" {
userAgent = cfg.userAgent
}

s := &IDService{
Host: h,
Host: h,
UserAgent: userAgent,

ctx: ctx,
currid: make(map[network.Conn]chan struct{}),
observedAddrs: NewObservedAddrSet(ctx),
Expand Down Expand Up @@ -306,7 +339,7 @@ func (ids *IDService) populateMessage(mes *pb.Identify, c network.Conn) {

// set protocol versions
pv := LibP2PVersion
av := ClientVersion
av := ids.UserAgent
mes.ProtocolVersion = &pv
mes.AgentVersion = &av
}
Expand Down
38 changes: 38 additions & 0 deletions p2p/protocol/identify/id_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"time"

"github.com/libp2p/go-eventbus"
libp2p "github.com/libp2p/go-libp2p"
ic "github.com/libp2p/go-libp2p-core/crypto"
"github.com/libp2p/go-libp2p-core/event"
"github.com/libp2p/go-libp2p-core/helpers"
Expand Down Expand Up @@ -367,3 +368,40 @@ func TestIdentifyDeltaWhileIdentifyingConn(t *testing.T) {
t.Fatalf("timed out while waiting for an event for the protocol changes in h2")
}
}

func TestUserAgent(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()

h1, err := libp2p.New(
ctx,
libp2p.UserAgent("foo"),
libp2p.ListenAddrStrings("/ip4/127.0.0.1/tcp/0"),
)
if err != nil {
t.Fatal(err)
}
defer h1.Close()

h2, err := libp2p.New(
ctx,
libp2p.UserAgent("bar"),
libp2p.ListenAddrStrings("/ip4/127.0.0.1/tcp/0"),
)
if err != nil {
t.Fatal(err)
}
defer h2.Close()

err = h1.Connect(ctx, peer.AddrInfo{ID: h2.ID(), Addrs: h2.Addrs()})
if err != nil {
t.Fatal(err)
}
av, err := h1.Peerstore().Get(h2.ID(), "AgentVersion")
if err != nil {
t.Fatal(err)
}
if ver, ok := av.(string); !ok || ver != "bar" {
t.Errorf("expected agent version %q, got %q", "bar", av)
}
}
13 changes: 13 additions & 0 deletions p2p/protocol/identify/opts.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package identify

type config struct {
userAgent string
}

type Option func(*config)

func UserAgent(ua string) Option {
return func(cfg *config) {
cfg.userAgent = ua
}
}