Add socks proxy support to proxy feature

- Socks proxy support implemented.
- Monitor proxy agent create flow refactored
  and moved under proxy class.

Thanks for suggestion @thomasleveil
pull/840/head
Uğur Erkan 3 years ago
parent 9e27acb511
commit 8078d0618d

@ -1,6 +1,4 @@
const https = require("https"); const https = require("https");
const HttpProxyAgent = require("http-proxy-agent");
const HttpsProxyAgent = require("https-proxy-agent");
const dayjs = require("dayjs"); const dayjs = require("dayjs");
const utc = require("dayjs/plugin/utc"); const utc = require("dayjs/plugin/utc");
let timezone = require("dayjs/plugin/timezone"); let timezone = require("dayjs/plugin/timezone");
@ -13,6 +11,7 @@ const { tcping, ping, dnsResolve, checkCertificate, checkStatusCode, getTotalCli
const { R } = require("redbean-node"); const { R } = require("redbean-node");
const { BeanModel } = require("redbean-node/dist/bean-model"); const { BeanModel } = require("redbean-node/dist/bean-model");
const { Notification } = require("../notification"); const { Notification } = require("../notification");
const { Proxy } = require("../proxy");
const { demoMode } = require("../config"); const { demoMode } = require("../config");
const version = require("../../package.json").version; const version = require("../../package.json").version;
const apicache = require("../modules/apicache"); const apicache = require("../modules/apicache");
@ -204,31 +203,13 @@ class Monitor extends BeanModel {
const proxy = await R.load("proxy", this.proxy_id); const proxy = await R.load("proxy", this.proxy_id);
if (proxy && proxy.active) { if (proxy && proxy.active) {
const httpProxyAgentOptions = { const { httpAgent, httpsAgent } = Proxy.createAgents(proxy, {
protocol: proxy.protocol, httpsAgentOptions: httpsAgentOptions,
host: proxy.host, });
port: proxy.port,
};
const httpsProxyAgentOptions = {
...httpsAgentOptions,
protocol: proxy.protocol,
hostname: proxy.host,
port: proxy.port,
};
if (proxy.auth) {
httpProxyAgentOptions.auth = `${proxy.username}:${proxy.password}`;
httpsProxyAgentOptions.auth = `${proxy.username}:${proxy.password}`;
}
debug(`[${this.name}] HTTP options: ${JSON.stringify({
"http": httpProxyAgentOptions,
"https": httpsProxyAgentOptions,
})}`);
options.proxy = false; options.proxy = false;
options.httpAgent = new HttpProxyAgent(httpProxyAgentOptions); options.httpAgent = httpAgent;
options.httpsAgent = new HttpsProxyAgent(httpsProxyAgentOptions); options.httpsAgent = httpsAgent;
} }
} }

