@helia/verified-fetch
Version:
A fetch-like API for obtaining verified & trustless IPFS content on the web
83 lines • 3.91 kB
JavaScript
import * as ipldDagCbor from '@ipld/dag-cbor';
import * as ipldDagJson from '@ipld/dag-json';
import * as dagPb from '@ipld/dag-pb';
import toBuffer from 'it-to-buffer';
import * as json from 'multiformats/codecs/json';
import * as raw from 'multiformats/codecs/raw';
import { identity } from 'multiformats/hashes/identity';
import { CODEC_CBOR } from "../constants.js";
import { getContentTypesForCid, MEDIA_TYPE_CBOR, MEDIA_TYPE_DAG_CBOR, MEDIA_TYPE_DAG_JSON, MEDIA_TYPE_JSON, MEDIA_TYPE_OCTET_STREAM, MEDIA_TYPE_RAW } from "../utils/content-types.js";
import { convertOutput } from "../utils/convert-output.js";
import { getContentDispositionFilename } from "../utils/get-content-disposition-filename.js";
import { notAcceptableResponse, okResponse, partialContentResponse } from '../utils/responses.js';
import { BasePlugin } from './plugin-base.js';
/**
* Handles loading JSON and CBOR content
*/
export class IpldPlugin extends BasePlugin {
id = 'ipld-plugin';
codes = [
CODEC_CBOR,
ipldDagCbor.code,
ipldDagJson.code,
json.code,
raw.code,
dagPb.code,
identity.code
];
canHandle({ terminalElement, accept }) {
const supportsCid = this.codes.includes(terminalElement.cid.code);
const supportsAccept = accept.length === 0 ||
accept.some(header => header.contentType.mediaType === MEDIA_TYPE_CBOR ||
header.contentType.mediaType === MEDIA_TYPE_DAG_CBOR ||
header.contentType.mediaType === MEDIA_TYPE_JSON ||
header.contentType.mediaType === MEDIA_TYPE_DAG_JSON ||
header.contentType.mediaType === MEDIA_TYPE_RAW ||
header.contentType.mediaType === MEDIA_TYPE_OCTET_STREAM);
return supportsCid && supportsAccept;
}
async handle(context) {
const { url, resource, accept, ipfsRoots, terminalElement, blockstore, options, requestedMimeTypes } = context;
this.log.trace('fetching %c/%s', terminalElement.cid, url.pathname);
let block;
if (terminalElement.node == null) {
block = await toBuffer(blockstore.get(terminalElement.cid, options));
}
else if (terminalElement.type === 'object' || terminalElement.type === 'raw' || terminalElement.type === 'identity') {
block = terminalElement.node;
}
else {
block = dagPb.encode(terminalElement.node);
}
let contentType;
try {
// maybe convert output to different binary format
const result = convertOutput(terminalElement.cid, block, accept);
block = result.output;
contentType = result.contentType;
}
catch (err) {
this.log.error('could not decode object from block - %e', err);
return notAcceptableResponse(resource, requestedMimeTypes, getContentTypesForCid(terminalElement.cid));
}
const headers = {
'content-length': `${block.byteLength}`,
'content-type': contentType.mediaType,
'content-disposition': `${url.searchParams.get('download') === 'true' ? 'attachment' : contentType.disposition}; ${getContentDispositionFilename(url.searchParams.get('filename') ?? `${terminalElement.cid}${contentType.extension}`)}`,
'x-ipfs-roots': ipfsRoots.map(cid => cid.toV1()).join(','),
'x-content-type-options': 'nosniff',
'accept-ranges': 'bytes'
};
if (context.range != null) {
return partialContentResponse(resource, async function* (offset, length) {
yield block.subarray(offset, offset + length);
}, context.range, block.byteLength, {
headers
});
}
return okResponse(resource, block, {
headers
});
}
}
//# sourceMappingURL=plugin-handle-ipld.js.map