Skip to content

Commit 58a212d

Browse files
NikolasHaimerlNikolas HaimerlbajtosNikolas Haimerljuliangruber
authored
feat: support of http-path multiaddresses (#116)
* add poc * support http-path * add tests for http-path * add test for empty http-path * Update test/multiaddr.test.js Co-authored-by: Miroslav Bajtoš <[email protected]> * chore: fix test formatting * fmt * fmt * scheme * Update lib/multiaddr.js Co-authored-by: Julian Gruber <[email protected]> * Update lib/multiaddr.js Co-authored-by: Julian Gruber <[email protected]> * Update lib/multiaddr.js Co-authored-by: Julian Gruber <[email protected]> * Update lib/multiaddr.js Co-authored-by: Julian Gruber <[email protected]> * fmt * lint * test naming * Update lib/multiaddr.js Co-authored-by: Julian Gruber <[email protected]> * Update lib/multiaddr.js Co-authored-by: Julian Gruber <[email protected]> * Update test/spark.js Co-authored-by: Julian Gruber <[email protected]> --------- Co-authored-by: Nikolas Haimerl <[email protected]> Co-authored-by: Miroslav Bajtoš <[email protected]> Co-authored-by: Nikolas Haimerl <[email protected]> Co-authored-by: Julian Gruber <[email protected]>
1 parent c11b261 commit 58a212d

File tree

4 files changed

+53
-10
lines changed

4 files changed

+53
-10
lines changed

lib/multiaddr.js

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,31 @@
33
* @returns {string} Parsed URI, e.g. `http://127.0.0.1:80`
44
*/
55
export function multiaddrToHttpUrl (addr) {
6-
const [, hostType, hostValue, ipProtocol, port, scheme, ...rest] = addr.split('/')
6+
const [multiAddr, httpPathMultiAddr] = addr.split('/http-path')
7+
const [, hostType, hostValue, ...multiAddrParts] = multiAddr.split('/')
8+
let scheme, path, rest, port
9+
if (addr.includes('/http-path')) {
10+
[scheme, ...rest] = multiAddrParts
11+
try {
12+
// Remove leading slash and parse URI-encoded path
13+
// See https://github.com/multiformats/multiaddr/blob/cab92e8e6da2e70c5f1b8aa59976e71e6922b392/protocols/http-path.md#usage
14+
path = decodeURIComponent(httpPathMultiAddr.substring(1))
15+
} catch (err) {
16+
throw Object.assign(
17+
new Error(`Cannot parse "${addr}": unsupported http path`, { cause: err }),
18+
{ code: 'INVALID_HTTP_PATH' }
19+
)
20+
}
21+
} else {
22+
let ipProtocol
23+
;[ipProtocol, port, scheme, ...rest] = multiAddrParts
724

8-
if (ipProtocol !== 'tcp') {
9-
throw Object.assign(
10-
new Error(`Cannot parse "${addr}": unsupported protocol "${ipProtocol}"`),
11-
{ code: 'UNSUPPORTED_MULTIADDR_PROTO' }
12-
)
25+
if (ipProtocol !== 'tcp') {
26+
throw Object.assign(
27+
new Error(`Cannot parse "${addr}": unsupported protocol "${ipProtocol}"`),
28+
{ code: 'UNSUPPORTED_MULTIADDR_PROTO' }
29+
)
30+
}
1331
}
1432

1533
if (scheme !== 'http' && scheme !== 'https') {
@@ -26,7 +44,10 @@ export function multiaddrToHttpUrl (addr) {
2644
)
2745
}
2846

29-
return `${scheme}://${getUriHost(hostType, hostValue)}${getUriPort(scheme, port)}`
47+
let url = `${scheme}://${getUriHost(hostType, hostValue)}`
48+
if (port) url += getUriPort(scheme, port)
49+
if (path) url += path
50+
return url
3051
}
3152

3253
function getUriHost (hostType, hostValue) {

lib/spark.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -341,6 +341,8 @@ function mapErrorToStatusCode (err) {
341341
return 703
342342
case 'MULTIADDR_HAS_TOO_MANY_PARTS':
343343
return 704
344+
case 'INVALID_HTTP_PATH':
345+
return 705
344346
}
345347

346348
// 9xx for content verification errors

test/multiaddr.test.js

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,10 @@ const HAPPY_CASES = [
99
['/ip4/127.0.0.1/tcp/8080/https', 'https://127.0.0.1:8080'],
1010
['/dns/meridian.space/tcp/8080/http', 'http://meridian.space:8080'],
1111
['/dns4/meridian.space/tcp/8080/http', 'http://meridian.space:8080'],
12-
['/dns6/meridian.space/tcp/8080/http', 'http://meridian.space:8080']
12+
['/dns6/meridian.space/tcp/8080/http', 'http://meridian.space:8080'],
13+
['/dns/meridian.space/https/http-path/%2Fipni-provider%2FproviderID', 'https://meridian.space/ipni-provider/providerID'],
14+
['/dns/meridian.space/https/http-path/', 'https://meridian.space'],
15+
['/dns/meridian.space/https/http-path', 'https://meridian.space']
1316
]
1417

1518
for (const [multiaddr, expectedUri] of HAPPY_CASES) {
@@ -22,7 +25,10 @@ for (const [multiaddr, expectedUri] of HAPPY_CASES) {
2225
const ERROR_CASES = [
2326
['/ip4/127.0.0.1/tcp/80', 'Cannot parse "/ip4/127.0.0.1/tcp/80": unsupported scheme "undefined"'],
2427
['/ip4/127.0.0.1/udp/90', 'Cannot parse "/ip4/127.0.0.1/udp/90": unsupported protocol "udp"'],
25-
['/ip4/127.0.0.1/tcp/8080/http/p2p/pubkey', 'Cannot parse "/ip4/127.0.0.1/tcp/8080/http/p2p/pubkey": too many parts']
28+
['/ip4/127.0.0.1/tcp/8080/http/p2p/pubkey', 'Cannot parse "/ip4/127.0.0.1/tcp/8080/http/p2p/pubkey": too many parts'],
29+
// NOTE: This is a valid multiaddr value that we decided to not support yet.
30+
['/dns/meridian.space/tcp/8080/http/http-path/%2Fipni-provider%2FproviderID', 'Cannot parse "/dns/meridian.space/tcp/8080/http/http-path/%2Fipni-provider%2FproviderID": unsupported scheme "tcp"'],
31+
['/dns/meridian.space/http/http-path/invalid%path', 'Cannot parse "/dns/meridian.space/http/http-path/invalid%path": unsupported http path']
2632
]
2733

2834
for (const [multiaddr, expectedError] of ERROR_CASES) {

test/spark.js

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -205,20 +205,34 @@ test('fetchCAR fails with statusCode=702 (protocol is not tcp)', async () => {
205205
assertEquals(stats.statusCode, 702, 'stats.statusCode')
206206
})
207207

208-
test('fetchCAR fails with statusCode=703 (scheme is not http/https)', async () => {
208+
test('fetchCAR fails with statusCode=703 (scheme is not http/https) - multiaddr without http-path', async () => {
209209
const spark = new Spark()
210210
const stats = newStats()
211211
await spark.fetchCAR('http', '/ip4/1.2.3.4/tcp/80/ldap', KNOWN_CID, stats)
212212
assertEquals(stats.statusCode, 703, 'stats.statusCode')
213213
})
214214

215+
test('fetchCAR fails with statusCode=703 (scheme is not supported) - multiaddr with http-path', async () => {
216+
const spark = new Spark()
217+
const stats = newStats()
218+
await spark.fetchCAR('http', '/dns/meridian.space/tcp/8080/http/http-path/%2Fipni-provider%2FproviderID', KNOWN_CID, stats)
219+
assertEquals(stats.statusCode, 703, 'stats.statusCode')
220+
})
221+
215222
test('fetchCAR fails with statusCode=704 (multiaddr has too many parts)', async () => {
216223
const spark = new Spark()
217224
const stats = newStats()
218225
await spark.fetchCAR('http', '/ip4/1.2.3.4/tcp/80/http/p2p/pubkey', KNOWN_CID, stats)
219226
assertEquals(stats.statusCode, 704, 'stats.statusCode')
220227
})
221228

229+
test('fetchCAR fails with statusCode=705 (multiaddr has invalid http-path)', async () => {
230+
const spark = new Spark()
231+
const stats = newStats()
232+
await spark.fetchCAR('http', '/dns/meridian.space/http/http-path/invalid%path', KNOWN_CID, stats)
233+
assertEquals(stats.statusCode, 705, 'stats.statusCode')
234+
})
235+
222236
test('fetchCAR fails with statusCode=801 (DNS error)', async () => {
223237
const spark = new Spark()
224238
const stats = newStats()

0 commit comments

Comments
 (0)