-
-
Notifications
You must be signed in to change notification settings - Fork 3k
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
feat: add gateway to http over libp2p #10108
Changes from all commits
f7aa120
a11c542
fb5caca
5b0da08
9d32f71
bc6bee1
bf548a3
2a1d91f
b8c741d
f2c43d5
d86192a
df66dac
1efd9d4
8d28507
3fa7ef8
3f33e61
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -17,7 +17,24 @@ defaults: | |
run: | ||
shell: bash | ||
|
||
env: | ||
# hostnames expected by https://github.com/ipfs/gateway-conformance | ||
GATEWAY_PUBLIC_GATEWAYS: | | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why does this had to be moved to the global scope ? This should do nothing for the libp2p gateway. |
||
{ | ||
"example.com": { | ||
"UseSubdomains": true, | ||
"InlineDNSLink": true, | ||
"Paths": ["/ipfs", "/ipns"] | ||
}, | ||
"localhost": { | ||
"UseSubdomains": true, | ||
"InlineDNSLink": true, | ||
"Paths": ["/ipfs", "/ipns"] | ||
} | ||
} | ||
|
||
jobs: | ||
# Testing all gateway features via TCP port specified in Addresses.Gateway | ||
gateway-conformance: | ||
runs-on: ubuntu-latest | ||
timeout-minutes: 10 | ||
|
@@ -33,6 +50,9 @@ jobs: | |
uses: actions/setup-go@v4 | ||
with: | ||
go-version: 1.21.x | ||
- uses: protocol/cache-go-action@v1 | ||
with: | ||
name: ${{ github.job }} | ||
- name: Checkout kubo-gateway | ||
uses: actions/checkout@v4 | ||
with: | ||
|
@@ -43,22 +63,8 @@ jobs: | |
|
||
# 3. Init the kubo-gateway | ||
- name: Init kubo-gateway | ||
env: | ||
GATEWAY_PUBLIC_GATEWAYS: | | ||
{ | ||
"example.com": { | ||
"UseSubdomains": true, | ||
"InlineDNSLink": true, | ||
"Paths": ["/ipfs", "/ipns"] | ||
}, | ||
"localhost": { | ||
"UseSubdomains": true, | ||
"InlineDNSLink": true, | ||
"Paths": ["/ipfs", "/ipns"] | ||
} | ||
} | ||
run: | | ||
./ipfs init | ||
./ipfs init -e | ||
./ipfs config --json Gateway.PublicGateways "$GATEWAY_PUBLIC_GATEWAYS" | ||
working-directory: kubo-gateway/cmd/ipfs | ||
|
||
|
@@ -115,3 +121,109 @@ jobs: | |
with: | ||
name: gateway-conformance.json | ||
path: output.json | ||
|
||
# Testing trustless gateway feature subset exposed as libp2p protocol | ||
gateway-conformance-libp2p-experiment: | ||
runs-on: ubuntu-latest | ||
timeout-minutes: 10 | ||
steps: | ||
# 1. Download the gateway-conformance fixtures | ||
- name: Download gateway-conformance fixtures | ||
uses: ipfs/gateway-conformance/.github/actions/[email protected] | ||
with: | ||
output: fixtures | ||
|
||
# 2. Build the kubo-gateway | ||
- name: Setup Go | ||
uses: actions/setup-go@v4 | ||
with: | ||
go-version: 1.20.x | ||
Jorropo marked this conversation as resolved.
Show resolved
Hide resolved
|
||
- uses: protocol/cache-go-action@v1 | ||
with: | ||
name: ${{ github.job }} | ||
- name: Checkout kubo-gateway | ||
uses: actions/checkout@v3 | ||
with: | ||
path: kubo-gateway | ||
- name: Build kubo-gateway | ||
run: make build | ||
working-directory: kubo-gateway | ||
|
||
# 3. Init the kubo-gateway | ||
- name: Init kubo-gateway | ||
run: | | ||
./ipfs init --profile=test | ||
./ipfs config --json Gateway.PublicGateways "$GATEWAY_PUBLIC_GATEWAYS" | ||
./ipfs config --json Experimental.GatewayOverLibp2p true | ||
./ipfs config Addresses.Gateway "/ip4/127.0.0.1/tcp/8080" | ||
./ipfs config Addresses.API "/ip4/127.0.0.1/tcp/5001" | ||
working-directory: kubo-gateway/cmd/ipfs | ||
|
||
# 4. Populate the Kubo gateway with the gateway-conformance fixtures | ||
- name: Import fixtures | ||
run: | | ||
# Import car files | ||
find ./fixtures -name '*.car' -exec kubo-gateway/cmd/ipfs/ipfs dag import --pin-roots=false {} \; | ||
|
||
# 5. Start the kubo-gateway | ||
- name: Start kubo-gateway | ||
run: | | ||
( ./ipfs daemon & ) | sed '/Daemon is ready/q' | ||
while [[ "$(./ipfs id | jq '.Addresses | length')" == '0' ]]; do sleep 1; done | ||
working-directory: kubo-gateway/cmd/ipfs | ||
|
||
# 6. Setup a kubo http-p2p-proxy to expose libp2p protocol as a regular HTTP port for gateway conformance tests | ||
- name: Init p2p-proxy kubo node | ||
env: | ||
IPFS_PATH: "~/.kubo-p2p-proxy" | ||
run: | | ||
./ipfs init --profile=test -e | ||
./ipfs config --json Experimental.Libp2pStreamMounting true | ||
./ipfs config Addresses.Gateway "/ip4/127.0.0.1/tcp/8081" | ||
./ipfs config Addresses.API "/ip4/127.0.0.1/tcp/5002" | ||
working-directory: kubo-gateway/cmd/ipfs | ||
|
||
# 7. Start the kubo http-p2p-proxy | ||
- name: Start kubo http-p2p-proxy | ||
env: | ||
IPFS_PATH: "~/.kubo-p2p-proxy" | ||
run: | | ||
( ./ipfs daemon & ) | sed '/Daemon is ready/q' | ||
while [[ "$(./ipfs id | jq '.Addresses | length')" == '0' ]]; do sleep 1; done | ||
working-directory: kubo-gateway/cmd/ipfs | ||
|
||
# 8. Start forwarding data from the http-p2p-proxy to the node serving the Gateway API over libp2p | ||
- name: Start http-over-libp2p forwarding proxy | ||
run: | | ||
gatewayNodeId=$(./ipfs --api=/ip4/127.0.0.1/tcp/5001 id -f="<id>") | ||
./ipfs --api=/ip4/127.0.0.1/tcp/5002 swarm connect $(./ipfs --api=/ip4/127.0.0.1/tcp/5001 swarm addrs local --id | head -n 1) | ||
./ipfs --api=/ip4/127.0.0.1/tcp/5002 p2p forward --allow-custom-protocol /http/1.1 /ip4/127.0.0.1/tcp/8092 /p2p/$gatewayNodeId | ||
working-directory: kubo-gateway/cmd/ipfs | ||
|
||
# 9. Run the gateway-conformance tests over libp2p | ||
- name: Run gateway-conformance tests over libp2p | ||
uses: ipfs/gateway-conformance/.github/actions/[email protected] | ||
with: | ||
gateway-url: http://127.0.0.1:8092 | ||
json: output.json | ||
xml: output.xml | ||
html: output.html | ||
markdown: output.md | ||
args: --specs "trustless-gateway,-trustless-ipns-gateway" -skip 'TestGatewayCar/GET_response_for_application/vnd.ipld.car/Header_Content-Length' | ||
|
||
# 10. Upload the results | ||
- name: Upload MD summary | ||
if: failure() || success() | ||
run: cat output.md >> $GITHUB_STEP_SUMMARY | ||
- name: Upload HTML report | ||
if: failure() || success() | ||
uses: actions/upload-artifact@v3 | ||
with: | ||
name: gateway-conformance-libp2p.html | ||
path: output.html | ||
- name: Upload JSON report | ||
if: failure() || success() | ||
uses: actions/upload-artifact@v3 | ||
with: | ||
name: gateway-conformance-libp2p.json | ||
path: output.json |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -15,6 +15,9 @@ import ( | |
|
||
multierror "github.com/hashicorp/go-multierror" | ||
|
||
options "github.com/ipfs/boxo/coreiface/options" | ||
cmds "github.com/ipfs/go-ipfs-cmds" | ||
mprome "github.com/ipfs/go-metrics-prometheus" | ||
version "github.com/ipfs/kubo" | ||
utilmain "github.com/ipfs/kubo/cmd/ipfs/util" | ||
oldcmds "github.com/ipfs/kubo/commands" | ||
|
@@ -30,14 +33,12 @@ import ( | |
fsrepo "github.com/ipfs/kubo/repo/fsrepo" | ||
"github.com/ipfs/kubo/repo/fsrepo/migrations" | ||
"github.com/ipfs/kubo/repo/fsrepo/migrations/ipfsfetcher" | ||
goprocess "github.com/jbenet/goprocess" | ||
p2pcrypto "github.com/libp2p/go-libp2p/core/crypto" | ||
pnet "github.com/libp2p/go-libp2p/core/pnet" | ||
"github.com/libp2p/go-libp2p/core/protocol" | ||
p2phttp "github.com/libp2p/go-libp2p/p2p/http" | ||
sockets "github.com/libp2p/go-socket-activation" | ||
|
||
options "github.com/ipfs/boxo/coreiface/options" | ||
cmds "github.com/ipfs/go-ipfs-cmds" | ||
mprome "github.com/ipfs/go-metrics-prometheus" | ||
goprocess "github.com/jbenet/goprocess" | ||
ma "github.com/multiformats/go-multiaddr" | ||
manet "github.com/multiformats/go-multiaddr/net" | ||
prometheus "github.com/prometheus/client_golang/prometheus" | ||
|
@@ -551,6 +552,12 @@ take effect. | |
return err | ||
} | ||
|
||
// add trustless gateway over libp2p | ||
p2pGwErrc, err := serveTrustlessGatewayOverLibp2p(cctx) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
// Add ipfs version info to prometheus metrics | ||
ipfsInfoMetric := promauto.NewGaugeVec(prometheus.GaugeOpts{ | ||
Name: "ipfs_info", | ||
|
@@ -617,7 +624,7 @@ take effect. | |
// collect long-running errors and block for shutdown | ||
// TODO(cryptix): our fuse currently doesn't follow this pattern for graceful shutdown | ||
var errs error | ||
for err := range merge(apiErrc, gwErrc, gcErrc) { | ||
for err := range merge(apiErrc, gwErrc, gcErrc, p2pGwErrc) { | ||
if err != nil { | ||
errs = multierror.Append(errs, err) | ||
} | ||
|
@@ -899,6 +906,61 @@ func serveHTTPGateway(req *cmds.Request, cctx *oldcmds.Context) (<-chan error, e | |
return errc, nil | ||
} | ||
|
||
const gatewayProtocolID protocol.ID = "/ipfs/gateway" // FIXME: specify https://github.com/ipfs/specs/issues/433 | ||
|
||
func serveTrustlessGatewayOverLibp2p(cctx *oldcmds.Context) (<-chan error, error) { | ||
node, err := cctx.ConstructNode() | ||
if err != nil { | ||
return nil, fmt.Errorf("serveHTTPGatewayOverLibp2p: ConstructNode() failed: %s", err) | ||
} | ||
cfg, err := node.Repo.Config() | ||
if err != nil { | ||
return nil, fmt.Errorf("could not read config: %w", err) | ||
} | ||
|
||
if !cfg.Experimental.GatewayOverLibp2p { | ||
errCh := make(chan error) | ||
close(errCh) | ||
return errCh, nil | ||
} | ||
|
||
opts := []corehttp.ServeOption{ | ||
corehttp.MetricsCollectionOption("libp2p-gateway"), | ||
corehttp.Libp2pGatewayOption(), | ||
corehttp.VersionOption(), | ||
} | ||
|
||
handler, err := corehttp.MakeHandler(node, nil, opts...) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
h := p2phttp.Host{ | ||
StreamHost: node.PeerHost, | ||
} | ||
|
||
tmpProtocol := protocol.ID("/kubo/delete-me") | ||
h.SetHTTPHandler(tmpProtocol, http.NotFoundHandler()) | ||
h.WellKnownHandler.RemoveProtocolMeta(tmpProtocol) | ||
Comment on lines
+942
to
+944
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is an unfortunate hack as a result of a go-libp2p bug which will be linked shortly. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @aschmahmann @Jorropo do we still need this after libp2p/go-libp2p#2546 being merged? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes because libp2p/go-libp2p@9f14eb7 is not released. |
||
|
||
h.WellKnownHandler.AddProtocolMeta(gatewayProtocolID, p2phttp.ProtocolMeta{Path: "/"}) | ||
h.ServeMux = http.NewServeMux() | ||
h.ServeMux.Handle("/", handler) | ||
Comment on lines
+946
to
+948
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. AFAICT mounting at I think we should feel free to move this going forward though since the point of There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Note: we have to do the work here a little more explicitly than if we weren't mounting at |
||
|
||
errc := make(chan error, 1) | ||
go func() { | ||
defer close(errc) | ||
errc <- h.Serve() | ||
}() | ||
|
||
go func() { | ||
<-node.Process.Closing() | ||
h.Close() | ||
}() | ||
|
||
return errc, nil | ||
} | ||
|
||
// collects options and opens the fuse mountpoint. | ||
func mountFuse(req *cmds.Request, cctx *oldcmds.Context) error { | ||
cfg, err := cctx.GetConfig() | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ℹ️ I've separated action for testing HTTP Gateway from experimental subset over libp2p (in 1efd9d4)
This way experiment does not impact existing CI and branch protection rule (which requires full conformance on the HTTP port to pass).