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

wip: add serve gateway #70

Draft
wants to merge 6 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all 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
28 changes: 27 additions & 1 deletion android/bridge/src/main/java/ipfs/gomobile/android/IPFS.java
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,8 @@ synchronized public boolean isStarted() {
}

/**
* Starts this IPFS instance.
* Starts this IPFS instance. Also serve config Gateway & API located inside
* the config (if any)
*
* @throws NodeStartException If the node is already started or if its startup fails
*/
Expand All @@ -161,6 +162,9 @@ synchronized public void start() throws NodeStartException {
openRepoIfClosed();
node = Core.newNode(repo);
node.serveUnixSocketAPI(absSockPath);

// serve config Addresses API & Gateway
node.serveConfig();
} catch (Exception e) {
throw new NodeStartException("Node start failed", e);
}
Expand Down Expand Up @@ -304,6 +308,24 @@ synchronized public RequestBuilder newRequest(@NonNull String command)
return new RequestBuilder(requestBuilder);
}

/**
* Serves node gateway over the given multiaddr
*
* @param multiaddr The multiaddr to listen on
* @param writable If true: will also support support `POST`, `PUT`, and `DELETE` methods.
* @return The MultiAddr the node is serving on
* @throws NodeListenException If the node failed to serve
* @see <a href="https://docs.ipfs.io/concepts/ipfs-gateway/#gateway-providers">IPFS Doc</a>
*/
synchronized public String serveGatewayMultiaddr(@NonNull String multiaddr, @NonNull Boolean writable) throws NodeListenException {
try {
return node.serveGatewayMultiaddr(multiaddr, writable);
} catch (Exception e) {
throw new NodeListenException("failed to listen on gateway", e);
}

}

