hypercore-fetch 9.0.6 → 9.0.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.js +24 -17
- package/package.json +2 -2
- package/test.js +48 -3
package/index.js
CHANGED
|
@@ -209,7 +209,8 @@ export default async function makeHyperFetch ({
|
|
|
209
209
|
}
|
|
210
210
|
})
|
|
211
211
|
router.get(`hyper://*/${SPECIAL_FOLDER}/${EXTENSIONS_FOLDER_NAME}/*`, async function listenExtension (request) {
|
|
212
|
-
const { hostname, pathname } = new URL(request.url)
|
|
212
|
+
const { hostname, pathname: rawPathname } = new URL(request.url)
|
|
213
|
+
const pathname = decodeURI(rawPathname)
|
|
213
214
|
const name = pathname.slice(`/${SPECIAL_FOLDER}/${EXTENSIONS_FOLDER_NAME}/`.length)
|
|
214
215
|
|
|
215
216
|
const core = await getCore(`hyper://${hostname}/`)
|
|
@@ -229,7 +230,9 @@ export default async function makeHyperFetch ({
|
|
|
229
230
|
}
|
|
230
231
|
})
|
|
231
232
|
router.post(`hyper://*/${SPECIAL_FOLDER}/${EXTENSIONS_FOLDER_NAME}/*`, async function broadcastExtension (request) {
|
|
232
|
-
const { hostname, pathname } = new URL(request.url)
|
|
233
|
+
const { hostname, pathname: rawPathname } = new URL(request.url)
|
|
234
|
+
const pathname = decodeURI(rawPathname)
|
|
235
|
+
|
|
233
236
|
const name = pathname.slice(`/${SPECIAL_FOLDER}/${EXTENSIONS_FOLDER_NAME}/`.length)
|
|
234
237
|
|
|
235
238
|
const core = await getCore(`hyper://${hostname}/`)
|
|
@@ -241,7 +244,9 @@ export default async function makeHyperFetch ({
|
|
|
241
244
|
return { status: 200 }
|
|
242
245
|
})
|
|
243
246
|
router.post(`hyper://*/${SPECIAL_FOLDER}/${EXTENSIONS_FOLDER_NAME}/*/*`, async function extensionToPeer (request) {
|
|
244
|
-
const { hostname, pathname } = new URL(request.url)
|
|
247
|
+
const { hostname, pathname: rawPathname } = new URL(request.url)
|
|
248
|
+
const pathname = decodeURI(rawPathname)
|
|
249
|
+
|
|
245
250
|
const subFolder = pathname.slice(`/${SPECIAL_FOLDER}/${EXTENSIONS_FOLDER_NAME}/`.length)
|
|
246
251
|
const [name, extensionPeer] = subFolder.split('/')
|
|
247
252
|
|
|
@@ -275,7 +280,7 @@ export default async function makeHyperFetch ({
|
|
|
275
280
|
try {
|
|
276
281
|
const drive = await getDriveFromKey(key, true)
|
|
277
282
|
|
|
278
|
-
return { body: drive.
|
|
283
|
+
return { body: drive.url }
|
|
279
284
|
} catch (e) {
|
|
280
285
|
if (e.message === ERROR_KEY_NOT_CREATED) {
|
|
281
286
|
return {
|
|
@@ -300,11 +305,12 @@ export default async function makeHyperFetch ({
|
|
|
300
305
|
|
|
301
306
|
const drive = await getDriveFromKey(key, false)
|
|
302
307
|
|
|
303
|
-
return { body: drive.
|
|
308
|
+
return { body: drive.url }
|
|
304
309
|
})
|
|
305
310
|
|
|
306
311
|
router.put('hyper://*/**', async function putFiles (request) {
|
|
307
|
-
const { hostname, pathname } = new URL(request.url)
|
|
312
|
+
const { hostname, pathname: rawPathname } = new URL(request.url)
|
|
313
|
+
const pathname = decodeURI(rawPathname)
|
|
308
314
|
const contentType = request.headers.get('Content-Type') || ''
|
|
309
315
|
const isFormData = contentType.includes('multipart/form-data')
|
|
310
316
|
|
|
@@ -339,7 +345,8 @@ export default async function makeHyperFetch ({
|
|
|
339
345
|
return { status: 201, headers: { Location: request.url } }
|
|
340
346
|
})
|
|
341
347
|
router.delete('hyper://*/**', async function putFiles (request) {
|
|
342
|
-
const { hostname, pathname } = new URL(request.url)
|
|
348
|
+
const { hostname, pathname: rawPathname } = new URL(request.url)
|
|
349
|
+
const pathname = decodeURI(rawPathname)
|
|
343
350
|
|
|
344
351
|
const drive = await getDrive(`hyper://${hostname}`)
|
|
345
352
|
|
|
@@ -368,14 +375,16 @@ export default async function makeHyperFetch ({
|
|
|
368
375
|
|
|
369
376
|
router.head('hyper://*/**', async function headFiles (request) {
|
|
370
377
|
const url = new URL(request.url)
|
|
371
|
-
const { hostname, pathname, searchParams } = url
|
|
378
|
+
const { hostname, pathname: rawPathname, searchParams } = url
|
|
379
|
+
const pathname = decodeURI(rawPathname)
|
|
380
|
+
|
|
372
381
|
const accept = request.headers.get('Accept') || ''
|
|
373
382
|
const isRanged = request.headers.get('Range') || ''
|
|
374
383
|
const noResolve = searchParams.has('noResolve')
|
|
375
384
|
const isDirectory = pathname.endsWith('/')
|
|
376
385
|
|
|
377
386
|
const drive = await getDrive(`hyper://${hostname}`)
|
|
378
|
-
const fullURL = new URL(pathname, drive.
|
|
387
|
+
const fullURL = new URL(pathname, drive.url).href
|
|
379
388
|
|
|
380
389
|
const resHeaders = {
|
|
381
390
|
ETag: `${drive.version}`,
|
|
@@ -435,14 +444,12 @@ export default async function makeHyperFetch ({
|
|
|
435
444
|
return { status: 404, body: 'Not Found' }
|
|
436
445
|
}
|
|
437
446
|
|
|
438
|
-
resHeaders.Link = new URL(path, drive.core.url).href
|
|
439
|
-
|
|
440
447
|
resHeaders.ETag = `${entry.seq}`
|
|
448
|
+
resHeaders['Content-Length'] = `${entry.value.blob.byteLength}`
|
|
441
449
|
|
|
442
450
|
const contentType = getMimeType(path)
|
|
443
451
|
resHeaders[HEADER_CONTENT_TYPE] = contentType
|
|
444
452
|
|
|
445
|
-
|
|
446
453
|
if (entry?.value?.metadata?.mtime) {
|
|
447
454
|
const date = new Date(entry.value.metadata.mtime)
|
|
448
455
|
resHeaders[HEADER_LAST_MODIFIED] = date.toUTCString()
|
|
@@ -476,13 +483,15 @@ export default async function makeHyperFetch ({
|
|
|
476
483
|
// TODO: Redirect on directories without trailing slash
|
|
477
484
|
router.get('hyper://*/**', async function getFiles (request) {
|
|
478
485
|
const url = new URL(request.url)
|
|
479
|
-
const { hostname, pathname, searchParams } = url
|
|
486
|
+
const { hostname, pathname: rawPathname, searchParams } = url
|
|
487
|
+
const pathname = decodeURI(rawPathname)
|
|
488
|
+
|
|
480
489
|
const accept = request.headers.get('Accept') || ''
|
|
481
490
|
const noResolve = searchParams.has('noResolve')
|
|
482
491
|
const isDirectory = pathname.endsWith('/')
|
|
483
492
|
|
|
484
493
|
const drive = await getDrive(`hyper://${hostname}`)
|
|
485
|
-
const fullURL = new URL(pathname, drive.
|
|
494
|
+
const fullURL = new URL(pathname, drive.url).href
|
|
486
495
|
|
|
487
496
|
if (isDirectory) {
|
|
488
497
|
const resHeaders = {
|
|
@@ -547,11 +556,10 @@ async function serveFile (headers, drive, pathname) {
|
|
|
547
556
|
const isRanged = headers.get('Range') || ''
|
|
548
557
|
const contentType = getMimeType(pathname)
|
|
549
558
|
|
|
550
|
-
const fullURL = new URL(pathname, drive.
|
|
559
|
+
const fullURL = new URL(pathname, drive.url).href
|
|
551
560
|
|
|
552
561
|
const entry = await drive.entry(pathname)
|
|
553
562
|
|
|
554
|
-
|
|
555
563
|
const resHeaders = {
|
|
556
564
|
ETag: `${entry.seq}`,
|
|
557
565
|
[HEADER_CONTENT_TYPE]: contentType,
|
|
@@ -559,7 +567,6 @@ async function serveFile (headers, drive, pathname) {
|
|
|
559
567
|
Link: `<${fullURL}>; rel="canonical"`
|
|
560
568
|
}
|
|
561
569
|
|
|
562
|
-
|
|
563
570
|
if (entry?.value?.metadata?.mtime) {
|
|
564
571
|
const date = new Date(entry.value.metadata.mtime)
|
|
565
572
|
resHeaders[HEADER_LAST_MODIFIED] = date.toUTCString()
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "hypercore-fetch",
|
|
3
|
-
"version": "9.0.
|
|
3
|
+
"version": "9.0.8",
|
|
4
4
|
"description": "Implementation of Fetch that uses the Dat SDK for loading p2p content",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.js",
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
},
|
|
33
33
|
"devDependencies": {
|
|
34
34
|
"@rangermauve/fetch-event-source": "^1.0.3",
|
|
35
|
-
"hyper-sdk": "^4.
|
|
35
|
+
"hyper-sdk": "^4.2.3",
|
|
36
36
|
"standard": "^17.0.0",
|
|
37
37
|
"tape": "^5.2.2"
|
|
38
38
|
}
|
package/test.js
CHANGED
|
@@ -57,7 +57,7 @@ test('Quick check', async (t) => {
|
|
|
57
57
|
|
|
58
58
|
t.deepEqual(await existsResponse.json(), [], 'Empty dir on create')
|
|
59
59
|
|
|
60
|
-
const uploadLocation = new URL('./example.txt', created)
|
|
60
|
+
const uploadLocation = new URL('./example .txt', created)
|
|
61
61
|
|
|
62
62
|
const uploadResponse = await fetch(uploadLocation, {
|
|
63
63
|
method: 'put',
|
|
@@ -74,7 +74,7 @@ test('Quick check', async (t) => {
|
|
|
74
74
|
const contentType = uploadedContentResponse.headers.get('Content-Type')
|
|
75
75
|
const contentLink = uploadedContentResponse.headers.get('Link')
|
|
76
76
|
|
|
77
|
-
t.match(contentLink, /^<hyper:\/\/[0-9a-z]{52}\/example.txt>; rel="canonical"$/, 'Link header includes both public key and path.')
|
|
77
|
+
t.match(contentLink, /^<hyper:\/\/[0-9a-z]{52}\/example%20.txt>; rel="canonical"$/, 'Link header includes both public key and path.')
|
|
78
78
|
t.equal(contentType, 'text/plain; charset=utf-8', 'Content got expected mime type')
|
|
79
79
|
t.equal(content, SAMPLE_CONTENT, 'Got uploaded content back out')
|
|
80
80
|
|
|
@@ -82,7 +82,7 @@ test('Quick check', async (t) => {
|
|
|
82
82
|
|
|
83
83
|
await checkResponse(dirResponse, t)
|
|
84
84
|
|
|
85
|
-
t.deepEqual(await dirResponse.json(), ['example.txt'], 'File got added')
|
|
85
|
+
t.deepEqual(await dirResponse.json(), ['example .txt'], 'File got added')
|
|
86
86
|
})
|
|
87
87
|
|
|
88
88
|
test('GET full url for created keys', async (t) => {
|
|
@@ -111,6 +111,31 @@ test('GET full url for created keys', async (t) => {
|
|
|
111
111
|
t.equal(existingURL, createdURL, 'URL same as in initial create')
|
|
112
112
|
})
|
|
113
113
|
|
|
114
|
+
test('HEAD request', async (t) => {
|
|
115
|
+
const created = await nextURL(t)
|
|
116
|
+
const uploadLocation = new URL('./example.txt', created)
|
|
117
|
+
await fetch(uploadLocation, { method: 'put', body: SAMPLE_CONTENT })
|
|
118
|
+
|
|
119
|
+
const headResponse = await fetch(uploadLocation, { method: 'head' })
|
|
120
|
+
|
|
121
|
+
await checkResponse(headResponse, t, 'Able to load HEAD')
|
|
122
|
+
|
|
123
|
+
const headersEtag = headResponse.headers.get('Etag')
|
|
124
|
+
const headersContentType = headResponse.headers.get('Content-Type')
|
|
125
|
+
const headersContentLength = headResponse.headers.get('Content-Length')
|
|
126
|
+
const headersAcceptRanges = headResponse.headers.get('Accept-Ranges')
|
|
127
|
+
const headersLastModified = headResponse.headers.get('Last-Modified')
|
|
128
|
+
const headersLink = headResponse.headers.get('Link')
|
|
129
|
+
|
|
130
|
+
// Version at which the file was added
|
|
131
|
+
t.equal(headersEtag, '1', 'Headers got expected etag')
|
|
132
|
+
t.equal(headersContentType, 'text/plain; charset=utf-8', 'Headers got expected mime type')
|
|
133
|
+
t.ok(headersContentLength, "Headers have 'Content-Length' set.")
|
|
134
|
+
t.ok(headersLastModified, "Headers have 'Last-Modified' set.")
|
|
135
|
+
t.equal(headersAcceptRanges, 'bytes')
|
|
136
|
+
t.match(headersLink, /^<hyper:\/\/[0-9a-z]{52}\/example.txt>; rel="canonical"$/, 'Link header includes both public key and path.')
|
|
137
|
+
})
|
|
138
|
+
|
|
114
139
|
test('PUT file', async (t) => {
|
|
115
140
|
const created = await nextURL(t)
|
|
116
141
|
|
|
@@ -421,6 +446,26 @@ test('EventSource extension messages', async (t) => {
|
|
|
421
446
|
t.ok(lastEventId, 'Event contained peer ID')
|
|
422
447
|
})
|
|
423
448
|
|
|
449
|
+
test('Resolve DNS', async (t) => {
|
|
450
|
+
const loadResponse = await fetch('hyper://example2.mauve.moe/')
|
|
451
|
+
|
|
452
|
+
const entries = await loadResponse.json()
|
|
453
|
+
|
|
454
|
+
t.pass('Loaded contents and ')
|
|
455
|
+
})
|
|
456
|
+
|
|
457
|
+
test('Error on invalid hostname', async (t) => {
|
|
458
|
+
const loadResponse = await fetch('hyper://example/')
|
|
459
|
+
|
|
460
|
+
console.log(loadResponse.status)
|
|
461
|
+
|
|
462
|
+
if(loadResponse.ok) {
|
|
463
|
+
throw new Error('Loading without DNS or a public key should have failed')
|
|
464
|
+
} else {
|
|
465
|
+
t.pass("Invalid names led to an error")
|
|
466
|
+
}
|
|
467
|
+
})
|
|
468
|
+
|
|
424
469
|
async function checkResponse (response, t, successMessage = 'Response OK') {
|
|
425
470
|
if (!response.ok) {
|
|
426
471
|
const message = await response.text()
|