@neottia/searchable-core 0.2.0
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.
- package/LICENSE +21 -0
- package/NOTICE.md +5 -0
- package/README.md +71 -0
- package/config.schema.json +246 -0
- package/dist/config.d.ts +423 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +343 -0
- package/dist/config.js.map +1 -0
- package/dist/errors.d.ts +32 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +41 -0
- package/dist/errors.js.map +1 -0
- package/dist/extraction-worker.d.ts +2 -0
- package/dist/extraction-worker.d.ts.map +1 -0
- package/dist/extraction-worker.js +38 -0
- package/dist/extraction-worker.js.map +1 -0
- package/dist/fetcher.d.ts +5 -0
- package/dist/fetcher.d.ts.map +1 -0
- package/dist/fetcher.js +173 -0
- package/dist/fetcher.js.map +1 -0
- package/dist/http.d.ts +54 -0
- package/dist/http.d.ts.map +1 -0
- package/dist/http.js +372 -0
- package/dist/http.js.map +1 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +14 -0
- package/dist/index.js.map +1 -0
- package/dist/migration-worker.d.ts +2 -0
- package/dist/migration-worker.d.ts.map +1 -0
- package/dist/migration-worker.js +60 -0
- package/dist/migration-worker.js.map +1 -0
- package/dist/migration.d.ts +19 -0
- package/dist/migration.d.ts.map +1 -0
- package/dist/migration.js +221 -0
- package/dist/migration.js.map +1 -0
- package/dist/providers.d.ts +9 -0
- package/dist/providers.d.ts.map +1 -0
- package/dist/providers.js +210 -0
- package/dist/providers.js.map +1 -0
- package/dist/redaction.d.ts +9 -0
- package/dist/redaction.d.ts.map +1 -0
- package/dist/redaction.js +161 -0
- package/dist/redaction.js.map +1 -0
- package/dist/runtime.d.ts +20 -0
- package/dist/runtime.d.ts.map +1 -0
- package/dist/runtime.js +205 -0
- package/dist/runtime.js.map +1 -0
- package/dist/schemas.d.ts +49 -0
- package/dist/schemas.d.ts.map +1 -0
- package/dist/schemas.js +64 -0
- package/dist/schemas.js.map +1 -0
- package/dist/services.d.ts +20 -0
- package/dist/services.d.ts.map +1 -0
- package/dist/services.js +2 -0
- package/dist/services.js.map +1 -0
- package/dist/stash.d.ts +65 -0
- package/dist/stash.d.ts.map +1 -0
- package/dist/stash.js +490 -0
- package/dist/stash.js.map +1 -0
- package/dist/tool-contracts.d.ts +104 -0
- package/dist/tool-contracts.d.ts.map +1 -0
- package/dist/tool-contracts.js +71 -0
- package/dist/tool-contracts.js.map +1 -0
- package/dist/tools.d.ts +23 -0
- package/dist/tools.d.ts.map +1 -0
- package/dist/tools.js +134 -0
- package/dist/tools.js.map +1 -0
- package/dist/utf8.d.ts +2 -0
- package/dist/utf8.d.ts.map +1 -0
- package/dist/utf8.js +14 -0
- package/dist/utf8.js.map +1 -0
- package/dist/worker.d.ts +12 -0
- package/dist/worker.d.ts.map +1 -0
- package/dist/worker.js +45 -0
- package/dist/worker.js.map +1 -0
- package/package.json +63 -0
package/dist/fetcher.js
ADDED
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import { existsSync } from 'node:fs';
|
|
2
|
+
import { fileURLToPath } from 'node:url';
|
|
3
|
+
import { Worker } from 'node:worker_threads';
|
|
4
|
+
import { SearchableError } from './errors.js';
|
|
5
|
+
import { assertSafeWebUrl, checkSearchableDeadline, createSearchableDeadline, remainingSearchableDeadline, shortenSearchableDeadline, } from './http.js';
|
|
6
|
+
import { runBoundedWorker } from './worker.js';
|
|
7
|
+
export async function fetchPage(transport, requested, context) {
|
|
8
|
+
assertSafeWebUrl(requested);
|
|
9
|
+
const deadline = context.transportDeadline ?? createSearchableDeadline(context.config.fetch.overall_timeout_ms, context.deadline);
|
|
10
|
+
const failures = [];
|
|
11
|
+
for (const strategy of context.config.fetch.strategies) {
|
|
12
|
+
checkOperation(context.signal, deadline);
|
|
13
|
+
try {
|
|
14
|
+
if (strategy === 'direct')
|
|
15
|
+
return await directFetch(transport, requested, context, deadline, 'direct');
|
|
16
|
+
if (requested.search || requested.hash || requested.username || requested.password) {
|
|
17
|
+
failures.push(`${strategy}: FALLBACK_DISCLOSURE_BLOCKED`);
|
|
18
|
+
continue;
|
|
19
|
+
}
|
|
20
|
+
if (strategy === 'jina')
|
|
21
|
+
return await jinaFetch(transport, requested, context, deadline);
|
|
22
|
+
return await waybackFetch(transport, requested, context, deadline);
|
|
23
|
+
}
|
|
24
|
+
catch (error) {
|
|
25
|
+
if (context.signal?.aborted)
|
|
26
|
+
throw cancellationError();
|
|
27
|
+
if (error instanceof SearchableError && error.code === 'DEADLINE_EXCEEDED')
|
|
28
|
+
throw error;
|
|
29
|
+
failures.push(`${strategy}: ${error instanceof SearchableError ? error.code : 'FETCH_FAILED'}`);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
throw new SearchableError('service', 'FETCH_FAILED', 'Every configured fetch strategy failed.', [], {
|
|
33
|
+
strategies: failures,
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
async function directFetch(transport, requested, context, deadline, source) {
|
|
37
|
+
const response = await transport.request({
|
|
38
|
+
url: requested,
|
|
39
|
+
headers: {
|
|
40
|
+
accept: 'text/html,application/xhtml+xml',
|
|
41
|
+
'user-agent': 'Mozilla/5.0 (compatible; NeottiaSearchable/1.0)',
|
|
42
|
+
},
|
|
43
|
+
signal: context.signal,
|
|
44
|
+
deadline: attemptDeadline(deadline, context.config.fetch.timeout_ms),
|
|
45
|
+
maximumBytes: context.config.fetch.max_response_bytes,
|
|
46
|
+
redirectPolicy: 'safe-web',
|
|
47
|
+
});
|
|
48
|
+
assertFetchStatus(response);
|
|
49
|
+
const contentType = response.headers['content-type']?.toLowerCase() ?? '';
|
|
50
|
+
if (!contentType.includes('text/html') && !contentType.includes('application/xhtml+xml'))
|
|
51
|
+
throw new SearchableError('service', 'FETCH_CONTENT_TYPE_UNSUPPORTED', 'Direct extraction requires HTML or XHTML.');
|
|
52
|
+
return extractHtml(response, requested, context, deadline, source);
|
|
53
|
+
}
|
|
54
|
+
async function jinaFetch(transport, requested, context, deadline) {
|
|
55
|
+
const endpoint = new URL(`https://r.jina.ai/${requested.href}`);
|
|
56
|
+
const response = await transport.request({
|
|
57
|
+
url: endpoint,
|
|
58
|
+
headers: { accept: 'text/plain', 'user-agent': 'Neottia Searchable/1' },
|
|
59
|
+
signal: context.signal,
|
|
60
|
+
deadline: attemptDeadline(deadline, context.config.fetch.timeout_ms),
|
|
61
|
+
maximumBytes: context.config.fetch.max_response_bytes,
|
|
62
|
+
redirectPolicy: 'none',
|
|
63
|
+
});
|
|
64
|
+
assertFetchStatus(response);
|
|
65
|
+
const markdown = Buffer.from(response.bytes).toString('utf8').trim();
|
|
66
|
+
assertExtracted(markdown, context);
|
|
67
|
+
const titleMatch = /^Title:\s*(.+)$/imu.exec(markdown);
|
|
68
|
+
return {
|
|
69
|
+
title: titleMatch?.[1]?.trim() || requested.hostname,
|
|
70
|
+
content: markdown,
|
|
71
|
+
url: requested.href,
|
|
72
|
+
source: 'jina',
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
async function waybackFetch(transport, requested, context, deadline) {
|
|
76
|
+
const availability = new URL('https://archive.org/wayback/available');
|
|
77
|
+
availability.searchParams.set('url', requested.href);
|
|
78
|
+
const response = await transport.request({
|
|
79
|
+
url: availability,
|
|
80
|
+
signal: context.signal,
|
|
81
|
+
deadline: attemptDeadline(deadline, context.config.fetch.timeout_ms),
|
|
82
|
+
maximumBytes: Math.min(256 * 1024, context.config.fetch.max_response_bytes),
|
|
83
|
+
redirectPolicy: 'none',
|
|
84
|
+
});
|
|
85
|
+
assertFetchStatus(response);
|
|
86
|
+
let snapshot;
|
|
87
|
+
try {
|
|
88
|
+
const parsed = JSON.parse(Buffer.from(response.bytes).toString('utf8'));
|
|
89
|
+
if (!isRecord(parsed))
|
|
90
|
+
throw new Error('invalid root');
|
|
91
|
+
const archived = isRecord(parsed['archived_snapshots']) ? parsed['archived_snapshots'] : {};
|
|
92
|
+
const closest = isRecord(archived['closest']) ? archived['closest'] : {};
|
|
93
|
+
snapshot = closest['url'];
|
|
94
|
+
}
|
|
95
|
+
catch {
|
|
96
|
+
throw new SearchableError('service', 'WAYBACK_RESPONSE_INVALID', 'The Wayback availability response is invalid.');
|
|
97
|
+
}
|
|
98
|
+
if (typeof snapshot !== 'string')
|
|
99
|
+
throw new SearchableError('service', 'WAYBACK_NOT_FOUND', 'Wayback has no snapshot for this URL.');
|
|
100
|
+
let snapshotUrl;
|
|
101
|
+
try {
|
|
102
|
+
snapshotUrl = new URL(snapshot);
|
|
103
|
+
}
|
|
104
|
+
catch {
|
|
105
|
+
throw new SearchableError('service', 'WAYBACK_RESPONSE_INVALID', 'Wayback returned an invalid snapshot URL.');
|
|
106
|
+
}
|
|
107
|
+
if (snapshotUrl.protocol !== 'https:' ||
|
|
108
|
+
snapshotUrl.hostname !== 'web.archive.org' ||
|
|
109
|
+
snapshotUrl.username ||
|
|
110
|
+
snapshotUrl.password)
|
|
111
|
+
throw new SearchableError('service', 'WAYBACK_RESPONSE_INVALID', 'Wayback returned an untrusted snapshot URL.');
|
|
112
|
+
return directFetch(transport, snapshotUrl, context, deadline, 'wayback').then((value) => ({
|
|
113
|
+
...value,
|
|
114
|
+
url: requested.href,
|
|
115
|
+
source: 'wayback',
|
|
116
|
+
}));
|
|
117
|
+
}
|
|
118
|
+
async function extractHtml(response, requested, context, overallDeadline, source) {
|
|
119
|
+
const deadline = attemptDeadline(overallDeadline, context.config.fetch.timeout_ms);
|
|
120
|
+
checkOperation(context.signal, deadline);
|
|
121
|
+
const localWorker = new URL('./extraction-worker.js', import.meta.url);
|
|
122
|
+
const workerUrl = existsSync(fileURLToPath(localWorker))
|
|
123
|
+
? localWorker
|
|
124
|
+
: new URL('../dist/extraction-worker.js', import.meta.url);
|
|
125
|
+
const worker = new Worker(workerUrl, {
|
|
126
|
+
workerData: {
|
|
127
|
+
html: Buffer.from(response.bytes).toString('utf8'),
|
|
128
|
+
finalUrl: response.finalUrl.href,
|
|
129
|
+
requestedUrl: requested.href,
|
|
130
|
+
source,
|
|
131
|
+
maximumContentBytes: context.config.security.limits.max_content_bytes,
|
|
132
|
+
},
|
|
133
|
+
});
|
|
134
|
+
return runBoundedWorker(worker, {
|
|
135
|
+
...(context.signal === undefined ? {} : { signal: context.signal }),
|
|
136
|
+
timeoutMs: remainingSearchableDeadline(deadline),
|
|
137
|
+
cancellationError: () => new SearchableError('cancelled', 'OPERATION_CANCELLED', 'Searchable operation was cancelled.'),
|
|
138
|
+
timeoutError: () => new SearchableError('service', 'DEADLINE_EXCEEDED', 'Page extraction exceeded its deadline.'),
|
|
139
|
+
workerError: () => new SearchableError('service', 'EXTRACTION_FAILED', 'Page extraction worker failed.'),
|
|
140
|
+
decode(message) {
|
|
141
|
+
if (!isRecord(message))
|
|
142
|
+
throw new SearchableError('service', 'EXTRACTION_FAILED', 'Page extraction returned an invalid result.');
|
|
143
|
+
if (message['error'] === 'EXTRACTED_CONTENT_TOO_LARGE')
|
|
144
|
+
throw new SearchableError('resource_limit', 'EXTRACTED_CONTENT_TOO_LARGE', 'Extracted content exceeds the configured byte limit.');
|
|
145
|
+
if (!isRecord(message['result']))
|
|
146
|
+
throw new SearchableError('service', 'EXTRACTION_FAILED', 'Readability could not extract page content.');
|
|
147
|
+
return message['result'];
|
|
148
|
+
},
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
function assertExtracted(content, context) {
|
|
152
|
+
if (!content.trim())
|
|
153
|
+
throw new SearchableError('service', 'EXTRACTION_FAILED', 'Page extraction returned no content.');
|
|
154
|
+
if (Buffer.byteLength(content, 'utf8') > context.config.security.limits.max_content_bytes)
|
|
155
|
+
throw new SearchableError('resource_limit', 'EXTRACTED_CONTENT_TOO_LARGE', 'Extracted content exceeds the configured byte limit.');
|
|
156
|
+
}
|
|
157
|
+
function assertFetchStatus(response) {
|
|
158
|
+
if (response.status < 200 || response.status >= 300)
|
|
159
|
+
throw new SearchableError('service', 'FETCH_UPSTREAM_FAILED', `The fetch endpoint returned HTTP ${response.status}.`);
|
|
160
|
+
}
|
|
161
|
+
function attemptDeadline(overall, timeoutMs) {
|
|
162
|
+
return shortenSearchableDeadline(overall, timeoutMs);
|
|
163
|
+
}
|
|
164
|
+
function checkOperation(signal, deadline) {
|
|
165
|
+
checkSearchableDeadline(deadline, signal);
|
|
166
|
+
}
|
|
167
|
+
function cancellationError() {
|
|
168
|
+
return new SearchableError('cancelled', 'OPERATION_CANCELLED', 'Searchable operation was cancelled.');
|
|
169
|
+
}
|
|
170
|
+
function isRecord(value) {
|
|
171
|
+
return value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
172
|
+
}
|
|
173
|
+
//# sourceMappingURL=fetcher.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fetcher.js","sourceRoot":"","sources":["../src/fetcher.ts"],"names":[],"mappings":"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"}
|
package/dist/http.d.ts
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
export interface SearchableDnsAddress {
|
|
2
|
+
readonly address: string;
|
|
3
|
+
readonly family: 4 | 6;
|
|
4
|
+
}
|
|
5
|
+
export type SearchableDnsResolver = (hostname: string, signal?: AbortSignal) => Promise<readonly SearchableDnsAddress[]>;
|
|
6
|
+
export interface SearchableDeadline {
|
|
7
|
+
readonly expiresAt: number;
|
|
8
|
+
}
|
|
9
|
+
export declare function createSearchableDeadline(timeoutMs: number, epochDeadline?: number): SearchableDeadline;
|
|
10
|
+
export declare function shortenSearchableDeadline(deadline: SearchableDeadline, timeoutMs: number): SearchableDeadline;
|
|
11
|
+
export interface SearchableHttpRequest {
|
|
12
|
+
readonly url: URL;
|
|
13
|
+
readonly method?: 'GET' | 'POST';
|
|
14
|
+
readonly headers?: Readonly<Record<string, string>>;
|
|
15
|
+
readonly body?: Uint8Array;
|
|
16
|
+
readonly signal?: AbortSignal;
|
|
17
|
+
readonly deadline: number | SearchableDeadline;
|
|
18
|
+
readonly maximumBytes: number;
|
|
19
|
+
readonly redirectPolicy?: 'none' | 'safe-web';
|
|
20
|
+
readonly destination?: 'public' | 'trusted-local';
|
|
21
|
+
}
|
|
22
|
+
export interface SearchableHttpResponse {
|
|
23
|
+
readonly status: number;
|
|
24
|
+
readonly headers: Readonly<Record<string, string>>;
|
|
25
|
+
readonly bytes: Uint8Array;
|
|
26
|
+
readonly finalUrl: URL;
|
|
27
|
+
}
|
|
28
|
+
export interface SearchableHttpStreamResponse {
|
|
29
|
+
readonly status: number;
|
|
30
|
+
readonly headers: Readonly<Record<string, string>>;
|
|
31
|
+
readonly chunks: AsyncIterable<Uint8Array>;
|
|
32
|
+
readonly finalUrl: URL;
|
|
33
|
+
cancel(reason?: Error): void;
|
|
34
|
+
}
|
|
35
|
+
export interface SearchableHttpTransport {
|
|
36
|
+
request(input: SearchableHttpRequest): Promise<SearchableHttpResponse>;
|
|
37
|
+
stream?(input: SearchableHttpRequest): Promise<SearchableHttpStreamResponse>;
|
|
38
|
+
close?(): Promise<void>;
|
|
39
|
+
}
|
|
40
|
+
export declare function assertPublicAddress(address: string): void;
|
|
41
|
+
export declare function assertSafeWebUrl(url: URL): void;
|
|
42
|
+
export declare class SafeHttpClient implements SearchableHttpTransport {
|
|
43
|
+
private readonly resolveDns;
|
|
44
|
+
constructor(resolveDns?: SearchableDnsResolver);
|
|
45
|
+
close(): Promise<void>;
|
|
46
|
+
request(input: SearchableHttpRequest): Promise<SearchableHttpResponse>;
|
|
47
|
+
stream(input: SearchableHttpRequest): Promise<SearchableHttpStreamResponse>;
|
|
48
|
+
private requestOptions;
|
|
49
|
+
private requestOnce;
|
|
50
|
+
private requestStreamOnce;
|
|
51
|
+
}
|
|
52
|
+
export declare function remainingSearchableDeadline(deadline: SearchableDeadline): number;
|
|
53
|
+
export declare function checkSearchableDeadline(deadline: SearchableDeadline, signal?: AbortSignal): void;
|
|
54
|
+
//# sourceMappingURL=http.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":"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"}
|
package/dist/http.js
ADDED
|
@@ -0,0 +1,372 @@
|
|
|
1
|
+
import { lookup as systemLookup } from 'node:dns/promises';
|
|
2
|
+
import { request as httpRequest } from 'node:http';
|
|
3
|
+
import { request as httpsRequest } from 'node:https';
|
|
4
|
+
import { BlockList, isIP } from 'node:net';
|
|
5
|
+
import { performance } from 'node:perf_hooks';
|
|
6
|
+
import { createBrotliDecompress, createGunzip, createInflate } from 'node:zlib';
|
|
7
|
+
import { SearchableError } from './errors.js';
|
|
8
|
+
export function createSearchableDeadline(timeoutMs, epochDeadline) {
|
|
9
|
+
const parentRemaining = epochDeadline === undefined ? timeoutMs : Math.max(0, epochDeadline - Date.now());
|
|
10
|
+
return { expiresAt: performance.now() + Math.min(timeoutMs, parentRemaining) };
|
|
11
|
+
}
|
|
12
|
+
export function shortenSearchableDeadline(deadline, timeoutMs) {
|
|
13
|
+
return { expiresAt: Math.min(deadline.expiresAt, performance.now() + timeoutMs) };
|
|
14
|
+
}
|
|
15
|
+
const blocked = new BlockList();
|
|
16
|
+
for (const [network, prefix] of [
|
|
17
|
+
['0.0.0.0', 8],
|
|
18
|
+
['10.0.0.0', 8],
|
|
19
|
+
['100.64.0.0', 10],
|
|
20
|
+
['127.0.0.0', 8],
|
|
21
|
+
['169.254.0.0', 16],
|
|
22
|
+
['172.16.0.0', 12],
|
|
23
|
+
['192.0.0.0', 24],
|
|
24
|
+
['192.0.2.0', 24],
|
|
25
|
+
['192.88.99.0', 24],
|
|
26
|
+
['192.168.0.0', 16],
|
|
27
|
+
['198.18.0.0', 15],
|
|
28
|
+
['198.51.100.0', 24],
|
|
29
|
+
['203.0.113.0', 24],
|
|
30
|
+
['224.0.0.0', 4],
|
|
31
|
+
['240.0.0.0', 4],
|
|
32
|
+
])
|
|
33
|
+
blocked.addSubnet(network, prefix, 'ipv4');
|
|
34
|
+
for (const [network, prefix] of [
|
|
35
|
+
['::', 96],
|
|
36
|
+
['64:ff9b::', 96],
|
|
37
|
+
['64:ff9b:1::', 48],
|
|
38
|
+
['100::', 64],
|
|
39
|
+
['2001::', 32],
|
|
40
|
+
['2001:2::', 48],
|
|
41
|
+
['2001:10::', 28],
|
|
42
|
+
['2001:20::', 28],
|
|
43
|
+
['2001:db8::', 32],
|
|
44
|
+
['2002::', 16],
|
|
45
|
+
['3fff::', 20],
|
|
46
|
+
['5f00::', 16],
|
|
47
|
+
['fc00::', 7],
|
|
48
|
+
['fec0::', 10],
|
|
49
|
+
['fe80::', 10],
|
|
50
|
+
['ff00::', 8],
|
|
51
|
+
])
|
|
52
|
+
blocked.addSubnet(network, prefix, 'ipv6');
|
|
53
|
+
export function assertPublicAddress(address) {
|
|
54
|
+
const family = isIP(address);
|
|
55
|
+
const mappedIpv4 = address.toLowerCase().startsWith('::ffff:');
|
|
56
|
+
if (!family || mappedIpv4 || blocked.check(address, family === 4 ? 'ipv4' : 'ipv6'))
|
|
57
|
+
throw new SearchableError('service', 'NETWORK_DESTINATION_BLOCKED', 'The destination is not publicly routable.');
|
|
58
|
+
}
|
|
59
|
+
export function assertSafeWebUrl(url) {
|
|
60
|
+
if (!['http:', 'https:'].includes(url.protocol) || url.username || url.password)
|
|
61
|
+
throw new SearchableError('service', 'NETWORK_DESTINATION_BLOCKED', 'The web URL is not permitted.');
|
|
62
|
+
const hostname = stripIpv6Brackets(url.hostname).toLowerCase();
|
|
63
|
+
if (!hostname || hostname === 'localhost' || hostname.endsWith('.localhost') || hostname.includes('%'))
|
|
64
|
+
throw new SearchableError('service', 'NETWORK_DESTINATION_BLOCKED', 'The destination is not publicly routable.');
|
|
65
|
+
if (isIP(hostname))
|
|
66
|
+
assertPublicAddress(hostname);
|
|
67
|
+
}
|
|
68
|
+
export class SafeHttpClient {
|
|
69
|
+
resolveDns;
|
|
70
|
+
constructor(resolveDns = defaultDnsResolver) {
|
|
71
|
+
this.resolveDns = resolveDns;
|
|
72
|
+
}
|
|
73
|
+
async close() { }
|
|
74
|
+
async request(input) {
|
|
75
|
+
let url = new URL(input.url);
|
|
76
|
+
const visited = new Set();
|
|
77
|
+
const deadline = normalizeDeadline(input.deadline);
|
|
78
|
+
let headers = { ...(input.headers ?? {}) };
|
|
79
|
+
for (let redirects = 0; redirects <= 5; redirects += 1) {
|
|
80
|
+
checkDeadline(deadline, input.signal);
|
|
81
|
+
if (visited.has(url.href))
|
|
82
|
+
throw new SearchableError('service', 'REDIRECT_BLOCKED', 'The response contains a redirect loop.');
|
|
83
|
+
visited.add(url.href);
|
|
84
|
+
const response = await this.requestOnce({ ...input, url, headers }, deadline);
|
|
85
|
+
if (!isRedirect(response.status))
|
|
86
|
+
return response;
|
|
87
|
+
if (input.redirectPolicy !== 'safe-web' || redirects === 5)
|
|
88
|
+
throw new SearchableError('service', 'REDIRECT_BLOCKED', 'The response redirect is not permitted.');
|
|
89
|
+
const location = response.headers['location'];
|
|
90
|
+
if (!location)
|
|
91
|
+
throw new SearchableError('service', 'REDIRECT_BLOCKED', 'The response redirect has no location.');
|
|
92
|
+
let next;
|
|
93
|
+
try {
|
|
94
|
+
next = new URL(location, url);
|
|
95
|
+
}
|
|
96
|
+
catch {
|
|
97
|
+
throw new SearchableError('service', 'REDIRECT_BLOCKED', 'The response redirect location is invalid.');
|
|
98
|
+
}
|
|
99
|
+
assertSafeWebUrl(next);
|
|
100
|
+
if (url.protocol === 'https:' && next.protocol !== 'https:')
|
|
101
|
+
throw new SearchableError('service', 'REDIRECT_BLOCKED', 'HTTPS downgrade redirects are not permitted.');
|
|
102
|
+
if (next.origin !== url.origin)
|
|
103
|
+
headers = removeSensitiveHeaders(headers);
|
|
104
|
+
url = next;
|
|
105
|
+
}
|
|
106
|
+
throw new SearchableError('service', 'REDIRECT_BLOCKED', 'The response has too many redirects.');
|
|
107
|
+
}
|
|
108
|
+
async stream(input) {
|
|
109
|
+
if (input.redirectPolicy === 'safe-web')
|
|
110
|
+
throw new SearchableError('service', 'REDIRECT_BLOCKED', 'Streaming requests cannot follow redirects.');
|
|
111
|
+
const deadline = normalizeDeadline(input.deadline);
|
|
112
|
+
checkDeadline(deadline, input.signal);
|
|
113
|
+
return this.requestStreamOnce(input, deadline);
|
|
114
|
+
}
|
|
115
|
+
async requestOptions(input, deadline) {
|
|
116
|
+
const trusted = input.destination === 'trusted-local';
|
|
117
|
+
if (!trusted)
|
|
118
|
+
assertSafeWebUrl(input.url);
|
|
119
|
+
const hostname = stripIpv6Brackets(input.url.hostname);
|
|
120
|
+
const answers = trusted ? [] : await resolvePublic(this.resolveDns, hostname, deadline, input.signal);
|
|
121
|
+
checkDeadline(deadline, input.signal);
|
|
122
|
+
const selected = answers[0];
|
|
123
|
+
return {
|
|
124
|
+
protocol: input.url.protocol,
|
|
125
|
+
hostname,
|
|
126
|
+
port: input.url.port || undefined,
|
|
127
|
+
path: `${input.url.pathname}${input.url.search}`,
|
|
128
|
+
method: input.method ?? 'GET',
|
|
129
|
+
headers: { 'accept-encoding': 'gzip, deflate, br', ...(input.headers ?? {}) },
|
|
130
|
+
signal: input.signal,
|
|
131
|
+
...(selected === undefined
|
|
132
|
+
? {}
|
|
133
|
+
: {
|
|
134
|
+
lookup: (_host, _options, callback) => callback(null, selected.address, selected.family),
|
|
135
|
+
}),
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
async requestOnce(input, deadline) {
|
|
139
|
+
const options = await this.requestOptions(input, deadline);
|
|
140
|
+
const request = input.url.protocol === 'https:' ? httpsRequest : httpRequest;
|
|
141
|
+
return new Promise((resolve, reject) => {
|
|
142
|
+
let settled = false;
|
|
143
|
+
let activeResponse;
|
|
144
|
+
const finish = (action) => {
|
|
145
|
+
if (settled)
|
|
146
|
+
return;
|
|
147
|
+
settled = true;
|
|
148
|
+
clearTimeout(timer);
|
|
149
|
+
action();
|
|
150
|
+
};
|
|
151
|
+
const req = request(options, async (response) => {
|
|
152
|
+
activeResponse = response;
|
|
153
|
+
try {
|
|
154
|
+
assertBoundedContentLength(response.headers, input.maximumBytes);
|
|
155
|
+
const bytes = await readDecodedBody(response, input.maximumBytes, input.signal, deadline);
|
|
156
|
+
finish(() => resolve({
|
|
157
|
+
status: response.statusCode ?? 0,
|
|
158
|
+
headers: normalizeHeaders(response.headers),
|
|
159
|
+
bytes,
|
|
160
|
+
finalUrl: new URL(input.url),
|
|
161
|
+
}));
|
|
162
|
+
}
|
|
163
|
+
catch (error) {
|
|
164
|
+
response.destroy();
|
|
165
|
+
finish(() => reject(normalizeNetworkError(error, input.signal)));
|
|
166
|
+
}
|
|
167
|
+
});
|
|
168
|
+
const timer = setTimeout(() => {
|
|
169
|
+
const error = deadlineError();
|
|
170
|
+
activeResponse?.destroy(error);
|
|
171
|
+
req.destroy(error);
|
|
172
|
+
finish(() => reject(error));
|
|
173
|
+
}, remainingMilliseconds(deadline));
|
|
174
|
+
req.once('error', (error) => finish(() => reject(normalizeNetworkError(error, input.signal))));
|
|
175
|
+
if (input.body)
|
|
176
|
+
req.write(input.body);
|
|
177
|
+
req.end();
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
async requestStreamOnce(input, deadline) {
|
|
181
|
+
const options = await this.requestOptions(input, deadline);
|
|
182
|
+
const request = input.url.protocol === 'https:' ? httpsRequest : httpRequest;
|
|
183
|
+
return new Promise((resolve, reject) => {
|
|
184
|
+
let opened = false;
|
|
185
|
+
let activeResponse;
|
|
186
|
+
let decoded;
|
|
187
|
+
let closed = false;
|
|
188
|
+
const close = (reason) => {
|
|
189
|
+
if (closed)
|
|
190
|
+
return;
|
|
191
|
+
closed = true;
|
|
192
|
+
clearTimeout(timer);
|
|
193
|
+
decoded?.destroy(reason);
|
|
194
|
+
activeResponse?.destroy(reason);
|
|
195
|
+
req.destroy(reason);
|
|
196
|
+
};
|
|
197
|
+
const failOpen = (error) => {
|
|
198
|
+
if (opened)
|
|
199
|
+
return;
|
|
200
|
+
opened = true;
|
|
201
|
+
close(error instanceof Error ? error : undefined);
|
|
202
|
+
reject(normalizeNetworkError(error, input.signal));
|
|
203
|
+
};
|
|
204
|
+
const req = request(options, (response) => {
|
|
205
|
+
activeResponse = response;
|
|
206
|
+
try {
|
|
207
|
+
assertBoundedContentLength(response.headers, input.maximumBytes);
|
|
208
|
+
decoded = decodedResponseStream(response);
|
|
209
|
+
const chunks = boundedDecodedChunks(decoded, input.maximumBytes, input.signal, deadline, () => close());
|
|
210
|
+
opened = true;
|
|
211
|
+
resolve({
|
|
212
|
+
status: response.statusCode ?? 0,
|
|
213
|
+
headers: normalizeHeaders(response.headers),
|
|
214
|
+
chunks,
|
|
215
|
+
finalUrl: new URL(input.url),
|
|
216
|
+
cancel: close,
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
catch (error) {
|
|
220
|
+
failOpen(error);
|
|
221
|
+
}
|
|
222
|
+
});
|
|
223
|
+
const timer = setTimeout(() => {
|
|
224
|
+
const error = deadlineError();
|
|
225
|
+
if (opened)
|
|
226
|
+
close(error);
|
|
227
|
+
else
|
|
228
|
+
failOpen(error);
|
|
229
|
+
}, remainingMilliseconds(deadline));
|
|
230
|
+
req.once('error', (error) => {
|
|
231
|
+
if (!opened)
|
|
232
|
+
failOpen(error);
|
|
233
|
+
});
|
|
234
|
+
if (input.body)
|
|
235
|
+
req.write(input.body);
|
|
236
|
+
req.end();
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
function assertBoundedContentLength(headers, maximumBytes) {
|
|
241
|
+
const contentLength = Number(headers['content-length']);
|
|
242
|
+
if (Number.isFinite(contentLength) && contentLength > maximumBytes)
|
|
243
|
+
throw new SearchableError('resource_limit', 'RESPONSE_TOO_LARGE', 'The response exceeds the configured byte limit.');
|
|
244
|
+
}
|
|
245
|
+
async function defaultDnsResolver(hostname) {
|
|
246
|
+
const answers = await systemLookup(hostname, { all: true, verbatim: true });
|
|
247
|
+
return answers.map((answer) => ({ address: answer.address, family: answer.family }));
|
|
248
|
+
}
|
|
249
|
+
async function resolvePublic(resolver, hostname, deadline, signal) {
|
|
250
|
+
if (isIP(hostname)) {
|
|
251
|
+
assertPublicAddress(hostname);
|
|
252
|
+
return [{ address: hostname, family: isIP(hostname) }];
|
|
253
|
+
}
|
|
254
|
+
let answers;
|
|
255
|
+
try {
|
|
256
|
+
answers = await beforeDeadline(resolver(hostname, signal), deadline, signal);
|
|
257
|
+
}
|
|
258
|
+
catch (error) {
|
|
259
|
+
if (error instanceof SearchableError)
|
|
260
|
+
throw error;
|
|
261
|
+
throw new SearchableError('service', 'PROVIDER_UNAVAILABLE', 'The destination could not be resolved.');
|
|
262
|
+
}
|
|
263
|
+
if (!answers.length)
|
|
264
|
+
throw new SearchableError('service', 'PROVIDER_UNAVAILABLE', 'The destination did not resolve to an address.');
|
|
265
|
+
for (const answer of answers) {
|
|
266
|
+
const family = isIP(answer.address);
|
|
267
|
+
if (family !== answer.family)
|
|
268
|
+
throw new SearchableError('service', 'NETWORK_DESTINATION_BLOCKED', 'The DNS answer has an invalid address family.');
|
|
269
|
+
assertPublicAddress(answer.address);
|
|
270
|
+
}
|
|
271
|
+
return answers;
|
|
272
|
+
}
|
|
273
|
+
function beforeDeadline(promise, deadline, signal) {
|
|
274
|
+
return new Promise((resolve, reject) => {
|
|
275
|
+
let settled = false;
|
|
276
|
+
const finish = (action) => {
|
|
277
|
+
if (settled)
|
|
278
|
+
return;
|
|
279
|
+
settled = true;
|
|
280
|
+
clearTimeout(timer);
|
|
281
|
+
signal?.removeEventListener('abort', cancel);
|
|
282
|
+
action();
|
|
283
|
+
};
|
|
284
|
+
const cancel = () => finish(() => reject(new SearchableError('cancelled', 'OPERATION_CANCELLED', 'Searchable operation was cancelled.')));
|
|
285
|
+
const timer = setTimeout(() => finish(() => reject(deadlineError())), remainingMilliseconds(deadline));
|
|
286
|
+
signal?.addEventListener('abort', cancel, { once: true });
|
|
287
|
+
promise.then((value) => finish(() => resolve(value)), (error) => finish(() => reject(error)));
|
|
288
|
+
});
|
|
289
|
+
}
|
|
290
|
+
async function readDecodedBody(response, maximumBytes, signal, deadline) {
|
|
291
|
+
const chunks = [];
|
|
292
|
+
let size = 0;
|
|
293
|
+
for await (const chunk of boundedDecodedChunks(decodedResponseStream(response), maximumBytes, signal, deadline)) {
|
|
294
|
+
const bytes = Buffer.from(chunk);
|
|
295
|
+
size += bytes.byteLength;
|
|
296
|
+
chunks.push(bytes);
|
|
297
|
+
}
|
|
298
|
+
return Buffer.concat(chunks, size);
|
|
299
|
+
}
|
|
300
|
+
function decodedResponseStream(response) {
|
|
301
|
+
const headers = 'headers' in response ? response.headers : {};
|
|
302
|
+
const encoding = String(headers['content-encoding'] ?? '').toLowerCase();
|
|
303
|
+
if (encoding === 'gzip')
|
|
304
|
+
return response.pipe(createGunzip());
|
|
305
|
+
if (encoding === 'deflate')
|
|
306
|
+
return response.pipe(createInflate());
|
|
307
|
+
if (encoding === 'br')
|
|
308
|
+
return response.pipe(createBrotliDecompress());
|
|
309
|
+
return response;
|
|
310
|
+
}
|
|
311
|
+
async function* boundedDecodedChunks(stream, maximumBytes, signal, deadline, complete = () => { }) {
|
|
312
|
+
let size = 0;
|
|
313
|
+
try {
|
|
314
|
+
for await (const chunk of stream) {
|
|
315
|
+
checkDeadline(deadline, signal);
|
|
316
|
+
const bytes = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
|
|
317
|
+
size += bytes.byteLength;
|
|
318
|
+
if (size > maximumBytes)
|
|
319
|
+
throw new SearchableError('resource_limit', 'RESPONSE_TOO_LARGE', 'The response exceeds the configured byte limit.');
|
|
320
|
+
yield bytes;
|
|
321
|
+
}
|
|
322
|
+
checkDeadline(deadline, signal);
|
|
323
|
+
}
|
|
324
|
+
catch (error) {
|
|
325
|
+
stream.destroy(error instanceof Error ? error : undefined);
|
|
326
|
+
throw normalizeNetworkError(error, signal);
|
|
327
|
+
}
|
|
328
|
+
finally {
|
|
329
|
+
complete();
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
function normalizeDeadline(deadline) {
|
|
333
|
+
return typeof deadline === 'number' ? createSearchableDeadline(Math.max(0, deadline - Date.now())) : deadline;
|
|
334
|
+
}
|
|
335
|
+
export function remainingSearchableDeadline(deadline) {
|
|
336
|
+
return Math.max(1, deadline.expiresAt - performance.now());
|
|
337
|
+
}
|
|
338
|
+
function remainingMilliseconds(deadline) {
|
|
339
|
+
return remainingSearchableDeadline(deadline);
|
|
340
|
+
}
|
|
341
|
+
export function checkSearchableDeadline(deadline, signal) {
|
|
342
|
+
if (signal?.aborted)
|
|
343
|
+
throw new SearchableError('cancelled', 'OPERATION_CANCELLED', 'Searchable operation was cancelled.');
|
|
344
|
+
if (performance.now() >= deadline.expiresAt)
|
|
345
|
+
throw deadlineError();
|
|
346
|
+
}
|
|
347
|
+
function checkDeadline(deadline, signal) {
|
|
348
|
+
checkSearchableDeadline(deadline, signal);
|
|
349
|
+
}
|
|
350
|
+
function deadlineError() {
|
|
351
|
+
return new SearchableError('service', 'DEADLINE_EXCEEDED', 'The Searchable operation exceeded its deadline.');
|
|
352
|
+
}
|
|
353
|
+
function normalizeNetworkError(error, signal) {
|
|
354
|
+
if (signal?.aborted)
|
|
355
|
+
return new SearchableError('cancelled', 'OPERATION_CANCELLED', 'Searchable operation was cancelled.');
|
|
356
|
+
if (error instanceof SearchableError)
|
|
357
|
+
return error;
|
|
358
|
+
return new SearchableError('service', 'PROVIDER_UNAVAILABLE', 'The network request failed.');
|
|
359
|
+
}
|
|
360
|
+
function normalizeHeaders(headers) {
|
|
361
|
+
return Object.fromEntries(Object.entries(headers).flatMap(([key, value]) => value === undefined ? [] : [[key.toLowerCase(), Array.isArray(value) ? value.join(', ') : value]]));
|
|
362
|
+
}
|
|
363
|
+
function removeSensitiveHeaders(headers) {
|
|
364
|
+
return Object.fromEntries(Object.entries(headers).filter(([name]) => !/^(authorization|cookie|ocp-apim-subscription-key|proxy-authorization|x-api-key|x-subscription-token)$/iu.test(name)));
|
|
365
|
+
}
|
|
366
|
+
function stripIpv6Brackets(hostname) {
|
|
367
|
+
return hostname.startsWith('[') && hostname.endsWith(']') ? hostname.slice(1, -1) : hostname;
|
|
368
|
+
}
|
|
369
|
+
function isRedirect(status) {
|
|
370
|
+
return [301, 302, 303, 307, 308].includes(status);
|
|
371
|
+
}
|
|
372
|
+
//# sourceMappingURL=http.js.map
|
package/dist/http.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http.js","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":"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"}
|