@rdfc/http-utils-processor-ts
Version:
HTTP util functions to be used within RDF-Connect
131 lines (130 loc) • 4.56 kB
JavaScript
import { Processor } from "@rdfc/js-runner";
import { HttpUtilsError } from "./error.js";
import { timeout } from "./util/timeout.js";
import { statusCodeAccepted } from "./util/status.js";
import { parseHeaders } from "./util/headers.js";
import { Auth } from "./auth/index.js";
import { cronify } from "./util/cronify.js";
import { readResponseAsStream } from "./util/response.js";
class HttpFetchArguments {
method = "GET";
headers = [];
acceptStatusCodes = ["200-300"];
closeOnEnd = true;
bodyCanBeEmpty = false;
timeOutMilliseconds = null;
auth = null;
cron = null;
runOnInit = false;
errorsAreFatal = true;
outputAsBuffer = false;
outputAsStream = false;
constructor(partial) {
Object.assign(this, partial);
if (!partial.bodyCanBeEmpty && partial.method == "HEAD") {
throw HttpUtilsError.illegalParameters("Cannot use HEAD method with bodyCanBeEmpty set to false");
}
if (partial.closeOnEnd && partial.cron) {
throw HttpUtilsError.illegalParameters("Cannot close stream when using cron.");
}
if (this.acceptStatusCodes.length === 0) {
this.acceptStatusCodes.push("200-300");
}
statusCodeAccepted(0, this.acceptStatusCodes);
}
getAuth() {
return this.auth ? Auth.from(this.auth) : null;
}
}
export class HttpFetch extends Processor {
arguments;
auth = null;
requests = [];
async init() {
this.url = (Array.isArray(this.url) ? this.url : [this.url]);
this.arguments = new HttpFetchArguments(this.options || {});
this.auth = this.arguments.getAuth();
const headers = parseHeaders(this.arguments.headers);
this.requests = this.url.map((x) => {
return new Request(x, {
method: this.arguments.method,
headers,
});
});
}
async transform() {
}
async produce() {
let executeAllRequests = async () => {
const promises = this.requests
.map((req) => this.executeRequest(req))
.map((promise) => promise.catch((err) => {
if (this.arguments.errorsAreFatal) {
throw err;
}
else {
console.error(err);
}
}));
await Promise.all(promises);
if (this.arguments.closeOnEnd) {
await this.writer.close();
}
};
if (this.arguments.cron) {
executeAllRequests = cronify(executeAllRequests, this.arguments.cron, this.arguments.runOnInit);
}
return await executeAllRequests();
}
async executeRequest(req) {
this.logger.debug(`Executing request to '${req.url}'...`);
if (this.auth) {
await this.auth.authorize(req);
}
const fetchPromise = fetch(req).catch((err) => {
throw HttpUtilsError.genericFetchError(err);
});
const res = await timeout(this.arguments.timeOutMilliseconds, fetchPromise).catch((err) => {
if (err === "timeout") {
throw HttpUtilsError.timeOutError(this.arguments.timeOutMilliseconds);
}
else {
throw err;
}
});
if (!statusCodeAccepted(res.status, this.arguments.acceptStatusCodes)) {
if (res.status === 401 && this.auth) {
throw HttpUtilsError.credentialIssue();
}
else if (res.status === 401) {
throw HttpUtilsError.unauthorizedError();
}
else {
throw HttpUtilsError.statusCodeNotAccepted(res.status);
}
}
if (!res.body) {
if (!this.arguments.bodyCanBeEmpty) {
throw HttpUtilsError.noBodyInResponse();
}
if (!this.arguments.cron && this.arguments.closeOnEnd) {
await this.writer.close();
}
return;
}
try {
if (this.arguments.outputAsStream) {
await this.writer.stream(readResponseAsStream(res));
}
else {
const body = this.arguments.outputAsBuffer
? { buffer: Buffer.from(await res.arrayBuffer()) }
: { string: await res.text() };
await this.writer.any(body);
}
}
catch (e) {
console.error(e);
}
}
}