Skip to content

Commit

Permalink
feat: clarify tunnel and update todos
Browse files Browse the repository at this point in the history
  • Loading branch information
laurentsenta committed Mar 20, 2023
1 parent b6e696f commit bf819f6
Show file tree
Hide file tree
Showing 2 changed files with 68 additions and 48 deletions.
108 changes: 60 additions & 48 deletions tests/t0114_gateway_subdomains_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,20 +26,29 @@ func TestGatewaySubdomains(t *testing.T) {

tests := []CTest{}

// sugar: readable way to add more tests
with := func(moreTests []CTest) {
tests = append(tests, moreTests...)
}

// sugar: nicer looking sprintf call
Url := func(path string, args ...interface{}) string {
return fmt.Sprintf(path, args...)
}

// We're going to run the same test against multiple gateways (localhost, and a subdomain gateway)
gatewayURLs := []string{
SubdomainGatewayUrl,
SubdomainLocalhostGatewayUrl,
}

with(testLocalhostGatewayResponseShouldContain(t,
with(testGatewayWithManyProtocols(t,
"request for 127.0.0.1/ipfs/{CID} stays on path",
// TODO: this feature should get disabled if we're not taling with an IP gateway.
// TODO: if the gateway url is not an IP, this test should be skipped.
// There is no way to reach out the "path" gateway
// if you're testing the domain name dweb.link for example.
`IP remains old school path-based gateway`,
fmt.Sprintf("%s/ipfs/%s/", GatewayUrl, CIDv1),
Url("%s/ipfs/%s/", GatewayUrl, CIDv1),
Expect().
Status(200).
Body("hello\n").
Expand All @@ -52,13 +61,13 @@ func TestGatewaySubdomains(t *testing.T) {
t.Fatal(err)
}

with(testLocalhostGatewayResponseShouldContain(t,
with(testGatewayWithManyProtocols(t,
"request for example.com/ipfs/{CIDv1} redirects to subdomain",
`
subdomains should not return payload directly,
but redirect to URL with proper origin isolation
`,
fmt.Sprintf("%s/ipfs/%s/", gatewayURL, CIDv1),
Url("%s/ipfs/%s/", gatewayURL, CIDv1),
Expect().
Status(301).
Headers(
Expand All @@ -77,13 +86,13 @@ func TestGatewaySubdomains(t *testing.T) {
Response(),
))

with(testLocalhostGatewayResponseShouldContain(t,
with(testGatewayWithManyProtocols(t,
"request for example.com/ipfs/{DirCID} redirects to subdomain",
`
subdomains should not return payload directly,
but redirect to URL with proper origin isolation
`,
fmt.Sprintf("%s/ipfs/%s/", gatewayURL, DirCID),
Url("%s/ipfs/%s/", gatewayURL, DirCID),
Expect().
Status(301).
Headers(
Expand All @@ -93,10 +102,10 @@ func TestGatewaySubdomains(t *testing.T) {
).Response(),
))

with(testLocalhostGatewayResponseShouldContain(t,
with(testGatewayWithManyProtocols(t,
"request for example.com/ipfs/{CIDv0} redirects to CIDv1 representation in subdomain",
"",
fmt.Sprintf("%s/ipfs/%s/", gatewayURL, CIDv0),
Url("%s/ipfs/%s/", gatewayURL, CIDv0),
Expect().
Status(301).
Headers(
Expand All @@ -111,32 +120,32 @@ func TestGatewaySubdomains(t *testing.T) {

// API on localhost subdomain gateway

with(testLocalhostGatewayResponseShouldContain(t,
with(testGatewayWithManyProtocols(t,
"/api/v0 present on the root hostname",
"request for localhost/api",
fmt.Sprintf("%s/api/v0/refs?arg=%s&r=true", gatewayURL, DirCID),
Url("%s/api/v0/refs?arg=%s&r=true", gatewayURL, DirCID),
Expect().
Status(200).
Body(Contains(
"Ref",
)).Response(),
))

with(testLocalhostGatewayResponseShouldContain(t,
with(testGatewayWithManyProtocols(t,
"/api/v0 not mounted on content root subdomains",
"request for {cid}.ipfs.examle.com/api returns data if present on the content root",
fmt.Sprintf("%s://%s.ipfs.%s/api/file.txt", u.Scheme, DirCID, u.Host),
Url("%s://%s.ipfs.%s/api/file.txt", u.Scheme, DirCID, u.Host),
Expect().
Status(200).
Body(Contains(
"I am a txt file",
)).Response(),
))

with(testLocalhostGatewayResponseShouldContain(t,
with(testGatewayWithManyProtocols(t,
"request for {cid}.ipfs.localhost/api/v0/refs returns 404",
"",
fmt.Sprintf("%s://%s.ipfs.%s/api/v0/refs?arg=%s&r=true", u.Scheme, DirCID, u.Host, DirCID),
Url("%s://%s.ipfs.%s/api/v0/refs?arg=%s&r=true", u.Scheme, DirCID, u.Host, DirCID),
Expect().
Status(404).
Response(),
Expand All @@ -147,39 +156,39 @@ func TestGatewaySubdomains(t *testing.T) {
// (origin per content root at http://*.localhost)
// ============================================================================

with(testLocalhostGatewayResponseShouldContain(t,
with(testGatewayWithManyProtocols(t,
"request for {CID}.ipfs.example.com should return expected payload",
"",
fmt.Sprintf("%s://%s.ipfs.%s", u.Scheme, CIDv1, u.Host),
Url("%s://%s.ipfs.%s", u.Scheme, CIDv1, u.Host),
Expect().
Status(200).
Body(Contains(CIDVal)).
Response(),
))

with(testLocalhostGatewayResponseShouldContain(t,
with(testGatewayWithManyProtocols(t,
"request for {CID}.ipfs.example.com/ipfs/{CID} should return HTTP 404",
"ensure /ipfs/ namespace is not mounted on subdomain",
fmt.Sprintf("%s://%s.ipfs.%s/ipfs/%s", u.Scheme, CIDv1, u.Host, CIDv1),
Url("%s://%s.ipfs.%s/ipfs/%s", u.Scheme, CIDv1, u.Host, CIDv1),
Expect().
Status(404).
Response(),
))

with(testLocalhostGatewayResponseShouldContain(t,
with(testGatewayWithManyProtocols(t,
"request for {CID}.ipfs.example.com/ipfs/file.txt should return data from a file in CID content root",
"ensure requests to /ipfs/* are not blocked, if content root has such subdirectory",
fmt.Sprintf("%s://%s.ipfs.%s/ipfs/file.txt", u.Scheme, DirCID, u.Host),
Url("%s://%s.ipfs.%s/ipfs/file.txt", u.Scheme, DirCID, u.Host),
Expect().
Status(200).
Body(Contains("I am a txt file")).
Response(),
))

with(testLocalhostGatewayResponseShouldContain(t,
with(testGatewayWithManyProtocols(t,
"valid file and subdirectory paths in directory listing at {cid}.ipfs.localhost",
"{CID}.ipfs.example.com/sub/dir (Directory Listing)",
fmt.Sprintf("%s://%s.ipfs.%s/", u.Scheme, DirCID, u.Host),
Url("%s://%s.ipfs.%s/", u.Scheme, DirCID, u.Host),
Expect().
Status(200).
Body(And(
Expand All @@ -190,10 +199,10 @@ func TestGatewaySubdomains(t *testing.T) {
Response(),
))

with(testLocalhostGatewayResponseShouldContain(t,
with(testGatewayWithManyProtocols(t,
"valid parent directory path in directory listing at {cid}.ipfs.localhost/sub/dir",
"",
fmt.Sprintf("%s://%s.ipfs.%s/ipfs/ipns/", u.Scheme, DirCID, u.Host),
Url("%s://%s.ipfs.%s/ipfs/ipns/", u.Scheme, DirCID, u.Host),
Expect().
Status(200).
Body(And(
Expand All @@ -204,10 +213,10 @@ func TestGatewaySubdomains(t *testing.T) {
Response(),
))

with(testLocalhostGatewayResponseShouldContain(t,
with(testGatewayWithManyProtocols(t,
"request for deep path resource at {cid}.ipfs.localhost/sub/dir/file",
"",
fmt.Sprintf("%s://%s.ipfs.%s/ipfs/ipns/bar", u.Scheme, DirCID, u.Host),
Url("%s://%s.ipfs.%s/ipfs/ipns/bar", u.Scheme, DirCID, u.Host),
Expect().
Status(200).
Body(Contains("text-file-content")).
Expand All @@ -220,10 +229,10 @@ func TestGatewaySubdomains(t *testing.T) {

// # api.localhost/api

with(testLocalhostGatewayResponseShouldContain(t,
with(testGatewayWithManyProtocols(t,
"request for api.localhost returns API response",
"Note: we use DIR_CID so refs -r returns some CIDs for child nodes",
fmt.Sprintf("%s://api.%s/api/v0/refs?arg=%s&r=true", u.Scheme, u.Host, DirCID),
Url("%s://api.%s/api/v0/refs?arg=%s&r=true", u.Scheme, u.Host, DirCID),
Expect().
Status(200).
Body(Contains("Ref")).
Expand All @@ -247,33 +256,33 @@ func TestGatewaySubdomains(t *testing.T) {
// # path requests to the root hostname should redirect
// # to a subdomain URL with proper origin isolation

// TODO: lidel - for some reason in 114, we only test the simple case, with Host, no proxy or tunnel.
// TODO(lidel): for some reason in 114, we only test the simple case, with Host, no proxy or tunnel.
// Do we need to port `test_hostname_gateway_response_should_contain` or can we
// reuse the same "larger" test.
with(testLocalhostGatewayResponseShouldContain(t,
with(testGatewayWithManyProtocols(t,
"request for example.com/ipfs/{CIDv1} produces redirect to {CIDv1}.ipfs.example.com",
"path requests to the root hostname should redirect to a subdomain URL with proper origin isolation",
fmt.Sprintf("%s://%s/ipfs/%s/", u.Scheme, u.Host, CIDv1),
Url("%s://%s/ipfs/%s/", u.Scheme, u.Host, CIDv1),
Expect().
Headers(
Header("Location").Equals("%s://%s.ipfs.%s/", u.Scheme, CIDv1, u.Host),
).
Response(),
))

with(testLocalhostGatewayResponseShouldContain(t,
with(testGatewayWithManyProtocols(t,
"request for example.com/ipfs/{InvalidCID} produces useful error before redirect",
"error message should include original CID (and it should be case-sensitive, as we can't assume everyone uses base32)",
fmt.Sprintf("%s://%s/ipfs/QmInvalidCID", u.Scheme, u.Host),
Url("%s://%s/ipfs/QmInvalidCID", u.Scheme, u.Host),
Expect().
Body(Contains("invalid path \"/ipfs/QmInvalidCID\"")).
Response(),
))

with(testLocalhostGatewayResponseShouldContain(t,
with(testGatewayWithManyProtocols(t,
"request for example.com/ipfs/{CIDv0} produces redirect to {CIDv1}.ipfs.example.com",
"",
fmt.Sprintf("%s://%s/ipfs/%s/", u.Scheme, u.Host, CIDv0),
Url("%s://%s/ipfs/%s/", u.Scheme, u.Host, CIDv0),
Expect().
Status(301).
Headers(
Expand All @@ -282,7 +291,7 @@ func TestGatewaySubdomains(t *testing.T) {
Response(),
))

with(testLocalhostGatewayResponseShouldContain(t,
with(testGatewayWithManyProtocols(t,
"request for http://example.com/ipfs/{CID} with X-Forwarded-Proto: https produces redirect to HTTPS URL",
"Support X-Forwarded-Proto",
Request().
Expand All @@ -303,7 +312,7 @@ func TestGatewaySubdomains(t *testing.T) {
Run(t, tests)
}

func testLocalhostGatewayResponseShouldContain(t *testing.T, label string, hint string, reqUrl interface{}, expected CResponse) []CTest {
func testGatewayWithManyProtocols(t *testing.T, label string, hint string, reqUrl interface{}, expected CResponse) []CTest {
t.Helper()

baseUrl := ""
Expand All @@ -314,9 +323,7 @@ func testLocalhostGatewayResponseShouldContain(t *testing.T, label string, hint
baseUrl = reqUrl.(string)
case RequestBuilder:
baseReq = req
// TODO: we use the Request builder to easily move URL and headers around and make them reusable.
// This probably deserves a better API that throws if you passed a URL with just a path (we don't resolve relative URLs in the builder).
baseUrl = req.URL_
baseUrl = req.GetURL()
default:
t.Fatalf("invalid type for reqUrl: %T", reqUrl)
}
Expand All @@ -325,14 +332,18 @@ func testLocalhostGatewayResponseShouldContain(t *testing.T, label string, hint
if err != nil {
t.Fatal(err)
}

// Careful: host and hostname are reversed in the sharness t0114 test.
// host := u.Host // not used, because we don't need the proxy.
// Because you might be testing an IPFS node in CI, or on your local machine, the test are designed
// to test the subdomain behavior (querying http://{CID}.my-subdomain-gateway.io/) even if the node is
// actually living on http://127.0.0.1:8080 or somewhere else.
//
// The test knows two addresses:
// - GatewayUrl: the url we connect to, it might be "dweb.link", "127.0.0.1:8080", etc.
// - SubdomainGatewayUrl: the url we test for subdomain requests, it might be "dweb.link", "localhost", "example.com", etc.

// host is the hostname of the gateway we are testing, it might be `localhost` or `example.com`
host := u.Host

// proxy is the base url we use in the test suite

// raw url is the url but we replace the host with our local url
// raw url is the url but we replace the host with our local url, it might be `http://127.0.0.1/ipfs/something`
u.Host = GatewayHost
rawUrl := u.String()

Expand All @@ -358,8 +369,9 @@ func testLocalhostGatewayResponseShouldContain(t *testing.T, label string, hint
DoNotFollowRedirects().
Request(),
Response: expected,
// TODO(lidel): do we need to test different http behavior ?
// The previous test was using 2 proxy (--proxy and --proxy1.0)
},
// TODO: port the proxy1.0 as well (use http 1) but also http2?
{
Name: fmt.Sprintf("%s (HTTP proxy tunneling via CONNECT)", label),
Hint: fmt.Sprintf("%s\n%s", hint, `HTTP proxy
Expand Down
8 changes: 8 additions & 0 deletions tooling/test/sugar.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,14 @@ func (r RequestBuilder) URL(path string, args ...any) RequestBuilder {
return r
}

func (r RequestBuilder) GetURL() string {
if r.Path_ != "" {
panic("not implemented")
}

return r.URL_
}

func (r RequestBuilder) Proxy(path string, args ...any) RequestBuilder {
r.Proxy_ = fmt.Sprintf(path, args...)
return r
Expand Down

0 comments on commit bf819f6

Please sign in to comment.