@ -1,7 +1,13 @@
const { R } = require("redbean-node"); const { R } = require("redbean-node");
const HttpProxyAgent = require("http-proxy-agent");
const HttpsProxyAgent = require("https-proxy-agent");
const SocksProxyAgent = require("socks-proxy-agent");
const { debug } = require("../src/util");
class Proxy { class Proxy {
static SUPPORTED_PROXY_PROTOCOLS = ["http", "https", "socks", "socks5", "socks4"]
/** /**
* Saves and updates given proxy entity * Saves and updates given proxy entity
* *
@ -25,8 +31,11 @@ class Proxy {
} }
// Make sure given proxy protocol is supported // Make sure given proxy protocol is supported
if (!["http", "https"].includes(proxy.protocol)) { if (!this.SUPPORTED_PROXY_PROTOCOLS.includes(proxy.protocol)) {
throw new Error(`Unsupported proxy protocol "${proxy.protocol}. Supported protocols are http and https."`); throw new Error(`
Unsupported proxy protocol "${proxy.protocol}.
Supported protocols are ${this.SUPPORTED_PROXY_PROTOCOLS.join(", ")}."`
);
} }
// When proxy is default update deactivate old default proxy // When proxy is default update deactivate old default proxy
@ -73,6 +82,68 @@ class Proxy {
// Delete proxy from list // Delete proxy from list
await R.trash(bean); await R.trash(bean);
} }
/**
* Create HTTP and HTTPS agents related with given proxy bean object
*
* @param proxy proxy bean object
* @param options http and https agent options
* @return {{httpAgent: Agent, httpsAgent: Agent}}
*/
static createAgents(proxy, options) {
const { httpAgentOptions, httpsAgentOptions } = options || {};
let agent;
let httpAgent;
let httpsAgent;
const proxyOptions = {
protocol: proxy.protocol,
host: proxy.host,
port: proxy.port,
};
if (proxy.auth) {
proxyOptions.auth = `${proxy.username}:${proxy.password}`;
}
debug(`Proxy Options: ${JSON.stringify(proxyOptions)}`);
debug(`HTTP Agent Options: ${JSON.stringify(httpAgentOptions)}`);
debug(`HTTPS Agent Options: ${JSON.stringify(httpsAgentOptions)}`);
switch (proxy.protocol) {
case "http":
case "https":
httpAgent = new HttpProxyAgent({
...httpAgentOptions || {},
...proxyOptions
});
httpsAgent = new HttpsProxyAgent({
...httpsAgentOptions || {},
...proxyOptions,
});
break;
case "socks":
case "socks5":
case "socks4":
agent = new SocksProxyAgent({
...httpAgentOptions,
...httpsAgentOptions,
...proxyOptions,
});
httpAgent = agent;
httpsAgent = agent;
break;
default: throw new Error(`Unsupported proxy protocol provided. ${proxy.protocol}`);
}
return {
httpAgent,
httpsAgent
};
}
} }
/** /**

@ -15,6 +15,9 @@
<select id="proxy-protocol" v-model="proxy.protocol" class="form-select"> <select id="proxy-protocol" v-model="proxy.protocol" class="form-select">
<option value="https">HTTPS</option> <option value="https">HTTPS</option>
<option value="http">HTTP</option> <option value="http">HTTP</option>
<option value="socks">SOCKS</option>
<option value="socks5">SOCKS v5</option>
<option value="socks4">SOCKS v4</option>
</select> </select>
</div> </div>

@ -11,7 +11,8 @@
<ul class="list-group mb-3" style="border-radius: 1rem;"> <ul class="list-group mb-3" style="border-radius: 1rem;">
<li v-for="(proxy, index) in $root.proxyList" :key="index" class="list-group-item"> <li v-for="(proxy, index) in $root.proxyList" :key="index" class="list-group-item">
{{ proxy.host }}<span v-if="proxy.default === true" class="badge bg-primary ms-2">{{ $t("Default") }}</span><br> {{ proxy.host }}:{{ proxy.port }} ({{ proxy.protocol }})
<span v-if="proxy.default === true" class="badge bg-primary ms-2">{{ $t("Default") }}</span><br>
<a href="#" @click="$refs.proxyDialog.show(proxy.id)">{{ $t("Edit") }}</a> <a href="#" @click="$refs.proxyDialog.show(proxy.id)">{{ $t("Edit") }}</a>
</li> </li>
</ul> </ul>

@ -237,7 +237,7 @@
<input :id="`proxy-${proxy.id}`" v-model="monitor.proxyId" :value="proxy.id" name="proxy" class="form-check-input" type="radio"> <input :id="`proxy-${proxy.id}`" v-model="monitor.proxyId" :value="proxy.id" name="proxy" class="form-check-input" type="radio">
<label class="form-check-label" :for="`proxy-${proxy.id}`"> <label class="form-check-label" :for="`proxy-${proxy.id}`">
{{ proxy.host }} {{ proxy.host }}:{{ proxy.port }} ({{ proxy.protocol }})
<a href="#" @click="$refs.proxyDialog.show(proxy.id)">{{ $t("Edit") }}</a> <a href="#" @click="$refs.proxyDialog.show(proxy.id)">{{ $t("Edit") }}</a>
</label> </label>

Loading…
Cancel
Save