Skip to content

Commit

Permalink
daemon: support unix domain sockets for the API/gateway
Browse files Browse the repository at this point in the history
All the work was client-side. Unix domain socket multiaddrs already worked
server-side.

fixes #4218
  • Loading branch information
Stebalien committed Sep 27, 2019
1 parent 9ea7c6a commit a8b7375
Show file tree
Hide file tree
Showing 10 changed files with 92 additions and 57 deletions.
7 changes: 6 additions & 1 deletion cmd/ipfs/daemon.go
Original file line number Diff line number Diff line change
Expand Up @@ -469,7 +469,12 @@ func serveHTTPApi(req *cmds.Request, cctx *oldcmds.Context) (<-chan error, error
// we might have listened to /tcp/0 - lets see what we are listing on
apiMaddr = apiLis.Multiaddr()
fmt.Printf("API server listening on %s\n", apiMaddr)
fmt.Printf("WebUI: http://%s/webui\n", apiLis.Addr())

// Browsers require TCP.
switch apiLis.Addr().Network() {
case "tcp", "tcp4", "tcp6":
fmt.Printf("WebUI: http://%s/webui\n", apiLis.Addr())
}
listeners = append(listeners, apiLis)
}

Expand Down
30 changes: 24 additions & 6 deletions cmd/ipfs/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import (
"errors"
"fmt"
"math/rand"
"net"
"net/http"
"os"
"runtime/pprof"
"strings"
Expand All @@ -22,7 +24,7 @@ import (

"github.com/ipfs/go-ipfs-cmds"
"github.com/ipfs/go-ipfs-cmds/cli"
"github.com/ipfs/go-ipfs-cmds/http"
cmdhttp "github.com/ipfs/go-ipfs-cmds/http"
"github.com/ipfs/go-ipfs-config"
u "github.com/ipfs/go-ipfs-util"
logging "github.com/ipfs/go-log"
Expand Down Expand Up @@ -249,23 +251,39 @@ func makeExecutor(req *cmds.Request, env interface{}) (cmds.Executor, error) {
if err != nil {
return nil, err
}
_, host, err := manet.DialArgs(apiAddr)
network, host, err := manet.DialArgs(apiAddr)
if err != nil {
return nil, err
}

// Construct the executor.
opts := []http.ClientOpt{
http.ClientWithAPIPrefix(corehttp.APIPath),
opts := []cmdhttp.ClientOpt{
cmdhttp.ClientWithAPIPrefix(corehttp.APIPath),
}

// Fallback on a local executor if we (a) have a repo and (b) aren't
// forcing a daemon.
if !daemonRequested && fsrepo.IsInitialized(cctx.ConfigRoot) {
opts = append(opts, http.ClientWithFallback(exe))
opts = append(opts, cmdhttp.ClientWithFallback(exe))
}

switch network {
case "tcp", "tcp4", "tcp6":
case "unix":
path := host
host = "unix"
opts = append(opts, cmdhttp.ClientWithHTTPClient(&http.Client{
Transport: &http.Transport{
DialContext: func(_ context.Context, _, _ string) (net.Conn, error) {
return net.Dial("unix", path)
},
},
}))
default:
return nil, fmt.Errorf("unsupported API address: %s", apiAddr)
}

return http.NewClient(host, opts...), nil
return cmdhttp.NewClient(host, opts...), nil
}

// commandDetails returns a command's details for the command given by |path|.
Expand Down
16 changes: 16 additions & 0 deletions docs/config.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,16 +77,32 @@ Contains information about various listener addresses to be used by this node.
- `API`
Multiaddr or array of multiaddrs describing the address to serve the local HTTP API on.

Supported Transports:

* tcp/ip{4,6} - `/ipN/.../tcp/...`
* unix - `/unix/path/to/socket`

Default: `/ip4/127.0.0.1/tcp/5001`

- `Gateway`
Multiaddr or array of multiaddrs describing the address to serve the local gateway on.

Supported Transports:

* tcp/ip{4,6} - `/ipN/.../tcp/...`
* unix - `/unix/path/to/socket`

Default: `/ip4/127.0.0.1/tcp/8080`

- `Swarm`
Array of multiaddrs describing which addresses to listen on for p2p swarm connections.

Supported Transports:

* tcp/ip{4,6} - `/ipN/.../tcp/...`
* websocket - `/ipN/.../tcp/.../ws`
* quic - `/ipN/.../udp/.../quic`

Default:
```json
[
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ require (
github.com/ipfs/go-fs-lock v0.0.1
github.com/ipfs/go-ipfs-blockstore v0.1.0
github.com/ipfs/go-ipfs-chunker v0.0.1
github.com/ipfs/go-ipfs-cmds v0.1.0
github.com/ipfs/go-ipfs-cmds v0.1.1
github.com/ipfs/go-ipfs-config v0.0.11
github.com/ipfs/go-ipfs-ds-help v0.0.1
github.com/ipfs/go-ipfs-exchange-interface v0.0.1
Expand Down
8 changes: 4 additions & 4 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -192,8 +192,8 @@ github.com/ipfs/go-ipfs-blocksutil v0.0.1 h1:Eh/H4pc1hsvhzsQoMEP3Bke/aW5P5rVM1IW
github.com/ipfs/go-ipfs-blocksutil v0.0.1/go.mod h1:Yq4M86uIOmxmGPUHv/uI7uKqZNtLb449gwKqXjIsnRk=
github.com/ipfs/go-ipfs-chunker v0.0.1 h1:cHUUxKFQ99pozdahi+uSC/3Y6HeRpi9oTeUHbE27SEw=
github.com/ipfs/go-ipfs-chunker v0.0.1/go.mod h1:tWewYK0we3+rMbOh7pPFGDyypCtvGcBFymgY4rSDLAw=
github.com/ipfs/go-ipfs-cmds v0.1.0 h1:0CEde9EcxByej8+L6d1PST57J4ambRPyCTjLG5Ymou8=
github.com/ipfs/go-ipfs-cmds v0.1.0/go.mod h1:TiK4e7/V31tuEb8YWDF8lN3qrnDH+BS7ZqWIeYJlAs8=
github.com/ipfs/go-ipfs-cmds v0.1.1 h1:H9/BLf5rcsULHMj/x8gC0e5o+raYhqk1OQsfzbGMNM4=
github.com/ipfs/go-ipfs-cmds v0.1.1/go.mod h1:k1zMXcOLtljA9iAnZHddbH69yVm5+weRL0snmMD/rK0=
github.com/ipfs/go-ipfs-config v0.0.11 h1:5/4nas2CQXiKr2/MLxU24GDGTBvtstQIQezuk7ltOQQ=
github.com/ipfs/go-ipfs-config v0.0.11/go.mod h1:wveA8UT5ywN26oKStByzmz1CO6cXwLKKM6Jn/Hfw08I=
github.com/ipfs/go-ipfs-delay v0.0.0-20181109222059-70721b86a9a8/go.mod h1:8SP1YXK1M1kXuc4KJZINY3TQQ03J2rwBG9QfXmbRPrw=
Expand Down Expand Up @@ -626,8 +626,8 @@ github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/procfs v0.0.3 h1:CTwfnzjQ+8dS6MhHHu4YswVAD99sL2wjPqP+VkURmKE=
github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ=
github.com/rs/cors v1.6.0 h1:G9tHG9lebljV9mfp9SNPDL36nCDxmo3zTlAf1YgvzmI=
github.com/rs/cors v1.6.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU=
github.com/rs/cors v1.7.0 h1:+88SsELBHx5r+hZ8TCkggzSstaWNbDvThkVK8H6f9ik=
github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU=
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
github.com/sirupsen/logrus v1.2.0 h1:juTguoYk5qI21pwyTXY3B3Y5cOTH3ZUyZCg1v/mihuo=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
Expand Down
50 changes: 8 additions & 42 deletions test/dependencies/pollEndpoint/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,6 @@ package main

import (
"flag"
"fmt"
"io/ioutil"
"net/http"
"net/url"
"os"
"time"

Expand All @@ -16,11 +12,10 @@ import (
)

var (
host = flag.String("host", "/ip4/127.0.0.1/tcp/5001", "the multiaddr host to dial on")
endpoint = flag.String("ep", "/version", "which http endpoint path to hit")
tries = flag.Int("tries", 10, "how many tries to make before failing")
timeout = flag.Duration("tout", time.Second, "how long to wait between attempts")
verbose = flag.Bool("v", false, "verbose logging")
host = flag.String("host", "/ip4/127.0.0.1/tcp/5001", "the multiaddr host to dial on")
tries = flag.Int("tries", 10, "how many tries to make before failing")
timeout = flag.Duration("tout", time.Second, "how long to wait between attempts")
verbose = flag.Bool("v", false, "verbose logging")
)

var log = logging.Logger("pollEndpoint")
Expand All @@ -33,56 +28,27 @@ func main() {
if err != nil {
log.Fatal("NewMultiaddr() failed: ", err)
}
p := addr.Protocols()
if len(p) < 2 {
log.Fatal("need two protocols in host flag (/ip/tcp): ", addr)
}
_, host, err := manet.DialArgs(addr)
if err != nil {
log.Fatal("manet.DialArgs() failed: ", err)
}

if *verbose { // lower log level
logging.SetDebugLogging()
}

// construct url to dial
var u url.URL
u.Scheme = "http"
u.Host = host
u.Path = *endpoint

// show what we got
start := time.Now()
log.Debugf("starting at %s, tries: %d, timeout: %s, url: %s", start, *tries, *timeout, u)
log.Debugf("starting at %s, tries: %d, timeout: %s, addr: %s", start, *tries, *timeout, addr)

for *tries > 0 {

err := checkOK(http.Get(u.String()))
c, err := manet.Dial(addr)
if err == nil {
log.Debugf("ok - endpoint reachable with %d tries remaining, took %s", *tries, time.Since(start))
c.Close()
os.Exit(0)
}
log.Debug("get failed: ", err)
log.Debug("connect failed: ", err)
time.Sleep(*timeout)
*tries--
}

log.Error("failed.")
os.Exit(1)
}

func checkOK(resp *http.Response, err error) error {
if err == nil { // request worked
defer resp.Body.Close()
if resp.StatusCode == http.StatusOK {
return nil
}
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
fmt.Fprintf(os.Stderr, "pollEndpoint: ioutil.ReadAll() Error: %s", err)
}
return fmt.Errorf("response not OK. %d %s %q", resp.StatusCode, resp.Status, string(body))
}
return err
}
2 changes: 1 addition & 1 deletion test/sharness/lib/test-lib.sh
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,7 @@ test_launch_ipfs_daemon() {

# we say the daemon is ready when the API server is ready.
test_expect_success "'ipfs daemon' is ready" '
pollEndpoint -ep=/version -host=$API_MADDR -v -tout=1s -tries=60 2>poll_apierr > poll_apiout ||
pollEndpoint -host=$API_MADDR -v -tout=1s -tries=60 2>poll_apierr > poll_apiout ||
test_fsh cat actual_daemon || test_fsh cat daemon_err || test_fsh cat poll_apierr || test_fsh cat poll_apiout
'
}
Expand Down
2 changes: 1 addition & 1 deletion test/sharness/t0060-daemon.sh
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ test_expect_success "'ipfs daemon' should be able to run with a pipe attached to
'

test_expect_success "daemon with pipe eventually becomes live" '
pollEndpoint -host='$API_MADDR' -ep=/version -v -tout=1s -tries=10 >stdin_poll_apiout 2>stdin_poll_apierr &&
pollEndpoint -host='$API_MADDR' -v -tout=1s -tries=10 >stdin_poll_apiout 2>stdin_poll_apierr &&
test_kill_repeat_10_sec $DAEMON_PID ||
test_fsh cat stdin_daemon_out || test_fsh cat stdin_daemon_err || test_fsh cat stdin_poll_apiout || test_fsh cat stdin_poll_apierr
'
Expand Down
30 changes: 30 additions & 0 deletions test/sharness/t0067-unix-api.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#!/usr/bin/env bash
#
# MIT Licensed; see the LICENSE file in this repository.
#

test_description="Test unix API transport"

. lib/test-lib.sh

test_init_ipfs

# We can't use the trash dir as the full name must be longer less than 108 bytes
# long (because that's the max unix domain socket path length).
SOCKDIR="$(mktemp -d "${TMPDIR:-/tmp}/unix-api-sharness.XXXXXX")"

test_expect_success "configure" '
peerid=$(ipfs config Identity.PeerID) &&
ipfs config Addresses.API "/unix/$SOCKDIR/sock"
'

test_launch_ipfs_daemon

test_expect_success "client works" '
printf "$peerid" >expected &&
ipfs --api="/unix/$SOCKDIR/sock" id -f="<id>" >actual &&
test_cmp expected actual
'

test_kill_ipfs_daemon
test_done
2 changes: 1 addition & 1 deletion test/sharness/t0111-gateway-writeable.sh
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ test_launch_ipfs_daemon
port=$GWAY_PORT

test_expect_success "ipfs daemon up" '
pollEndpoint -host $GWAY_MADDR -ep=/version -v -tout=1s -tries=60 2>poll_apierr > poll_apiout ||
pollEndpoint -host $GWAY_MADDR -v -tout=1s -tries=60 2>poll_apierr > poll_apiout ||
test_fsh cat poll_apierr || test_fsh cat poll_apiout
'

Expand Down

0 comments on commit a8b7375

Please sign in to comment.