@scrypted/auth-fetch 1.0.2

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.
@@ -0,0 +1,10 @@
1
+ /// <reference types="node" />
2
+ import { HttpFetchOptions, HttpFetchResponseType, fetcher } from '../../../server/src/fetch';
3
+ export interface AuthFetchCredentialState {
4
+ username: string;
5
+ password: string;
6
+ }
7
+ export interface AuthFetchOptions {
8
+ credential?: AuthFetchCredentialState;
9
+ }
10
+ export declare function createAuthFetch<B, M>(h: fetcher<B, M>, parser: (body: M, responseType: HttpFetchResponseType | undefined) => Promise<any>): <T extends HttpFetchOptions<B>>(options: T & AuthFetchOptions) => Promise<import("../../../server/src/fetch").HttpFetchResponse<T extends import("../../../server/src/fetch").HttpFetchBufferOptions<B> ? Buffer : T extends import("../../../server/src/fetch").HttpFetchTextOptions<B> ? string : T extends import("../../../server/src/fetch").HttpFetchReadableOptions<B> ? M : T extends import("../../../server/src/fetch").HttpFetchJsonOptions<B> ? any : Buffer>>;
@@ -0,0 +1,136 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ Object.defineProperty(exports, "__esModule", { value: true });
26
+ exports.createAuthFetch = void 0;
27
+ const fetch_1 = require("../../../server/src/fetch");
28
+ async function getAuth(options, url, method) {
29
+ if (!options.credential)
30
+ return;
31
+ const { BASIC, DIGEST, parseWWWAuthenticateHeader } = await Promise.resolve().then(() => __importStar(require('http-auth-utils')));
32
+ const credential = options.credential;
33
+ const { digest, basic } = credential;
34
+ if (digest) {
35
+ credential.count ||= 0;
36
+ ++credential.count;
37
+ const nc = ('00000000' + credential.count).slice(-8);
38
+ const cnonce = [...Array(24)].map(() => Math.floor(Math.random() * 16).toString(16)).join('');
39
+ const parsedURL = new URL(url);
40
+ const uri = parsedURL.pathname + parsedURL.search;
41
+ const { DIGEST, buildAuthorizationHeader } = await Promise.resolve().then(() => __importStar(require('http-auth-utils')));
42
+ const response = DIGEST.computeHash({
43
+ username: options.credential.username,
44
+ password: options.credential.password,
45
+ method,
46
+ uri,
47
+ nc,
48
+ cnonce,
49
+ algorithm: 'MD5',
50
+ qop: digest.data.qop,
51
+ ...digest.data,
52
+ });
53
+ const header = buildAuthorizationHeader(DIGEST, {
54
+ username: options.credential.username,
55
+ uri,
56
+ nc,
57
+ cnonce,
58
+ algorithm: digest.data.algorithm,
59
+ qop: digest.data.qop,
60
+ response,
61
+ ...digest.data,
62
+ });
63
+ return header;
64
+ }
65
+ else if (basic) {
66
+ const { BASIC, buildAuthorizationHeader } = await Promise.resolve().then(() => __importStar(require('http-auth-utils')));
67
+ const header = buildAuthorizationHeader(BASIC, {
68
+ username: options.credential.username,
69
+ password: options.credential.password,
70
+ });
71
+ return header;
72
+ }
73
+ }
74
+ function createAuthFetch(h, parser) {
75
+ const authHttpFetch = async (options) => {
76
+ const method = (0, fetch_1.getFetchMethod)(options);
77
+ const headers = (0, fetch_1.createHeadersArray)(options.headers);
78
+ options.headers = headers;
79
+ (0, fetch_1.setDefaultHttpFetchAccept)(headers, options.responseType);
80
+ const initialHeader = await getAuth(options, options.url, method);
81
+ // try to provide an authorization if a session exists, but don't override Authorization if provided already.
82
+ // 401 will trigger a proper auth.
83
+ if (initialHeader && !(0, fetch_1.hasHeader)(headers, 'Authorization'))
84
+ (0, fetch_1.setHeader)(headers, 'Authorization', initialHeader);
85
+ const controller = new AbortController();
86
+ options.signal?.addEventListener('abort', () => controller.abort(options.signal?.reason));
87
+ const initialResponse = await h({
88
+ ...options,
89
+ signal: controller.signal,
90
+ // need to intercept the status code to check for 401.
91
+ // all other status codes will be handled according to the initial request options.
92
+ checkStatusCode(statusCode) {
93
+ // can handle a 401 if an credential is provided.
94
+ // however, not providing a credential is also valid, and should
95
+ // fall through to the normal response handling which may be interested
96
+ // in the 401 response.
97
+ if (statusCode === 401 && options.credential)
98
+ return true;
99
+ if (options?.checkStatusCode === undefined || options?.checkStatusCode) {
100
+ const checker = typeof options?.checkStatusCode === 'function' ? options.checkStatusCode : fetch_1.checkStatus;
101
+ return checker(statusCode);
102
+ }
103
+ return true;
104
+ },
105
+ responseType: 'readable',
106
+ });
107
+ // if it's not a 401, just return the response.
108
+ if (initialResponse.statusCode !== 401) {
109
+ return {
110
+ ...initialResponse,
111
+ body: await parser(initialResponse.body, options.responseType),
112
+ };
113
+ }
114
+ let authenticateHeaders = initialResponse.headers.get('www-authenticate');
115
+ if (!authenticateHeaders)
116
+ throw new Error('Did not find WWW-Authenticate header.');
117
+ if (typeof authenticateHeaders === 'string')
118
+ authenticateHeaders = [authenticateHeaders];
119
+ const { BASIC, DIGEST, parseWWWAuthenticateHeader } = await Promise.resolve().then(() => __importStar(require('http-auth-utils')));
120
+ const parsedHeaders = authenticateHeaders.map(h => parseWWWAuthenticateHeader(h));
121
+ const digest = parsedHeaders.find(p => p.type === 'Digest');
122
+ const basic = parsedHeaders.find(p => p.type === 'Basic');
123
+ const credential = options.credential;
124
+ credential.digest = digest;
125
+ credential.basic = basic;
126
+ if (!digest && !basic)
127
+ throw new Error(`Unknown WWW-Authenticate type: ${parsedHeaders[0]?.type}`);
128
+ const header = await getAuth(options, options.url, method);
129
+ if (header)
130
+ (0, fetch_1.setHeader)(headers, 'Authorization', header);
131
+ return h(options);
132
+ };
133
+ return authHttpFetch;
134
+ }
135
+ exports.createAuthFetch = createAuthFetch;
136
+ //# sourceMappingURL=auth-fetch.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth-fetch.js","sourceRoot":"","sources":["../../../../src/auth-fetch.ts"],"names":[],"mappings":"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","sourcesContent":["import { HttpFetchOptions, HttpFetchResponseType, checkStatus, createHeadersArray, fetcher, getFetchMethod, hasHeader, setDefaultHttpFetchAccept, setHeader } from '../../../server/src/fetch';\n\nexport interface AuthFetchCredentialState {\n username: string;\n password: string;\n}\n\nexport interface AuthFetchOptions {\n credential?: AuthFetchCredentialState;\n}\n\nasync function getAuth(options: AuthFetchOptions, url: string | URL, method: string) {\n if (!options.credential)\n return;\n\n const { BASIC, DIGEST, parseWWWAuthenticateHeader } = await import('http-auth-utils');\n\n const credential = options.credential as AuthFetchCredentialState & {\n count?: number;\n digest?: ReturnType<typeof parseWWWAuthenticateHeader<typeof DIGEST>>;\n basic?: ReturnType<typeof parseWWWAuthenticateHeader<typeof BASIC>>;\n };\n const { digest, basic } = credential;\n\n if (digest) {\n credential.count ||= 0;\n ++credential.count;\n const nc = ('00000000' + credential.count).slice(-8);\n const cnonce = [...Array(24)].map(() => Math.floor(Math.random() * 16).toString(16)).join('');\n const parsedURL = new URL(url);\n const uri = parsedURL.pathname + parsedURL.search;\n\n const { DIGEST, buildAuthorizationHeader } = await import('http-auth-utils');\n\n const response = DIGEST.computeHash({\n username: options.credential.username,\n password: options.credential.password,\n method,\n uri,\n nc,\n cnonce,\n algorithm: 'MD5',\n qop: digest.data.qop!,\n ...digest.data,\n });\n\n const header = buildAuthorizationHeader(DIGEST, {\n username: options.credential.username,\n uri,\n nc,\n cnonce,\n algorithm: digest.data.algorithm!,\n qop: digest.data.qop!,\n response,\n ...digest.data,\n });\n\n return header;\n }\n else if (basic) {\n const { BASIC, buildAuthorizationHeader } = await import('http-auth-utils');\n\n const header = buildAuthorizationHeader(BASIC, {\n username: options.credential.username,\n password: options.credential.password,\n });\n\n return header;\n }\n}\n\nexport function createAuthFetch<B, M>(\n h: fetcher<B, M>,\n parser: (body: M, responseType: HttpFetchResponseType | undefined) => Promise<any>\n) {\n const authHttpFetch = async <T extends HttpFetchOptions<B>>(options: T & AuthFetchOptions): ReturnType<typeof h<T>> => {\n const method = getFetchMethod(options);\n const headers = createHeadersArray(options.headers);\n options.headers = headers;\n setDefaultHttpFetchAccept(headers, options.responseType);\n\n const initialHeader = await getAuth(options, options.url, method);\n // try to provide an authorization if a session exists, but don't override Authorization if provided already.\n // 401 will trigger a proper auth.\n if (initialHeader && !hasHeader(headers, 'Authorization'))\n setHeader(headers, 'Authorization', initialHeader);\n\n\n const controller = new AbortController();\n options.signal?.addEventListener('abort', () => controller.abort(options.signal?.reason));\n\n const initialResponse = await h({\n ...options,\n signal: controller.signal,\n // need to intercept the status code to check for 401.\n // all other status codes will be handled according to the initial request options.\n checkStatusCode(statusCode) {\n // can handle a 401 if an credential is provided.\n // however, not providing a credential is also valid, and should\n // fall through to the normal response handling which may be interested\n // in the 401 response.\n if (statusCode === 401 && options.credential)\n return true;\n if (options?.checkStatusCode === undefined || options?.checkStatusCode) {\n const checker = typeof options?.checkStatusCode === 'function' ? options.checkStatusCode : checkStatus;\n return checker(statusCode);\n }\n return true;\n },\n responseType: 'readable',\n });\n\n // if it's not a 401, just return the response.\n if (initialResponse.statusCode !== 401) {\n return {\n ...initialResponse,\n body: await parser(initialResponse.body, options.responseType),\n };\n }\n\n let authenticateHeaders: string | string[] | null = initialResponse.headers.get('www-authenticate');\n if (!authenticateHeaders)\n throw new Error('Did not find WWW-Authenticate header.');\n\n\n if (typeof authenticateHeaders === 'string')\n authenticateHeaders = [authenticateHeaders];\n\n const { BASIC, DIGEST, parseWWWAuthenticateHeader } = await import('http-auth-utils');\n const parsedHeaders = authenticateHeaders.map(h => parseWWWAuthenticateHeader(h));\n\n const digest = parsedHeaders.find(p => p.type === 'Digest') as ReturnType<typeof parseWWWAuthenticateHeader<typeof DIGEST>>;\n const basic = parsedHeaders.find(p => p.type === 'Basic') as ReturnType<typeof parseWWWAuthenticateHeader<typeof BASIC>>;\n const credential = options.credential as AuthFetchCredentialState & {\n count?: number;\n digest?: ReturnType<typeof parseWWWAuthenticateHeader<typeof DIGEST>>;\n basic?: ReturnType<typeof parseWWWAuthenticateHeader<typeof BASIC>>;\n };\n\n credential.digest = digest;\n credential.basic = basic;\n\n if (!digest && !basic)\n throw new Error(`Unknown WWW-Authenticate type: ${parsedHeaders[0]?.type}`);\n\n const header = await getAuth(options, options.url, method);\n if (header)\n setHeader(headers, 'Authorization', header);\n\n return h(options);\n }\n\n return authHttpFetch;\n}\n"]}
@@ -0,0 +1,6 @@
1
+ /// <reference types="node" />
2
+ /// <reference types="node" />
3
+ /// <reference types="node" />
4
+ import type { Readable } from "stream";
5
+ import type { IncomingMessage } from "http";
6
+ export declare const authFetch: <T extends import("../../../server/src/fetch").HttpFetchOptions<Readable>>(options: T & import("./auth-fetch").AuthFetchOptions) => Promise<import("../../../server/src/fetch").HttpFetchResponse<T extends import("../../../server/src/fetch").HttpFetchBufferOptions<Readable> ? Buffer : T extends import("../../../server/src/fetch").HttpFetchTextOptions<Readable> ? string : T extends import("../../../server/src/fetch").HttpFetchReadableOptions<Readable> ? IncomingMessage : T extends import("../../../server/src/fetch").HttpFetchJsonOptions<Readable> ? any : Buffer>>;
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.authFetch = void 0;
4
+ const auth_fetch_1 = require("./auth-fetch");
5
+ const http_fetch_1 = require("../../../server/src/fetch/http-fetch");
6
+ const fetch_1 = require("../../../server/src/fetch");
7
+ function init() {
8
+ try {
9
+ require('net');
10
+ require('events');
11
+ return (0, auth_fetch_1.createAuthFetch)(http_fetch_1.httpFetch, http_fetch_1.httpFetchParseIncomingMessage);
12
+ }
13
+ catch (e) {
14
+ }
15
+ return (0, auth_fetch_1.createAuthFetch)(fetch_1.domFetch, fetch_1.domFetchParseIncomingMessage);
16
+ }
17
+ exports.authFetch = init();
18
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/index.ts"],"names":[],"mappings":";;;AAAA,6CAA+C;AAC/C,qEAAgG;AAGhG,qDAAmF;AAEnF,SAAS,IAAI;IACT,IAAI,CAAC;QACD,OAAO,CAAC,KAAK,CAAC,CAAC;QACf,OAAO,CAAC,QAAQ,CAAC,CAAC;QAClB,OAAO,IAAA,4BAAe,EAA4B,sBAAS,EAAE,0CAA6B,CAAC,CAAC;IAChG,CAAC;IACD,OAAO,CAAC,EAAE,CAAC;IACX,CAAC;IAED,OAAO,IAAA,4BAAe,EAAqB,gBAAQ,EAAE,oCAA4B,CAAC,CAAC;AACvF,CAAC;AAEY,QAAA,SAAS,GAAG,IAAI,EAAE,CAAC","sourcesContent":["import { createAuthFetch } from \"./auth-fetch\";\nimport { httpFetch, httpFetchParseIncomingMessage } from '../../../server/src/fetch/http-fetch';\nimport type { Readable } from \"stream\";\nimport type { IncomingMessage } from \"http\";\nimport { domFetch, domFetchParseIncomingMessage } from \"../../../server/src/fetch\";\n\nfunction init() {\n try {\n require('net');\n require('events');\n return createAuthFetch<Readable, IncomingMessage>(httpFetch, httpFetchParseIncomingMessage);\n }\n catch (e) {\n }\n\n return createAuthFetch<BodyInit, Response>(domFetch, domFetchParseIncomingMessage);\n}\n\nexport const authFetch = init();\n"]}
@@ -0,0 +1,13 @@
1
+ /// <reference types="node" />
2
+ /// <reference types="node" />
3
+ /// <reference types="node" />
4
+ import type { IncomingMessage } from 'http';
5
+ import type { Readable } from 'stream';
6
+ import { HttpFetchBufferOptions, HttpFetchJsonOptions, HttpFetchOptions, HttpFetchReadableOptions, HttpFetchResponse, HttpFetchResponseType, HttpFetchTextOptions } from '.';
7
+ export type { HttpFetchBufferOptions, HttpFetchJsonOptions, HttpFetchOptions, HttpFetchReadableOptions, HttpFetchResponse, HttpFetchResponseType, HttpFetchTextOptions, checkStatus, setDefaultHttpFetchAccept } from '.';
8
+ export interface FetchParser<T> {
9
+ parse(message: IncomingMessage): Promise<T>;
10
+ }
11
+ export declare function getHttpFetchParser(responseType: HttpFetchResponseType | undefined): FetchParser<any>;
12
+ export declare function httpFetchParseIncomingMessage(readable: IncomingMessage, responseType: HttpFetchResponseType | undefined): Promise<any>;
13
+ export declare function httpFetch<T extends HttpFetchOptions<Readable>>(options: T): Promise<HttpFetchResponse<T extends HttpFetchBufferOptions<Readable> ? Buffer : T extends HttpFetchTextOptions<Readable> ? string : T extends HttpFetchReadableOptions<Readable> ? IncomingMessage : T extends HttpFetchJsonOptions<Readable> ? any : Buffer>>;
@@ -0,0 +1,124 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.httpFetch = exports.httpFetchParseIncomingMessage = exports.getHttpFetchParser = void 0;
4
+ const _1 = require(".");
5
+ async function readMessageBuffer(response) {
6
+ const buffers = [];
7
+ response.on('data', buffer => buffers.push(buffer));
8
+ const { once } = require('events');
9
+ await once(response, 'end');
10
+ return Buffer.concat(buffers);
11
+ }
12
+ const TextParser = {
13
+ async parse(message) {
14
+ return (await readMessageBuffer(message)).toString();
15
+ }
16
+ };
17
+ const JSONParser = {
18
+ async parse(message) {
19
+ return JSON.parse((await readMessageBuffer(message)).toString());
20
+ }
21
+ };
22
+ const BufferParser = {
23
+ async parse(message) {
24
+ return readMessageBuffer(message);
25
+ }
26
+ };
27
+ const StreamParser = {
28
+ async parse(message) {
29
+ return message;
30
+ }
31
+ };
32
+ function getHttpFetchParser(responseType) {
33
+ switch (responseType) {
34
+ case 'json':
35
+ return JSONParser;
36
+ case 'text':
37
+ return TextParser;
38
+ case 'readable':
39
+ return StreamParser;
40
+ }
41
+ return BufferParser;
42
+ }
43
+ exports.getHttpFetchParser = getHttpFetchParser;
44
+ function httpFetchParseIncomingMessage(readable, responseType) {
45
+ return getHttpFetchParser(responseType).parse(readable);
46
+ }
47
+ exports.httpFetchParseIncomingMessage = httpFetchParseIncomingMessage;
48
+ async function httpFetch(options) {
49
+ const headers = (0, _1.createHeadersArray)(options.headers);
50
+ (0, _1.setDefaultHttpFetchAccept)(headers, options.responseType);
51
+ const { once } = require('events');
52
+ const { PassThrough, Readable } = require('stream');
53
+ const { http, https } = require('follow-redirects');
54
+ const { url } = options;
55
+ const isSecure = url.toString().startsWith('https:');
56
+ const proto = isSecure ? https : http;
57
+ let { body } = options;
58
+ if (body && !(body instanceof Readable)) {
59
+ const newBody = new PassThrough();
60
+ newBody.write(Buffer.from((0, _1.createStringOrBufferBody)(headers, body)));
61
+ newBody.end();
62
+ body = newBody;
63
+ }
64
+ let controller;
65
+ let timeout;
66
+ if (options.timeout) {
67
+ controller = new AbortController();
68
+ timeout = setTimeout(() => controller.abort(), options.timeout);
69
+ options.signal?.addEventListener('abort', () => controller.abort(options.signal?.reason));
70
+ }
71
+ const signal = controller?.signal || options.signal;
72
+ signal?.addEventListener('abort', () => request.destroy(new Error(options.signal?.reason || 'abort')));
73
+ const nodeHeaders = {};
74
+ for (const [k, v] of headers) {
75
+ if (nodeHeaders[k]) {
76
+ nodeHeaders[k].push(v);
77
+ }
78
+ else {
79
+ nodeHeaders[k] = [v];
80
+ }
81
+ }
82
+ const request = proto.request(url, {
83
+ method: (0, _1.getFetchMethod)(options),
84
+ rejectUnauthorized: options.rejectUnauthorized,
85
+ family: options.family,
86
+ headers: nodeHeaders,
87
+ signal,
88
+ timeout: options.timeout,
89
+ });
90
+ if (body)
91
+ body.pipe(request);
92
+ else
93
+ request.end();
94
+ try {
95
+ const [response] = await once(request, 'response');
96
+ if (options?.checkStatusCode === undefined || options?.checkStatusCode) {
97
+ try {
98
+ const checker = typeof options?.checkStatusCode === 'function' ? options.checkStatusCode : _1.checkStatus;
99
+ if (!response.statusCode || !checker(response.statusCode))
100
+ throw new Error(`http response statusCode ${response.statusCode}`);
101
+ }
102
+ catch (e) {
103
+ readMessageBuffer(response).catch(() => { });
104
+ throw e;
105
+ }
106
+ }
107
+ const incomingHeaders = new Headers();
108
+ for (const [k, v] of Object.entries(response.headers)) {
109
+ for (const vv of (typeof v === 'string' ? [v] : v)) {
110
+ incomingHeaders.append(k, vv);
111
+ }
112
+ }
113
+ return {
114
+ statusCode: response.statusCode,
115
+ headers: incomingHeaders,
116
+ body: await httpFetchParseIncomingMessage(response, options.responseType),
117
+ };
118
+ }
119
+ finally {
120
+ clearTimeout(timeout);
121
+ }
122
+ }
123
+ exports.httpFetch = httpFetch;
124
+ //# sourceMappingURL=http-fetch.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http-fetch.js","sourceRoot":"","sources":["../../../../../../server/src/fetch/http-fetch.ts"],"names":[],"mappings":"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","sourcesContent":["import type events from 'events';\nimport type followRedirects from 'follow-redirects';\nimport type { IncomingMessage } from 'http';\nimport type stream from 'stream';\nimport type { Readable } from 'stream';\nimport { HttpFetchBufferOptions, HttpFetchJsonOptions, HttpFetchOptions, HttpFetchReadableOptions, HttpFetchResponse, HttpFetchResponseType, HttpFetchTextOptions, checkStatus, createHeadersArray, createStringOrBufferBody, getFetchMethod, setDefaultHttpFetchAccept } from '.';\nexport type { HttpFetchBufferOptions, HttpFetchJsonOptions, HttpFetchOptions, HttpFetchReadableOptions, HttpFetchResponse, HttpFetchResponseType, HttpFetchTextOptions, checkStatus, setDefaultHttpFetchAccept } from '.';\n\nasync function readMessageBuffer(response: IncomingMessage) {\n const buffers: Buffer[] = [];\n response.on('data', buffer => buffers.push(buffer));\n const { once } = require('events') as typeof events;\n await once(response, 'end');\n return Buffer.concat(buffers);\n}\n\nexport interface FetchParser<T> {\n parse(message: IncomingMessage): Promise<T>;\n}\n\nconst TextParser: FetchParser<string> = {\n async parse(message: IncomingMessage) {\n return (await readMessageBuffer(message)).toString();\n }\n}\nconst JSONParser: FetchParser<any> = {\n async parse(message: IncomingMessage) {\n return JSON.parse((await readMessageBuffer(message)).toString());\n }\n}\n\nconst BufferParser: FetchParser<Buffer> = {\n async parse(message: IncomingMessage) {\n return readMessageBuffer(message);\n }\n}\n\nconst StreamParser: FetchParser<IncomingMessage> = {\n async parse(message: IncomingMessage) {\n return message;\n }\n}\n\nexport function getHttpFetchParser(responseType: HttpFetchResponseType | undefined) {\n switch (responseType) {\n case 'json':\n return JSONParser;\n case 'text':\n return TextParser;\n case 'readable':\n return StreamParser;\n }\n return BufferParser;\n}\n\nexport function httpFetchParseIncomingMessage(readable: IncomingMessage, responseType: HttpFetchResponseType | undefined) {\n return getHttpFetchParser(responseType).parse(readable);\n}\n\nexport async function httpFetch<T extends HttpFetchOptions<Readable>>(options: T): Promise<HttpFetchResponse<\n // first one serves as default.\n T extends HttpFetchBufferOptions<Readable> ? Buffer\n : T extends HttpFetchTextOptions<Readable> ? string\n : T extends HttpFetchReadableOptions<Readable> ? IncomingMessage\n : T extends HttpFetchJsonOptions<Readable> ? any : Buffer\n>> {\n const headers = createHeadersArray(options.headers);\n setDefaultHttpFetchAccept(headers, options.responseType);\n\n const { once } = require('events') as typeof events;\n const { PassThrough, Readable } = require('stream') as typeof stream;\n const { http, https } = require('follow-redirects') as typeof followRedirects;\n\n const { url } = options;\n const isSecure = url.toString().startsWith('https:');\n const proto = isSecure ? https : http;\n\n let { body } = options;\n if (body && !(body instanceof Readable)) {\n const newBody = new PassThrough();\n newBody.write(Buffer.from(createStringOrBufferBody(headers, body)));\n newBody.end();\n body = newBody;\n }\n\n let controller: AbortController | undefined;\n let timeout: NodeJS.Timeout;\n if (options.timeout) {\n controller = new AbortController();\n timeout = setTimeout(() => controller!.abort(), options.timeout);\n\n options.signal?.addEventListener('abort', () => controller!.abort(options.signal?.reason));\n }\n\n const signal = controller?.signal || options.signal;\n signal?.addEventListener('abort', () => request.destroy(new Error(options.signal?.reason || 'abort')));\n\n const nodeHeaders: Record<string, string[]> = {};\n for (const [k, v] of headers) {\n if (nodeHeaders[k]) {\n nodeHeaders[k].push(v);\n }\n else {\n nodeHeaders[k] = [v];\n }\n }\n\n const request = proto.request(url, {\n method: getFetchMethod(options),\n rejectUnauthorized: options.rejectUnauthorized,\n family: options.family,\n headers: nodeHeaders,\n signal,\n timeout: options.timeout,\n });\n\n if (body)\n body.pipe(request);\n else\n request.end();\n\n try {\n const [response] = await once(request, 'response') as [IncomingMessage];\n\n\n if (options?.checkStatusCode === undefined || options?.checkStatusCode) {\n try {\n const checker = typeof options?.checkStatusCode === 'function' ? options.checkStatusCode : checkStatus;\n if (!response.statusCode || !checker(response.statusCode))\n throw new Error(`http response statusCode ${response.statusCode}`);\n }\n catch (e) {\n readMessageBuffer(response).catch(() => { });\n throw e;\n }\n }\n\n const incomingHeaders = new Headers();\n for (const [k, v] of Object.entries(response.headers)) {\n for (const vv of (typeof v === 'string' ? [v] : v!)) {\n incomingHeaders.append(k, vv)\n }\n }\n\n return {\n statusCode: response.statusCode!,\n headers: incomingHeaders,\n body: await httpFetchParseIncomingMessage(response, options.responseType),\n };\n }\n finally {\n clearTimeout(timeout!);\n }\n}\n\n"]}
@@ -0,0 +1,57 @@
1
+ /// <reference types="node" />
2
+ export type HttpFetchResponseType = 'json' | 'text' | 'buffer' | 'readable';
3
+ export interface HttpFetchOptionsBase<B> {
4
+ url: string | URL;
5
+ family?: 4 | 6;
6
+ method?: string;
7
+ headers?: HeadersInit;
8
+ signal?: AbortSignal;
9
+ timeout?: number;
10
+ rejectUnauthorized?: boolean;
11
+ /**
12
+ * Checks the status code. Defaults to true.
13
+ */
14
+ checkStatusCode?: boolean | ((statusCode: number) => boolean);
15
+ body?: B | string | ArrayBufferView | any;
16
+ withCredentials?: boolean;
17
+ }
18
+ export interface HttpFetchJsonOptions<B> extends HttpFetchOptionsBase<B> {
19
+ responseType: 'json';
20
+ }
21
+ export interface HttpFetchBufferOptions<B> extends HttpFetchOptionsBase<B> {
22
+ responseType: 'buffer';
23
+ }
24
+ export interface HttpFetchDefaultOptions<B> extends HttpFetchOptionsBase<B> {
25
+ responseType?: undefined;
26
+ }
27
+ export interface HttpFetchTextOptions<B> extends HttpFetchOptionsBase<B> {
28
+ responseType: 'text';
29
+ }
30
+ export interface HttpFetchReadableOptions<B> extends HttpFetchOptionsBase<B> {
31
+ responseType: 'readable';
32
+ }
33
+ export type HttpFetchOptions<B> = HttpFetchJsonOptions<B> | HttpFetchBufferOptions<B> | HttpFetchDefaultOptions<B> | HttpFetchTextOptions<B> | HttpFetchReadableOptions<B>;
34
+ export declare function fetchStatusCodeOk(statusCode: number): boolean;
35
+ export declare function checkStatus(statusCode: number): boolean;
36
+ export declare function getFetchMethod(options: HttpFetchOptions<any>): string;
37
+ export interface HttpFetchResponse<T> {
38
+ statusCode: number;
39
+ headers: Headers;
40
+ body: T;
41
+ }
42
+ export type fetcher<B, M> = <T extends HttpFetchOptions<B>>(options: T) => Promise<HttpFetchResponse<T extends HttpFetchBufferOptions<B> ? Buffer : T extends HttpFetchTextOptions<B> ? string : T extends HttpFetchReadableOptions<B> ? M : T extends HttpFetchJsonOptions<B> ? any : Buffer>>;
43
+ export declare function getHttpFetchAccept(responseType: HttpFetchResponseType | undefined): "application/json" | "text/plain" | undefined;
44
+ export declare function hasHeader(headers: [string, string][], key: string): [string, string] | undefined;
45
+ export declare function removeHeader(headers: [string, string][], key: string): void;
46
+ export declare function setHeader(headers: [string, string][], key: string, value: string): void;
47
+ export declare function setDefaultHttpFetchAccept(headers: [string, string][], responseType: HttpFetchResponseType | undefined): void;
48
+ export declare function createHeadersArray(headers: HeadersInit | undefined): [string, string][];
49
+ /**
50
+ *
51
+ * @param headers
52
+ * @param body
53
+ * @returns Returns the body and Content-Type header that was set.
54
+ */
55
+ export declare function createStringOrBufferBody(headers: [string, string][], body: any): any;
56
+ export declare function domFetchParseIncomingMessage(response: Response, responseType: HttpFetchResponseType | undefined): Promise<any>;
57
+ export declare function domFetch<T extends HttpFetchOptions<BodyInit>>(options: T): Promise<HttpFetchResponse<T extends HttpFetchBufferOptions<BodyInit> ? Buffer : T extends HttpFetchTextOptions<BodyInit> ? string : T extends HttpFetchReadableOptions<BodyInit> ? Response : T extends HttpFetchJsonOptions<BodyInit> ? any : Buffer>>;
@@ -0,0 +1,158 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.domFetch = exports.domFetchParseIncomingMessage = exports.createStringOrBufferBody = exports.createHeadersArray = exports.setDefaultHttpFetchAccept = exports.setHeader = exports.removeHeader = exports.hasHeader = exports.getHttpFetchAccept = exports.getFetchMethod = exports.checkStatus = exports.fetchStatusCodeOk = void 0;
4
+ function fetchStatusCodeOk(statusCode) {
5
+ return statusCode >= 200 && statusCode <= 299;
6
+ }
7
+ exports.fetchStatusCodeOk = fetchStatusCodeOk;
8
+ function checkStatus(statusCode) {
9
+ if (!fetchStatusCodeOk(statusCode))
10
+ throw new Error(`http response statusCode ${statusCode}`);
11
+ return true;
12
+ }
13
+ exports.checkStatus = checkStatus;
14
+ function getFetchMethod(options) {
15
+ const method = options.method || (options.body ? 'POST' : 'GET');
16
+ return method;
17
+ }
18
+ exports.getFetchMethod = getFetchMethod;
19
+ function getHttpFetchAccept(responseType) {
20
+ switch (responseType) {
21
+ case 'json':
22
+ return 'application/json';
23
+ case 'text':
24
+ return 'text/plain';
25
+ }
26
+ return;
27
+ }
28
+ exports.getHttpFetchAccept = getHttpFetchAccept;
29
+ function hasHeader(headers, key) {
30
+ key = key.toLowerCase();
31
+ return headers.find(([k]) => k.toLowerCase() === key);
32
+ }
33
+ exports.hasHeader = hasHeader;
34
+ function removeHeader(headers, key) {
35
+ key = key.toLowerCase();
36
+ const filteredHeaders = headers.filter(([headerKey, _]) => headerKey.toLowerCase() !== key);
37
+ headers.length = 0;
38
+ filteredHeaders.forEach(header => headers.push(header));
39
+ }
40
+ exports.removeHeader = removeHeader;
41
+ function setHeader(headers, key, value) {
42
+ removeHeader(headers, key);
43
+ headers.push([key, value]);
44
+ }
45
+ exports.setHeader = setHeader;
46
+ function setDefaultHttpFetchAccept(headers, responseType) {
47
+ if (hasHeader(headers, 'Accept'))
48
+ return;
49
+ const accept = getHttpFetchAccept(responseType);
50
+ if (accept)
51
+ setHeader(headers, 'Accept', accept);
52
+ }
53
+ exports.setDefaultHttpFetchAccept = setDefaultHttpFetchAccept;
54
+ function createHeadersArray(headers) {
55
+ const headersArray = [];
56
+ if (!headers)
57
+ return headersArray;
58
+ if (headers instanceof Headers) {
59
+ for (const [k, v] of headers.entries()) {
60
+ headersArray.push([k, v]);
61
+ }
62
+ return headersArray;
63
+ }
64
+ if (headers instanceof Array) {
65
+ for (const [k, v] of headers) {
66
+ headersArray.push([k, v]);
67
+ }
68
+ return headersArray;
69
+ }
70
+ for (const k of Object.keys(headers)) {
71
+ const v = headers[k];
72
+ headersArray.push([k, v]);
73
+ }
74
+ return headersArray;
75
+ }
76
+ exports.createHeadersArray = createHeadersArray;
77
+ /**
78
+ *
79
+ * @param headers
80
+ * @param body
81
+ * @returns Returns the body and Content-Type header that was set.
82
+ */
83
+ function createStringOrBufferBody(headers, body) {
84
+ let contentType;
85
+ if (typeof body === 'object') {
86
+ body = JSON.stringify(body);
87
+ contentType = 'application/json';
88
+ }
89
+ else if (typeof body === 'string') {
90
+ contentType = 'text/plain';
91
+ }
92
+ if (contentType && !hasHeader(headers, 'Content-Type'))
93
+ setHeader(headers, 'Content-Type', contentType);
94
+ if (!hasHeader(headers, 'Content-Length')) {
95
+ body = Buffer.from(body);
96
+ setHeader(headers, 'Content-Length', body.length.toString());
97
+ }
98
+ return body;
99
+ }
100
+ exports.createStringOrBufferBody = createStringOrBufferBody;
101
+ async function domFetchParseIncomingMessage(response, responseType) {
102
+ switch (responseType) {
103
+ case 'json':
104
+ return response.json();
105
+ case 'text':
106
+ return response.text();
107
+ case 'readable':
108
+ return response;
109
+ }
110
+ return new Uint8Array(await response.arrayBuffer());
111
+ }
112
+ exports.domFetchParseIncomingMessage = domFetchParseIncomingMessage;
113
+ async function domFetch(options) {
114
+ const headers = createHeadersArray(options.headers);
115
+ setDefaultHttpFetchAccept(headers, options.responseType);
116
+ let { body } = options;
117
+ if (body && !(body instanceof ReadableStream)) {
118
+ body = createStringOrBufferBody(headers, body);
119
+ }
120
+ let controller;
121
+ let timeout;
122
+ if (options.timeout) {
123
+ controller = new AbortController();
124
+ timeout = setTimeout(() => controller.abort(), options.timeout);
125
+ options.signal?.addEventListener('abort', () => controller.abort(options.signal?.reason));
126
+ }
127
+ try {
128
+ const { url } = options;
129
+ const response = await fetch(url, {
130
+ method: getFetchMethod(options),
131
+ credentials: options.withCredentials ? 'include' : undefined,
132
+ headers,
133
+ signal: controller?.signal || options.signal,
134
+ body,
135
+ });
136
+ if (options?.checkStatusCode === undefined || options?.checkStatusCode) {
137
+ try {
138
+ const checker = typeof options?.checkStatusCode === 'function' ? options.checkStatusCode : checkStatus;
139
+ if (!checker(response.status))
140
+ throw new Error(`http response statusCode ${response.status}`);
141
+ }
142
+ catch (e) {
143
+ response.arrayBuffer().catch(() => { });
144
+ throw e;
145
+ }
146
+ }
147
+ return {
148
+ statusCode: response.status,
149
+ headers: response.headers,
150
+ body: await domFetchParseIncomingMessage(response, options.responseType),
151
+ };
152
+ }
153
+ finally {
154
+ clearTimeout(timeout);
155
+ }
156
+ }
157
+ exports.domFetch = domFetch;
158
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../../server/src/fetch/index.ts"],"names":[],"mappings":"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","sourcesContent":["export type HttpFetchResponseType = 'json' | 'text' | 'buffer' | 'readable';\nexport interface HttpFetchOptionsBase<B> {\n url: string | URL;\n family?: 4 | 6;\n method?: string;\n headers?: HeadersInit;\n signal?: AbortSignal,\n timeout?: number;\n rejectUnauthorized?: boolean;\n /**\n * Checks the status code. Defaults to true.\n */\n checkStatusCode?: boolean | ((statusCode: number) => boolean);\n body?: B | string | ArrayBufferView | any;\n withCredentials?: boolean;\n}\n\nexport interface HttpFetchJsonOptions<B> extends HttpFetchOptionsBase<B> {\n responseType: 'json';\n}\n\nexport interface HttpFetchBufferOptions<B> extends HttpFetchOptionsBase<B> {\n responseType: 'buffer';\n}\n\nexport interface HttpFetchDefaultOptions<B> extends HttpFetchOptionsBase<B> {\n responseType?: undefined;\n}\n\nexport interface HttpFetchTextOptions<B> extends HttpFetchOptionsBase<B> {\n responseType: 'text';\n}\nexport interface HttpFetchReadableOptions<B> extends HttpFetchOptionsBase<B> {\n responseType: 'readable';\n}\n\nexport type HttpFetchOptions<B> = HttpFetchJsonOptions<B> | HttpFetchBufferOptions<B> | HttpFetchDefaultOptions<B> | HttpFetchTextOptions<B> | HttpFetchReadableOptions<B>;\n\nexport function fetchStatusCodeOk(statusCode: number) {\n return statusCode >= 200 && statusCode <= 299;\n}\n\nexport function checkStatus(statusCode: number) {\n if (!fetchStatusCodeOk(statusCode))\n throw new Error(`http response statusCode ${statusCode}`);\n return true;\n}\n\nexport function getFetchMethod(options: HttpFetchOptions<any>) {\n const method = options.method || (options.body ? 'POST' : 'GET');\n return method;\n}\n\nexport interface HttpFetchResponse<T> {\n statusCode: number;\n headers: Headers;\n body: T;\n}\n\nexport type fetcher<B, M> = <T extends HttpFetchOptions<B>>(options: T) => Promise<HttpFetchResponse<\n // first one serves as default.\n T extends HttpFetchBufferOptions<B> ? Buffer\n : T extends HttpFetchTextOptions<B> ? string\n : T extends HttpFetchReadableOptions<B> ? M\n : T extends HttpFetchJsonOptions<B> ? any : Buffer\n>>;\n\n\nexport function getHttpFetchAccept(responseType: HttpFetchResponseType | undefined) {\n switch (responseType) {\n case 'json':\n return 'application/json';\n case 'text':\n return 'text/plain';\n }\n return;\n}\n\nexport function hasHeader(headers: [string, string][], key: string) {\n key = key.toLowerCase();\n return headers.find(([k]) => k.toLowerCase() === key);\n}\n\nexport function removeHeader(headers: [string, string][], key: string) {\n key = key.toLowerCase();\n const filteredHeaders = headers.filter(([headerKey, _]) => headerKey.toLowerCase() !== key);\n headers.length = 0;\n filteredHeaders.forEach(header => headers.push(header));\n}\n\nexport function setHeader(headers: [string, string][], key: string, value: string) {\n removeHeader(headers, key);\n headers.push([key, value]);\n}\n\nexport function setDefaultHttpFetchAccept(headers: [string, string][], responseType: HttpFetchResponseType | undefined) {\n if (hasHeader(headers, 'Accept'))\n return;\n const accept = getHttpFetchAccept(responseType);\n if (accept)\n setHeader(headers, 'Accept', accept);\n}\n\nexport function createHeadersArray(headers: HeadersInit | undefined): [string, string][] {\n const headersArray: [string, string][] = [];\n if (!headers)\n return headersArray;\n if (headers instanceof Headers) {\n for (const [k, v] of headers.entries()) {\n headersArray.push([k, v]);\n }\n return headersArray;\n }\n\n if (headers instanceof Array) {\n for (const [k, v] of headers) {\n headersArray.push([k, v]);\n }\n return headersArray;\n }\n\n for (const k of Object.keys(headers)) {\n const v = headers[k];\n headersArray.push([k, v]);\n }\n\n return headersArray;\n}\n\n/**\n *\n * @param headers\n * @param body\n * @returns Returns the body and Content-Type header that was set.\n */\nexport function createStringOrBufferBody(headers: [string, string][], body: any) {\n let contentType: string | undefined;\n if (typeof body === 'object') {\n body = JSON.stringify(body);\n contentType = 'application/json';\n }\n else if (typeof body === 'string') {\n contentType = 'text/plain';\n }\n\n if (contentType && !hasHeader(headers, 'Content-Type'))\n setHeader(headers, 'Content-Type', contentType);\n\n if (!hasHeader(headers, 'Content-Length')) {\n body = Buffer.from(body);\n setHeader(headers, 'Content-Length', body.length.toString());\n }\n return body;\n}\n\nexport async function domFetchParseIncomingMessage(response: Response, responseType: HttpFetchResponseType | undefined) {\n switch (responseType) {\n case 'json':\n return response.json();\n case 'text':\n return response.text();\n case 'readable':\n return response;\n }\n return new Uint8Array(await response.arrayBuffer());\n}\n\nexport async function domFetch<T extends HttpFetchOptions<BodyInit>>(options: T): Promise<HttpFetchResponse<\n // first one serves as default.\n T extends HttpFetchBufferOptions<BodyInit> ? Buffer\n : T extends HttpFetchTextOptions<BodyInit> ? string\n : T extends HttpFetchReadableOptions<BodyInit> ? Response\n : T extends HttpFetchJsonOptions<BodyInit> ? any : Buffer\n>> {\n const headers = createHeadersArray(options.headers);\n setDefaultHttpFetchAccept(headers, options.responseType);\n\n let { body } = options;\n if (body && !(body instanceof ReadableStream)) {\n body = createStringOrBufferBody(headers, body);\n }\n\n let controller: AbortController | undefined;\n let timeout: NodeJS.Timeout;\n if (options.timeout) {\n controller = new AbortController();\n timeout = setTimeout(() => controller!.abort(), options.timeout);\n\n options.signal?.addEventListener('abort', () => controller!.abort(options.signal?.reason));\n }\n\n try {\n const { url } = options;\n const response = await fetch(url, {\n method: getFetchMethod(options),\n credentials: options.withCredentials ? 'include' : undefined,\n headers,\n signal: controller?.signal || options.signal,\n body,\n });\n\n if (options?.checkStatusCode === undefined || options?.checkStatusCode) {\n try {\n const checker = typeof options?.checkStatusCode === 'function' ? options.checkStatusCode : checkStatus;\n if (!checker(response.status))\n throw new Error(`http response statusCode ${response.status}`);\n }\n catch (e) {\n response.arrayBuffer().catch(() => { });\n throw e;\n }\n }\n\n return {\n statusCode: response.status,\n headers: response.headers,\n body: await domFetchParseIncomingMessage(response, options.responseType),\n };\n }\n finally {\n clearTimeout(timeout!);\n }\n}\n"]}
package/package.json ADDED
@@ -0,0 +1,23 @@
1
+ {
2
+ "name": "@scrypted/auth-fetch",
3
+ "version": "1.0.2",
4
+ "description": "",
5
+ "main": "dist/index.js",
6
+ "scripts": {
7
+ "prebuild": "rimraf dist",
8
+ "build": "tsc --outDir dist",
9
+ "prepublishOnly": "npm run build",
10
+ "test": "echo \"Error: no test specified\" && exit 1"
11
+ },
12
+ "devDependencies": {
13
+ "@types/node": "^20.9.4",
14
+ "rimraf": "^5.0.5",
15
+ "typescript": "^5.3.2"
16
+ },
17
+ "dependencies": {
18
+ "follow-redirects": "^1.15.4",
19
+ "http-auth-utils": "^5.0.1"
20
+ },
21
+ "author": "",
22
+ "license": "ISC"
23
+ }
@@ -0,0 +1,154 @@
1
+ import { HttpFetchOptions, HttpFetchResponseType, checkStatus, createHeadersArray, fetcher, getFetchMethod, hasHeader, setDefaultHttpFetchAccept, setHeader } from '../../../server/src/fetch';
2
+
3
+ export interface AuthFetchCredentialState {
4
+ username: string;
5
+ password: string;
6
+ }
7
+
8
+ export interface AuthFetchOptions {
9
+ credential?: AuthFetchCredentialState;
10
+ }
11
+
12
+ async function getAuth(options: AuthFetchOptions, url: string | URL, method: string) {
13
+ if (!options.credential)
14
+ return;
15
+
16
+ const { BASIC, DIGEST, parseWWWAuthenticateHeader } = await import('http-auth-utils');
17
+
18
+ const credential = options.credential as AuthFetchCredentialState & {
19
+ count?: number;
20
+ digest?: ReturnType<typeof parseWWWAuthenticateHeader<typeof DIGEST>>;
21
+ basic?: ReturnType<typeof parseWWWAuthenticateHeader<typeof BASIC>>;
22
+ };
23
+ const { digest, basic } = credential;
24
+
25
+ if (digest) {
26
+ credential.count ||= 0;
27
+ ++credential.count;
28
+ const nc = ('00000000' + credential.count).slice(-8);
29
+ const cnonce = [...Array(24)].map(() => Math.floor(Math.random() * 16).toString(16)).join('');
30
+ const parsedURL = new URL(url);
31
+ const uri = parsedURL.pathname + parsedURL.search;
32
+
33
+ const { DIGEST, buildAuthorizationHeader } = await import('http-auth-utils');
34
+
35
+ const response = DIGEST.computeHash({
36
+ username: options.credential.username,
37
+ password: options.credential.password,
38
+ method,
39
+ uri,
40
+ nc,
41
+ cnonce,
42
+ algorithm: 'MD5',
43
+ qop: digest.data.qop!,
44
+ ...digest.data,
45
+ });
46
+
47
+ const header = buildAuthorizationHeader(DIGEST, {
48
+ username: options.credential.username,
49
+ uri,
50
+ nc,
51
+ cnonce,
52
+ algorithm: digest.data.algorithm!,
53
+ qop: digest.data.qop!,
54
+ response,
55
+ ...digest.data,
56
+ });
57
+
58
+ return header;
59
+ }
60
+ else if (basic) {
61
+ const { BASIC, buildAuthorizationHeader } = await import('http-auth-utils');
62
+
63
+ const header = buildAuthorizationHeader(BASIC, {
64
+ username: options.credential.username,
65
+ password: options.credential.password,
66
+ });
67
+
68
+ return header;
69
+ }
70
+ }
71
+
72
+ export function createAuthFetch<B, M>(
73
+ h: fetcher<B, M>,
74
+ parser: (body: M, responseType: HttpFetchResponseType | undefined) => Promise<any>
75
+ ) {
76
+ const authHttpFetch = async <T extends HttpFetchOptions<B>>(options: T & AuthFetchOptions): ReturnType<typeof h<T>> => {
77
+ const method = getFetchMethod(options);
78
+ const headers = createHeadersArray(options.headers);
79
+ options.headers = headers;
80
+ setDefaultHttpFetchAccept(headers, options.responseType);
81
+
82
+ const initialHeader = await getAuth(options, options.url, method);
83
+ // try to provide an authorization if a session exists, but don't override Authorization if provided already.
84
+ // 401 will trigger a proper auth.
85
+ if (initialHeader && !hasHeader(headers, 'Authorization'))
86
+ setHeader(headers, 'Authorization', initialHeader);
87
+
88
+
89
+ const controller = new AbortController();
90
+ options.signal?.addEventListener('abort', () => controller.abort(options.signal?.reason));
91
+
92
+ const initialResponse = await h({
93
+ ...options,
94
+ signal: controller.signal,
95
+ // need to intercept the status code to check for 401.
96
+ // all other status codes will be handled according to the initial request options.
97
+ checkStatusCode(statusCode) {
98
+ // can handle a 401 if an credential is provided.
99
+ // however, not providing a credential is also valid, and should
100
+ // fall through to the normal response handling which may be interested
101
+ // in the 401 response.
102
+ if (statusCode === 401 && options.credential)
103
+ return true;
104
+ if (options?.checkStatusCode === undefined || options?.checkStatusCode) {
105
+ const checker = typeof options?.checkStatusCode === 'function' ? options.checkStatusCode : checkStatus;
106
+ return checker(statusCode);
107
+ }
108
+ return true;
109
+ },
110
+ responseType: 'readable',
111
+ });
112
+
113
+ // if it's not a 401, just return the response.
114
+ if (initialResponse.statusCode !== 401) {
115
+ return {
116
+ ...initialResponse,
117
+ body: await parser(initialResponse.body, options.responseType),
118
+ };
119
+ }
120
+
121
+ let authenticateHeaders: string | string[] | null = initialResponse.headers.get('www-authenticate');
122
+ if (!authenticateHeaders)
123
+ throw new Error('Did not find WWW-Authenticate header.');
124
+
125
+
126
+ if (typeof authenticateHeaders === 'string')
127
+ authenticateHeaders = [authenticateHeaders];
128
+
129
+ const { BASIC, DIGEST, parseWWWAuthenticateHeader } = await import('http-auth-utils');
130
+ const parsedHeaders = authenticateHeaders.map(h => parseWWWAuthenticateHeader(h));
131
+
132
+ const digest = parsedHeaders.find(p => p.type === 'Digest') as ReturnType<typeof parseWWWAuthenticateHeader<typeof DIGEST>>;
133
+ const basic = parsedHeaders.find(p => p.type === 'Basic') as ReturnType<typeof parseWWWAuthenticateHeader<typeof BASIC>>;
134
+ const credential = options.credential as AuthFetchCredentialState & {
135
+ count?: number;
136
+ digest?: ReturnType<typeof parseWWWAuthenticateHeader<typeof DIGEST>>;
137
+ basic?: ReturnType<typeof parseWWWAuthenticateHeader<typeof BASIC>>;
138
+ };
139
+
140
+ credential.digest = digest;
141
+ credential.basic = basic;
142
+
143
+ if (!digest && !basic)
144
+ throw new Error(`Unknown WWW-Authenticate type: ${parsedHeaders[0]?.type}`);
145
+
146
+ const header = await getAuth(options, options.url, method);
147
+ if (header)
148
+ setHeader(headers, 'Authorization', header);
149
+
150
+ return h(options);
151
+ }
152
+
153
+ return authHttpFetch;
154
+ }
package/src/index.ts ADDED
@@ -0,0 +1,19 @@
1
+ import { createAuthFetch } from "./auth-fetch";
2
+ import { httpFetch, httpFetchParseIncomingMessage } from '../../../server/src/fetch/http-fetch';
3
+ import type { Readable } from "stream";
4
+ import type { IncomingMessage } from "http";
5
+ import { domFetch, domFetchParseIncomingMessage } from "../../../server/src/fetch";
6
+
7
+ function init() {
8
+ try {
9
+ require('net');
10
+ require('events');
11
+ return createAuthFetch<Readable, IncomingMessage>(httpFetch, httpFetchParseIncomingMessage);
12
+ }
13
+ catch (e) {
14
+ }
15
+
16
+ return createAuthFetch<BodyInit, Response>(domFetch, domFetchParseIncomingMessage);
17
+ }
18
+
19
+ export const authFetch = init();
package/tsconfig.json ADDED
@@ -0,0 +1,17 @@
1
+ {
2
+ "compilerOptions": {
3
+ "module": "commonjs",
4
+ "target": "esnext",
5
+ "noImplicitAny": true,
6
+ "outDir": "./dist",
7
+ "esModuleInterop": true,
8
+ "sourceMap": true,
9
+ "inlineSources": true,
10
+ "declaration": true,
11
+ "resolveJsonModule": true,
12
+ "strict": true
13
+ },
14
+ "include": [
15
+ "src/**/*"
16
+ ],
17
+ }