mirror of
				https://github.com/cp6/my-idlers.git
				synced 2025-11-04 08:09:09 +00:00 
			
		
		
		
	
		
			
	
	
		
			150 lines
		
	
	
	
		
			3.6 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
		
		
			
		
	
	
			150 lines
		
	
	
	
		
			3.6 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| 
								 | 
							
								'use strict';
							 | 
						||
| 
								 | 
							
								const http = require('http');
							 | 
						||
| 
								 | 
							
								const https = require('https');
							 | 
						||
| 
								 | 
							
								const resolveALPN = require('resolve-alpn');
							 | 
						||
| 
								 | 
							
								const QuickLRU = require('quick-lru');
							 | 
						||
| 
								 | 
							
								const Http2ClientRequest = require('./client-request');
							 | 
						||
| 
								 | 
							
								const calculateServerName = require('./utils/calculate-server-name');
							 | 
						||
| 
								 | 
							
								const urlToOptions = require('./utils/url-to-options');
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								const cache = new QuickLRU({maxSize: 100});
							 | 
						||
| 
								 | 
							
								const queue = new Map();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								const installSocket = (agent, socket, options) => {
							 | 
						||
| 
								 | 
							
									socket._httpMessage = {shouldKeepAlive: true};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									const onFree = () => {
							 | 
						||
| 
								 | 
							
										agent.emit('free', socket, options);
							 | 
						||
| 
								 | 
							
									};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									socket.on('free', onFree);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									const onClose = () => {
							 | 
						||
| 
								 | 
							
										agent.removeSocket(socket, options);
							 | 
						||
| 
								 | 
							
									};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									socket.on('close', onClose);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									const onRemove = () => {
							 | 
						||
| 
								 | 
							
										agent.removeSocket(socket, options);
							 | 
						||
| 
								 | 
							
										socket.off('close', onClose);
							 | 
						||
| 
								 | 
							
										socket.off('free', onFree);
							 | 
						||
| 
								 | 
							
										socket.off('agentRemove', onRemove);
							 | 
						||
| 
								 | 
							
									};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									socket.on('agentRemove', onRemove);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									agent.emit('free', socket, options);
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								const resolveProtocol = async options => {
							 | 
						||
| 
								 | 
							
									const name = `${options.host}:${options.port}:${options.ALPNProtocols.sort()}`;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									if (!cache.has(name)) {
							 | 
						||
| 
								 | 
							
										if (queue.has(name)) {
							 | 
						||
| 
								 | 
							
											const result = await queue.get(name);
							 | 
						||
| 
								 | 
							
											return result.alpnProtocol;
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										const {path, agent} = options;
							 | 
						||
| 
								 | 
							
										options.path = options.socketPath;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										const resultPromise = resolveALPN(options);
							 | 
						||
| 
								 | 
							
										queue.set(name, resultPromise);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										try {
							 | 
						||
| 
								 | 
							
											const {socket, alpnProtocol} = await resultPromise;
							 | 
						||
| 
								 | 
							
											cache.set(name, alpnProtocol);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											options.path = path;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											if (alpnProtocol === 'h2') {
							 | 
						||
| 
								 | 
							
												// https://github.com/nodejs/node/issues/33343
							 | 
						||
| 
								 | 
							
												socket.destroy();
							 | 
						||
| 
								 | 
							
											} else {
							 | 
						||
| 
								 | 
							
												const {globalAgent} = https;
							 | 
						||
| 
								 | 
							
												const defaultCreateConnection = https.Agent.prototype.createConnection;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
												if (agent) {
							 | 
						||
| 
								 | 
							
													if (agent.createConnection === defaultCreateConnection) {
							 | 
						||
| 
								 | 
							
														installSocket(agent, socket, options);
							 | 
						||
| 
								 | 
							
													} else {
							 | 
						||
| 
								 | 
							
														socket.destroy();
							 | 
						||
| 
								 | 
							
													}
							 | 
						||
| 
								 | 
							
												} else if (globalAgent.createConnection === defaultCreateConnection) {
							 | 
						||
| 
								 | 
							
													installSocket(globalAgent, socket, options);
							 | 
						||
| 
								 | 
							
												} else {
							 | 
						||
| 
								 | 
							
													socket.destroy();
							 | 
						||
| 
								 | 
							
												}
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											queue.delete(name);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											return alpnProtocol;
							 | 
						||
| 
								 | 
							
										} catch (error) {
							 | 
						||
| 
								 | 
							
											queue.delete(name);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											throw error;
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									return cache.get(name);
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								module.exports = async (input, options, callback) => {
							 | 
						||
| 
								 | 
							
									if (typeof input === 'string' || input instanceof URL) {
							 | 
						||
| 
								 | 
							
										input = urlToOptions(new URL(input));
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									if (typeof options === 'function') {
							 | 
						||
| 
								 | 
							
										callback = options;
							 | 
						||
| 
								 | 
							
										options = undefined;
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									options = {
							 | 
						||
| 
								 | 
							
										ALPNProtocols: ['h2', 'http/1.1'],
							 | 
						||
| 
								 | 
							
										...input,
							 | 
						||
| 
								 | 
							
										...options,
							 | 
						||
| 
								 | 
							
										resolveSocket: true
							 | 
						||
| 
								 | 
							
									};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									if (!Array.isArray(options.ALPNProtocols) || options.ALPNProtocols.length === 0) {
							 | 
						||
| 
								 | 
							
										throw new Error('The `ALPNProtocols` option must be an Array with at least one entry');
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									options.protocol = options.protocol || 'https:';
							 | 
						||
| 
								 | 
							
									const isHttps = options.protocol === 'https:';
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									options.host = options.hostname || options.host || 'localhost';
							 | 
						||
| 
								 | 
							
									options.session = options.tlsSession;
							 | 
						||
| 
								 | 
							
									options.servername = options.servername || calculateServerName(options);
							 | 
						||
| 
								 | 
							
									options.port = options.port || (isHttps ? 443 : 80);
							 | 
						||
| 
								 | 
							
									options._defaultAgent = isHttps ? https.globalAgent : http.globalAgent;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									const agents = options.agent;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									if (agents) {
							 | 
						||
| 
								 | 
							
										if (agents.addRequest) {
							 | 
						||
| 
								 | 
							
											throw new Error('The `options.agent` object can contain only `http`, `https` or `http2` properties');
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										options.agent = agents[isHttps ? 'https' : 'http'];
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									if (isHttps) {
							 | 
						||
| 
								 | 
							
										const protocol = await resolveProtocol(options);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										if (protocol === 'h2') {
							 | 
						||
| 
								 | 
							
											if (agents) {
							 | 
						||
| 
								 | 
							
												options.agent = agents.http2;
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											return new Http2ClientRequest(options, callback);
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									return http.request(options, callback);
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								module.exports.protocolCache = cache;
							 |