Skip to content

Commit

Permalink
fix: unixfs dir redirect (#33)
Browse files Browse the repository at this point in the history
* fix: check redirect trailing slash if path is empty

* test: add interop test for unixfs directory redirect

* chore: add gateway-conformance-fixtures car file

* test: add verified-fetch unit test for root directory redirect
  • Loading branch information
SgtPooki authored Mar 25, 2024
1 parent 4589c26 commit 32ca87f
Show file tree
Hide file tree
Showing 4 changed files with 48 additions and 1 deletion.
Binary file not shown.
19 changes: 19 additions & 0 deletions packages/interop/src/unixfs-dir.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,25 @@ describe('@helia/verified-fetch - unixfs directory', () => {
await verifiedFetch.stop()
})

describe('unixfs-dir-redirect', () => {
before(async () => {
await loadFixtureDataCar(controller, 'gateway-conformance-fixtures.car')
});

[
'https://example.com/ipfs/bafybeifq2rzpqnqrsdupncmkmhs3ckxxjhuvdcbvydkgvch3ms24k5lo7q',
'ipfs://bafybeifq2rzpqnqrsdupncmkmhs3ckxxjhuvdcbvydkgvch3ms24k5lo7q',
'http://example.com/ipfs/bafybeifq2rzpqnqrsdupncmkmhs3ckxxjhuvdcbvydkgvch3ms24k5lo7q'
].forEach((url: string) => {
it(`request to unixfs directory with ${url} should return a 301 with a trailing slash`, async () => {
const response = await verifiedFetch(url, { redirect: 'manual' })
expect(response).to.be.ok()
expect(response.status).to.equal(301)
expect(response.headers.get('location')).to.equal(`${url}/`)
})
})
})

describe('XKCD Barrel Part 1', () => {
before(async () => {
// This is the content of https://explore.ipld.io/#/explore/QmdmQXB2mzChmMeKY47C43LxUdg1NDJ5MWcKMKxDu7RgQm/1%20-%20Barrel%20-%20Part%201
Expand Down
3 changes: 2 additions & 1 deletion packages/verified-fetch/src/verified-fetch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -299,9 +299,10 @@ export class VerifiedFetch {
let resolvedCID = terminalElement?.cid ?? cid
if (terminalElement?.type === 'directory') {
const dirCid = terminalElement.cid
const redirectCheckNeeded = path === '' ? !resource.toString().endsWith('/') : !path.endsWith('/')

// https://specs.ipfs.tech/http-gateways/path-gateway/#use-in-directory-url-normalization
if (path !== '' && !path.endsWith('/')) {
if (redirectCheckNeeded) {
if (options?.redirect === 'error') {
this.log('could not redirect to %s/ as redirect option was set to "error"', resource)
throw new TypeError('Failed to fetch')
Expand Down
27 changes: 27 additions & 0 deletions packages/verified-fetch/test/verified-fetch.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,33 @@ describe('@helia/verifed-fetch', () => {
expect(ipfsResponse.url).to.equal(`ipfs://${res.cid}/foo`)
})

it('should return a 301 with a trailing slash when a root directory is requested without a trailing slash', async () => {
const finalRootFileContent = new Uint8Array([0x01, 0x02, 0x03])

const fs = unixfs(helia)
const res = await last(fs.addAll([{
path: '/index.html',
content: finalRootFileContent
}], {
wrapWithDirectory: true
}))

if (res == null) {
throw new Error('Import failed')
}

const stat = await fs.stat(res.cid)
expect(stat.type).to.equal('directory')

const ipfsResponse = await verifiedFetch.fetch(`https://ipfs.local/ipfs/${res.cid}`, {
redirect: 'manual'
})
expect(ipfsResponse).to.be.ok()
expect(ipfsResponse.status).to.equal(301)
expect(ipfsResponse.headers.get('location')).to.equal(`https://ipfs.local/ipfs/${res.cid}/`)
expect(ipfsResponse.url).to.equal(`https://ipfs.local/ipfs/${res.cid}`)
})

it('should return a 301 with a trailing slash when a gateway directory is requested without a trailing slash', async () => {
const finalRootFileContent = new Uint8Array([0x01, 0x02, 0x03])

Expand Down

0 comments on commit 32ca87f

Please sign in to comment.