Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
34 changes: 22 additions & 12 deletions src/handlers/unixfs-dir.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,19 +50,9 @@ export async function handleUnixfsDir (request, env, ctx) {
if (!entry.type.includes('directory')) throw new Error('non UnixFS directory entry')
if (!unixfs) throw new Error('missing UnixFS service')

// serve index.html if directory contains one
try {
const indexPath = `${dataCid}${path}${path.endsWith('/') ? '' : '/'}index.html`
const fileEntry = await unixfs.getUnixfs(indexPath, { signal: controller?.signal })
ctx.unixfsEntry = fileEntry
return handleUnixfsFile(request, env, ctx)
} catch (/** @type {any} */ err) {
if (err.code !== 'ERR_NOT_FOUND') throw err
}

const headers = {
'Content-Type': 'text/html',
Etag: `"DirIndex-gateway-lib@2.0.2_CID-${entry.cid}"`
Etag: `"DirIndex-gateway-lib@2.0.3_CID-${entry.cid}"`
}

if (request.method === 'HEAD') {
Expand All @@ -72,7 +62,27 @@ export async function handleUnixfsDir (request, env, ctx) {
throw new HttpError('method not allowed', { status: 405 })
}

const isSubdomain = new URL(request.url).hostname.includes('.ipfs.')
const url = new URL(request.url)

// redirect directory missing trailing slash
if (!url.pathname.endsWith('/')) {
return new Response(null, {
status: 301,
headers: { Location: `${url.pathname}/${url.search}` }
})
}

// serve index.html if directory contains one
try {
const indexPath = `${dataCid}${path}${path.endsWith('/') ? '' : '/'}index.html`
const fileEntry = await unixfs.getUnixfs(indexPath, { signal: controller?.signal })
ctx.unixfsEntry = fileEntry
return handleUnixfsFile(request, env, ctx)
} catch (/** @type {any} */ err) {
if (err.code !== 'ERR_NOT_FOUND') throw err
}

const isSubdomain = url.hostname.includes('.ipfs.')
/** @param {string} p CID path like "<cid>[/optional/path]" */
const entryPath = p => isSubdomain ? p.split('/').slice(1).join('/') : `/ipfs/${p}`

Expand Down
27 changes: 26 additions & 1 deletion test/handlers/unixfs-dir.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,34 @@ describe('UnixFS directory handler', () => {
const dagula = new Dagula(blockstore)
const ctx = { waitUntil, unixfs: dagula, dataCid: dirBlock.cid, path, searchParams }
const env = { DEBUG: 'true' }
const req = new Request('http://localhost/ipfs/bafy')
const req = new Request('http://localhost/ipfs/bafy/')
const res = await handleUnixfs(req, env, ctx)
const html = await res.text()
assert(html.includes('Puzzle%20People%20%231.png'))
})

it('redirects missing trailing slash', async () => {
const waitUntil = mockWaitUntil()
const path = '/test'
const searchParams = new URLSearchParams()
const fileBlock = await encode({ value: fromString('test'), codec: raw, hasher })
const dirData = pb.createNode(new UnixFS({ type: 'directory' }).marshal(), [{
Name: 'Puzzle People #1.png',
Hash: fileBlock.cid
}])
const dirBlock = await encode({ value: dirData, codec: pb, hasher })
const rootDirData = pb.createNode(new UnixFS({ type: 'directory' }).marshal(), [{
Name: 'test',
Hash: dirBlock.cid
}])
const rootDirBlock = await encode({ value: rootDirData, codec: pb, hasher })
const blockstore = mockBlockstore([rootDirBlock, dirBlock, fileBlock])
const dagula = new Dagula(blockstore)
const ctx = { waitUntil, unixfs: dagula, dataCid: rootDirBlock.cid, path, searchParams }
const env = { DEBUG: 'true' }
const req = new Request('http://localhost/ipfs/bafy/test')
const res = await handleUnixfs(req, env, ctx)
assert.equal(res.status, 301)
assert.equal(res.headers.get('location'), '/ipfs/bafy/test/')
})
})