Skip to content

Commit 9fd5bdb

Browse files
authored
fix: handle ports in multiaddrs with SNI tuples (#154)
When non-default http/https ports are encountered, ensure we set them in the final URI correctly. Also remove default 80/443 ports from `ws://` and `wss://` URIs same as `http://` and `https://`.
1 parent c3036d6 commit 9fd5bdb

File tree

2 files changed

+54
-34
lines changed

2 files changed

+54
-34
lines changed

src/index.ts

Lines changed: 46 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -46,20 +46,35 @@ const ASSUME_HTTP_CODES = [
4646

4747
interface Interpreter { (value: string, ma: StringTuple[]): string }
4848

49-
function extractSNI (ma: StringTuple[]): string | null {
50-
let sniProtoCode: number
49+
function extractSNI (ma: StringTuple[]): string | undefined {
50+
return extractTuple('sni', ma)?.[1]
51+
}
52+
53+
function extractPort (ma: StringTuple[]): string {
54+
const port = extractTuple('tcp', ma)?.[1]
55+
56+
if (port == null) {
57+
return ''
58+
}
59+
60+
return `:${port}`
61+
}
62+
63+
function extractTuple (name: string, ma: StringTuple[]): StringTuple | undefined {
64+
let code: number
65+
5166
try {
52-
sniProtoCode = protocols('sni').code
67+
code = protocols(name).code
5368
} catch (e) {
54-
// No SNI protocol in multiaddr
55-
return null
69+
// No support for protocol in multiaddr
70+
return
5671
}
72+
5773
for (const [proto, value] of ma) {
58-
if (proto === sniProtoCode && value !== undefined) {
59-
return value
74+
if (proto === code && value != null) {
75+
return [proto, value]
6076
}
6177
}
62-
return null
6378
}
6479

6580
function hasTLS (ma: StringTuple[]): boolean {
@@ -68,7 +83,7 @@ function hasTLS (ma: StringTuple[]): boolean {
6883

6984
function interpretNext (headProtoCode: number, headProtoVal: string, restMa: StringTuple[]): string {
7085
const interpreter = interpreters[protocols(headProtoCode).name]
71-
if (interpreter === undefined) {
86+
if (interpreter == null) {
7287
throw new Error(`Can't interpret protocol ${protocols(headProtoCode).name}`)
7388
}
7489
const restVal = interpreter(headProtoVal, restMa)
@@ -88,14 +103,14 @@ const interpreters: Record<string, Interpreter> = {
88103
},
89104
tcp: (value: string, restMa: StringTuple[]) => {
90105
const tailProto = restMa.pop()
91-
if (tailProto === undefined) {
106+
if (tailProto == null) {
92107
throw new Error('Unexpected end of multiaddr')
93108
}
94109
return `tcp://${interpretNext(tailProto[0], tailProto[1] ?? '', restMa)}:${value}`
95110
},
96111
udp: (value: string, restMa: StringTuple[]) => {
97112
const tailProto = restMa.pop()
98-
if (tailProto === undefined) {
113+
if (tailProto == null) {
99114
throw new Error('Unexpected end of multiaddr')
100115
}
101116
return `udp://${interpretNext(tailProto[0], tailProto[1] ?? '', restMa)}:${value}`
@@ -106,27 +121,28 @@ const interpreters: Record<string, Interpreter> = {
106121
dns: (value: string, restMa: StringTuple[]) => value,
107122
ipfs: (value: string, restMa: StringTuple[]) => {
108123
const tailProto = restMa.pop()
109-
if (tailProto === undefined) {
124+
if (tailProto == null) {
110125
throw new Error('Unexpected end of multiaddr')
111126
}
112127
return `${interpretNext(tailProto[0], tailProto[1] ?? '', restMa)}/ipfs/${value}`
113128
},
114129
p2p: (value: string, restMa: StringTuple[]) => {
115130
const tailProto = restMa.pop()
116-
if (tailProto === undefined) {
131+
if (tailProto == null) {
117132
throw new Error('Unexpected end of multiaddr')
118133
}
119134
return `${interpretNext(tailProto[0], tailProto[1] ?? '', restMa)}/p2p/${value}`
120135
},
121136
http: (value: string, restMa: StringTuple[]) => {
122137
const maHasTLS = hasTLS(restMa)
123138
const sni = extractSNI(restMa)
124-
if (maHasTLS && sni !== null) {
125-
return `https://${sni}`
139+
const port = extractPort(restMa)
140+
if (maHasTLS && sni != null) {
141+
return `https://${sni}${port}`
126142
}
127143
const protocol = maHasTLS ? 'https://' : 'http://'
128144
const tailProto = restMa.pop()
129-
if (tailProto === undefined) {
145+
if (tailProto == null) {
130146
throw new Error('Unexpected end of multiaddr')
131147
}
132148
let baseVal = interpretNext(tailProto[0], tailProto[1] ?? '', restMa)
@@ -136,7 +152,7 @@ const interpreters: Record<string, Interpreter> = {
136152
},
137153
'http-path': (value: string, restMa: StringTuple[]) => {
138154
const tailProto = restMa.pop()
139-
if (tailProto === undefined) {
155+
if (tailProto == null) {
140156
throw new Error('Unexpected end of multiaddr')
141157
}
142158
const baseVal = interpretNext(tailProto[0], tailProto[1] ?? '', restMa)
@@ -147,7 +163,7 @@ const interpreters: Record<string, Interpreter> = {
147163
// Noop, the parent context knows that it's tls. We don't need to do
148164
// anything here
149165
const tailProto = restMa.pop()
150-
if (tailProto === undefined) {
166+
if (tailProto == null) {
151167
throw new Error('Unexpected end of multiaddr')
152168
}
153169
return interpretNext(tailProto[0], tailProto[1] ?? '', restMa)
@@ -156,14 +172,14 @@ const interpreters: Record<string, Interpreter> = {
156172
// Noop, the parent context uses the sni information, we don't need to do
157173
// anything here
158174
const tailProto = restMa.pop()
159-
if (tailProto === undefined) {
175+
if (tailProto == null) {
160176
throw new Error('Unexpected end of multiaddr')
161177
}
162178
return interpretNext(tailProto[0], tailProto[1] ?? '', restMa)
163179
},
164180
https: (value: string, restMa: StringTuple[]) => {
165181
const tailProto = restMa.pop()
166-
if (tailProto === undefined) {
182+
if (tailProto == null) {
167183
throw new Error('Unexpected end of multiaddr')
168184
}
169185
let baseVal = interpretNext(tailProto[0], tailProto[1] ?? '', restMa)
@@ -174,12 +190,13 @@ const interpreters: Record<string, Interpreter> = {
174190
ws: (value: string, restMa: StringTuple[]) => {
175191
const maHasTLS = hasTLS(restMa)
176192
const sni = extractSNI(restMa)
177-
if (maHasTLS && sni !== null) {
178-
return `wss://${sni}`
193+
const port = extractPort(restMa)
194+
if (maHasTLS && sni != null) {
195+
return `wss://${sni}${port}`
179196
}
180197
const protocol = maHasTLS ? 'wss://' : 'ws://'
181198
const tailProto = restMa.pop()
182-
if (tailProto === undefined) {
199+
if (tailProto == null) {
183200
throw new Error('Unexpected end of multiaddr')
184201
}
185202
let baseVal = interpretNext(tailProto[0], tailProto[1] ?? '', restMa)
@@ -189,7 +206,7 @@ const interpreters: Record<string, Interpreter> = {
189206
},
190207
wss: (value: string, restMa: StringTuple[]) => {
191208
const tailProto = restMa.pop()
192-
if (tailProto === undefined) {
209+
if (tailProto == null) {
193210
throw new Error('Unexpected end of multiaddr')
194211
}
195212
let baseVal = interpretNext(tailProto[0], tailProto[1] ?? '', restMa)
@@ -199,21 +216,21 @@ const interpreters: Record<string, Interpreter> = {
199216
},
200217
'p2p-websocket-star': (value: string, restMa: StringTuple[]) => {
201218
const tailProto = restMa.pop()
202-
if (tailProto === undefined) {
219+
if (tailProto == null) {
203220
throw new Error('Unexpected end of multiaddr')
204221
}
205222
return `${interpretNext(tailProto[0], tailProto[1] ?? '', restMa)}/p2p-websocket-star`
206223
},
207224
'p2p-webrtc-star': (value: string, restMa: StringTuple[]) => {
208225
const tailProto = restMa.pop()
209-
if (tailProto === undefined) {
226+
if (tailProto == null) {
210227
throw new Error('Unexpected end of multiaddr')
211228
}
212229
return `${interpretNext(tailProto[0], tailProto[1] ?? '', restMa)}/p2p-webrtc-star`
213230
},
214231
'p2p-webrtc-direct': (value: string, restMa: StringTuple[]) => {
215232
const tailProto = restMa.pop()
216-
if (tailProto === undefined) {
233+
if (tailProto == null) {
217234
throw new Error('Unexpected end of multiaddr')
218235
}
219236
return `${interpretNext(tailProto[0], tailProto[1] ?? '', restMa)}/p2p-webrtc-direct`
@@ -224,7 +241,7 @@ export function multiaddrToUri (input: Multiaddr | string | Uint8Array, opts?: M
224241
const ma = multiaddr(input)
225242
const parts = ma.stringTuples()
226243
const head = parts.pop()
227-
if (head === undefined) {
244+
if (head == null) {
228245
throw new Error('Unexpected end of multiaddr')
229246
}
230247

@@ -248,7 +265,7 @@ export function multiaddrToUri (input: Multiaddr | string | Uint8Array, opts?: M
248265
}
249266
}
250267

251-
if (uri.startsWith('http://') || uri.startsWith('https://')) {
268+
if (uri.startsWith('http://') || uri.startsWith('https://') || uri.startsWith('ws://') || uri.startsWith('wss://')) {
252269
// this will strip default ports while keeping paths intact
253270
uri = new URL(uri).toString()
254271

test/test.spec.ts

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,10 @@ describe('multiaddr-to-uri', () => {
1212
['/ip4/0.0.7.6/tcp/1234/http', 'http://0.0.7.6:1234'],
1313
['/ip4/0.0.7.6/tcp/1234/https', 'https://0.0.7.6:1234'],
1414
['/ip4/0.0.7.6/tcp/1234/tls/http', 'https://0.0.7.6:1234'],
15-
['/ip4/1.2.3.4/tcp/1234/tls/sni/ipfs.io/http', 'https://ipfs.io'],
16-
['/ip4/1.2.3.4/tcp/1234/tls/sni/ipfs.io/http/http-path/foo%2fbar', 'https://ipfs.io/foo/bar'],
15+
['/ip4/1.2.3.4/tcp/443/tls/sni/ipfs.io/http', 'https://ipfs.io'],
16+
['/ip4/1.2.3.4/tcp/1234/tls/sni/ipfs.io/http', 'https://ipfs.io:1234'],
17+
['/ip4/1.2.3.4/tcp/443/tls/sni/ipfs.io/http/http-path/foo%2fbar', 'https://ipfs.io/foo/bar'],
18+
['/ip4/1.2.3.4/tcp/1234/tls/sni/ipfs.io/http/http-path/foo%2fbar', 'https://ipfs.io:1234/foo/bar'],
1719
['/ip4/0.0.7.6/udp/1234', 'udp://0.0.7.6:1234'],
1820
['/ip6/::/udp/0', 'udp://[::]:0'],
1921
['/dns/a.com/tcp/1234', 'http://a.com:1234'],
@@ -35,7 +37,8 @@ describe('multiaddr-to-uri', () => {
3537
['/ip6/::/tcp/0/ws', 'ws://[::]:0'],
3638
['/dnsaddr/ipfs.io/wss', 'wss://ipfs.io'],
3739
['/dnsaddr/ipfs.io/tls/ws', 'wss://ipfs.io'],
38-
['/ip4/1.2.3.4/tcp/1234/tls/sni/ipfs.io/ws', 'wss://ipfs.io'],
40+
['/ip4/1.2.3.4/tcp/443/tls/sni/ipfs.io/ws', 'wss://ipfs.io'],
41+
['/ip4/1.2.3.4/tcp/1234/tls/sni/ipfs.io/ws', 'wss://ipfs.io:1234'],
3942
['/ip4/1.2.3.4/tcp/3456/wss', 'wss://1.2.3.4:3456'],
4043
['/ip6/::/tcp/0/wss', 'wss://[::]:0'],
4144
[
@@ -56,7 +59,7 @@ describe('multiaddr-to-uri', () => {
5659
],
5760
[
5861
'/dns4/wrtc-star.discovery.libp2p.io/tcp/443/wss/p2p-webrtc-star/ipfs/QmTysQQiTGMdfRsDQp516oZ9bR3FiSCDnicUnqny2q1d79',
59-
'wss://wrtc-star.discovery.libp2p.io:443/p2p-webrtc-star/p2p/QmTysQQiTGMdfRsDQp516oZ9bR3FiSCDnicUnqny2q1d79'
62+
'wss://wrtc-star.discovery.libp2p.io/p2p-webrtc-star/p2p/QmTysQQiTGMdfRsDQp516oZ9bR3FiSCDnicUnqny2q1d79'
6063
],
6164
['/ip4/1.2.3.4/tcp/3456/http/p2p-webrtc-direct', 'http://1.2.3.4:3456/p2p-webrtc-direct'],
6265
['/ip6/::/tcp/0/http/p2p-webrtc-direct', 'http://[::]:0/p2p-webrtc-direct'],
@@ -72,7 +75,7 @@ describe('multiaddr-to-uri', () => {
7275
],
7376
[
7477
'/dns4/ws-star.discovery.libp2p.io/tcp/443/wss/p2p-websocket-star/ipfs/QmP3vadpN9dqZ7j6KtmwP5Y4prg7XqdS7ixgZMWtXxBAbp',
75-
'wss://ws-star.discovery.libp2p.io:443/p2p-websocket-star/p2p/QmP3vadpN9dqZ7j6KtmwP5Y4prg7XqdS7ixgZMWtXxBAbp'
78+
'wss://ws-star.discovery.libp2p.io/p2p-websocket-star/p2p/QmP3vadpN9dqZ7j6KtmwP5Y4prg7XqdS7ixgZMWtXxBAbp'
7679
],
7780
[
7881
'/ip4/127.0.0.1/tcp/20008/ws/ipfs/QmUjNmr8TgJCn1Ao7DvMy4cjoZU15b9bwSCBLE3vwXiwgj',

0 commit comments

Comments
 (0)