Skip to content

Commit

Permalink
fix: append query path to path resolved from ipns name
Browse files Browse the repository at this point in the history
If an IPNS name resolves to a CID+path, and the requested URL also
has a path, the resolved path should be the path from the IPNS
record joined with the path from the requested URL, not the other
way round.
  • Loading branch information
achingbrain committed Feb 29, 2024
1 parent a584a1a commit 4d0ffa6
Show file tree
Hide file tree
Showing 3 changed files with 190 additions and 3 deletions.
1 change: 1 addition & 0 deletions packages/verified-fetch/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@
"@helia/json": "^3.0.1",
"@helia/utils": "^0.0.2",
"@ipld/car": "^5.3.0",
"@libp2p/interface-compliance-tests": "^5.3.2",
"@libp2p/logger": "^4.0.7",
"@libp2p/peer-id-factory": "^4.0.7",
"@sgtpooki/file-type": "^1.0.1",
Expand Down
7 changes: 4 additions & 3 deletions packages/verified-fetch/src/utils/parse-url-string.ts
Original file line number Diff line number Diff line change
Expand Up @@ -136,13 +136,14 @@ export async function parseUrlString ({ urlString, ipns, logger }: ParseUrlStrin
*/
const pathParts = []

if (resolvedPath != null && resolvedPath.length > 0) {
pathParts.push(resolvedPath)
}

if (urlPath.length > 0) {
pathParts.push(urlPath)
}

if (resolvedPath != null && resolvedPath.length > 0) {
pathParts.push(resolvedPath)
}
const path = pathParts.join('/')

return {
Expand Down
185 changes: 185 additions & 0 deletions packages/verified-fetch/test/utils/parse-url-string.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
import { matchPeerId } from '@libp2p/interface-compliance-tests/matchers'
import { defaultLogger } from '@libp2p/logger'
import { createEd25519PeerId } from '@libp2p/peer-id-factory'
import { expect } from 'aegir/chai'
import { CID } from 'multiformats/cid'
import { stubInterface } from 'sinon-ts'
import { parseUrlString } from '../../src/utils/parse-url-string.js'
import type { IPNS } from '@helia/ipns'
import type { ComponentLogger } from '@libp2p/interface'
import type { StubbedInstance } from 'sinon-ts'

describe('parse-url-string', () => {
let logger: ComponentLogger
let ipns: StubbedInstance<IPNS>

beforeEach(() => {
logger = defaultLogger()
ipns = stubInterface<IPNS>()
})

it('should parse an ipfs:// url', async () => {
const cid = CID.parse('QmQJ8fxavY54CUsxMSx9aE9Rdcmvhx8awJK2jzJp4iAqCr')

await expect(parseUrlString({
urlString: `ipfs://${cid}`,
ipns,
logger
})).to.eventually.deep.equal({
protocol: 'ipfs',
path: '',
cid,
query: {}
})
})

it('should parse an ipfs:// url with a path', async () => {
const cid = CID.parse('QmQJ8fxavY54CUsxMSx9aE9Rdcmvhx8awJK2jzJp4iAqCr')
const path = 'foo/bar/baz.txt'

await expect(parseUrlString({
urlString: `ipfs://${cid}/${path}`,
ipns,
logger
})).to.eventually.deep.equal({
protocol: 'ipfs',
path,
cid,
query: {}
})
})

it('should parse an ipfs:// url with a path and a query', async () => {
const cid = CID.parse('QmQJ8fxavY54CUsxMSx9aE9Rdcmvhx8awJK2jzJp4iAqCr')
const path = 'foo/bar/baz.txt'

await expect(parseUrlString({
urlString: `ipfs://${cid}/${path}?format=car&download=true&filename=qux.zip`,
ipns,
logger
})).to.eventually.deep.equal({
protocol: 'ipfs',
path,
cid,
query: {
format: 'car',
download: true,
filename: 'qux.zip'
}
})
})

it('should parse an ipns:// url', async () => {
const cid = CID.parse('QmQJ8fxavY54CUsxMSx9aE9Rdcmvhx8awJK2jzJp4iAqCr')
const peerId = await createEd25519PeerId()

ipns.resolve.withArgs(matchPeerId(peerId)).resolves({
cid,
path: ''
})

await expect(parseUrlString({
urlString: `ipns://${peerId}`,
ipns,
logger
})).to.eventually.deep.equal({
protocol: 'ipns',
path: '',
cid,
query: {}
})
})

it('should parse an ipns:// url with a path', async () => {
const cid = CID.parse('QmQJ8fxavY54CUsxMSx9aE9Rdcmvhx8awJK2jzJp4iAqCr')
const peerId = await createEd25519PeerId()
const path = 'foo/bar/baz.txt'

ipns.resolve.withArgs(matchPeerId(peerId)).resolves({
cid,
path: ''
})

await expect(parseUrlString({
urlString: `ipns://${peerId}/${path}`,
ipns,
logger
})).to.eventually.deep.equal({
protocol: 'ipns',
path,
cid,
query: {}
})
})

it('should parse an ipns:// url that resolves to a path', async () => {
const cid = CID.parse('QmQJ8fxavY54CUsxMSx9aE9Rdcmvhx8awJK2jzJp4iAqCr')
const peerId = await createEd25519PeerId()
const path = 'foo/bar/baz.txt'

ipns.resolve.withArgs(matchPeerId(peerId)).resolves({
cid,
path
})

await expect(parseUrlString({
urlString: `ipns://${peerId}`,
ipns,
logger
})).to.eventually.deep.equal({
protocol: 'ipns',
path,
cid,
query: {}
})
})

it('should parse an ipns:// url with a path that resolves to a path', async () => {
const cid = CID.parse('QmQJ8fxavY54CUsxMSx9aE9Rdcmvhx8awJK2jzJp4iAqCr')
const peerId = await createEd25519PeerId()
const recordPath = 'foo'
const requestPath = 'bar/baz.txt'

ipns.resolve.withArgs(matchPeerId(peerId)).resolves({
cid,
path: recordPath
})

await expect(parseUrlString({
urlString: `ipns://${peerId}/${requestPath}`,
ipns,
logger
})).to.eventually.deep.equal({
protocol: 'ipns',
path: `${recordPath}/${requestPath}`,
cid,
query: {}
})
})

it('should parse an ipns:// url with a path and a query', async () => {
const cid = CID.parse('QmQJ8fxavY54CUsxMSx9aE9Rdcmvhx8awJK2jzJp4iAqCr')
const peerId = await createEd25519PeerId()
const path = 'foo/bar/baz.txt'

ipns.resolve.withArgs(matchPeerId(peerId)).resolves({
cid,
path
})

await expect(parseUrlString({
urlString: `ipns://${peerId}?format=car&download=true&filename=qux.zip`,
ipns,
logger
})).to.eventually.deep.equal({
protocol: 'ipns',
path,
cid,
query: {
format: 'car',
download: true,
filename: 'qux.zip'
}
})
})
})

0 comments on commit 4d0ffa6

Please sign in to comment.