Software zum Installieren eines Smart-Mirror Frameworks , zum Nutzen von hochschulrelevanten Informationen, auf einem Raspberry-Pi.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

index.ts 8.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345
  1. import net from 'net';
  2. import http from 'http';
  3. import https from 'https';
  4. import { Duplex } from 'stream';
  5. import { EventEmitter } from 'events';
  6. import createDebug from 'debug';
  7. import promisify from './promisify';
  8. const debug = createDebug('agent-base');
  9. function isAgent(v: any): v is createAgent.AgentLike {
  10. return Boolean(v) && typeof v.addRequest === 'function';
  11. }
  12. function isSecureEndpoint(): boolean {
  13. const { stack } = new Error();
  14. if (typeof stack !== 'string') return false;
  15. return stack.split('\n').some(l => l.indexOf('(https.js:') !== -1 || l.indexOf('node:https:') !== -1);
  16. }
  17. function createAgent(opts?: createAgent.AgentOptions): createAgent.Agent;
  18. function createAgent(
  19. callback: createAgent.AgentCallback,
  20. opts?: createAgent.AgentOptions
  21. ): createAgent.Agent;
  22. function createAgent(
  23. callback?: createAgent.AgentCallback | createAgent.AgentOptions,
  24. opts?: createAgent.AgentOptions
  25. ) {
  26. return new createAgent.Agent(callback, opts);
  27. }
  28. namespace createAgent {
  29. export interface ClientRequest extends http.ClientRequest {
  30. _last?: boolean;
  31. _hadError?: boolean;
  32. method: string;
  33. }
  34. export interface AgentRequestOptions {
  35. host?: string;
  36. path?: string;
  37. // `port` on `http.RequestOptions` can be a string or undefined,
  38. // but `net.TcpNetConnectOpts` expects only a number
  39. port: number;
  40. }
  41. export interface HttpRequestOptions
  42. extends AgentRequestOptions,
  43. Omit<http.RequestOptions, keyof AgentRequestOptions> {
  44. secureEndpoint: false;
  45. }
  46. export interface HttpsRequestOptions
  47. extends AgentRequestOptions,
  48. Omit<https.RequestOptions, keyof AgentRequestOptions> {
  49. secureEndpoint: true;
  50. }
  51. export type RequestOptions = HttpRequestOptions | HttpsRequestOptions;
  52. export type AgentLike = Pick<createAgent.Agent, 'addRequest'> | http.Agent;
  53. export type AgentCallbackReturn = Duplex | AgentLike;
  54. export type AgentCallbackCallback = (
  55. err?: Error | null,
  56. socket?: createAgent.AgentCallbackReturn
  57. ) => void;
  58. export type AgentCallbackPromise = (
  59. req: createAgent.ClientRequest,
  60. opts: createAgent.RequestOptions
  61. ) =>
  62. | createAgent.AgentCallbackReturn
  63. | Promise<createAgent.AgentCallbackReturn>;
  64. export type AgentCallback = typeof Agent.prototype.callback;
  65. export type AgentOptions = {
  66. timeout?: number;
  67. };
  68. /**
  69. * Base `http.Agent` implementation.
  70. * No pooling/keep-alive is implemented by default.
  71. *
  72. * @param {Function} callback
  73. * @api public
  74. */
  75. export class Agent extends EventEmitter {
  76. public timeout: number | null;
  77. public maxFreeSockets: number;
  78. public maxTotalSockets: number;
  79. public maxSockets: number;
  80. public sockets: {
  81. [key: string]: net.Socket[];
  82. };
  83. public freeSockets: {
  84. [key: string]: net.Socket[];
  85. };
  86. public requests: {
  87. [key: string]: http.IncomingMessage[];
  88. };
  89. public options: https.AgentOptions;
  90. private promisifiedCallback?: createAgent.AgentCallbackPromise;
  91. private explicitDefaultPort?: number;
  92. private explicitProtocol?: string;
  93. constructor(
  94. callback?: createAgent.AgentCallback | createAgent.AgentOptions,
  95. _opts?: createAgent.AgentOptions
  96. ) {
  97. super();
  98. let opts = _opts;
  99. if (typeof callback === 'function') {
  100. this.callback = callback;
  101. } else if (callback) {
  102. opts = callback;
  103. }
  104. // Timeout for the socket to be returned from the callback
  105. this.timeout = null;
  106. if (opts && typeof opts.timeout === 'number') {
  107. this.timeout = opts.timeout;
  108. }
  109. // These aren't actually used by `agent-base`, but are required
  110. // for the TypeScript definition files in `@types/node` :/
  111. this.maxFreeSockets = 1;
  112. this.maxSockets = 1;
  113. this.maxTotalSockets = Infinity;
  114. this.sockets = {};
  115. this.freeSockets = {};
  116. this.requests = {};
  117. this.options = {};
  118. }
  119. get defaultPort(): number {
  120. if (typeof this.explicitDefaultPort === 'number') {
  121. return this.explicitDefaultPort;
  122. }
  123. return isSecureEndpoint() ? 443 : 80;
  124. }
  125. set defaultPort(v: number) {
  126. this.explicitDefaultPort = v;
  127. }
  128. get protocol(): string {
  129. if (typeof this.explicitProtocol === 'string') {
  130. return this.explicitProtocol;
  131. }
  132. return isSecureEndpoint() ? 'https:' : 'http:';
  133. }
  134. set protocol(v: string) {
  135. this.explicitProtocol = v;
  136. }
  137. callback(
  138. req: createAgent.ClientRequest,
  139. opts: createAgent.RequestOptions,
  140. fn: createAgent.AgentCallbackCallback
  141. ): void;
  142. callback(
  143. req: createAgent.ClientRequest,
  144. opts: createAgent.RequestOptions
  145. ):
  146. | createAgent.AgentCallbackReturn
  147. | Promise<createAgent.AgentCallbackReturn>;
  148. callback(
  149. req: createAgent.ClientRequest,
  150. opts: createAgent.AgentOptions,
  151. fn?: createAgent.AgentCallbackCallback
  152. ):
  153. | createAgent.AgentCallbackReturn
  154. | Promise<createAgent.AgentCallbackReturn>
  155. | void {
  156. throw new Error(
  157. '"agent-base" has no default implementation, you must subclass and override `callback()`'
  158. );
  159. }
  160. /**
  161. * Called by node-core's "_http_client.js" module when creating
  162. * a new HTTP request with this Agent instance.
  163. *
  164. * @api public
  165. */
  166. addRequest(req: ClientRequest, _opts: RequestOptions): void {
  167. const opts: RequestOptions = { ..._opts };
  168. if (typeof opts.secureEndpoint !== 'boolean') {
  169. opts.secureEndpoint = isSecureEndpoint();
  170. }
  171. if (opts.host == null) {
  172. opts.host = 'localhost';
  173. }
  174. if (opts.port == null) {
  175. opts.port = opts.secureEndpoint ? 443 : 80;
  176. }
  177. if (opts.protocol == null) {
  178. opts.protocol = opts.secureEndpoint ? 'https:' : 'http:';
  179. }
  180. if (opts.host && opts.path) {
  181. // If both a `host` and `path` are specified then it's most
  182. // likely the result of a `url.parse()` call... we need to
  183. // remove the `path` portion so that `net.connect()` doesn't
  184. // attempt to open that as a unix socket file.
  185. delete opts.path;
  186. }
  187. delete opts.agent;
  188. delete opts.hostname;
  189. delete opts._defaultAgent;
  190. delete opts.defaultPort;
  191. delete opts.createConnection;
  192. // Hint to use "Connection: close"
  193. // XXX: non-documented `http` module API :(
  194. req._last = true;
  195. req.shouldKeepAlive = false;
  196. let timedOut = false;
  197. let timeoutId: ReturnType<typeof setTimeout> | null = null;
  198. const timeoutMs = opts.timeout || this.timeout;
  199. const onerror = (err: NodeJS.ErrnoException) => {
  200. if (req._hadError) return;
  201. req.emit('error', err);
  202. // For Safety. Some additional errors might fire later on
  203. // and we need to make sure we don't double-fire the error event.
  204. req._hadError = true;
  205. };
  206. const ontimeout = () => {
  207. timeoutId = null;
  208. timedOut = true;
  209. const err: NodeJS.ErrnoException = new Error(
  210. `A "socket" was not created for HTTP request before ${timeoutMs}ms`
  211. );
  212. err.code = 'ETIMEOUT';
  213. onerror(err);
  214. };
  215. const callbackError = (err: NodeJS.ErrnoException) => {
  216. if (timedOut) return;
  217. if (timeoutId !== null) {
  218. clearTimeout(timeoutId);
  219. timeoutId = null;
  220. }
  221. onerror(err);
  222. };
  223. const onsocket = (socket: AgentCallbackReturn) => {
  224. if (timedOut) return;
  225. if (timeoutId != null) {
  226. clearTimeout(timeoutId);
  227. timeoutId = null;
  228. }
  229. if (isAgent(socket)) {
  230. // `socket` is actually an `http.Agent` instance, so
  231. // relinquish responsibility for this `req` to the Agent
  232. // from here on
  233. debug(
  234. 'Callback returned another Agent instance %o',
  235. socket.constructor.name
  236. );
  237. (socket as createAgent.Agent).addRequest(req, opts);
  238. return;
  239. }
  240. if (socket) {
  241. socket.once('free', () => {
  242. this.freeSocket(socket as net.Socket, opts);
  243. });
  244. req.onSocket(socket as net.Socket);
  245. return;
  246. }
  247. const err = new Error(
  248. `no Duplex stream was returned to agent-base for \`${req.method} ${req.path}\``
  249. );
  250. onerror(err);
  251. };
  252. if (typeof this.callback !== 'function') {
  253. onerror(new Error('`callback` is not defined'));
  254. return;
  255. }
  256. if (!this.promisifiedCallback) {
  257. if (this.callback.length >= 3) {
  258. debug('Converting legacy callback function to promise');
  259. this.promisifiedCallback = promisify(this.callback);
  260. } else {
  261. this.promisifiedCallback = this.callback;
  262. }
  263. }
  264. if (typeof timeoutMs === 'number' && timeoutMs > 0) {
  265. timeoutId = setTimeout(ontimeout, timeoutMs);
  266. }
  267. if ('port' in opts && typeof opts.port !== 'number') {
  268. opts.port = Number(opts.port);
  269. }
  270. try {
  271. debug(
  272. 'Resolving socket for %o request: %o',
  273. opts.protocol,
  274. `${req.method} ${req.path}`
  275. );
  276. Promise.resolve(this.promisifiedCallback(req, opts)).then(
  277. onsocket,
  278. callbackError
  279. );
  280. } catch (err) {
  281. Promise.reject(err).catch(callbackError);
  282. }
  283. }
  284. freeSocket(socket: net.Socket, opts: AgentOptions) {
  285. debug('Freeing socket %o %o', socket.constructor.name, opts);
  286. socket.destroy();
  287. }
  288. destroy() {
  289. debug('Destroying agent %o', this.constructor.name);
  290. }
  291. }
  292. // So that `instanceof` works correctly
  293. createAgent.prototype = createAgent.Agent.prototype;
  294. }
  295. export = createAgent;