UNPKG

auto-pod

Version:

Automatically choose tunneling or direct connection for cocoapods. As easy as `pod` itself. Help you get rid of GFW of China.

231 lines 8.59 kB
"use strict"; // Copyright 2019 (c) Karl Cauchy // Rearranged from https://github.com/oyyd/http-proxy-to-socks // LICENSE: See at LICENSE.md Object.defineProperty(exports, "__esModule", { value: true }); // inspired by https://github.com/asluchevskiy/http-to-socks-proxy const fs = require("fs"); const http = require("http"); const matcher = require("matcher"); const net_1 = require("net"); const url = require("url"); const socks_1 = require("socks"); const Agent_1 = require("./Agent"); const Logger_1 = require("./Logger"); class ProxyServer extends http.Server { constructor(options) { super(); this.proxyList = []; this.includes = []; this.excludes = []; this.onReadElement = () => { return this.randomElement(this.proxyList); }; // if (options.socks) { // // stand alone proxy loging // this.loadProxy(options.socks); // } else if (options.socksListFileName) { // // proxy list loading // this.loadProxyFile(options.socksListFileName); // if (options.proxyListReloadTimeout) { // setInterval( // () => { // this.loadProxyFile(options.socksListFileName); // }, // options.proxyListReloadTimeout * 1000, // ); // } // } this.loadProxies(options.proxies); this.addListener('request', this.requestListener.bind(this, this.onReadElement)); this.addListener('connect', this.connectListener.bind(this, this.onReadElement)); this.includes = options.includes; this.excludes = options.excludes; this.forceTunneling = options.forceTunneling; } loadProxy(proxyLine) { try { this.proxyList.push(this.parseProxyLine(proxyLine)); } catch (ex) { Logger_1.getLogger().error(ex.message); } } loadProxies(proxies) { for (const proxy of proxies) { this.loadProxy(proxy); } } getProxyObject(host, port, login, password) { return { authentication: { username: login || '', password: password || '' }, command: 'connect', host, lookup: true, port: parseInt(port, 10), version: 5, }; } parseProxyLine(line) { if (typeof line === 'string') { const proxyInfo = line.split(':'); if (proxyInfo.length !== 4 && proxyInfo.length !== 2) { throw new Error(`Incorrect proxy line: ${line}`); } return this.getProxyObject.apply(this, proxyInfo); } // Directly use IProxy. return { authentication: line.authentication, command: 'connect', host: line.host, lookup: line.lookup, port: line.nport, version: line.version, }; } loadProxyFile(fileName) { Logger_1.getLogger().info(`Loading proxy list from file: ${fileName}`); fs.readFile(fileName, (err, data) => { if (err) { Logger_1.getLogger().error(`Impossible to read the proxy file : ${fileName} error : ${err.message}`); return; } const lines = data.toString().split('\n'); const proxyList = []; for (let i = 0; i < lines.length; i += 1) { if (!(lines[i] !== '' && lines[i].charAt(0) !== '#')) { try { proxyList.push(this.parseProxyLine(lines[i])); } catch (ex) { Logger_1.getLogger().error(ex.message); } } } this.proxyList = proxyList; }); } // ----------- Private functions ----------- randomElement(array) { return array[Math.floor(Math.random() * array.length)]; } doUseAgent(host) { if (this.forceTunneling) { return false; } // Check if we should use the socksAgent. const includes = matcher([host], this.includes); const excludes = matcher([host], this.excludes); let useAgent = false; if (includes.length > 0) { // Then this is considered to be yes. useAgent = true; } if (excludes.length > 0) { useAgent = false; } return useAgent; } requestListener(getProxyInfo, request, response) { Logger_1.getLogger().info(`request: ${request.url}`); const proxy = getProxyInfo(); const ph = url.parse(request.url); // const port = parseInt(ph.port, 10) || 80; const socksAgent = new Agent_1.SocksProxyAgent(proxy); // Check if we should use the socksAgent. const useAgent = this.doUseAgent(ph.hostname); const options = { agent: useAgent ? socksAgent : http.globalAgent, headers: request.headers, hostname: ph.hostname, method: request.method, path: ph.path, port: ph.port, }; const proxyRequest = http.request(options); request.on('error', (err) => { Logger_1.getLogger().error(`${err.message}`); proxyRequest.destroy(err); }); proxyRequest.on('error', (error) => { Logger_1.getLogger().error(`${error.message} on proxy ${proxy.host}:${proxy.port}`); response.writeHead(500); response.end('Connection error\n'); }); proxyRequest.on('response', (proxyResponse) => { proxyResponse.pipe(response); response.writeHead(proxyResponse.statusCode, proxyResponse.headers); }); request.pipe(proxyRequest); } connectListener(getProxyInfo, request, socketRequest, head) { Logger_1.getLogger().debug(`connect: ${request.url}`); const proxy = getProxyInfo(); const ph = url.parse(`http://${request.url}`); const { hostname: host, port } = ph; const options = { command: proxy.command, destination: { host, port: parseInt(port, 10) }, proxy: { host: proxy.host, password: '', port: proxy.port, type: proxy.version, userId: '', }, timeout: proxy.timeout, type: proxy.version, }; if (proxy.authentication) { options.proxy.userId = proxy.authentication.username; options.proxy.password = proxy.authentication.password; } let socket; socketRequest.on('error', (err) => { Logger_1.getLogger().error(`${err.message}`); if (socket) { socket.destroy(err); } }); // Check if we should use the socksAgent. const useAgent = this.doUseAgent(host); function tunneling(error, tSocket) { socket = tSocket; if (error) { // error in SocksSocket creation Logger_1.getLogger() .error(`${error.message} connection creating on ${proxy.host}:${proxy.port}`); socketRequest.write(`HTTP/${request.httpVersion} 500 Connection error\r\n\r\n`); return; } socket.on('error', (err) => { Logger_1.getLogger().error(`${err.message}`); socketRequest.destroy(err); }); // tunneling to the host socket.pipe(socketRequest); socketRequest.pipe(socket); socket.write(head); socketRequest.write(`HTTP/${request.httpVersion} 200 Connection established\r\n\r\n`); socket.resume(); } if (!useAgent) { // Direct connection socket = new net_1.Socket(); socket.connect({ host, port: parseInt(port, 10) || 443 }, (error) => { Logger_1.getLogger().info(`Direct connect: ${request.url}`); tunneling(error, socket); }); return; } socks_1.SocksClient.createConnection(options, (error, // req: http.IncomingMessage, originalSocket) => { Logger_1.getLogger().info(`Tunneling connect: ${request.url}`); tunneling(error, originalSocket.socket); }); } } exports.ProxyServer = ProxyServer; //# sourceMappingURL=ProxyServer.js.map