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

feat: add configurable redirect #142

Merged
merged 2 commits into from
Mar 7, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
1 change: 1 addition & 0 deletions packages/edge-gateway/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
wrangler secret put LOKI_URL --env $(whoami) # Get from Loki
wrangler secret put LOKI_TOKEN --env $(whoami) # Get from Loki
wrangler secret put CDN_GATEWAYS_RACE --env $(whoami) # JSON String with array of CDN Gateways URLs (eg. echo -e '["https://freeway.dag.haus"]' | wrangler secret ...)
wrangler secret put IPFS_GATEWAY_REDIRECT_HOSTNAME --env $(whoami) # string with domain to redirect if not possible to resolve within CDN (eg. echo -e 'dweb.link' | wrangler secret ...)
wrangler secret put IPFS_GATEWAYS_RACE_L1 --env $(whoami) # JSON String with array of IPFS Gateways URLs (eg. echo -e '["https://ipfs.io","https://dagula.dag.haus"]' | wrangler secret ...)
wrangler secret put IPFS_GATEWAYS_RACE_L2 --env $(whoami) # JSON String with array of IPFS Gateways URLs (eg. echo -e '["https://cf.dag.haus","https://w3link.mypinata.cloud"]' | wrangler secret ...)
wrangler secret put CID_VERIFIER_AUTHORIZATION_TOKEN --env $(whoami) # Get from 1Password
Expand Down
2 changes: 2 additions & 0 deletions packages/edge-gateway/src/bindings.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export interface EnvInput {
CDN_GATEWAYS_RACE: string
DENYLIST: Fetcher
DENYLIST_URL: string
IPFS_GATEWAY_REDIRECT_HOSTNAME?: string
IPFS_GATEWAYS_RACE_L1: string
IPFS_GATEWAYS_RACE_L2: string
GATEWAY_HOSTNAME: string
Expand All @@ -48,6 +49,7 @@ export interface EnvTransformed {
IPFS_GATEWAY_HOSTNAME: string
IPNS_GATEWAY_HOSTNAME: string
cdnGateways: Array<string>
ipfsGatewayRedirectHostname?: string
ipfsGatewaysL1: Array<string>
ipfsGatewaysL2: Array<string>
sentry?: Toucan
Expand Down
3 changes: 3 additions & 0 deletions packages/edge-gateway/src/env.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ export function envAll (request, env, ctx) {
// Set CDN Gateways
env.cdnGateways = parseGatewayUrls(env.CDN_GATEWAYS_RACE, DEFAULT_CDN_GATEWAYS, env)

// Set public IPFS gateway redirect URL if configured
env.ipfsGatewayRedirectHostname = env.IPFS_GATEWAY_REDIRECT_HOSTNAME

// Set Layer 1 racer
env.ipfsGatewaysL1 = parseGatewayUrls(env.IPFS_GATEWAYS_RACE_L1, DEFAULT_RACE_L1_GATEWAYS, env)
env.gwRacerL1 = createGatewayRacer(env.ipfsGatewaysL1, {
Expand Down
11 changes: 10 additions & 1 deletion packages/edge-gateway/src/gateway.js
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,16 @@ export async function gatewayGet (request, env, ctx) {
)
}

// 3rd layer resolution - Public Gateways race
// 3rd layer resolution - Redirect to Public Gateway if set
if (env.ipfsGatewayRedirectHostname) {
const url = new URL(
`https://${cid}.ipfs.${env.ipfsGatewayRedirectHostname}${pathname}${search}`
)

return Response.redirect(url.toString(), 303)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[303] indicates that the redirects don't link to the requested resource itself, but to another page (such as a confirmation page, a representation of a real-world object — see HTTP range-14 — or an upload-progress page). This response code is often sent back as a result of PUT or POST. The method used to display this redirected page is always GET.

Maybe we want https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/307 instead?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@olizilla 's proposal was 303 https://hackmd.io/@olizilla/w3s-redirect . So, would like to get input if there was something that made 303 the suggestion.

For the documentation, I agree that 307 seems more appropriate

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yep, agree, 307 sounds closer to what we want. I didn't realise that "303 See other" implied we were linking to a different resource.

}

// 4th layer resolution - Public Gateways race
const {
response: winnerGwResponse,
url: winnerUrl,
Expand Down
3 changes: 2 additions & 1 deletion packages/edge-gateway/src/version.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export async function versionGet (request, env) {
commit: env.COMMITHASH,
branch: env.BRANCH,
raceGatewaysL1: env.ipfsGatewaysL1,
raceGatewaysL2: env.ipfsGatewaysL2
raceGatewaysL2: env.ipfsGatewaysL2,
ipfsGatewayRedirectHostname: env.ipfsGatewayRedirectHostname
})
}
2 changes: 1 addition & 1 deletion packages/edge-gateway/test/mocks/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@
"name": "mocks",
"version": "1.0.0",
"description": "just here to fix cjs loading"
}
}
83 changes: 83 additions & 0 deletions packages/edge-gateway/test/redirect.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import { test, getMiniflare } from './utils/setup.js'

const IPFS_GATEWAY_REDIRECT_HOSTNAME = 'dweb.link'

// Create a new Miniflare environment for each test
test.before((t) => {
t.context = {
mf: getMiniflare({
IPFS_GATEWAY_REDIRECT_HOSTNAME
})
}
})

test('Redirects if redirect hostname set', async (t) => {
const { mf } = t.context
const url = 'https://bafkreihl44bu5rqxctfvl3ahcln7gnjgmjqi7v5wfwojqwriqnq7wo4n7u.ipfs.localhost:8787/'
const response = await mf.dispatchFetch(url)

t.is(response.status, 303)
t.is(
response.headers.get('location'),
url.replace('localhost:8787', IPFS_GATEWAY_REDIRECT_HOSTNAME)
)
})

test('Redirects if redirect hostname set with pathname', async (t) => {
const { mf } = t.context
const url = 'https://bafkreihl44bu5rqxctfvl3ahcln7gnjgmjqi7v5wfwojqwriqnq7wo4n7u.ipfs.localhost:8787/file.txt'
const response = await mf.dispatchFetch(url)

t.is(response.status, 303)
t.is(
response.headers.get('location'),
url.replace('localhost:8787', IPFS_GATEWAY_REDIRECT_HOSTNAME)
)
})

test('Redirects if redirect hostname set with query', async (t) => {
const { mf } = t.context
const url = 'https://bafkreihl44bu5rqxctfvl3ahcln7gnjgmjqi7v5wfwojqwriqnq7wo4n7u.ipfs.localhost:8787/?format=car'
const response = await mf.dispatchFetch(url)

t.is(response.status, 303)
t.is(
response.headers.get('location'),
url.replace('localhost:8787', IPFS_GATEWAY_REDIRECT_HOSTNAME)
)
})

test('Redirects if redirect hostname set with pathname and query', async (t) => {
const { mf } = t.context
const url = 'https://bafkreihl44bu5rqxctfvl3ahcln7gnjgmjqi7v5wfwojqwriqnq7wo4n7u.ipfs.localhost:8787/file.txt?format=car'
const response = await mf.dispatchFetch(url)

t.is(response.status, 303)
t.is(
response.headers.get('location'),
url.replace('localhost:8787', IPFS_GATEWAY_REDIRECT_HOSTNAME)
)
})

test('should get from cdn gateways race if they can resolve before redirecting', async (t) => {
const url =
'https://bafkreihl44bu5rqxctfvl3ahcln7gnjgmjqi7v5wfwojqwriqnq7wo4n7u.ipfs.localhost:8787'

const mf = getMiniflare({
CDN_GATEWAYS_RACE: '["http://localhost:9082"]',
IPFS_GATEWAY_REDIRECT_HOSTNAME
})

const response = await mf.dispatchFetch(
url
)
await response.waitUntil()
t.is(await response.text(), 'Hello dot.storage! 😎')

// Validate content headers
t.is(response.headers.get('content-length'), '23')

// Validate x-dotstorage headers
t.is(response.headers.get('x-dotstorage-resolution-layer'), 'dotstorage-race')
t.is(response.headers.get('x-dotstorage-resolution-id'), 'http://localhost:9082')
})