/**
* Sets the primary and secondary DNS for gomobile (hacky, will be removed in future version)
*
Expand Down Expand Up @@ -337,6 +359,10 @@ public static class ExtraOptionException extends Exception {
ExtraOptionException(String message, Throwable err) { super(message, err); }
}

public static class NodeListenException extends Exception {
NodeListenException(String message, Throwable err) { super(message, err); }
}

public static class ConfigCreationException extends Exception {
ConfigCreationException(String message, Throwable err) { super(message, err); }
}
Expand Down
57 changes: 50 additions & 7 deletions go/bind/core/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ package core

import (
"context"
"fmt"
"log"
"net"
"sync"
Expand Down Expand Up @@ -69,33 +70,75 @@ func (n *Node) Close() error {
}

func (n *Node) ServeUnixSocketAPI(sockpath string) (err error) {
_, err = n.ServeMultiaddr("/unix/" + sockpath)
_, err = n.ServeAPIMultiaddr("/unix/" + sockpath)
return
}

// ServeTCPAPI on the given port and return the current listening maddr
func (n *Node) ServeTCPAPI(port string) (string, error) {
return n.ServeMultiaddr("/ip4/127.0.0.1/tcp/" + port)
return n.ServeAPIMultiaddr("/ip4/127.0.0.1/tcp/" + port)
}

func (n *Node) ServeConfigAPI() error {
func (n *Node) ServeConfig() error {
cfg, err := n.ipfsMobile.Repo.Config()
if err != nil {
return err
return fmt.Errorf("unable to get config: %s", err.Error())
}

if len(cfg.Addresses.API) > 0 {
for _, maddr := range cfg.Addresses.API {
if _, err := n.ServeMultiaddr(maddr); err != nil {
log.Printf("cannot serve `%s`: %s", maddr, err.Error())
if _, err := n.ServeAPIMultiaddr(maddr); err != nil {
return fmt.Errorf("cannot serve `%s`: %s", maddr, err.Error())
}
}
}

if len(cfg.Addresses.Gateway) > 0 {
for _, maddr := range cfg.Addresses.Gateway {
// public gateway should be readonly by default
if _, err := n.ServeGatewayMultiaddr(maddr, false); err != nil {
return fmt.Errorf("cannot serve `%s`: %s", maddr, err.Error())
}
}
}

return nil
}

func (n *Node) ServeMultiaddr(smaddr string) (string, error) {
func (n *Node) ServeUnixSocketGateway(sockpath string, writable bool) (err error) {
_, err = n.ServeGatewayMultiaddr("/unix/"+sockpath, writable)
return
}

func (n *Node) ServeTCPGateway(port string, writable bool) (string, error) {
return n.ServeGatewayMultiaddr("/ip4/127.0.0.1/tcp/"+port, writable)
}

func (n *Node) ServeGatewayMultiaddr(smaddr string, writable bool) (string, error) {
maddr, err := ma.NewMultiaddr(smaddr)
if err != nil {
return "", err
}

ml, err := manet.Listen(maddr)
if err != nil {
return "", err
}

n.muListeners.Lock()
n.listeners = append(n.listeners, ml)
n.muListeners.Unlock()

go func(l net.Listener) {
if err := n.ipfsMobile.ServeGateway(l, writable); err != nil {
log.Printf("serve error: %s", err.Error())
}
}(manet.NetListener(ml))

return ml.Multiaddr().String(), nil
}

func (n *Node) ServeAPIMultiaddr(smaddr string) (string, error) {
maddr, err := ma.NewMultiaddr(smaddr)
if err != nil {
return "", err
Expand Down
117 changes: 117 additions & 0 deletions go/bind/core/node_test.go
Original file line number Diff line number Diff line change
@@ -1,14 +1,19 @@
package core

import (
"bytes"
"context"
"fmt"
"io"
"net"
"net/http"
"path/filepath"
"testing"
"time"

ipfs_files "github.com/ipfs/go-ipfs-files"
ipfs_coreapi "github.com/ipfs/go-ipfs/core/coreapi"

ma "github.com/multiformats/go-multiaddr"
manet "github.com/multiformats/go-multiaddr/net"
)
Expand Down Expand Up @@ -93,3 +98,115 @@ func TestNodeServeAPI(t *testing.T) {
}
})
}

func TestNodeServeGateway(t *testing.T) {
var testcontent = []byte("hello world\n")

t.Run("tpc gateway", func(t *testing.T) {
path, clean := testingTempDir(t, "tpc_repo")
defer clean()

node, clean := testingNode(t, path)
defer clean()

smaddr, err := node.ServeTCPGateway("0", true)
if err != nil {
t.Fatal(err)
}

maddr, err := ma.NewMultiaddr(smaddr)
if err != nil {
t.Fatal(err)
}

addr, err := manet.ToNetAddr(maddr)
if err != nil {
t.Fatal(err)
}

api, err := ipfs_coreapi.NewCoreAPI(node.ipfsMobile.IpfsNode)
if err != nil {
t.Fatal(err)
}

file := ipfs_files.NewBytesFile(testcontent)
resolved, err := api.Unixfs().Add(context.Background(), file)
if err != nil {
t.Fatal(err)
}

cid := resolved.Cid()

url := fmt.Sprintf("http://%s/ipfs/%s", addr.String(), cid.String())
client := http.Client{Timeout: 5 * time.Second}

resp, err := client.Get(url)
if err != nil {
t.Fatal(err)
}
defer resp.Body.Close()

b, err := io.ReadAll(resp.Body)
if err != nil {
t.Fatal(err)
}

if bytes.Compare(b, testcontent) != 0 {
t.Fatalf("content `%s` are different from `%s`", b, testcontent)
}
})

t.Run("uds gateway", func(t *testing.T) {
path, clean := testingTempDir(t, "uds_repo")
defer clean()

sockdir, clean := testingTempDir(t, "uds_gateway")
defer clean()

node, clean := testingNode(t, path)
defer clean()

sock := filepath.Join(sockdir, "sock")
err := node.ServeUnixSocketGateway(sock, true)
if err != nil {
t.Fatal(err)
}

client := http.Client{
Timeout: 5 * time.Second,
Transport: &http.Transport{
DialContext: func(_ context.Context, _, _ string) (net.Conn, error) {
return net.Dial("unix", sock)
},
},
}

api, err := ipfs_coreapi.NewCoreAPI(node.ipfsMobile.IpfsNode)
if err != nil {
t.Fatal(err)
}

file := ipfs_files.NewBytesFile(testcontent)
resolved, err := api.Unixfs().Add(context.Background(), file)
if err != nil {
t.Fatal(err)
}

cid := resolved.Cid()
url := fmt.Sprintf("http://unix/ipfs/%s", cid.String())
resp, err := client.Get(url)
if err != nil {
t.Fatal(err)
}
defer resp.Body.Close()

b, err := io.ReadAll(resp.Body)
if err != nil {
t.Fatal(err)
}

if bytes.Compare(b, testcontent) != 0 {
t.Fatalf("content `%s` are different from `%s`", b, testcontent)
}
})
}
2 changes: 1 addition & 1 deletion go/bind/core/shell_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ func TestShell(t *testing.T) {

for clientk, clienttc := range casesClient {
t.Run(clientk, func(t *testing.T) {
maddr, err := node.ServeMultiaddr(clienttc.MAddr)
maddr, err := node.ServeAPIMultiaddr(clienttc.MAddr)
if err != nil {
t.Fatal(err)
}
Expand Down
1 change: 1 addition & 0 deletions go/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ require (
github.com/ipfs/go-ipfs v0.7.0
github.com/ipfs/go-ipfs-api v0.2.0
github.com/ipfs/go-ipfs-config v0.9.0
github.com/ipfs/go-ipfs-files v0.0.8
github.com/libp2p/go-libp2p v0.11.0
github.com/libp2p/go-libp2p-core v0.6.1
github.com/libp2p/go-libp2p-record v0.1.3
Expand Down
Loading