134 lines
4.8 KiB
JavaScript
134 lines
4.8 KiB
JavaScript
import { Agent as HTTPAgent } from "http";
|
|
import { Agent as HTTPSAgent } from "https";
|
|
import { fetch } from "@buttercup/fetch";
|
|
import { getPatcher } from "./compat/patcher.js";
|
|
import { isReactNative, isWeb } from "./compat/env.js";
|
|
import { generateDigestAuthHeader, parseDigestAuth, responseIndicatesDigestAuth } from "./auth/digest.js";
|
|
import { cloneShallow, merge } from "./tools/merge.js";
|
|
import { mergeHeaders } from "./tools/headers.js";
|
|
import { requestDataToFetchBody } from "./tools/body.js";
|
|
import { AuthType } from "./types.js";
|
|
import { setupAuth } from "./auth/index.js";
|
|
function getFetchOptions(requestOptions) {
|
|
let headers = {};
|
|
// Handle standard options
|
|
const opts = {
|
|
method: requestOptions.method
|
|
};
|
|
if (requestOptions.headers) {
|
|
headers = mergeHeaders(headers, requestOptions.headers);
|
|
}
|
|
if (typeof requestOptions.data !== "undefined") {
|
|
const [body, newHeaders] = requestDataToFetchBody(requestOptions.data);
|
|
opts.body = body;
|
|
headers = mergeHeaders(headers, newHeaders);
|
|
}
|
|
if (requestOptions.signal) {
|
|
opts.signal = requestOptions.signal;
|
|
}
|
|
if (requestOptions.withCredentials) {
|
|
opts.credentials = "include";
|
|
}
|
|
// Check for node-specific options
|
|
if (!isWeb() && !isReactNative()) {
|
|
if (requestOptions.httpAgent || requestOptions.httpsAgent) {
|
|
opts.agent = (parsedURL) => {
|
|
if (parsedURL.protocol === "http:") {
|
|
return requestOptions.httpAgent || new HTTPAgent();
|
|
}
|
|
return requestOptions.httpsAgent || new HTTPSAgent();
|
|
};
|
|
}
|
|
}
|
|
// Attach headers
|
|
opts.headers = headers;
|
|
return opts;
|
|
}
|
|
export function prepareRequestOptions(requestOptions, context, userOptions) {
|
|
const finalOptions = cloneShallow(requestOptions);
|
|
finalOptions.headers = mergeHeaders(context.headers, finalOptions.headers || {}, userOptions.headers || {});
|
|
if (typeof userOptions.data !== "undefined") {
|
|
finalOptions.data = userOptions.data;
|
|
}
|
|
if (userOptions.signal) {
|
|
finalOptions.signal = userOptions.signal;
|
|
}
|
|
if (context.httpAgent) {
|
|
finalOptions.httpAgent = context.httpAgent;
|
|
}
|
|
if (context.httpsAgent) {
|
|
finalOptions.httpsAgent = context.httpsAgent;
|
|
}
|
|
if (context.digest) {
|
|
finalOptions._digest = context.digest;
|
|
}
|
|
if (typeof context.withCredentials === "boolean") {
|
|
finalOptions.withCredentials = context.withCredentials;
|
|
}
|
|
return finalOptions;
|
|
}
|
|
export async function request(requestOptions, context) {
|
|
if (context.authType === AuthType.Auto) {
|
|
return requestAuto(requestOptions, context);
|
|
}
|
|
if (requestOptions._digest) {
|
|
return requestDigest(requestOptions);
|
|
}
|
|
return requestStandard(requestOptions);
|
|
}
|
|
async function requestAuto(requestOptions, context) {
|
|
const response = await requestStandard(requestOptions);
|
|
if (response.ok) {
|
|
context.authType = AuthType.Password;
|
|
return response;
|
|
}
|
|
if (response.status == 401 && responseIndicatesDigestAuth(response)) {
|
|
context.authType = AuthType.Digest;
|
|
setupAuth(context, context.username, context.password, undefined, undefined);
|
|
requestOptions._digest = context.digest;
|
|
return requestDigest(requestOptions);
|
|
}
|
|
return response;
|
|
}
|
|
async function requestDigest(requestOptions) {
|
|
// Remove client's digest authentication object from request options
|
|
const _digest = requestOptions._digest;
|
|
delete requestOptions._digest;
|
|
// If client is already using digest authentication, include the digest authorization header
|
|
if (_digest.hasDigestAuth) {
|
|
requestOptions = merge(requestOptions, {
|
|
headers: {
|
|
Authorization: generateDigestAuthHeader(requestOptions, _digest)
|
|
}
|
|
});
|
|
}
|
|
// Perform digest request + check
|
|
const response = await requestStandard(requestOptions);
|
|
if (response.status == 401) {
|
|
_digest.hasDigestAuth = parseDigestAuth(response, _digest);
|
|
if (_digest.hasDigestAuth) {
|
|
requestOptions = merge(requestOptions, {
|
|
headers: {
|
|
Authorization: generateDigestAuthHeader(requestOptions, _digest)
|
|
}
|
|
});
|
|
const response2 = await requestStandard(requestOptions);
|
|
if (response2.status == 401) {
|
|
_digest.hasDigestAuth = false;
|
|
}
|
|
else {
|
|
_digest.nc++;
|
|
}
|
|
return response2;
|
|
}
|
|
}
|
|
else {
|
|
_digest.nc++;
|
|
}
|
|
return response;
|
|
}
|
|
function requestStandard(requestOptions) {
|
|
const patcher = getPatcher();
|
|
return patcher.patchInline("request", (options) => patcher.patchInline("fetch", fetch, options.url, getFetchOptions(options)), requestOptions);
|
|
}
|