@helia/verified-fetch 4.1.0 → 5.0.0
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/README.md +6 -40
- package/dist/index.min.js +73 -534
- package/dist/index.min.js.map +4 -4
- package/dist/src/constants.d.ts +2 -0
- package/dist/src/constants.d.ts.map +1 -1
- package/dist/src/constants.js +2 -0
- package/dist/src/constants.js.map +1 -1
- package/dist/src/index.d.ts +162 -68
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +11 -43
- package/dist/src/index.js.map +1 -1
- package/dist/src/plugins/index.d.ts +0 -5
- package/dist/src/plugins/index.d.ts.map +1 -1
- package/dist/src/plugins/index.js +0 -4
- package/dist/src/plugins/index.js.map +1 -1
- package/dist/src/plugins/plugin-base.d.ts +8 -9
- package/dist/src/plugins/plugin-base.d.ts.map +1 -1
- package/dist/src/plugins/plugin-base.js +5 -6
- package/dist/src/plugins/plugin-base.js.map +1 -1
- package/dist/src/plugins/plugin-handle-car.d.ts +3 -3
- package/dist/src/plugins/plugin-handle-car.d.ts.map +1 -1
- package/dist/src/plugins/plugin-handle-car.js +38 -39
- package/dist/src/plugins/plugin-handle-car.js.map +1 -1
- package/dist/src/plugins/plugin-handle-ipld.d.ts +12 -0
- package/dist/src/plugins/plugin-handle-ipld.d.ts.map +1 -0
- package/dist/src/plugins/plugin-handle-ipld.js +83 -0
- package/dist/src/plugins/plugin-handle-ipld.js.map +1 -0
- package/dist/src/plugins/plugin-handle-ipns-record.d.ts +3 -3
- package/dist/src/plugins/plugin-handle-ipns-record.d.ts.map +1 -1
- package/dist/src/plugins/plugin-handle-ipns-record.js +25 -34
- package/dist/src/plugins/plugin-handle-ipns-record.js.map +1 -1
- package/dist/src/plugins/plugin-handle-tar.d.ts +3 -3
- package/dist/src/plugins/plugin-handle-tar.d.ts.map +1 -1
- package/dist/src/plugins/plugin-handle-tar.js +20 -22
- package/dist/src/plugins/plugin-handle-tar.js.map +1 -1
- package/dist/src/plugins/plugin-handle-unixfs.d.ts +14 -0
- package/dist/src/plugins/plugin-handle-unixfs.d.ts.map +1 -0
- package/dist/src/plugins/plugin-handle-unixfs.js +180 -0
- package/dist/src/plugins/plugin-handle-unixfs.js.map +1 -0
- package/dist/src/plugins/types.d.ts +1 -77
- package/dist/src/plugins/types.d.ts.map +1 -1
- package/dist/src/url-resolver.d.ts +29 -11
- package/dist/src/url-resolver.d.ts.map +1 -1
- package/dist/src/url-resolver.js +152 -74
- package/dist/src/url-resolver.js.map +1 -1
- package/dist/src/utils/content-type-parser.d.ts.map +1 -1
- package/dist/src/utils/content-type-parser.js +4 -3
- package/dist/src/utils/content-type-parser.js.map +1 -1
- package/dist/src/utils/content-types.d.ts +26 -0
- package/dist/src/utils/content-types.d.ts.map +1 -0
- package/dist/src/utils/content-types.js +137 -0
- package/dist/src/utils/content-types.js.map +1 -0
- package/dist/src/utils/convert-output.d.ts +17 -0
- package/dist/src/utils/convert-output.d.ts.map +1 -0
- package/dist/src/utils/convert-output.js +176 -0
- package/dist/src/utils/convert-output.js.map +1 -0
- package/dist/src/utils/error-to-response.d.ts +3 -0
- package/dist/src/utils/error-to-response.d.ts.map +1 -0
- package/dist/src/utils/error-to-response.js +40 -0
- package/dist/src/utils/error-to-response.js.map +1 -0
- package/dist/src/utils/get-content-disposition-filename.d.ts +1 -1
- package/dist/src/utils/get-content-disposition-filename.d.ts.map +1 -1
- package/dist/src/utils/get-content-disposition-filename.js +4 -0
- package/dist/src/utils/get-content-disposition-filename.js.map +1 -1
- package/dist/src/utils/get-e-tag.d.ts +20 -15
- package/dist/src/utils/get-e-tag.d.ts.map +1 -1
- package/dist/src/utils/get-e-tag.js +8 -22
- package/dist/src/utils/get-e-tag.js.map +1 -1
- package/dist/src/utils/get-offset-and-length.d.ts +12 -2
- package/dist/src/utils/get-offset-and-length.d.ts.map +1 -1
- package/dist/src/utils/get-offset-and-length.js +63 -21
- package/dist/src/utils/get-offset-and-length.js.map +1 -1
- package/dist/src/utils/get-range-header.d.ts +22 -0
- package/dist/src/utils/get-range-header.d.ts.map +1 -0
- package/dist/src/utils/get-range-header.js +69 -0
- package/dist/src/utils/get-range-header.js.map +1 -0
- package/dist/src/utils/parse-url-string.d.ts +2 -1
- package/dist/src/utils/parse-url-string.d.ts.map +1 -1
- package/dist/src/utils/parse-url-string.js +46 -71
- package/dist/src/utils/parse-url-string.js.map +1 -1
- package/dist/src/utils/resource-to-cache-key.d.ts +3 -3
- package/dist/src/utils/resource-to-cache-key.js +5 -5
- package/dist/src/utils/resource-to-cache-key.js.map +1 -1
- package/dist/src/utils/response-headers.d.ts +4 -14
- package/dist/src/utils/response-headers.d.ts.map +1 -1
- package/dist/src/utils/response-headers.js +36 -36
- package/dist/src/utils/response-headers.js.map +1 -1
- package/dist/src/utils/responses.d.ts +30 -11
- package/dist/src/utils/responses.d.ts.map +1 -1
- package/dist/src/utils/responses.js +146 -39
- package/dist/src/utils/responses.js.map +1 -1
- package/dist/src/verified-fetch.d.ts +16 -15
- package/dist/src/verified-fetch.d.ts.map +1 -1
- package/dist/src/verified-fetch.js +307 -236
- package/dist/src/verified-fetch.js.map +1 -1
- package/dist/typedoc-urls.json +64 -45
- package/package.json +4 -3
- package/src/constants.ts +3 -0
- package/src/index.ts +203 -71
- package/src/plugins/index.ts +0 -6
- package/src/plugins/plugin-base.ts +8 -10
- package/src/plugins/plugin-handle-car.ts +48 -46
- package/src/plugins/plugin-handle-ipld.ts +93 -0
- package/src/plugins/plugin-handle-ipns-record.ts +31 -41
- package/src/plugins/plugin-handle-tar.ts +25 -29
- package/src/plugins/plugin-handle-unixfs.ts +217 -0
- package/src/plugins/types.ts +0 -86
- package/src/url-resolver.ts +197 -83
- package/src/utils/content-type-parser.ts +4 -3
- package/src/utils/content-types.ts +159 -0
- package/src/utils/convert-output.ts +187 -0
- package/src/utils/error-to-response.ts +49 -0
- package/src/utils/get-content-disposition-filename.ts +7 -1
- package/src/utils/get-e-tag.ts +26 -35
- package/src/utils/get-offset-and-length.ts +75 -21
- package/src/utils/get-range-header.ts +107 -0
- package/src/utils/parse-url-string.ts +51 -80
- package/src/utils/resource-to-cache-key.ts +5 -5
- package/src/utils/response-headers.ts +40 -41
- package/src/utils/responses.ts +186 -45
- package/src/verified-fetch.ts +359 -267
- package/dist/src/plugins/plugin-handle-byte-range-context.d.ts +0 -14
- package/dist/src/plugins/plugin-handle-byte-range-context.d.ts.map +0 -1
- package/dist/src/plugins/plugin-handle-byte-range-context.js +0 -25
- package/dist/src/plugins/plugin-handle-byte-range-context.js.map +0 -1
- package/dist/src/plugins/plugin-handle-cbor.d.ts +0 -17
- package/dist/src/plugins/plugin-handle-cbor.d.ts.map +0 -1
- package/dist/src/plugins/plugin-handle-cbor.js +0 -94
- package/dist/src/plugins/plugin-handle-cbor.js.map +0 -1
- package/dist/src/plugins/plugin-handle-dag-cbor-html-preview.d.ts +0 -27
- package/dist/src/plugins/plugin-handle-dag-cbor-html-preview.d.ts.map +0 -1
- package/dist/src/plugins/plugin-handle-dag-cbor-html-preview.js +0 -279
- package/dist/src/plugins/plugin-handle-dag-cbor-html-preview.js.map +0 -1
- package/dist/src/plugins/plugin-handle-dag-cbor.d.ts +0 -17
- package/dist/src/plugins/plugin-handle-dag-cbor.d.ts.map +0 -1
- package/dist/src/plugins/plugin-handle-dag-cbor.js +0 -66
- package/dist/src/plugins/plugin-handle-dag-cbor.js.map +0 -1
- package/dist/src/plugins/plugin-handle-dag-pb.d.ts +0 -17
- package/dist/src/plugins/plugin-handle-dag-pb.d.ts.map +0 -1
- package/dist/src/plugins/plugin-handle-dag-pb.js +0 -209
- package/dist/src/plugins/plugin-handle-dag-pb.js.map +0 -1
- package/dist/src/plugins/plugin-handle-dag-walk.d.ts +0 -21
- package/dist/src/plugins/plugin-handle-dag-walk.d.ts.map +0 -1
- package/dist/src/plugins/plugin-handle-dag-walk.js +0 -95
- package/dist/src/plugins/plugin-handle-dag-walk.js.map +0 -1
- package/dist/src/plugins/plugin-handle-dir-index-html.d.ts +0 -10
- package/dist/src/plugins/plugin-handle-dir-index-html.d.ts.map +0 -1
- package/dist/src/plugins/plugin-handle-dir-index-html.js +0 -59
- package/dist/src/plugins/plugin-handle-dir-index-html.js.map +0 -1
- package/dist/src/plugins/plugin-handle-json.d.ts +0 -12
- package/dist/src/plugins/plugin-handle-json.d.ts.map +0 -1
- package/dist/src/plugins/plugin-handle-json.js +0 -73
- package/dist/src/plugins/plugin-handle-json.js.map +0 -1
- package/dist/src/plugins/plugin-handle-raw.d.ts +0 -9
- package/dist/src/plugins/plugin-handle-raw.d.ts.map +0 -1
- package/dist/src/plugins/plugin-handle-raw.js +0 -92
- package/dist/src/plugins/plugin-handle-raw.js.map +0 -1
- package/dist/src/plugins/plugins.d.ts +0 -6
- package/dist/src/plugins/plugins.d.ts.map +0 -1
- package/dist/src/plugins/plugins.js +0 -6
- package/dist/src/plugins/plugins.js.map +0 -1
- package/dist/src/utils/byte-range-context.d.ts +0 -103
- package/dist/src/utils/byte-range-context.d.ts.map +0 -1
- package/dist/src/utils/byte-range-context.js +0 -504
- package/dist/src/utils/byte-range-context.js.map +0 -1
- package/dist/src/utils/dag-cbor-to-safe-json.d.ts +0 -15
- package/dist/src/utils/dag-cbor-to-safe-json.d.ts.map +0 -1
- package/dist/src/utils/dag-cbor-to-safe-json.js +0 -54
- package/dist/src/utils/dag-cbor-to-safe-json.js.map +0 -1
- package/dist/src/utils/dir-index-html.d.ts +0 -19
- package/dist/src/utils/dir-index-html.d.ts.map +0 -1
- package/dist/src/utils/dir-index-html.js +0 -438
- package/dist/src/utils/dir-index-html.js.map +0 -1
- package/dist/src/utils/get-peer-id-from-string.d.ts +0 -3
- package/dist/src/utils/get-peer-id-from-string.d.ts.map +0 -1
- package/dist/src/utils/get-peer-id-from-string.js +0 -10
- package/dist/src/utils/get-peer-id-from-string.js.map +0 -1
- package/dist/src/utils/get-resolved-accept-header.d.ts +0 -9
- package/dist/src/utils/get-resolved-accept-header.d.ts.map +0 -1
- package/dist/src/utils/get-resolved-accept-header.js +0 -27
- package/dist/src/utils/get-resolved-accept-header.js.map +0 -1
- package/dist/src/utils/get-stream-from-async-iterable.d.ts +0 -9
- package/dist/src/utils/get-stream-from-async-iterable.d.ts.map +0 -1
- package/dist/src/utils/get-stream-from-async-iterable.js +0 -43
- package/dist/src/utils/get-stream-from-async-iterable.js.map +0 -1
- package/dist/src/utils/handle-redirects.d.ts +0 -16
- package/dist/src/utils/handle-redirects.d.ts.map +0 -1
- package/dist/src/utils/handle-redirects.js +0 -84
- package/dist/src/utils/handle-redirects.js.map +0 -1
- package/dist/src/utils/is-accept-explicit.d.ts +0 -15
- package/dist/src/utils/is-accept-explicit.d.ts.map +0 -1
- package/dist/src/utils/is-accept-explicit.js +0 -26
- package/dist/src/utils/is-accept-explicit.js.map +0 -1
- package/dist/src/utils/request-headers.d.ts +0 -13
- package/dist/src/utils/request-headers.d.ts.map +0 -1
- package/dist/src/utils/request-headers.js +0 -63
- package/dist/src/utils/request-headers.js.map +0 -1
- package/dist/src/utils/select-output-type.d.ts +0 -17
- package/dist/src/utils/select-output-type.d.ts.map +0 -1
- package/dist/src/utils/select-output-type.js +0 -153
- package/dist/src/utils/select-output-type.js.map +0 -1
- package/dist/src/utils/tlru.d.ts +0 -15
- package/dist/src/utils/tlru.d.ts.map +0 -1
- package/dist/src/utils/tlru.js +0 -34
- package/dist/src/utils/tlru.js.map +0 -1
- package/dist/src/utils/walk-path.d.ts +0 -27
- package/dist/src/utils/walk-path.d.ts.map +0 -1
- package/dist/src/utils/walk-path.js +0 -45
- package/dist/src/utils/walk-path.js.map +0 -1
- package/src/plugins/plugin-handle-byte-range-context.ts +0 -30
- package/src/plugins/plugin-handle-cbor.ts +0 -107
- package/src/plugins/plugin-handle-dag-cbor-html-preview.ts +0 -295
- package/src/plugins/plugin-handle-dag-cbor.ts +0 -83
- package/src/plugins/plugin-handle-dag-pb.ts +0 -248
- package/src/plugins/plugin-handle-dag-walk.ts +0 -110
- package/src/plugins/plugin-handle-dir-index-html.ts +0 -72
- package/src/plugins/plugin-handle-json.ts +0 -80
- package/src/plugins/plugin-handle-raw.ts +0 -110
- package/src/plugins/plugins.ts +0 -5
- package/src/utils/byte-range-context.ts +0 -597
- package/src/utils/dag-cbor-to-safe-json.ts +0 -63
- package/src/utils/dir-index-html.ts +0 -505
- package/src/utils/get-peer-id-from-string.ts +0 -12
- package/src/utils/get-resolved-accept-header.ts +0 -42
- package/src/utils/get-stream-from-async-iterable.ts +0 -49
- package/src/utils/handle-redirects.ts +0 -109
- package/src/utils/is-accept-explicit.ts +0 -38
- package/src/utils/request-headers.ts +0 -65
- package/src/utils/select-output-type.ts +0 -175
- package/src/utils/tlru.ts +0 -42
- package/src/utils/walk-path.ts +0 -68
|
@@ -1,248 +0,0 @@
|
|
|
1
|
-
import { unixfs } from '@helia/unixfs'
|
|
2
|
-
import { code as dagPbCode } from '@ipld/dag-pb'
|
|
3
|
-
import { AbortError } from '@libp2p/interface'
|
|
4
|
-
import { exporter } from 'ipfs-unixfs-exporter'
|
|
5
|
-
import { CustomProgressEvent } from 'progress-events'
|
|
6
|
-
import { getContentType } from '../utils/get-content-type.js'
|
|
7
|
-
import { getStreamFromAsyncIterable } from '../utils/get-stream-from-async-iterable.js'
|
|
8
|
-
import { setIpfsRoots } from '../utils/response-headers.js'
|
|
9
|
-
import { badGatewayResponse, badRangeResponse, movedPermanentlyResponse, okRangeResponse } from '../utils/responses.js'
|
|
10
|
-
import { BasePlugin } from './plugin-base.js'
|
|
11
|
-
import type { PluginContext } from './types.js'
|
|
12
|
-
import type { CIDDetail } from '../index.js'
|
|
13
|
-
import type { UnixFSEntry } from 'ipfs-unixfs-exporter'
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Handles UnixFS and dag-pb content.
|
|
17
|
-
*/
|
|
18
|
-
export class DagPbPlugin extends BasePlugin {
|
|
19
|
-
readonly id = 'dag-pb-plugin'
|
|
20
|
-
readonly codes = [dagPbCode]
|
|
21
|
-
|
|
22
|
-
canHandle ({ cid, accept, pathDetails, byteRangeContext }: PluginContext): boolean {
|
|
23
|
-
if (pathDetails == null) {
|
|
24
|
-
return false
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
if (byteRangeContext == null) {
|
|
28
|
-
return false
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
// TODO: this may be too restrictive?
|
|
32
|
-
if (accept != null && accept.mimeType !== 'application/octet-stream') {
|
|
33
|
-
return false
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
return cid.code === dagPbCode
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
/**
|
|
40
|
-
* @see https://specs.ipfs.tech/http-gateways/path-gateway/#use-in-directory-url-normalization
|
|
41
|
-
*/
|
|
42
|
-
getRedirectUrl (context: PluginContext): string | null {
|
|
43
|
-
const { resource, url, isDirectory } = context
|
|
44
|
-
|
|
45
|
-
let uri: URL
|
|
46
|
-
|
|
47
|
-
try {
|
|
48
|
-
// try the requested resource
|
|
49
|
-
uri = new URL(resource)
|
|
50
|
-
} catch {
|
|
51
|
-
// fall back to the canonical URL
|
|
52
|
-
uri = url
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
// directories must be requested with a trailing slash
|
|
56
|
-
if (isDirectory && !uri.pathname.endsWith('/')) {
|
|
57
|
-
// make sure we append slash to end of the path
|
|
58
|
-
uri.pathname += '/'
|
|
59
|
-
return uri.toString()
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
return null
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
async handle (context: PluginContext & Required<Pick<PluginContext, 'byteRangeContext' | 'pathDetails'>>): Promise<Response | null> {
|
|
66
|
-
const { cid, options, pathDetails, query } = context
|
|
67
|
-
const { contentTypeParser, helia, getBlockstore } = this.pluginOptions
|
|
68
|
-
const log = this.log
|
|
69
|
-
let resource = context.resource
|
|
70
|
-
const path = context.path
|
|
71
|
-
let filename = query?.filename
|
|
72
|
-
|
|
73
|
-
let redirected = false
|
|
74
|
-
|
|
75
|
-
const byteRangeContext = context.byteRangeContext
|
|
76
|
-
const ipfsRoots = pathDetails.ipfsRoots
|
|
77
|
-
const terminalElement = pathDetails.terminalElement
|
|
78
|
-
let resolvedCID = terminalElement.cid
|
|
79
|
-
const fs = unixfs({ ...helia, blockstore: getBlockstore(context.cid, context.resource, options?.session ?? true, options) })
|
|
80
|
-
|
|
81
|
-
context.isDirectory = terminalElement?.type === 'directory'
|
|
82
|
-
|
|
83
|
-
if (terminalElement?.type === 'directory') {
|
|
84
|
-
const redirectUrl = this.getRedirectUrl(context)
|
|
85
|
-
|
|
86
|
-
if (redirectUrl != null) {
|
|
87
|
-
log.trace('directory url normalization spec requires redirect...')
|
|
88
|
-
if (options?.redirect === 'error') {
|
|
89
|
-
log('could not redirect to %s as redirect option was set to "error"', redirectUrl)
|
|
90
|
-
throw new TypeError('Failed to fetch')
|
|
91
|
-
} else if (options?.redirect === 'manual') {
|
|
92
|
-
log('returning 301 permanent redirect to %s', redirectUrl)
|
|
93
|
-
return movedPermanentlyResponse(resource, redirectUrl)
|
|
94
|
-
}
|
|
95
|
-
log('following redirect to %s', redirectUrl)
|
|
96
|
-
|
|
97
|
-
// fall-through simulates following the redirect?
|
|
98
|
-
resource = redirectUrl
|
|
99
|
-
redirected = true
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
const dirCid = terminalElement.cid
|
|
103
|
-
const rootFilePath = 'index.html'
|
|
104
|
-
|
|
105
|
-
try {
|
|
106
|
-
log.trace('found directory at %c/%s, looking for index.html', cid, path)
|
|
107
|
-
|
|
108
|
-
const entry = await context.serverTiming.time('exporter-dir', '', exporter(`/ipfs/${dirCid}/${rootFilePath}`, helia.blockstore, {
|
|
109
|
-
signal: options?.signal,
|
|
110
|
-
onProgress: options?.onProgress
|
|
111
|
-
}))
|
|
112
|
-
|
|
113
|
-
log.trace('found root file at %c/%s with cid %c', dirCid, rootFilePath, entry.cid)
|
|
114
|
-
resolvedCID = entry.cid
|
|
115
|
-
|
|
116
|
-
// use `index.html` as the file name to help with content types
|
|
117
|
-
filename = rootFilePath
|
|
118
|
-
} catch (err: any) {
|
|
119
|
-
if (options?.signal?.aborted) {
|
|
120
|
-
throw new AbortError(options?.signal?.reason)
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
this.log.error('error loading path %c/%s - %e', dirCid, rootFilePath, err)
|
|
124
|
-
|
|
125
|
-
context.isDirectory = true
|
|
126
|
-
context.directoryEntries = []
|
|
127
|
-
context.modified++
|
|
128
|
-
|
|
129
|
-
this.log.trace('attempting to get directory entries because index.html was not found')
|
|
130
|
-
for await (const dirItem of fs.ls(dirCid, { signal: options?.signal, onProgress: options?.onProgress, extended: false })) {
|
|
131
|
-
context.directoryEntries.push(dirItem)
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
// dir-index-html plugin or dir-index-json (future idea?) plugin should handle this
|
|
135
|
-
return null
|
|
136
|
-
} finally {
|
|
137
|
-
options?.onProgress?.(new CustomProgressEvent<CIDDetail>('verified-fetch:request:end', { cid: dirCid, path: [rootFilePath] }))
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
try {
|
|
142
|
-
// attempt to get the exact file size, but timeout quickly.
|
|
143
|
-
const stat = await fs.stat(resolvedCID, {
|
|
144
|
-
extended: true,
|
|
145
|
-
signal: AbortSignal.timeout(500)
|
|
146
|
-
})
|
|
147
|
-
|
|
148
|
-
byteRangeContext.setFileSize(stat.size)
|
|
149
|
-
} catch (err: any) {
|
|
150
|
-
log.error('error getting exact file size for %c/%s - %e', cid, path, err)
|
|
151
|
-
byteRangeContext.setFileSize(pathDetails.terminalElement.size)
|
|
152
|
-
log.trace('using terminal element size of %d for %c/%s', pathDetails.terminalElement.size, cid, path)
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
try {
|
|
156
|
-
const entry = await context.serverTiming.time('exporter-file', '', exporter(resolvedCID, helia.blockstore, {
|
|
157
|
-
signal: options?.signal,
|
|
158
|
-
onProgress: options?.onProgress
|
|
159
|
-
}))
|
|
160
|
-
|
|
161
|
-
let firstChunk: Uint8Array
|
|
162
|
-
let contentType: string
|
|
163
|
-
if (byteRangeContext.isValidRangeRequest) {
|
|
164
|
-
contentType = await this.handleRangeRequest(context, entry)
|
|
165
|
-
} else {
|
|
166
|
-
const asyncIter = entry.content({
|
|
167
|
-
signal: options?.signal,
|
|
168
|
-
onProgress: options?.onProgress
|
|
169
|
-
})
|
|
170
|
-
log('got async iterator for %c/%s', cid, path)
|
|
171
|
-
|
|
172
|
-
const streamAndFirstChunk = await context.serverTiming.time('stream-and-chunk', '', getStreamFromAsyncIterable(asyncIter, {
|
|
173
|
-
onProgress: options?.onProgress,
|
|
174
|
-
signal: options?.signal
|
|
175
|
-
}))
|
|
176
|
-
const stream = streamAndFirstChunk.stream
|
|
177
|
-
firstChunk = streamAndFirstChunk.firstChunk
|
|
178
|
-
contentType = await context.serverTiming.time('get-content-type', '', getContentType({
|
|
179
|
-
path,
|
|
180
|
-
filename,
|
|
181
|
-
bytes: firstChunk,
|
|
182
|
-
contentTypeParser,
|
|
183
|
-
log
|
|
184
|
-
}))
|
|
185
|
-
|
|
186
|
-
byteRangeContext.setBody(stream)
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
// if not a valid range request, okRangeRequest will call okResponse
|
|
190
|
-
const response = okRangeResponse(resource, byteRangeContext.getBody(contentType), { byteRangeContext, log }, {
|
|
191
|
-
redirected
|
|
192
|
-
})
|
|
193
|
-
|
|
194
|
-
response.headers.set('Content-Type', byteRangeContext.getContentType() ?? contentType)
|
|
195
|
-
|
|
196
|
-
setIpfsRoots(response, ipfsRoots)
|
|
197
|
-
|
|
198
|
-
return response
|
|
199
|
-
} catch (err: any) {
|
|
200
|
-
if (options?.signal?.aborted) {
|
|
201
|
-
throw new AbortError(options?.signal?.reason)
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
log.error('error streaming %c/%s - %e', cid, path, err)
|
|
205
|
-
|
|
206
|
-
if (byteRangeContext.isRangeRequest && err.code === 'ERR_INVALID_PARAMS') {
|
|
207
|
-
return badRangeResponse(resource)
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
return badGatewayResponse(resource, 'Unable to stream content')
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
private async handleRangeRequest (context: PluginContext & Required<Pick<PluginContext, 'byteRangeContext' | 'pathDetails'>>, entry: UnixFSEntry): Promise<string> {
|
|
215
|
-
const { path, byteRangeContext, options } = context
|
|
216
|
-
const { contentTypeParser } = this.pluginOptions
|
|
217
|
-
const log = this.log
|
|
218
|
-
|
|
219
|
-
// get the first chunk in order to determine the content type
|
|
220
|
-
const asyncIter = entry.content({
|
|
221
|
-
signal: options?.signal,
|
|
222
|
-
onProgress: options?.onProgress,
|
|
223
|
-
offset: 0,
|
|
224
|
-
// 8kb in order to determine the content type
|
|
225
|
-
length: 8192
|
|
226
|
-
})
|
|
227
|
-
|
|
228
|
-
const { firstChunk } = await getStreamFromAsyncIterable(asyncIter, {
|
|
229
|
-
onProgress: options?.onProgress,
|
|
230
|
-
signal: options?.signal
|
|
231
|
-
})
|
|
232
|
-
const contentType = await context.serverTiming.time('get-content-type', '', getContentType({ bytes: firstChunk, path, contentTypeParser, log }))
|
|
233
|
-
|
|
234
|
-
byteRangeContext?.setBody((range): AsyncGenerator<Uint8Array, void, unknown> => {
|
|
235
|
-
if (options?.signal?.aborted) {
|
|
236
|
-
throw new AbortError(options?.signal?.reason ?? 'aborted while streaming')
|
|
237
|
-
}
|
|
238
|
-
return entry.content({
|
|
239
|
-
signal: options?.signal,
|
|
240
|
-
onProgress: options?.onProgress,
|
|
241
|
-
offset: range.start ?? 0,
|
|
242
|
-
length: byteRangeContext.getLength(range)
|
|
243
|
-
})
|
|
244
|
-
}, contentType)
|
|
245
|
-
|
|
246
|
-
return contentType
|
|
247
|
-
}
|
|
248
|
-
}
|
|
@@ -1,110 +0,0 @@
|
|
|
1
|
-
import * as dagCbor from '@ipld/dag-cbor'
|
|
2
|
-
import * as dagJson from '@ipld/dag-json'
|
|
3
|
-
import * as dagPb from '@ipld/dag-pb'
|
|
4
|
-
import toBuffer from 'it-to-buffer'
|
|
5
|
-
import * as json from 'multiformats/codecs/json'
|
|
6
|
-
import * as raw from 'multiformats/codecs/raw'
|
|
7
|
-
import { CODEC_CBOR, CODEC_IDENTITY } from '../constants.ts'
|
|
8
|
-
import { handlePathWalking } from '../utils/walk-path.js'
|
|
9
|
-
import { BasePlugin } from './plugin-base.js'
|
|
10
|
-
import type { PluginContext } from './types.js'
|
|
11
|
-
import type { PathWalkerResponse } from '../utils/walk-path.js'
|
|
12
|
-
|
|
13
|
-
const ENTITY_CODECS = [
|
|
14
|
-
CODEC_CBOR,
|
|
15
|
-
json.code,
|
|
16
|
-
raw.code
|
|
17
|
-
]
|
|
18
|
-
|
|
19
|
-
const WALKABLE_CODECS = [
|
|
20
|
-
dagPb.code,
|
|
21
|
-
dagCbor.code,
|
|
22
|
-
dagJson.code,
|
|
23
|
-
...ENTITY_CODECS
|
|
24
|
-
]
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* This plugin should almost always run first because it's going to handle path
|
|
28
|
-
* walking if needed, and will only say it can handle the request if path
|
|
29
|
-
* walking is possible (path is not empty, terminalCid is unknown, and the path
|
|
30
|
-
* has not been walked yet).
|
|
31
|
-
*
|
|
32
|
-
* Once this plugin has run, the PluginContext will be updated and then this
|
|
33
|
-
* plugin will return false for canHandle, so it won't run again.
|
|
34
|
-
*/
|
|
35
|
-
export class DagWalkPlugin extends BasePlugin {
|
|
36
|
-
readonly id = 'dag-walk-plugin'
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* Return false if the path has already been walked, otherwise return true if
|
|
40
|
-
* the CID is encoded with a codec that supports pathing.
|
|
41
|
-
*/
|
|
42
|
-
canHandle (context: PluginContext): boolean {
|
|
43
|
-
const { pathDetails, cid } = context
|
|
44
|
-
|
|
45
|
-
if (pathDetails != null) {
|
|
46
|
-
// path has already been walked
|
|
47
|
-
return false
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
return WALKABLE_CODECS.includes(cid.code)
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
async handle (context: PluginContext): Promise<Response | null> {
|
|
54
|
-
const { cid, resource, options } = context
|
|
55
|
-
const { getBlockstore } = this.pluginOptions
|
|
56
|
-
const blockstore = getBlockstore(cid, resource, options?.session ?? true, options)
|
|
57
|
-
|
|
58
|
-
let pathDetails: PathWalkerResponse | Response
|
|
59
|
-
|
|
60
|
-
// entity codecs contain all the bytes for an entity in one block and no
|
|
61
|
-
// path walking outside of that block is possible
|
|
62
|
-
if (ENTITY_CODECS.includes(cid.code)) {
|
|
63
|
-
let bytes: Uint8Array
|
|
64
|
-
|
|
65
|
-
if (cid.multihash.code === CODEC_IDENTITY) {
|
|
66
|
-
bytes = cid.multihash.digest
|
|
67
|
-
} else {
|
|
68
|
-
bytes = await toBuffer(blockstore.get(cid, context.options))
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
pathDetails = {
|
|
72
|
-
ipfsRoots: [cid],
|
|
73
|
-
terminalElement: {
|
|
74
|
-
name: cid.toString(),
|
|
75
|
-
path: cid.toString(),
|
|
76
|
-
depth: 0,
|
|
77
|
-
type: 'object',
|
|
78
|
-
node: bytes,
|
|
79
|
-
cid,
|
|
80
|
-
size: BigInt(bytes.byteLength),
|
|
81
|
-
content: async function * () {
|
|
82
|
-
yield bytes
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
} else {
|
|
87
|
-
// TODO: migrate handlePathWalking into this plugin
|
|
88
|
-
pathDetails = await context.serverTiming.time('path-walking', '', handlePathWalking({ ...context, blockstore, log: this.log }))
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
if (pathDetails instanceof Response) {
|
|
92
|
-
this.log.trace('path walking failed')
|
|
93
|
-
|
|
94
|
-
if (pathDetails.status === 404) {
|
|
95
|
-
// invalid or incorrect path.. we walked the path but nothing is there
|
|
96
|
-
// send the 404 response
|
|
97
|
-
return pathDetails
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
// some other error walking the path (codec doesn't support pathing,
|
|
101
|
-
// etc..), let the next plugin try to handle it
|
|
102
|
-
return null
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
context.modified++
|
|
106
|
-
context.pathDetails = pathDetails
|
|
107
|
-
|
|
108
|
-
return null
|
|
109
|
-
}
|
|
110
|
-
}
|
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
import { code as dagPbCode } from '@ipld/dag-pb'
|
|
2
|
-
import { base32 } from 'multiformats/bases/base32'
|
|
3
|
-
import { sha256 } from 'multiformats/hashes/sha2'
|
|
4
|
-
import { dirIndexHtml } from '../utils/dir-index-html.js'
|
|
5
|
-
import { getETag } from '../utils/get-e-tag.js'
|
|
6
|
-
import { getIpfsRoots } from '../utils/response-headers.js'
|
|
7
|
-
import { okRangeResponse } from '../utils/responses.js'
|
|
8
|
-
import { BasePlugin } from './plugin-base.js'
|
|
9
|
-
import type { PluginContext, VerifiedFetchPluginFactory } from './types.js'
|
|
10
|
-
import type { UnixFSEntry } from 'ipfs-unixfs-exporter'
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* Converts a list of directory entries into a small hash that can be used in the etag header.
|
|
14
|
-
*
|
|
15
|
-
* @see https://github.com/ipfs/boxo/blob/dc60fe747c375c631a92fcfd6c7456f44a760d24/gateway/assets/assets.go#L84
|
|
16
|
-
* @see https://github.com/ipfs/boxo/blob/dc60fe747c375c631a92fcfd6c7456f44a760d24/gateway/handler_unixfs_dir.go#L233-L235
|
|
17
|
-
*/
|
|
18
|
-
async function getAssetHash (directoryEntries: UnixFSEntry[]): Promise<string> {
|
|
19
|
-
const entryDetails = directoryEntries.reduce((acc, entry) => {
|
|
20
|
-
return `${acc}${entry.name}${entry.cid.toString()}`
|
|
21
|
-
}, '')
|
|
22
|
-
const hashBytes = await sha256.encode(new TextEncoder().encode(entryDetails))
|
|
23
|
-
return base32.encode(hashBytes)
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export class DirIndexHtmlPlugin extends BasePlugin {
|
|
27
|
-
readonly id = 'dir-index-html-plugin'
|
|
28
|
-
readonly codes = [dagPbCode]
|
|
29
|
-
canHandle (context: PluginContext): boolean {
|
|
30
|
-
const { cid, pathDetails, directoryEntries } = context
|
|
31
|
-
if (pathDetails == null) {
|
|
32
|
-
return false
|
|
33
|
-
}
|
|
34
|
-
if (pathDetails.terminalElement?.type !== 'directory') {
|
|
35
|
-
return false
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
if (directoryEntries == null || directoryEntries.length === 0) {
|
|
39
|
-
return false
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
return cid.code === dagPbCode
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
async handle (context: PluginContext & Required<Pick<PluginContext, 'byteRangeContext' | 'pathDetails' | 'directoryEntries'>>): Promise<Response> {
|
|
46
|
-
const { resource, pathDetails, directoryEntries } = context
|
|
47
|
-
|
|
48
|
-
const { terminalElement, ipfsRoots } = pathDetails
|
|
49
|
-
|
|
50
|
-
const gatewayURL = resource
|
|
51
|
-
const htmlResponse = dirIndexHtml(terminalElement, directoryEntries, { gatewayURL, log: this.log })
|
|
52
|
-
|
|
53
|
-
context.byteRangeContext.setBody(htmlResponse)
|
|
54
|
-
|
|
55
|
-
const etagPrefix = `DirIndex-${await getAssetHash(directoryEntries)}_CID-`
|
|
56
|
-
|
|
57
|
-
const response = okRangeResponse(resource, context.byteRangeContext.getBody('text/html'), { byteRangeContext: context.byteRangeContext, log: this.log }, {
|
|
58
|
-
headers: {
|
|
59
|
-
'Content-Type': context.byteRangeContext.getContentType() ?? 'text/html',
|
|
60
|
-
// see https://github.com/ipfs/gateway-conformance/pull/219
|
|
61
|
-
'Cache-Control': 'public, max-age=604800, stale-while-revalidate=2678400',
|
|
62
|
-
'X-Ipfs-Roots': getIpfsRoots(ipfsRoots),
|
|
63
|
-
// e.g. DirIndex-<asset_hash>_CID-<cid>
|
|
64
|
-
Etag: getETag({ cid: terminalElement.cid, reqFormat: context.reqFormat, contentPrefix: etagPrefix })
|
|
65
|
-
}
|
|
66
|
-
})
|
|
67
|
-
|
|
68
|
-
return response
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
export const dirIndexHtmlPluginFactory: VerifiedFetchPluginFactory = (opts) => new DirIndexHtmlPlugin(opts)
|
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
import * as ipldDagCbor from '@ipld/dag-cbor'
|
|
2
|
-
import * as ipldDagJson from '@ipld/dag-json'
|
|
3
|
-
import toBuffer from 'it-to-buffer'
|
|
4
|
-
import { code as jsonCode } from 'multiformats/codecs/json'
|
|
5
|
-
import { CODEC_CBOR } from '../constants.ts'
|
|
6
|
-
import { notAcceptableResponse, okRangeResponse } from '../utils/responses.js'
|
|
7
|
-
import { BasePlugin } from './plugin-base.js'
|
|
8
|
-
import type { PluginContext } from './types.js'
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Handles `dag-json` content, including requests with Accept: `application/vnd.ipld.dag-cbor` and `application/cbor`.
|
|
12
|
-
*/
|
|
13
|
-
export class JsonPlugin extends BasePlugin {
|
|
14
|
-
readonly id = 'json-plugin'
|
|
15
|
-
readonly codes = [ipldDagJson.code, jsonCode]
|
|
16
|
-
|
|
17
|
-
canHandle ({ cid, accept, byteRangeContext }: PluginContext): boolean {
|
|
18
|
-
if (byteRangeContext == null) {
|
|
19
|
-
return false
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
if (accept?.mimeType === 'application/vnd.ipld.dag-json' && cid.code !== ipldDagCbor.code && cid.code !== CODEC_CBOR) {
|
|
23
|
-
// we can handle application/vnd.ipld.dag-json, but if the CID codec is
|
|
24
|
-
// cbor related, cbor related plugins will handle it
|
|
25
|
-
// TODO: remove the need for deny-listing cases in plugins
|
|
26
|
-
return true
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
return ipldDagJson.code === cid.code || jsonCode === cid.code
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
async handle (context: PluginContext & Required<Pick<PluginContext, 'byteRangeContext'>>): Promise<Response> {
|
|
33
|
-
const { path, resource, cid, accept, options } = context
|
|
34
|
-
const { getBlockstore } = this.pluginOptions
|
|
35
|
-
const session = options?.session ?? true
|
|
36
|
-
|
|
37
|
-
this.log.trace('fetching %c/%s', cid, path)
|
|
38
|
-
|
|
39
|
-
const terminalCid = context.pathDetails?.terminalElement.cid ?? context.cid
|
|
40
|
-
const blockstore = getBlockstore(terminalCid, resource, session, options)
|
|
41
|
-
const block = await toBuffer(blockstore.get(terminalCid, options))
|
|
42
|
-
let body: string | Uint8Array
|
|
43
|
-
|
|
44
|
-
if (accept?.mimeType === 'application/vnd.ipld.dag-cbor' || accept?.mimeType === 'application/cbor') {
|
|
45
|
-
try {
|
|
46
|
-
// if vnd.ipld.dag-cbor has been specified, convert to the format - note
|
|
47
|
-
// that this supports more data types than regular JSON, the content-type
|
|
48
|
-
// response header is set so the user knows to process it differently
|
|
49
|
-
const obj = ipldDagJson.decode(block)
|
|
50
|
-
body = ipldDagCbor.encode(obj)
|
|
51
|
-
} catch (err) {
|
|
52
|
-
this.log.error('could not transform %c to application/vnd.ipld.dag-cbor - %e', err)
|
|
53
|
-
return notAcceptableResponse(resource)
|
|
54
|
-
}
|
|
55
|
-
} else {
|
|
56
|
-
// skip decoding
|
|
57
|
-
body = block
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
let contentType: string
|
|
61
|
-
if (accept == null) {
|
|
62
|
-
if (ipldDagJson.code === cid.code) {
|
|
63
|
-
contentType = 'application/vnd.ipld.dag-json'
|
|
64
|
-
} else {
|
|
65
|
-
contentType = 'application/json'
|
|
66
|
-
}
|
|
67
|
-
} else {
|
|
68
|
-
contentType = accept?.mimeType.split(';')[0]
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
context.byteRangeContext.setBody(body)
|
|
72
|
-
|
|
73
|
-
const response = okRangeResponse(resource, context.byteRangeContext.getBody(contentType), { byteRangeContext: context.byteRangeContext, log: this.log })
|
|
74
|
-
response.headers.set('content-type', context.byteRangeContext.getContentType() ?? contentType)
|
|
75
|
-
if (!context.byteRangeContext.isValidRangeRequest) {
|
|
76
|
-
response.headers.set('content-length', body.length.toString())
|
|
77
|
-
}
|
|
78
|
-
return response
|
|
79
|
-
}
|
|
80
|
-
}
|
|
@@ -1,110 +0,0 @@
|
|
|
1
|
-
import toBuffer from 'it-to-buffer'
|
|
2
|
-
import { code as rawCode } from 'multiformats/codecs/raw'
|
|
3
|
-
import { identity } from 'multiformats/hashes/identity'
|
|
4
|
-
import { getContentType } from '../utils/get-content-type.js'
|
|
5
|
-
import { notFoundResponse, okRangeResponse } from '../utils/responses.js'
|
|
6
|
-
import { BasePlugin } from './plugin-base.js'
|
|
7
|
-
import type { PluginContext } from './types.js'
|
|
8
|
-
import type { AcceptHeader } from '../utils/select-output-type.ts'
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* These are Accept header values that will cause content type sniffing to be
|
|
12
|
-
* skipped and set to these values.
|
|
13
|
-
*/
|
|
14
|
-
const RAW_HEADERS = [
|
|
15
|
-
'application/vnd.ipld.dag-json',
|
|
16
|
-
'application/vnd.ipld.raw',
|
|
17
|
-
'application/octet-stream'
|
|
18
|
-
]
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* if the user has specified an `Accept` header, and it's in our list of
|
|
22
|
-
* allowable "raw" format headers, use that instead of detecting the content
|
|
23
|
-
* type. This avoids the user from receiving something different when they
|
|
24
|
-
* signal that they want to `Accept` a specific mime type.
|
|
25
|
-
*/
|
|
26
|
-
function getOverriddenRawContentType ({ headers, accept }: { headers?: HeadersInit, accept?: AcceptHeader }): string | undefined {
|
|
27
|
-
// accept has already been resolved by getResolvedAcceptHeader, if we have it, use it.
|
|
28
|
-
const acceptHeader = accept?.mimeType ?? new Headers(headers).get('accept') ?? ''
|
|
29
|
-
|
|
30
|
-
// e.g. "Accept: text/html, application/xhtml+xml, application/xml;q=0.9, image/webp, */*;q=0.8"
|
|
31
|
-
const acceptHeaders = acceptHeader.split(',')
|
|
32
|
-
.map(s => s.split(';')[0])
|
|
33
|
-
.map(s => s.trim())
|
|
34
|
-
|
|
35
|
-
for (const mimeType of acceptHeaders) {
|
|
36
|
-
if (mimeType === '*/*') {
|
|
37
|
-
return
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
if (RAW_HEADERS.includes(mimeType ?? '')) {
|
|
41
|
-
return mimeType
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
export class RawPlugin extends BasePlugin {
|
|
47
|
-
readonly id = 'raw-plugin'
|
|
48
|
-
codes: number[] = [rawCode, identity.code]
|
|
49
|
-
|
|
50
|
-
canHandle ({ cid, accept, query, byteRangeContext }: PluginContext): boolean {
|
|
51
|
-
if (byteRangeContext == null) {
|
|
52
|
-
return false
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
return accept?.mimeType === 'application/vnd.ipld.raw' || query.format === 'raw'
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
async handle (context: PluginContext & Required<Pick<PluginContext, 'byteRangeContext'>>): Promise<Response> {
|
|
59
|
-
const { path, resource, cid, accept, query, options } = context
|
|
60
|
-
const { getBlockstore, contentTypeParser } = this.pluginOptions
|
|
61
|
-
const session = options?.session ?? true
|
|
62
|
-
const log = this.log
|
|
63
|
-
|
|
64
|
-
if (accept?.mimeType === 'application/vnd.ipld.raw' || query.format === 'raw') {
|
|
65
|
-
context.reqFormat = 'raw'
|
|
66
|
-
context.query.download = true
|
|
67
|
-
context.query.filename = context.query.filename ?? `${cid.toString()}.bin`
|
|
68
|
-
log.trace('set content disposition to force download')
|
|
69
|
-
} else {
|
|
70
|
-
log.trace('did not set content disposition, raw block will display inline')
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
if (path.length > 0 && cid.code === rawCode) {
|
|
74
|
-
log.trace('404-ing raw codec request for %c/%s', cid, path)
|
|
75
|
-
return notFoundResponse(resource)
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
const terminalCid = context.pathDetails?.terminalElement.cid ?? context.cid
|
|
79
|
-
const blockstore = getBlockstore(terminalCid, resource, session, options)
|
|
80
|
-
const result = await toBuffer(blockstore.get(terminalCid, options))
|
|
81
|
-
|
|
82
|
-
context.byteRangeContext.setBody(result)
|
|
83
|
-
|
|
84
|
-
// if the user has specified an `Accept` header that corresponds to a raw
|
|
85
|
-
// type, honour that header, so for example they don't request
|
|
86
|
-
// `application/vnd.ipld.raw` but get `application/octet-stream`
|
|
87
|
-
const contentType = await getContentType({
|
|
88
|
-
filename: query.filename,
|
|
89
|
-
bytes: result,
|
|
90
|
-
path,
|
|
91
|
-
defaultContentType: getOverriddenRawContentType({ headers: options?.headers, accept }),
|
|
92
|
-
contentTypeParser,
|
|
93
|
-
log
|
|
94
|
-
})
|
|
95
|
-
|
|
96
|
-
const response = okRangeResponse(resource, context.byteRangeContext.getBody(contentType), { byteRangeContext: context.byteRangeContext, log }, {
|
|
97
|
-
redirected: false
|
|
98
|
-
})
|
|
99
|
-
|
|
100
|
-
response.headers.set('content-type', context.byteRangeContext.getContentType() ?? contentType)
|
|
101
|
-
response.headers.set('x-ipfs-roots', terminalCid.toV1().toString())
|
|
102
|
-
|
|
103
|
-
// only set content-length if it is not a range request
|
|
104
|
-
if (!context.byteRangeContext.isRangeRequest) {
|
|
105
|
-
response.headers.set('content-length', result.byteLength.toString())
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
return response
|
|
109
|
-
}
|
|
110
|
-
}
|
package/src/plugins/plugins.ts
DELETED
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Export extension (non-default) plugins here
|
|
3
|
-
*/
|
|
4
|
-
export { DirIndexHtmlPlugin, dirIndexHtmlPluginFactory } from './plugin-handle-dir-index-html.js'
|
|
5
|
-
export { DagCborHtmlPreviewPlugin, dagCborHtmlPreviewPluginFactory } from './plugin-handle-dag-cbor-html-preview.js'
|