@helia/verified-fetch
Version:
A fetch-like API for obtaining verified & trustless IPFS content on the web
111 lines • 5.03 kB
JavaScript
import { BlockExporter, car, CIDPath, depthFirstWalker, naturalOrderWalker, SubgraphExporter, UnixFSExporter } from '@helia/car';
import { code as dagPbCode } from '@ipld/dag-pb';
import { createScalableCuckooFilter } from '@libp2p/utils';
import toBrowserReadableStream from 'it-to-browser-readablestream';
import { CONTENT_TYPE_CAR, MEDIA_TYPE_CAR } from "../utils/content-types.js";
import { getContentDispositionFilename } from "../utils/get-content-disposition-filename.js";
import { entityBytesToOffsetAndLength } from "../utils/get-offset-and-length.js";
import { badRequestResponse, notAcceptableResponse, okResponse } from '../utils/responses.js';
import { BasePlugin } from './plugin-base.js';
function getFilename(ipfsPath) {
// convert context.ipfsPath to a filename. replace all / with _, replace prefix protocol with empty string
const filename = ipfsPath
.replace(/\/ipfs\//, '')
.replace(/\/ipns\//, '')
.replace(/\/+$/g, '')
.replace(/\//g, '_');
return `${filename}.car`;
}
function getDagScope({ url }) {
const dagScope = url.searchParams.get('dag-scope');
if (dagScope === 'all' || dagScope === 'entity' || dagScope === 'block') {
return dagScope;
}
// entity-bytes implies entity scope
if (url.searchParams.has('entity-bytes')) {
return 'entity';
}
return 'all';
}
/**
* Accepts a `CID` and returns a `Response` with a body stream that is a CAR
* of the `DAG` referenced by the `CID`.
*/
export class CarPlugin extends BasePlugin {
id = 'car-plugin';
canHandle({ accept }) {
return accept.some(header => header.contentType.mediaType === MEDIA_TYPE_CAR);
}
async handle(context) {
const { options, url, accept, resource, blockstore, range, ipfsRoots, terminalElement, requestedMimeTypes } = context;
if (range != null) {
return badRequestResponse(resource, new Error('Range requests are not supported for CAR files'));
}
const acceptCar = accept.filter(header => header.contentType.mediaType === MEDIA_TYPE_CAR).pop();
// we have already asserted that the CAR media type is present so this
// branch should never be hit
if (acceptCar == null) {
return badRequestResponse(resource, new Error('Could not find CAR media type in accept header'));
}
const order = acceptCar.options.order === 'dfs' ? 'dfs' : 'unk';
const duplicates = acceptCar.options.dups !== 'n';
// TODO: `@ipld/car` only supports CARv1
if (acceptCar.options.version === '2' || url.searchParams.get('car-version') === '2') {
return notAcceptableResponse(resource, requestedMimeTypes, [
CONTENT_TYPE_CAR
]);
}
const helia = this.pluginOptions.helia;
const c = car({
blockstore,
getCodec: helia.getCodec,
logger: helia.logger
});
const carExportOptions = {
...options,
includeTraversalBlocks: true
};
if (!duplicates) {
carExportOptions.blockFilter = createScalableCuckooFilter(1024);
}
if (ipfsRoots.length > 1) {
carExportOptions.traversal = new CIDPath(ipfsRoots);
}
const dagScope = getDagScope(context);
const target = terminalElement.cid;
if (dagScope === 'block') {
carExportOptions.exporter = new BlockExporter();
}
else if (dagScope === 'entity') {
// if its unixFS, we need to enumerate a directory, or get all/some blocks
// for the entity, otherwise, use blockExporter
if (target.code === dagPbCode) {
const options = {
listingOnly: true
};
const slice = entityBytesToOffsetAndLength(terminalElement.size, url.searchParams.get('entity-bytes'));
options.offset = slice.offset;
options.length = slice.length;
carExportOptions.exporter = new UnixFSExporter(options);
}
else {
carExportOptions.exporter = new BlockExporter();
}
}
else {
carExportOptions.exporter = new SubgraphExporter({
walker: order === 'dfs' ? depthFirstWalker() : naturalOrderWalker()
});
}
const stream = toBrowserReadableStream(c.export(target, carExportOptions));
return okResponse(resource, stream, {
headers: {
'content-type': `${MEDIA_TYPE_CAR}; version=1; order=${order}; dups=${duplicates ? 'y' : 'n'}`,
'content-disposition': `attachment; ${getContentDispositionFilename(url.searchParams.get('filename') ?? getFilename(`/ipfs/${url.hostname}${url.pathname}`))}`,
'x-content-type-options': 'nosniff',
'accept-ranges': 'none'
}
});
}
}
//# sourceMappingURL=plugin-handle-car.js.map