@helia/verified-fetch 2.6.5 → 2.6.7
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/dist/index.min.js +44 -44
- package/dist/src/plugins/plugin-handle-byte-range-context.d.ts +13 -0
- package/dist/src/plugins/plugin-handle-byte-range-context.d.ts.map +1 -0
- package/dist/src/plugins/plugin-handle-byte-range-context.js +19 -0
- package/dist/src/plugins/plugin-handle-byte-range-context.js.map +1 -0
- package/dist/src/plugins/plugin-handle-car.d.ts +1 -1
- package/dist/src/plugins/plugin-handle-car.d.ts.map +1 -1
- package/dist/src/plugins/plugin-handle-car.js +9 -2
- package/dist/src/plugins/plugin-handle-car.js.map +1 -1
- package/dist/src/plugins/plugin-handle-dag-cbor.d.ts +2 -2
- package/dist/src/plugins/plugin-handle-dag-cbor.d.ts.map +1 -1
- package/dist/src/plugins/plugin-handle-dag-cbor.js +7 -6
- package/dist/src/plugins/plugin-handle-dag-cbor.js.map +1 -1
- package/dist/src/plugins/plugin-handle-dag-pb.d.ts +2 -2
- package/dist/src/plugins/plugin-handle-dag-pb.d.ts.map +1 -1
- package/dist/src/plugins/plugin-handle-dag-pb.js +5 -6
- package/dist/src/plugins/plugin-handle-dag-pb.js.map +1 -1
- package/dist/src/plugins/plugin-handle-dir-index-html.d.ts +1 -1
- package/dist/src/plugins/plugin-handle-dir-index-html.d.ts.map +1 -1
- package/dist/src/plugins/plugin-handle-dir-index-html.js +7 -15
- package/dist/src/plugins/plugin-handle-dir-index-html.js.map +1 -1
- package/dist/src/plugins/plugin-handle-ipns-record.d.ts +2 -2
- package/dist/src/plugins/plugin-handle-ipns-record.d.ts.map +1 -1
- package/dist/src/plugins/plugin-handle-ipns-record.js +7 -3
- package/dist/src/plugins/plugin-handle-ipns-record.js.map +1 -1
- package/dist/src/plugins/plugin-handle-json.d.ts +2 -2
- package/dist/src/plugins/plugin-handle-json.d.ts.map +1 -1
- package/dist/src/plugins/plugin-handle-json.js +7 -3
- package/dist/src/plugins/plugin-handle-json.js.map +1 -1
- package/dist/src/plugins/plugin-handle-raw.d.ts +2 -2
- package/dist/src/plugins/plugin-handle-raw.d.ts.map +1 -1
- package/dist/src/plugins/plugin-handle-raw.js +6 -5
- package/dist/src/plugins/plugin-handle-raw.js.map +1 -1
- package/dist/src/plugins/plugin-handle-tar.d.ts +2 -2
- package/dist/src/plugins/plugin-handle-tar.d.ts.map +1 -1
- package/dist/src/plugins/plugin-handle-tar.js +7 -3
- package/dist/src/plugins/plugin-handle-tar.js.map +1 -1
- package/dist/src/plugins/types.d.ts +10 -1
- package/dist/src/plugins/types.d.ts.map +1 -1
- package/dist/src/utils/byte-range-context.d.ts.map +1 -1
- package/dist/src/utils/byte-range-context.js +1 -0
- package/dist/src/utils/byte-range-context.js.map +1 -1
- package/dist/src/utils/walk-path.d.ts.map +1 -1
- package/dist/src/utils/walk-path.js +3 -1
- package/dist/src/utils/walk-path.js.map +1 -1
- package/dist/src/verified-fetch.d.ts.map +1 -1
- package/dist/src/verified-fetch.js +13 -1
- package/dist/src/verified-fetch.js.map +1 -1
- package/package.json +2 -1
- package/src/plugins/plugin-handle-byte-range-context.ts +22 -0
- package/src/plugins/plugin-handle-car.ts +10 -3
- package/src/plugins/plugin-handle-dag-cbor.ts +10 -7
- package/src/plugins/plugin-handle-dag-pb.ts +6 -7
- package/src/plugins/plugin-handle-dir-index-html.ts +11 -16
- package/src/plugins/plugin-handle-ipns-record.ts +9 -4
- package/src/plugins/plugin-handle-json.ts +9 -4
- package/src/plugins/plugin-handle-raw.ts +7 -6
- package/src/plugins/plugin-handle-tar.ts +9 -4
- package/src/plugins/types.ts +10 -1
- package/src/utils/byte-range-context.ts +1 -0
- package/src/utils/walk-path.ts +4 -1
- package/src/verified-fetch.ts +14 -1
|
@@ -3,7 +3,7 @@ import toBrowserReadableStream from 'it-to-browser-readablestream'
|
|
|
3
3
|
import { code as rawCode } from 'multiformats/codecs/raw'
|
|
4
4
|
import { getETag } from '../utils/get-e-tag.js'
|
|
5
5
|
import { tarStream } from '../utils/get-tar-stream.js'
|
|
6
|
-
import { notAcceptableResponse,
|
|
6
|
+
import { notAcceptableResponse, okRangeResponse } from '../utils/responses.js'
|
|
7
7
|
import { BasePlugin } from './plugin-base.js'
|
|
8
8
|
import type { PluginContext } from './types.js'
|
|
9
9
|
|
|
@@ -13,12 +13,15 @@ import type { PluginContext } from './types.js'
|
|
|
13
13
|
*/
|
|
14
14
|
export class TarPlugin extends BasePlugin {
|
|
15
15
|
readonly codes = []
|
|
16
|
-
canHandle ({ cid, accept, query }: PluginContext): boolean {
|
|
16
|
+
canHandle ({ cid, accept, query, byteRangeContext }: PluginContext): boolean {
|
|
17
17
|
this.log('checking if we can handle %c with accept %s', cid, accept)
|
|
18
|
+
if (byteRangeContext == null) {
|
|
19
|
+
return false
|
|
20
|
+
}
|
|
18
21
|
return accept === 'application/x-tar' || query.format === 'tar'
|
|
19
22
|
}
|
|
20
23
|
|
|
21
|
-
async handle (context: PluginContext): Promise<Response> {
|
|
24
|
+
async handle (context: PluginContext & Required<Pick<PluginContext, 'byteRangeContext'>>): Promise<Response> {
|
|
22
25
|
const { cid, path, resource, options, pathDetails } = context
|
|
23
26
|
const { getBlockstore } = this.pluginOptions
|
|
24
27
|
|
|
@@ -34,7 +37,9 @@ export class TarPlugin extends BasePlugin {
|
|
|
34
37
|
const blockstore = getBlockstore(terminusElement, resource, options?.session, options)
|
|
35
38
|
const stream = toBrowserReadableStream<Uint8Array>(tarStream(`/ipfs/${cid}/${path}`, blockstore, options))
|
|
36
39
|
|
|
37
|
-
|
|
40
|
+
context.byteRangeContext.setBody(stream)
|
|
41
|
+
|
|
42
|
+
const response = okRangeResponse(resource, context.byteRangeContext.getBody(), { byteRangeContext: context.byteRangeContext, log: this.log })
|
|
38
43
|
response.headers.set('content-type', 'application/x-tar')
|
|
39
44
|
|
|
40
45
|
response.headers.set('etag', getETag({ cid: terminusElement, reqFormat: context.reqFormat, weak: true }))
|
package/src/plugins/types.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { PluginError } from './errors.js'
|
|
2
2
|
import type { VerifiedFetchInit } from '../index.js'
|
|
3
3
|
import type { ContentTypeParser, RequestFormatShorthand } from '../types.js'
|
|
4
|
+
import type { ByteRangeContext } from '../utils/byte-range-context.js'
|
|
4
5
|
import type { ParsedUrlStringResults } from '../utils/parse-url-string.js'
|
|
5
6
|
import type { PathWalkerResponse } from '../utils/walk-path.js'
|
|
6
7
|
import type { AbortOptions, ComponentLogger, Logger } from '@libp2p/interface'
|
|
@@ -12,7 +13,7 @@ import type { CustomProgressEvent } from 'progress-events'
|
|
|
12
13
|
|
|
13
14
|
/**
|
|
14
15
|
* Contains common components and functions required by plugins to handle a request.
|
|
15
|
-
* - Read-Only: Plugins can read but shouldn
|
|
16
|
+
* - Read-Only: Plugins can read but shouldn't rewrite them.
|
|
16
17
|
* - Persistent: Relevant even after the request completes (e.g., logging or metrics).
|
|
17
18
|
*/
|
|
18
19
|
export interface PluginOptions {
|
|
@@ -47,6 +48,14 @@ export interface PluginContext extends ParsedUrlStringResults {
|
|
|
47
48
|
errors?: PluginError[]
|
|
48
49
|
reqFormat?: RequestFormatShorthand
|
|
49
50
|
pathDetails?: PathWalkerResponse
|
|
51
|
+
query: ParsedUrlStringResults['query']
|
|
52
|
+
/**
|
|
53
|
+
* ByteRangeContext contains information about the size of the content and range requests.
|
|
54
|
+
* This can be used to set the Content-Length header without loading the entire body.
|
|
55
|
+
*
|
|
56
|
+
* This is set by the ByteRangeContextPlugin
|
|
57
|
+
*/
|
|
58
|
+
byteRangeContext?: ByteRangeContext
|
|
50
59
|
[key: string]: unknown
|
|
51
60
|
}
|
|
52
61
|
|
|
@@ -107,6 +107,7 @@ export class ByteRangeContext {
|
|
|
107
107
|
this.log.trace('returning body with byteStart=%o, byteEnd=%o, byteSize=%o', byteStart, byteEnd, byteSize)
|
|
108
108
|
if (body instanceof ReadableStream) {
|
|
109
109
|
// stream should already be spliced by `unixfs.cat`
|
|
110
|
+
// TODO: if the content is not unixfs and unixfs.cat was not called, we need to slice the body here.
|
|
110
111
|
return body
|
|
111
112
|
}
|
|
112
113
|
return this.getSlicedBody(body)
|
package/src/utils/walk-path.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { DoesNotExistError } from '@helia/unixfs/errors'
|
|
2
2
|
import { type Logger } from '@libp2p/interface'
|
|
3
|
+
import { abortableSource } from 'abortable-iterator'
|
|
3
4
|
import { type Blockstore } from 'interface-blockstore'
|
|
4
5
|
import { walkPath as exporterWalk, type ExporterOptions, type ReadableStorage, type ObjectNode, type UnixFSEntry } from 'ipfs-unixfs-exporter'
|
|
5
6
|
import { badGatewayResponse, notFoundResponse } from './responses.js'
|
|
@@ -22,7 +23,9 @@ async function walkPath (blockstore: ReadableStorage, path: string, options?: Pa
|
|
|
22
23
|
const ipfsRoots: CID[] = []
|
|
23
24
|
let terminalElement: UnixFSEntry | undefined
|
|
24
25
|
|
|
25
|
-
|
|
26
|
+
const iterator = options?.signal != null ? abortableSource(exporterWalk(path, blockstore, options), options.signal) : exporterWalk(path, blockstore, options)
|
|
27
|
+
|
|
28
|
+
for await (const entry of iterator) {
|
|
26
29
|
ipfsRoots.push(entry.cid)
|
|
27
30
|
terminalElement = entry
|
|
28
31
|
}
|
package/src/verified-fetch.ts
CHANGED
|
@@ -4,6 +4,7 @@ import { prefixLogger } from '@libp2p/logger'
|
|
|
4
4
|
import { LRUCache } from 'lru-cache'
|
|
5
5
|
import { type CID } from 'multiformats/cid'
|
|
6
6
|
import { CustomProgressEvent } from 'progress-events'
|
|
7
|
+
import { ByteRangeContextPlugin } from './plugins/plugin-handle-byte-range-context.js'
|
|
7
8
|
import { CarPlugin } from './plugins/plugin-handle-car.js'
|
|
8
9
|
import { DagCborPlugin } from './plugins/plugin-handle-dag-cbor.js'
|
|
9
10
|
import { DagPbPlugin } from './plugins/plugin-handle-dag-pb.js'
|
|
@@ -90,6 +91,7 @@ export class VerifiedFetch {
|
|
|
90
91
|
|
|
91
92
|
const defaultPlugins = [
|
|
92
93
|
new DagWalkPlugin(pluginOptions),
|
|
94
|
+
new ByteRangeContextPlugin(pluginOptions),
|
|
93
95
|
new IpnsRecordPlugin(pluginOptions),
|
|
94
96
|
new CarPlugin(pluginOptions),
|
|
95
97
|
new RawPlugin(pluginOptions),
|
|
@@ -151,13 +153,24 @@ export class VerifiedFetch {
|
|
|
151
153
|
* Server-Timing header to the response if it has been collected. It should be used for any final processing of the
|
|
152
154
|
* response before it is returned to the user.
|
|
153
155
|
*/
|
|
154
|
-
private handleFinalResponse (response: Response, { query, cid, reqFormat, ttl, protocol, ipfsPath, pathDetails }: Partial<PluginContext> = {}): Response {
|
|
156
|
+
private handleFinalResponse (response: Response, { query, cid, reqFormat, ttl, protocol, ipfsPath, pathDetails, byteRangeContext }: Partial<PluginContext> = {}): Response {
|
|
155
157
|
if (this.serverTimingHeaders.length > 0) {
|
|
156
158
|
const headerString = this.serverTimingHeaders.join(', ')
|
|
157
159
|
response.headers.set('Server-Timing', headerString)
|
|
158
160
|
this.serverTimingHeaders = []
|
|
159
161
|
}
|
|
160
162
|
|
|
163
|
+
// if there are multiple ranges, we should omit the content-length header. see https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Transfer-Encoding
|
|
164
|
+
if (response.headers.get('Transfer-Encoding') !== 'chunked') {
|
|
165
|
+
if (byteRangeContext != null) {
|
|
166
|
+
const contentLength = byteRangeContext.length
|
|
167
|
+
if (contentLength != null) {
|
|
168
|
+
this.log.trace('Setting Content-Length from byteRangeContext: %d', contentLength)
|
|
169
|
+
response.headers.set('Content-Length', contentLength.toString())
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
161
174
|
// set Content-Disposition header
|
|
162
175
|
let contentDisposition: string | undefined
|
|
163
176
|
|