Skip to content

Commit

Permalink
test: add handle-redirects tests
Browse files Browse the repository at this point in the history
  • Loading branch information
SgtPooki committed May 24, 2024
1 parent 967a4e3 commit 2be475c
Show file tree
Hide file tree
Showing 2 changed files with 95 additions and 2 deletions.
13 changes: 11 additions & 2 deletions packages/verified-fetch/src/utils/handle-redirects.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ interface GetRedirectResponse {
options?: Omit<VerifiedFetchInit, 'signal'> & AbortOptions
logger: ComponentLogger

/**
* Only used in testing.
*/
fetch?: typeof globalThis.fetch
}

function maybeAddTraillingSlash (path: string): string {
Expand All @@ -21,7 +25,7 @@ function maybeAddTraillingSlash (path: string): string {
}

// See https://specs.ipfs.tech/http-gateways/path-gateway/#location-response-header
export async function getRedirectResponse ({ resource, options, logger, cid }: GetRedirectResponse): Promise<null | Response> {
export async function getRedirectResponse ({ resource, options, logger, cid, fetch = globalThis.fetch }: GetRedirectResponse): Promise<null | Response> {
const log = logger.forComponent('helia:verified-fetch:get-redirect-response')

if (typeof resource !== 'string' || options == null || ['ipfs://', 'ipns://'].some((prefix) => resource.startsWith(prefix))) {
Expand Down Expand Up @@ -81,7 +85,12 @@ export async function getRedirectResponse ({ resource, options, logger, cid }: G
throw new Error('subdomain not supported')
}
} catch (err: any) {
log('subdomain not supported, redirecting to path', err)
log('subdomain not supported', err)
if (pathUrl.href === reqUrl.href) {
log('path url is the same as the request url, not setting location header')
return null
}
// pathUrl is different from request URL (maybe even with just a trailing slash)
return movedPermanentlyResponse(resource.toString(), pathUrl.href)
}
} catch (e) {
Expand Down
84 changes: 84 additions & 0 deletions packages/verified-fetch/test/utils/handle-redirects.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import { prefixLogger } from '@libp2p/logger'
import { expect } from 'aegir/chai'
import { CID } from 'multiformats/cid'
import Sinon from 'sinon'
import { getRedirectResponse } from '../../src/utils/handle-redirects.js'

const logger = prefixLogger('test:handle-redirects')
describe('handle-redirects', () => {
describe('getRedirectResponse', () => {
const sandbox = Sinon.createSandbox()
const cid = CID.parse('bafkqabtimvwgy3yk')

let fetchStub: Sinon.SinonStub

beforeEach(() => {
fetchStub = sandbox.stub(globalThis, 'fetch')
})

afterEach(() => {
sandbox.restore()
})

const nullResponses = [
{ resource: cid, options: {}, logger, cid, testTitle: 'should return null if resource is not a string' },
{ resource: 'http://ipfs.io/ipfs/bafkqabtimvwgy3yk', options: undefined, logger, cid, testTitle: 'should return null if options is undefined' },
{ resource: 'ipfs://', options: {}, logger, cid, testTitle: 'should return null for ipfs:// protocol urls' },
{ resource: 'ipns://', options: {}, logger, cid, testTitle: 'should return null for ipns:// protocol urls' }
]

nullResponses.forEach(({ resource, options, logger, cid, testTitle }) => {
it(testTitle, async () => {
const response = await getRedirectResponse({ resource, options, logger, cid })
expect(response).to.be.null()
})
})

it('should attempt to get the current host from the headers', async () => {
const resource = 'http://ipfs.io/ipfs/bafkqabtimvwgy3yk'
const options = { headers: new Headers({ 'x-forwarded-host': 'localhost:3931' }) }
fetchStub.returns(Promise.resolve(new Response(null, { status: 200 })))

const response = await getRedirectResponse({ resource, options, logger, cid, fetch: fetchStub })
expect(fetchStub.calledOnce).to.be.true()
expect(response).to.not.be.null()
expect(response).to.have.property('status', 301)
const location = response?.headers.get('location')
expect(location).to.equal('http://bafkqabtimvwgy3yk.ipfs.localhost:3931/')
})

it('should return redirect response to requested host with trailing slash when HEAD fetch fails', async () => {
const resource = 'http://ipfs.io/ipfs/bafkqabtimvwgy3yk'
const options = { headers: new Headers({ 'x-forwarded-host': 'localhost:3931' }) }
fetchStub.returns(Promise.reject(new Response(null, { status: 404 })))

const response = await getRedirectResponse({ resource, options, logger, cid, fetch: fetchStub })
expect(fetchStub.calledOnce).to.be.true()
expect(response).to.not.be.null()
expect(response).to.have.property('status', 301)
const location = response?.headers.get('location')
// note that the URL returned in location header has trailing slash.
expect(location).to.equal('http://ipfs.io/ipfs/bafkqabtimvwgy3yk/')
})

it('should not return redirect response to x-forwarded-host if HEAD fetch fails', async () => {
const resource = 'http://ipfs.io/ipfs/bafkqabtimvwgy3yk/file.txt'
const options = { headers: new Headers({ 'x-forwarded-host': 'localhost:3931' }) }
fetchStub.returns(Promise.reject(new Response(null, { status: 404 })))

const response = await getRedirectResponse({ resource, options, logger, cid, fetch: fetchStub })
expect(fetchStub.calledOnce).to.be.true()
expect(response).to.be.null()
})

it('should not return redirect response to x-forwarded-host when HEAD fetch fails and trailing slash already exists', async () => {
const resource = 'http://ipfs.io/ipfs/bafkqabtimvwgy3yk/'
const options = { headers: new Headers({ 'x-forwarded-host': 'localhost:3931' }) }
fetchStub.returns(Promise.reject(new Response(null, { status: 404 })))

const response = await getRedirectResponse({ resource, options, logger, cid, fetch: fetchStub })
expect(fetchStub.calledOnce).to.be.true()
expect(response).to.be.null()
})
})
})

0 comments on commit 2be475c

Please sign in to comment.