From a692dc48a7c45274475e1460ab0691e9aa976ec0 Mon Sep 17 00:00:00 2001 From: Laurent Senta Date: Fri, 30 Jun 2023 09:34:08 +0200 Subject: [PATCH] fix: restore t0109 and t0114 --- test/sharness/t0109-gateway-web-_redirects.sh | 165 ++++++++ test/sharness/t0114-gateway-subdomains.sh | 368 +++++++++++++++++- 2 files changed, 530 insertions(+), 3 deletions(-) diff --git a/test/sharness/t0109-gateway-web-_redirects.sh b/test/sharness/t0109-gateway-web-_redirects.sh index d6a27db7f048..0bc2a23b6007 100755 --- a/test/sharness/t0109-gateway-web-_redirects.sh +++ b/test/sharness/t0109-gateway-web-_redirects.sh @@ -21,12 +21,160 @@ CAR_ROOT_CID=QmQyqMY5vUBSbSxyitJqthgwZunCQjDVtNd8ggVCxzuPQ4 REDIRECTS_DIR_CID=$(ipfs resolve -r /ipfs/$CAR_ROOT_CID/examples | cut -d "/" -f3) REDIRECTS_DIR_HOSTNAME="${REDIRECTS_DIR_CID}.ipfs.localhost:$GWAY_PORT" +test_expect_success "request for $REDIRECTS_DIR_HOSTNAME/redirect-one redirects with default of 301, per _redirects file" ' + curl -sD - --resolve $REDIRECTS_DIR_HOSTNAME:127.0.0.1 "http://$REDIRECTS_DIR_HOSTNAME/redirect-one" > response && + test_should_contain "301 Moved Permanently" response && + test_should_contain "Location: /one.html" response +' + +test_expect_success "request for $REDIRECTS_DIR_HOSTNAME/301-redirect-one redirects with 301, per _redirects file" ' + curl -sD - --resolve $REDIRECTS_DIR_HOSTNAME:127.0.0.1 "http://$REDIRECTS_DIR_HOSTNAME/301-redirect-one" > response && + test_should_contain "301 Moved Permanently" response && + test_should_contain "Location: /one.html" response +' + +test_expect_success "request for $REDIRECTS_DIR_HOSTNAME/302-redirect-two redirects with 302, per _redirects file" ' + curl -sD - --resolve $REDIRECTS_DIR_HOSTNAME:127.0.0.1 "http://$REDIRECTS_DIR_HOSTNAME/302-redirect-two" > response && + test_should_contain "302 Found" response && + test_should_contain "Location: /two.html" response +' + +test_expect_success "request for $REDIRECTS_DIR_HOSTNAME/200-index returns 200, per _redirects file" ' + curl -sD - --resolve $REDIRECTS_DIR_HOSTNAME:127.0.0.1 "http://$REDIRECTS_DIR_HOSTNAME/200-index" > response && + test_should_contain "my index" response && + test_should_contain "200 OK" response +' + +test_expect_success "request for $REDIRECTS_DIR_HOSTNAME/posts/:year/:month/:day/:title redirects with 301 and placeholders, per _redirects file" ' + curl -sD - --resolve $REDIRECTS_DIR_HOSTNAME:127.0.0.1 "http://$REDIRECTS_DIR_HOSTNAME/posts/2022/01/01/hello-world" > response && + test_should_contain "301 Moved Permanently" response && + test_should_contain "Location: /articles/2022/01/01/hello-world" response +' + +test_expect_success "request for $REDIRECTS_DIR_HOSTNAME/splat/one.html redirects with 301 and splat placeholder, per _redirects file" ' + curl -sD - --resolve $REDIRECTS_DIR_HOSTNAME:127.0.0.1 "http://$REDIRECTS_DIR_HOSTNAME/splat/one.html" > response && + test_should_contain "301 Moved Permanently" response && + test_should_contain "Location: /redirected-splat/one.html" response +' + +# ensure custom 4xx works and has the same cache headers as regular /ipfs/ path +CUSTOM_4XX_CID=$(ipfs resolve -r /ipfs/$CAR_ROOT_CID/examples/404.html | cut -d "/" -f3) +test_expect_success "request for $REDIRECTS_DIR_HOSTNAME/not-found/has-no-redirects-entry returns custom 404, per _redirects file" ' + curl -sD - --resolve $REDIRECTS_DIR_HOSTNAME:127.0.0.1 "http://$REDIRECTS_DIR_HOSTNAME/not-found/has-no-redirects-entry" > response && + test_should_contain "404 Not Found" response && + test_should_contain "Cache-Control: public, max-age=29030400, immutable" response && + test_should_contain "Etag: \"$CUSTOM_4XX_CID\"" response && + test_should_contain "my 404" response +' + +CUSTOM_4XX_CID=$(ipfs resolve -r /ipfs/$CAR_ROOT_CID/examples/410.html | cut -d "/" -f3) +test_expect_success "request for $REDIRECTS_DIR_HOSTNAME/gone/has-no-redirects-entry returns custom 410, per _redirects file" ' + curl -sD - --resolve $REDIRECTS_DIR_HOSTNAME:127.0.0.1 "http://$REDIRECTS_DIR_HOSTNAME/gone/has-no-redirects-entry" > response && + test_should_contain "410 Gone" response && + test_should_contain "Cache-Control: public, max-age=29030400, immutable" response && + test_should_contain "Etag: \"$CUSTOM_4XX_CID\"" response && + test_should_contain "my 410" response +' + +CUSTOM_4XX_CID=$(ipfs resolve -r /ipfs/$CAR_ROOT_CID/examples/451.html | cut -d "/" -f3) +test_expect_success "request for $REDIRECTS_DIR_HOSTNAME/unavail/has-no-redirects-entry returns custom 451, per _redirects file" ' + curl -sD - --resolve $REDIRECTS_DIR_HOSTNAME:127.0.0.1 "http://$REDIRECTS_DIR_HOSTNAME/unavail/has-no-redirects-entry" > response && + test_should_contain "451 Unavailable For Legal Reasons" response && + test_should_contain "Cache-Control: public, max-age=29030400, immutable" response && + test_should_contain "Etag: \"$CUSTOM_4XX_CID\"" response && + test_should_contain "my 451" response +' + +test_expect_success "request for $REDIRECTS_DIR_HOSTNAME/catch-all returns 200, per _redirects file" ' + curl -sD - --resolve $REDIRECTS_DIR_HOSTNAME:127.0.0.1 "http://$REDIRECTS_DIR_HOSTNAME/catch-all" > response && + test_should_contain "200 OK" response && + test_should_contain "my index" response +' + # This test ensures _redirects is supported only on Web Gateways that use Host header (DNSLink, Subdomain) test_expect_success "request for http://127.0.0.1:$GWAY_PORT/ipfs/$REDIRECTS_DIR_CID/301-redirect-one returns generic 404 (no custom 404 from _redirects since no origin isolation)" ' curl -sD - "http://127.0.0.1:$GWAY_PORT/ipfs/$REDIRECTS_DIR_CID/301-redirect-one" > response && test_should_contain "404 Not Found" response && test_should_not_contain "my 404" response ' + +# With CRLF line terminator +NEWLINE_REDIRECTS_DIR_CID=$(ipfs resolve -r /ipfs/$CAR_ROOT_CID/newlines | cut -d "/" -f3) +NEWLINE_REDIRECTS_DIR_HOSTNAME="${NEWLINE_REDIRECTS_DIR_CID}.ipfs.localhost:$GWAY_PORT" + +test_expect_success "newline: _redirects has CRLF line terminators" ' + ipfs cat /ipfs/$NEWLINE_REDIRECTS_DIR_CID/_redirects | file - > response && + test_should_contain "with CRLF line terminators" response +' + +test_expect_success "newline: request for $NEWLINE_REDIRECTS_DIR_HOSTNAME/redirect-one redirects with default of 301, per _redirects file" ' + curl -sD - --resolve $NEWLINE_REDIRECTS_DIR_HOSTNAME:127.0.0.1 "http://$NEWLINE_REDIRECTS_DIR_HOSTNAME/redirect-one" > response && + test_should_contain "301 Moved Permanently" response && + test_should_contain "Location: /one.html" response +' + +# Good codes +GOOD_REDIRECTS_DIR_CID=$(ipfs resolve -r /ipfs/$CAR_ROOT_CID/good-codes | cut -d "/" -f3) +GOOD_REDIRECTS_DIR_HOSTNAME="${GOOD_REDIRECTS_DIR_CID}.ipfs.localhost:$GWAY_PORT" + +test_expect_success "good codes: request for $GOOD_REDIRECTS_DIR_HOSTNAME/redirect-one redirects with default of 301, per _redirects file" ' + curl -sD - --resolve $GOOD_REDIRECTS_DIR_HOSTNAME:127.0.0.1 "http://$GOOD_REDIRECTS_DIR_HOSTNAME/a301" > response && + test_should_contain "301 Moved Permanently" response && + test_should_contain "Location: /b301" response +' + +# Bad codes +BAD_REDIRECTS_DIR_CID=$(ipfs resolve -r /ipfs/$CAR_ROOT_CID/bad-codes | cut -d "/" -f3) +BAD_REDIRECTS_DIR_HOSTNAME="${BAD_REDIRECTS_DIR_CID}.ipfs.localhost:$GWAY_PORT" + +# if accessing a path that doesn't exist, read _redirects and fail parsing, and return error +test_expect_success "bad codes: request for $BAD_REDIRECTS_DIR_HOSTNAME/not-found returns error about bad code" ' + curl -sD - --resolve $BAD_REDIRECTS_DIR_HOSTNAME:127.0.0.1 "http://$BAD_REDIRECTS_DIR_HOSTNAME/not-found" > response && + test_should_contain "500" response && + test_should_contain "status code 999 is not supported" response +' + +# if accessing a path that does exist, don't read _redirects and therefore don't fail parsing +test_expect_success "bad codes: request for $BAD_REDIRECTS_DIR_HOSTNAME/found.html doesn't return error about bad code" ' + curl -sD - --resolve $BAD_REDIRECTS_DIR_HOSTNAME:127.0.0.1 "http://$BAD_REDIRECTS_DIR_HOSTNAME/found.html" > response && + test_should_contain "200" response && + test_should_contain "my found" response && + test_should_not_contain "unsupported redirect status" response +' + +# Invalid file, containing "hello" +INVALID_REDIRECTS_DIR_CID=$(ipfs resolve -r /ipfs/$CAR_ROOT_CID/invalid | cut -d "/" -f3) +INVALID_REDIRECTS_DIR_HOSTNAME="${INVALID_REDIRECTS_DIR_CID}.ipfs.localhost:$GWAY_PORT" + +# if accessing a path that doesn't exist, read _redirects and fail parsing, and return error +test_expect_success "invalid file: request for $INVALID_REDIRECTS_DIR_HOSTNAME/not-found returns error about invalid redirects file" ' + curl -sD - --resolve $INVALID_REDIRECTS_DIR_HOSTNAME:127.0.0.1 "http://$INVALID_REDIRECTS_DIR_HOSTNAME/not-found" > response && + test_should_contain "500" response && + test_should_contain "could not parse _redirects:" response +' + +# Invalid file, containing forced redirect +INVALID_REDIRECTS_DIR_CID=$(ipfs resolve -r /ipfs/$CAR_ROOT_CID/forced | cut -d "/" -f3) +INVALID_REDIRECTS_DIR_HOSTNAME="${INVALID_REDIRECTS_DIR_CID}.ipfs.localhost:$GWAY_PORT" + +# if accessing a path that doesn't exist, read _redirects and fail parsing, and return error +test_expect_success "invalid file: request for $INVALID_REDIRECTS_DIR_HOSTNAME/not-found returns error about invalid redirects file" ' + curl -sD - --resolve $INVALID_REDIRECTS_DIR_HOSTNAME:127.0.0.1 "http://$INVALID_REDIRECTS_DIR_HOSTNAME/not-found" > response && + test_should_contain "500" response && + test_should_contain "could not parse _redirects:" response && + test_should_contain "forced redirects (or \"shadowing\") are not supported" response +' + +# if accessing a path that doesn't exist and _redirects file is too large, return error +TOO_LARGE_REDIRECTS_DIR_CID=$(ipfs resolve -r /ipfs/$CAR_ROOT_CID/too-large | cut -d "/" -f3) +TOO_LARGE_REDIRECTS_DIR_HOSTNAME="${TOO_LARGE_REDIRECTS_DIR_CID}.ipfs.localhost:$GWAY_PORT" +test_expect_success "invalid file: request for $TOO_LARGE_REDIRECTS_DIR_HOSTNAME/not-found returns error about too large redirects file" ' + curl -sD - --resolve $TOO_LARGE_REDIRECTS_DIR_HOSTNAME:127.0.0.1 "http://$TOO_LARGE_REDIRECTS_DIR_HOSTNAME/not-found" > response && + test_should_contain "500" response && + test_should_contain "could not parse _redirects:" response && + test_should_contain "redirects file size cannot exceed" response +' + test_kill_ipfs_daemon # disable wildcard DNSLink gateway @@ -62,6 +210,23 @@ test_expect_success "spoofed DNSLink record resolves in cli" " test_should_contain \"index.html\" result " +test_expect_success "request for $DNSLINK_FQDN/redirect-one redirects with default of 301, per _redirects file" ' + curl -sD - --resolve $DNSLINK_FQDN:$GWAY_PORT:127.0.0.1 "http://$DNSLINK_FQDN:$GWAY_PORT/redirect-one" > response && + test_should_contain "301 Moved Permanently" response && + test_should_contain "Location: /one.html" response +' + +# ensure custom 404 works and has the same cache headers as regular /ipns/ paths +test_expect_success "request for $DNSLINK_FQDN/en/has-no-redirects-entry returns custom 404, per _redirects file" ' + curl -sD - --resolve $DNSLINK_FQDN:$GWAY_PORT:127.0.0.1 "http://$DNSLINK_FQDN:$GWAY_PORT/not-found/has-no-redirects-entry" > response && + test_should_contain "404 Not Found" response && + test_should_contain "Etag: \"Qmd9GD7Bauh6N2ZLfNnYS3b7QVAijbud83b8GE8LPMNBBP\"" response && + test_should_not_contain "Cache-Control: public, max-age=29030400, immutable" response && + test_should_not_contain "immutable" response && + test_should_contain "Date: " response && + test_should_contain "my 404" response +' + test_expect_success "request for $NO_DNSLINK_FQDN/redirect-one does not redirect, since DNSLink is disabled" ' curl -sD - --resolve $NO_DNSLINK_FQDN:$GWAY_PORT:127.0.0.1 "http://$NO_DNSLINK_FQDN:$GWAY_PORT/redirect-one" > response && test_should_not_contain "one.html" response && diff --git a/test/sharness/t0114-gateway-subdomains.sh b/test/sharness/t0114-gateway-subdomains.sh index 4ec5dc2bd00d..8be102c4f414 100755 --- a/test/sharness/t0114-gateway-subdomains.sh +++ b/test/sharness/t0114-gateway-subdomains.sh @@ -139,6 +139,60 @@ test_localhost_gateway_response_should_contain \ "http://127.0.0.1:$GWAY_PORT/ipfs/$CIDv1" \ "$CID_VAL" +# 'localhost' hostname is used for subdomains, and should not return +# payload directly, but redirect to URL with proper origin isolation + +test_localhost_gateway_response_should_contain \ + "request for localhost/ipfs/{CIDv1} returns HTTP 301 Moved Permanently" \ + "http://localhost:$GWAY_PORT/ipfs/$CIDv1" \ + "301 Moved Permanently" + +test_localhost_gateway_response_should_contain \ + "request for localhost/ipfs/{CIDv1} returns Location HTTP header for subdomain redirect in browsers" \ + "http://localhost:$GWAY_PORT/ipfs/$CIDv1" \ + "Location: http://$CIDv1.ipfs.localhost:$GWAY_PORT/" + +test_localhost_gateway_response_should_contain \ + "request for localhost/ipfs/{DIR_CID} returns HTTP 301 Moved Permanently" \ + "http://localhost:$GWAY_PORT/ipfs/$DIR_CID" \ + "301 Moved Permanently" + +test_localhost_gateway_response_should_contain \ + "request for localhost/ipfs/{DIR_CID} returns Location HTTP header for subdomain redirect in browsers" \ + "http://localhost:$GWAY_PORT/ipfs/$DIR_CID/" \ + "Location: http://$DIR_CID.ipfs.localhost:$GWAY_PORT/" + +# We return human-readable body with HTTP 301 so existing cli scripts that use path-based +# gateway are informed to enable following HTTP redirects +test_localhost_gateway_response_should_contain \ + "request for localhost/ipfs/{CIDv1} includes human-readable link and redirect info in HTTP 301 body" \ + "http://localhost:$GWAY_PORT/ipfs/$CIDv1" \ + ">Moved Permanently" + +test_localhost_gateway_response_should_contain \ + "request for localhost/ipfs/{CIDv0} redirects to CIDv1 representation in subdomain" \ + "http://localhost:$GWAY_PORT/ipfs/$CIDv0" \ + "Location: http://${CIDv0to1}.ipfs.localhost:$GWAY_PORT/" + +# /ipns/ + +test_localhost_gateway_response_should_contain \ + "request for localhost/ipns/{CIDv0} redirects to CIDv1 with libp2p-key multicodec in subdomain" \ + "http://localhost:$GWAY_PORT/ipns/$RSA_IPNS_IDv0" \ + "Location: http://${RSA_IPNS_IDv1}.ipns.localhost:$GWAY_PORT/" + +test_localhost_gateway_response_should_contain \ + "request for localhost/ipns/{CIDv0} redirects to CIDv1 with libp2p-key multicodec in subdomain" \ + "http://localhost:$GWAY_PORT/ipns/$ED25519_IPNS_IDv0" \ + "Location: http://${ED25519_IPNS_IDv1}.ipns.localhost:$GWAY_PORT/" + +# /ipns/ + +test_localhost_gateway_response_should_contain \ + "request for localhost/ipns/{fqdn} redirects to DNSLink in subdomain" \ + "http://localhost:$GWAY_PORT/ipns/en.wikipedia-on-ipfs.org/wiki" \ + "Location: http://en.wikipedia-on-ipfs.org.ipns.localhost:$GWAY_PORT/wiki" + # API on localhost subdomain gateway # /api/v0 present on the root hostname @@ -163,6 +217,70 @@ test_localhost_gateway_response_should_contain \ ## (origin per content root at http://*.localhost) ## ============================================================================ +# {CID}.ipfs.localhost + +test_localhost_gateway_response_should_contain \ + "request for {CID}.ipfs.localhost should return expected payload" \ + "http://${CIDv1}.ipfs.localhost:$GWAY_PORT" \ + "$CID_VAL" + +# ensure /ipfs/ namespace is not mounted on subdomain +test_localhost_gateway_response_should_contain \ + "request for {CID}.ipfs.localhost/ipfs/{CID} should return HTTP 404" \ + "http://${CIDv1}.ipfs.localhost:$GWAY_PORT/ipfs/$CIDv1" \ + "404 Not Found" + +# ensure requests to /ipfs/* are not blocked, if content root has such subdirectory +test_localhost_gateway_response_should_contain \ + "request for {CID}.ipfs.localhost/ipfs/file.txt should return data from a file in CID content root" \ + "http://${DIR_CID}.ipfs.localhost:$GWAY_PORT/ipfs/file.txt" \ + "I am a txt file" + +# {CID}.ipfs.localhost/sub/dir (Directory Listing) +DIR_HOSTNAME="${DIR_CID}.ipfs.localhost:$GWAY_PORT" + +test_expect_success "valid file and subdirectory paths in directory listing at {cid}.ipfs.localhost" ' + curl -s --resolve $DIR_HOSTNAME:127.0.0.1 "http://$DIR_HOSTNAME" > list_response && + test_should_contain "hello" list_response && + test_should_contain "ipfs" list_response +' + +test_expect_success "valid parent directory path in directory listing at {cid}.ipfs.localhost/sub/dir" ' + curl -s --resolve $DIR_HOSTNAME:127.0.0.1 "http://$DIR_HOSTNAME/ipfs/ipns/" > list_response && + test_should_contain ".." list_response && + test_should_contain "bar" list_response +' + +test_expect_success "request for deep path resource at {cid}.ipfs.localhost/sub/dir/file" ' + curl -s --resolve $DIR_HOSTNAME:127.0.0.1 "http://$DIR_HOSTNAME/ipfs/ipns/bar" > list_response && + test_should_contain "text-file-content" list_response +' + + +# *.ipns.localhost + +# .ipns.localhost + +test_localhost_gateway_response_should_contain \ + "request for {CIDv1-libp2p-key}.ipns.localhost returns expected payload" \ + "http://${RSA_IPNS_IDv1}.ipns.localhost:$GWAY_PORT" \ + "$CID_VAL" + +test_localhost_gateway_response_should_contain \ + "request for {CIDv1-libp2p-key}.ipns.localhost returns expected payload" \ + "http://${ED25519_IPNS_IDv1}.ipns.localhost:$GWAY_PORT" \ + "$CID_VAL" + +test_localhost_gateway_response_should_contain \ + "localhost request for {CIDv1-dag-pb}.ipns.localhost redirects to CID with libp2p-key multicodec" \ + "http://${RSA_IPNS_IDv1_DAGPB}.ipns.localhost:$GWAY_PORT" \ + "Location: http://${RSA_IPNS_IDv1}.ipns.localhost:$GWAY_PORT/" + +test_localhost_gateway_response_should_contain \ + "localhost request for {CIDv1-dag-pb}.ipns.localhost redirects to CID with libp2p-key multicodec" \ + "http://${ED25519_IPNS_IDv1_DAGPB}.ipns.localhost:$GWAY_PORT" \ + "Location: http://${ED25519_IPNS_IDv1}.ipns.localhost:$GWAY_PORT/" + # .ipns.localhost # DNSLink test requires a daemon in online mode with precached /ipns/ mapping @@ -171,6 +289,11 @@ DNSLINK_FQDN="dnslink-test.example.com" export IPFS_NS_MAP="$DNSLINK_FQDN:/ipfs/$CIDv1" test_launch_ipfs_daemon +test_localhost_gateway_response_should_contain \ + "request for {dnslink}.ipns.localhost returns expected payload" \ + "http://$DNSLINK_FQDN.ipns.localhost:$GWAY_PORT" \ + "$CID_VAL" + # api.localhost/api # Note: we use DIR_CID so refs -r returns some CIDs for child nodes @@ -200,6 +323,17 @@ ipfs config --json Gateway.PublicGateways '{ test_kill_ipfs_daemon test_launch_ipfs_daemon_without_network +test_localhost_gateway_response_should_contain \ + "request for localhost/ipns/{fqdn} redirects to DNSLink in subdomain with DNS inlining" \ + "http://localhost:$GWAY_PORT/ipns/en.wikipedia-on-ipfs.org/wiki" \ + "Location: http://en-wikipedia--on--ipfs-org.ipns.localhost:$GWAY_PORT/wiki" + +test_hostname_gateway_response_should_contain \ + "request for example.com/ipns/{fqdn} redirects to DNSLink in subdomain with DNS inlining" \ + "example.com" \ + "http://127.0.0.1:$GWAY_PORT/ipns/en.wikipedia-on-ipfs.org/wiki" \ + "Location: http://en-wikipedia--on--ipfs-org.ipns.example.com/wiki" + ## ============================================================================ ## Test subdomain-based requests with a custom hostname config ## (origin per content root at http://*.example.com) @@ -216,6 +350,154 @@ ipfs config --json Gateway.PublicGateways '{ test_kill_ipfs_daemon test_launch_ipfs_daemon_without_network + +# example.com/ip(f|n)s/* +# ============================================================================= + +# path requests to the root hostname should redirect +# to a subdomain URL with proper origin isolation + +test_hostname_gateway_response_should_contain \ + "request for example.com/ipfs/{CIDv1} produces redirect to {CIDv1}.ipfs.example.com" \ + "example.com" \ + "http://127.0.0.1:$GWAY_PORT/ipfs/$CIDv1" \ + "Location: http://$CIDv1.ipfs.example.com/" + +# error message should include original CID +# (and it should be case-sensitive, as we can't assume everyone uses base32) +test_hostname_gateway_response_should_contain \ + "request for example.com/ipfs/{InvalidCID} produces useful error before redirect" \ + "example.com" \ + "http://127.0.0.1:$GWAY_PORT/ipfs/QmInvalidCID" \ + 'invalid path \"/ipfs/QmInvalidCID\"' + +test_hostname_gateway_response_should_contain \ + "request for example.com/ipfs/{CIDv0} produces redirect to {CIDv1}.ipfs.example.com" \ + "example.com" \ + "http://127.0.0.1:$GWAY_PORT/ipfs/$CIDv0" \ + "Location: http://${CIDv0to1}.ipfs.example.com/" + +# Support X-Forwarded-Proto +test_expect_success "request for http://example.com/ipfs/{CID} with X-Forwarded-Proto: https produces redirect to HTTPS URL" " + curl -H \"X-Forwarded-Proto: https\" -H \"Host: example.com\" -sD - \"http://127.0.0.1:$GWAY_PORT/ipfs/$CIDv1\" > response && + test_should_contain \"Location: https://$CIDv1.ipfs.example.com/\" response +" + +# Support ipfs:// in https://developer.mozilla.org/en-US/docs/Web/API/Navigator/registerProtocolHandler +test_hostname_gateway_response_should_contain \ + "request for example.com/ipfs/?uri=ipfs%3A%2F%2F.. produces redirect to /ipfs/.. content path" \ + "example.com" \ + "http://127.0.0.1:$GWAY_PORT/ipfs/?uri=ipfs%3A%2F%2FQmXoypizjW3WknFiJnKLwHCnL72vedxjQkDDP1mXWo6uco%2Fwiki%2FDiego_Maradona.html" \ + "Location: /ipfs/QmXoypizjW3WknFiJnKLwHCnL72vedxjQkDDP1mXWo6uco/wiki/Diego_Maradona.html" + +# example.com/ipns/ + +test_hostname_gateway_response_should_contain \ + "request for example.com/ipns/{CIDv0} redirects to CIDv1 with libp2p-key multicodec in subdomain" \ + "example.com" \ + "http://127.0.0.1:$GWAY_PORT/ipns/$RSA_IPNS_IDv0" \ + "Location: http://${RSA_IPNS_IDv1}.ipns.example.com/" + +test_hostname_gateway_response_should_contain \ + "request for example.com/ipns/{CIDv0} redirects to CIDv1 with libp2p-key multicodec in subdomain" \ + "example.com" \ + "http://127.0.0.1:$GWAY_PORT/ipns/$ED25519_IPNS_IDv0" \ + "Location: http://${ED25519_IPNS_IDv1}.ipns.example.com/" + +# example.com/ipns/ + +test_hostname_gateway_response_should_contain \ + "request for example.com/ipns/{fqdn} redirects to DNSLink in subdomain" \ + "example.com" \ + "http://127.0.0.1:$GWAY_PORT/ipns/en.wikipedia-on-ipfs.org/wiki" \ + "Location: http://en.wikipedia-on-ipfs.org.ipns.example.com/wiki" + +# DNSLink on Public gateway with a single-level wildcard TLS cert +# "Option C" from https://github.com/ipfs/in-web-browsers/issues/169 +test_expect_success \ + "request for example.com/ipns/{fqdn} with X-Forwarded-Proto redirects to TLS-safe label in subdomain" " + curl -H \"Host: example.com\" -H \"X-Forwarded-Proto: https\" -sD - \"http://127.0.0.1:$GWAY_PORT/ipns/en.wikipedia-on-ipfs.org/wiki\" > response && + test_should_contain \"Location: https://en-wikipedia--on--ipfs-org.ipns.example.com/wiki\" response + " + +# Support ipns:// in https://developer.mozilla.org/en-US/docs/Web/API/Navigator/registerProtocolHandler +test_hostname_gateway_response_should_contain \ + "request for example.com/ipns/?uri=ipns%3A%2F%2F.. produces redirect to /ipns/.. content path" \ + "example.com" \ + "http://127.0.0.1:$GWAY_PORT/ipns/?uri=ipns%3A%2F%2Fen.wikipedia-on-ipfs.org" \ + "Location: /ipns/en.wikipedia-on-ipfs.org" + +# *.ipfs.example.com: subdomain requests made with custom FQDN in Host header + +test_hostname_gateway_response_should_contain \ + "request for {CID}.ipfs.example.com should return expected payload" \ + "${CIDv1}.ipfs.example.com" \ + "http://127.0.0.1:$GWAY_PORT/" \ + "$CID_VAL" + +test_hostname_gateway_response_should_contain \ + "request for {CID}.ipfs.example.com/ipfs/{CID} should return HTTP 404" \ + "${CIDv1}.ipfs.example.com" \ + "http://127.0.0.1:$GWAY_PORT/ipfs/$CIDv1" \ + "404 Not Found" + +# {CID}.ipfs.example.com/sub/dir (Directory Listing) +DIR_FQDN="${DIR_CID}.ipfs.example.com" + +test_expect_success "valid file and directory paths in directory listing at {cid}.ipfs.example.com" ' + curl -s -H "Host: $DIR_FQDN" http://127.0.0.1:$GWAY_PORT > list_response && + test_should_contain "hello" list_response && + test_should_contain "ipfs" list_response +' + +test_expect_success "valid parent directory path in directory listing at {cid}.ipfs.example.com/sub/dir" ' + curl -s -H "Host: $DIR_FQDN" http://127.0.0.1:$GWAY_PORT/ipfs/ipns/ > list_response && + test_should_contain ".." list_response && + test_should_contain "bar" list_response +' + +# Note 1: we test for sneaky subdir names {cid}.ipfs.example.com/ipfs/ipns/ :^) +# Note 2: example.com/ipfs/.. present in HTML will be redirected to subdomain, so this is expected behavior +test_expect_success "valid breadcrumb links in the header of directory listing at {cid}.ipfs.example.com/sub/dir" ' + curl -s -H "Host: $DIR_FQDN" http://127.0.0.1:$GWAY_PORT/ipfs/ipns/ > list_response && + test_should_contain "Index of" list_response && + test_should_contain "/ipfs/${DIR_CID}/ipfs/ipns" list_response +' + +test_expect_success "request for deep path resource {cid}.ipfs.example.com/sub/dir/file" ' + curl -s -H "Host: $DIR_FQDN" http://127.0.0.1:$GWAY_PORT/ipfs/ipns/bar > list_response && + test_should_contain "text-file-content" list_response +' + +# *.ipns.example.com +# ============================================================================ + +# .ipns.example.com + +test_hostname_gateway_response_should_contain \ + "request for {CIDv1-libp2p-key}.ipns.example.com returns expected payload" \ + "${RSA_IPNS_IDv1}.ipns.example.com" \ + "http://127.0.0.1:$GWAY_PORT" \ + "$CID_VAL" + +test_hostname_gateway_response_should_contain \ + "request for {CIDv1-libp2p-key}.ipns.example.com returns expected payload" \ + "${ED25519_IPNS_IDv1}.ipns.example.com" \ + "http://127.0.0.1:$GWAY_PORT" \ + "$CID_VAL" + +test_hostname_gateway_response_should_contain \ + "hostname request for {CIDv1-dag-pb}.ipns.localhost redirects to CID with libp2p-key multicodec" \ + "${RSA_IPNS_IDv1_DAGPB}.ipns.example.com" \ + "http://127.0.0.1:$GWAY_PORT" \ + "Location: http://${RSA_IPNS_IDv1}.ipns.example.com/" + +test_hostname_gateway_response_should_contain \ + "hostname request for {CIDv1-dag-pb}.ipns.localhost redirects to CID with libp2p-key multicodec" \ + "${ED25519_IPNS_IDv1_DAGPB}.ipns.example.com" \ + "http://127.0.0.1:$GWAY_PORT" \ + "Location: http://${ED25519_IPNS_IDv1}.ipns.example.com/" + # API on subdomain gateway example.com # ============================================================================ @@ -275,9 +557,6 @@ DNSLINK_FQDN="dnslink-subdomain-gw-test.example.org" export IPFS_NS_MAP="$DNSLINK_FQDN:/ipfs/$CIDv1" test_launch_ipfs_daemon -# TODO: dns inlining? -# this lives in subdomain_gateway_ipns_test.go for now - test_hostname_gateway_response_should_contain \ "request for {dnslink}.ipns.example.com returns expected payload" \ "$DNSLINK_FQDN.ipns.example.com" \ @@ -292,6 +571,59 @@ test_expect_success \ test_should_contain \"$CID_VAL\" response " +## Test subdomain handling of CIDs that do not fit in a single DNS Label (>63chars) +## https://github.com/ipfs/go-ipfs/issues/7318 +## ============================================================================ + +# local: *.localhost +test_localhost_gateway_response_should_contain \ + "request for a ED25519 libp2p-key at localhost/ipns/{b58mh} returns Location HTTP header for DNS-safe subdomain redirect in browsers" \ + "http://localhost:$GWAY_PORT/ipns/$IPNS_ED25519_B58MH" \ + "Location: http://${IPNS_ED25519_B36CID}.ipns.localhost:$GWAY_PORT/" + +# router should not redirect to hostnames that could fail due to DNS limits +test_localhost_gateway_response_should_contain \ + "request for a too long CID at localhost/ipfs/{CIDv1} returns human readable error" \ + "http://localhost:$GWAY_PORT/ipfs/$CIDv1_TOO_LONG" \ + "CID incompatible with DNS label length limit of 63" + +test_localhost_gateway_response_should_contain \ + "request for a too long CID at localhost/ipfs/{CIDv1} returns HTTP Error 400 Bad Request" \ + "http://localhost:$GWAY_PORT/ipfs/$CIDv1_TOO_LONG" \ + "400 Bad Request" + +# direct request should also fail (provides the same UX as router and avoids confusion) +test_localhost_gateway_response_should_contain \ + "request for a too long CID at {CIDv1}.ipfs.localhost returns expected payload" \ + "http://$CIDv1_TOO_LONG.ipfs.localhost:$GWAY_PORT" \ + "400 Bad Request" + +# public subdomain gateway: *.example.com + +test_hostname_gateway_response_should_contain \ + "request for a ED25519 libp2p-key at example.com/ipns/{b58mh} returns Location HTTP header for DNS-safe subdomain redirect in browsers" \ + "example.com" \ + "http://127.0.0.1:$GWAY_PORT/ipns/$IPNS_ED25519_B58MH" \ + "Location: http://${IPNS_ED25519_B36CID}.ipns.example.com" + +test_hostname_gateway_response_should_contain \ + "request for a too long CID at example.com/ipfs/{CIDv1} returns human readable error" \ + "example.com" \ + "http://127.0.0.1:$GWAY_PORT/ipfs/$CIDv1_TOO_LONG" \ + "CID incompatible with DNS label length limit of 63" + +test_hostname_gateway_response_should_contain \ + "request for a too long CID at example.com/ipfs/{CIDv1} returns HTTP Error 400 Bad Request" \ + "example.com" \ + "http://127.0.0.1:$GWAY_PORT/ipfs/$CIDv1_TOO_LONG" \ + "400 Bad Request" + +test_hostname_gateway_response_should_contain \ + "request for a too long CID at {CIDv1}.ipfs.example.com returns HTTP Error 400 Bad Request" \ + "$CIDv1_TOO_LONG.ipfs.example.com" \ + "http://127.0.0.1:$GWAY_PORT/" \ + "400 Bad Request" + # Disable selected Paths for the subdomain gateway hostname # ============================================================================= @@ -493,6 +825,36 @@ test_hostname_gateway_response_should_contain \ "http://127.0.0.1:$GWAY_PORT/" \ "$CID_VAL" +## ============================================================================ +## Test support for X-Forwarded-Host +## ============================================================================ + +# set explicit subdomain gateway config for the hostname +ipfs config --json Gateway.PublicGateways '{ + "example.com": { + "UseSubdomains": true, + "Paths": ["/ipfs", "/ipns", "/api"] + } +}' || exit 1 +# restart daemon to apply config changes +test_kill_ipfs_daemon +test_launch_ipfs_daemon_without_network + +test_expect_success "request for http://fake.domain.com/ipfs/{CID} doesn't match the example.com gateway" " + curl -H \"Host: fake.domain.com\" -sD - \"http://127.0.0.1:$GWAY_PORT/ipfs/$CIDv1\" > response && + test_should_contain \"200 OK\" response +" + +test_expect_success "request for http://fake.domain.com/ipfs/{CID} with X-Forwarded-Host: example.com match the example.com gateway" " + curl -H \"Host: fake.domain.com\" -H \"X-Forwarded-Host: example.com\" -sD - \"http://127.0.0.1:$GWAY_PORT/ipfs/$CIDv1\" > response && + test_should_contain \"Location: http://$CIDv1.ipfs.example.com/\" response +" + +test_expect_success "request for http://fake.domain.com/ipfs/{CID} with X-Forwarded-Host: example.com and X-Forwarded-Proto: https match the example.com gateway, redirect with https" " + curl -H \"Host: fake.domain.com\" -H \"X-Forwarded-Host: example.com\" -H \"X-Forwarded-Proto: https\" -sD - \"http://127.0.0.1:$GWAY_PORT/ipfs/$CIDv1\" > response && + test_should_contain \"Location: https://$CIDv1.ipfs.example.com/\" response +" + ## ============================================================================ ## Test support for wildcards in gateway config ## ============================================================================