From 9261abd2b16e6582045cd7a1eef535bbeb58d5ff Mon Sep 17 00:00:00 2001 From: AkiraFukushima Date: Mon, 21 Oct 2019 22:24:59 +0900 Subject: [PATCH] refs #69 Add support to using socks proxy for REST, streaming and websocket --- example/typescript/yarn.lock | 33 +++++++++++++++++++++++++++++++++ package.json | 1 + src/mastodon.ts | 16 ++++++++-------- src/proxy_config.ts | 24 ++++++++++++++++++++---- yarn.lock | 32 ++++++++++++++++++++++++++++++++ 5 files changed, 94 insertions(+), 12 deletions(-) diff --git a/example/typescript/yarn.lock b/example/typescript/yarn.lock index 6bd7cf5..11a7a32 100644 --- a/example/typescript/yarn.lock +++ b/example/typescript/yarn.lock @@ -55,6 +55,13 @@ agent-base@^4.3.0: dependencies: es6-promisify "^5.0.0" +agent-base@~4.2.1: + version "4.2.1" + resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-4.2.1.tgz#d89e5999f797875674c07d87f260fc41e83e8ca9" + integrity sha512-JVwXMr9nHYTUXsBFKUqhJwvlcYU/blreOEUkhNR2eXZIvwd+c+o5V4MgDPKWnMS/56awN3TRzIP+KoPn+roQtg== + dependencies: + es6-promisify "^5.0.0" + ajv@^5.3.0: version "5.5.2" resolved "https://registry.yarnpkg.com/ajv/-/ajv-5.5.2.tgz#73b5eeca3fab653e3d3f9422b341ad42205dc965" @@ -301,6 +308,11 @@ https-proxy-agent@^3.0.0: agent-base "^4.3.0" debug "^3.1.0" +ip@^1.1.5: + version "1.1.5" + resolved "https://registry.yarnpkg.com/ip/-/ip-1.1.5.tgz#bdded70114290828c0a039e72ef25f5aaec4354a" + integrity sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo= + is-buffer@^2.0.2: version "2.0.3" resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-2.0.3.tgz#4ecf3fcf749cbd1e472689e109ac66261a25e725" @@ -375,6 +387,7 @@ log4js@^5.2.2: moment "^2.24.0" oauth "^0.9.15" request "^2.87.0" + socks-proxy-agent h3poteto/node-socks-proxy-agent#master typescript "^3.4.5" ws "^7.0.1" @@ -476,6 +489,26 @@ safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0: resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== +smart-buffer@4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/smart-buffer/-/smart-buffer-4.0.2.tgz#5207858c3815cc69110703c6b94e46c15634395d" + integrity sha512-JDhEpTKzXusOqXZ0BUIdH+CjFdO/CR3tLlf5CN34IypI+xMmXW1uB16OOY8z3cICbJlDAVJzNbwBhNO0wt9OAw== + +socks-proxy-agent@h3poteto/node-socks-proxy-agent#master: + version "4.0.2" + resolved "https://codeload.github.com/h3poteto/node-socks-proxy-agent/tar.gz/5be42d4a3f98c5a156d713ea88a54b825f26d1f3" + dependencies: + agent-base "~4.2.1" + socks "~2.3.2" + +socks@~2.3.2: + version "2.3.2" + resolved "https://registry.yarnpkg.com/socks/-/socks-2.3.2.tgz#ade388e9e6d87fdb11649c15746c578922a5883e" + integrity sha512-pCpjxQgOByDHLlNqlnh/mNSAxIUkyBBuwwhTcV+enZGbDaClPvHdvm6uvOwZfFJkam7cGhBNbb4JxiP8UZkRvQ== + dependencies: + ip "^1.1.5" + smart-buffer "4.0.2" + sshpk@^1.7.0: version "1.14.2" resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.14.2.tgz#c6fc61648a3d9c4e764fd3fcdf4ea105e492ba98" diff --git a/package.json b/package.json index 0a83c55..442cfd7 100644 --- a/package.json +++ b/package.json @@ -58,6 +58,7 @@ "moment": "^2.24.0", "oauth": "^0.9.15", "request": "^2.87.0", + "socks-proxy-agent": "h3poteto/node-socks-proxy-agent#master", "typescript": "^3.4.5", "ws": "^7.0.1" }, diff --git a/src/mastodon.ts b/src/mastodon.ts index c7b6ba9..748181f 100644 --- a/src/mastodon.ts +++ b/src/mastodon.ts @@ -1,5 +1,5 @@ import { OAuth2 } from 'oauth' -import axios, { AxiosResponse, CancelTokenSource } from 'axios' +import axios, { AxiosResponse, CancelTokenSource, AxiosRequestConfig } from 'axios' import StreamListener from './stream_listener' import WebSocket from './web_socket' @@ -239,7 +239,7 @@ export default class Mastodon implements MegalodonInstance { proxyConfig: ProxyConfig | false = false ): Promise> { const apiUrl = baseUrl - let options = { + let options: AxiosRequestConfig = { params: params } if (proxyConfig) { @@ -264,7 +264,7 @@ export default class Mastodon implements MegalodonInstance { baseUrl = DEFAULT_URL, proxyConfig: ProxyConfig | false = false ): Promise> { - let options = {} + let options: AxiosRequestConfig = {} if (proxyConfig) { options = Object.assign(options, { httpsAgent: proxyAgent(proxyConfig) @@ -288,7 +288,7 @@ export default class Mastodon implements MegalodonInstance { * @param params Query parameters */ public async get(path: string, params = {}): Promise> { - let options = { + let options: AxiosRequestConfig = { cancelToken: this.cancelTokenSource.token, headers: { Authorization: `Bearer ${this.accessToken}` @@ -326,7 +326,7 @@ export default class Mastodon implements MegalodonInstance { * @param params Form data. If you want to post file, please use FormData() */ public async put(path: string, params = {}): Promise> { - let options = { + let options: AxiosRequestConfig = { cancelToken: this.cancelTokenSource.token, headers: { Authorization: `Bearer ${this.accessToken}` @@ -363,7 +363,7 @@ export default class Mastodon implements MegalodonInstance { * @param params Form data. If you want to post file, please use FormData() */ public async patch(path: string, params = {}): Promise> { - let options = { + let options: AxiosRequestConfig = { cancelToken: this.cancelTokenSource.token, headers: { Authorization: `Bearer ${this.accessToken}` @@ -400,7 +400,7 @@ export default class Mastodon implements MegalodonInstance { * @param params Form data */ public async post(path: string, params = {}): Promise> { - let options = { + let options: AxiosRequestConfig = { cancelToken: this.cancelTokenSource.token, headers: { Authorization: `Bearer ${this.accessToken}` @@ -428,7 +428,7 @@ export default class Mastodon implements MegalodonInstance { * @param params Form data */ public async del(path: string, params = {}): Promise> { - let options = { + let options: AxiosRequestConfig = { cancelToken: this.cancelTokenSource.token, data: params, headers: { diff --git a/src/proxy_config.ts b/src/proxy_config.ts index c80a524..cda4e7d 100644 --- a/src/proxy_config.ts +++ b/src/proxy_config.ts @@ -1,4 +1,5 @@ import HttpsProxyAgent from 'https-proxy-agent' +import SocksProxyAgent from 'socks-proxy-agent' export type ProxyConfig = { host: string @@ -7,15 +8,30 @@ export type ProxyConfig = { username: string password: string } - protocol: string + protocol: 'http' | 'https' | 'socks4' | 'socks4a' | 'socks5' | 'socks5h' | 'socks' } -const proxyAgent = (proxyConfig: ProxyConfig): HttpsProxyAgent => { +class ProxyProtocolError extends Error {} + +const proxyAgent = (proxyConfig: ProxyConfig): HttpsProxyAgent | SocksProxyAgent => { let auth = '' if (proxyConfig.auth) { auth = `${proxyConfig.auth.username}:${proxyConfig.auth.password}@` } - const agent = new HttpsProxyAgent(`${proxyConfig.protocol}://${auth}${proxyConfig.host}:${proxyConfig.port}`) - return agent + switch (proxyConfig.protocol) { + case 'http': + case 'https': + const httpsAgent = new HttpsProxyAgent(`${proxyConfig.protocol}://${auth}${proxyConfig.host}:${proxyConfig.port}`) + return httpsAgent + case 'socks4': + case 'socks4a': + case 'socks5': + case 'socks5h': + case 'socks': + const socksAgent = new SocksProxyAgent(`${proxyConfig.protocol}://${auth}${proxyConfig.host}:${proxyConfig.port}`) + return socksAgent + default: + throw new ProxyProtocolError('protocol is not accepted') + } } export default proxyAgent diff --git a/yarn.lock b/yarn.lock index d2b9226..35bbd93 100644 --- a/yarn.lock +++ b/yarn.lock @@ -503,6 +503,13 @@ agent-base@^4.3.0: dependencies: es6-promisify "^5.0.0" +agent-base@~4.2.1: + version "4.2.1" + resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-4.2.1.tgz#d89e5999f797875674c07d87f260fc41e83e8ca9" + integrity sha512-JVwXMr9nHYTUXsBFKUqhJwvlcYU/blreOEUkhNR2eXZIvwd+c+o5V4MgDPKWnMS/56awN3TRzIP+KoPn+roQtg== + dependencies: + es6-promisify "^5.0.0" + ajv@^6.10.0, ajv@^6.10.2, ajv@^6.5.5: version "6.10.2" resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.10.2.tgz#d3cea04d6b017b2894ad69040fec8b623eb4bd52" @@ -1938,6 +1945,11 @@ invert-kv@^2.0.0: resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-2.0.0.tgz#7393f5afa59ec9ff5f67a27620d11c226e3eec02" integrity sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA== +ip@^1.1.5: + version "1.1.5" + resolved "https://registry.yarnpkg.com/ip/-/ip-1.1.5.tgz#bdded70114290828c0a039e72ef25f5aaec4354a" + integrity sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo= + is-accessor-descriptor@^0.1.6: version "0.1.6" resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz#a9e12cb3ae8d876727eeef3843f8a0897b5c98d6" @@ -3794,6 +3806,11 @@ slice-ansi@^2.1.0: astral-regex "^1.0.0" is-fullwidth-code-point "^2.0.0" +smart-buffer@4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/smart-buffer/-/smart-buffer-4.0.2.tgz#5207858c3815cc69110703c6b94e46c15634395d" + integrity sha512-JDhEpTKzXusOqXZ0BUIdH+CjFdO/CR3tLlf5CN34IypI+xMmXW1uB16OOY8z3cICbJlDAVJzNbwBhNO0wt9OAw== + snapdragon-node@^2.0.1: version "2.1.1" resolved "https://registry.yarnpkg.com/snapdragon-node/-/snapdragon-node-2.1.1.tgz#6c175f86ff14bdb0724563e8f3c1b021a286853b" @@ -3824,6 +3841,21 @@ snapdragon@^0.8.1: source-map-resolve "^0.5.0" use "^3.1.0" +socks-proxy-agent@h3poteto/node-socks-proxy-agent#master: + version "4.0.2" + resolved "https://codeload.github.com/h3poteto/node-socks-proxy-agent/tar.gz/5be42d4a3f98c5a156d713ea88a54b825f26d1f3" + dependencies: + agent-base "~4.2.1" + socks "~2.3.2" + +socks@~2.3.2: + version "2.3.2" + resolved "https://registry.yarnpkg.com/socks/-/socks-2.3.2.tgz#ade388e9e6d87fdb11649c15746c578922a5883e" + integrity sha512-pCpjxQgOByDHLlNqlnh/mNSAxIUkyBBuwwhTcV+enZGbDaClPvHdvm6uvOwZfFJkam7cGhBNbb4JxiP8UZkRvQ== + dependencies: + ip "^1.1.5" + smart-buffer "4.0.2" + source-map-resolve@^0.5.0: version "0.5.2" resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.2.tgz#72e2cc34095543e43b2c62b2c4c10d4a9054f259"