-
-
Notifications
You must be signed in to change notification settings - Fork 78
/
Copy pathActorHttp.ts
143 lines (127 loc) · 5.02 KB
/
ActorHttp.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
import type { IAction, IActorArgs, IActorOutput, IActorTest, Mediate } from '@comunica/core';
import { Actor } from '@comunica/core';
import { readableFromWeb } from 'readable-from-web';
const isStream = require('is-stream');
const toWebReadableStream = require('readable-stream-node-to-web');
/**
* A base actor for listening to HTTP events.
*
* Actor types:
* * Input: IActionHttp: The HTTP request.
* * Test: IActorHttpTest: An estimate for the response time.
* * Output: IActorHttpOutput: The HTTP response.
*
* @see IActionHttp
* @see IActorHttpTest
* @see IActorHttpOutput
*/
export abstract class ActorHttp<TS = undefined> extends Actor<IActionHttp, IActorTest, IActorHttpOutput, TS> {
/* eslint-disable max-len */
/**
* @param args -
* \ @defaultNested {<default_bus> a <cc:components/Bus.jsonld#Bus>} bus
* \ @defaultNested {HTTP request failed: none of the configured actors were able to handle ${action.input}} busFailMessage
*/
/* eslint-enable max-len */
public constructor(args: IActorHttpArgs<TS>) {
super(args);
}
/**
* Converts WhatWG streams to Node streams if required.
* Returns the input in case the stream already is a Node stream.
* @param {ReadableStream} body
* @returns {NodeJS.ReadableStream} A node stream.
*/
public static toNodeReadable(body: ReadableStream | null): NodeJS.ReadableStream {
return isStream(body) || body === null ?
<NodeJS.ReadableStream> <any> body :
<NodeJS.ReadableStream> <any> readableFromWeb(body);
}
/**
* Converts Node streams to WhatWG streams.
* @param {NodeJS.ReadableStream} body
* @returns {ReadableStream} A web stream.
*/
public static toWebReadableStream(body: NodeJS.ReadableStream | null): ReadableStream {
return toWebReadableStream(body);
}
/**
* Convert the given headers object into a raw hash.
* @param headers A headers object.
*/
public static headersToHash(headers: Headers): Record<string, string> {
const hash: Record<string, string> = {};
// eslint-disable-next-line unicorn/no-array-for-each
headers.forEach((value, key) => {
hash[key] = value;
});
return hash;
}
/**
* Extract the requested URL from the action input.
* @param {RequestInfo | URL} input The request input.
* @returns {URL} The extracted URL.
*/
public static getInputUrl(input: RequestInfo | URL): URL {
return new URL(input instanceof Request ? input.url : input);
}
/**
* Creates an appropriate User-Agent header string for Node.js or other environments.
* Within browsers, returns undefined, because the value should not be overridden due to potential CORS issues.
*/
public static createUserAgent(actorName: string, actorVersion: string): string | undefined {
if (!ActorHttp.isBrowser()) {
const versions = [
`Comunica/${actorVersion.split('.')[0]}.0`,
`${actorName}/${actorVersion}`,
];
if (typeof globalThis.navigator === 'object' && typeof globalThis.navigator.userAgent === 'string') {
// Most runtimes like Node.js 21+, Deno and Bun implement navigator.userAgent
versions.push(globalThis.navigator.userAgent);
} else if (
typeof globalThis.process === 'object' &&
typeof globalThis.process.versions === 'object' &&
typeof globalThis.process.versions.node === 'string'
) {
// TODO: remove this entire 'else if' when support for Node.js 20 is dropped, this only exists for that one
versions.push(`Node.js/${globalThis.process.versions.node.split('.')[0]}`);
}
if (
typeof globalThis.process === 'object' &&
typeof globalThis.process.platform === 'string' &&
typeof globalThis.process.arch === 'string'
) {
versions.splice(1, 0, `(${globalThis.process.platform}; ${globalThis.process.arch})`);
}
return versions.join(' ');
}
}
/**
* Attempts to determine whether the current environment is a browser or not.
* @returns {boolean} True for browsers and web workers, false for other runtimes.
*/
public static isBrowser(): boolean {
return (
// The window global and the document are available in browsers, but not in web workers
// https://developer.mozilla.org/en-US/docs/Glossary/Global_object
(typeof globalThis.window === 'object' && typeof globalThis.window.document === 'object') ||
// The importScripts function is only available in Web Workers
// https://developer.mozilla.org/en-US/docs/Web/API/WorkerGlobalScope/importScripts
(typeof (<any>globalThis).importScripts === 'function')
);
}
}
/**
* The HTTP input, which contains the HTTP request.
*/
export interface IActionHttp extends IAction {
input: RequestInfo;
init?: RequestInit;
}
/**
* The HTTP output, which contains the HTTP response.
*/
export interface IActorHttpOutput extends IActorOutput, Response {
}
export type IActorHttpArgs<TS = undefined> = IActorArgs<IActionHttp, IActorTest, IActorHttpOutput, TS>;
export type MediatorHttp = Mediate<IActionHttp, IActorHttpOutput>;