Reformat code, add toggle option for protocol support

This commit is contained in:
Younes Aassila 2025-02-07 15:54:31 +01:00
parent b0b0cb09a9
commit de97812b24
No known key found for this signature in database
10 changed files with 85 additions and 59 deletions

View File

@ -38,7 +38,7 @@ export default function onBeforeVideoWeaverRequest(
textLower.includes("https://example.com") && textLower.includes("https://example.com") &&
textLower.includes("https://help.twitch.tv/"); textLower.includes("https://help.twitch.tv/");
const proxy = const proxy =
details.proxyInfo && details.proxyInfo.type !== "DIRECT" details.proxyInfo && details.proxyInfo.type !== "direct"
? getUrlFromProxyInfo(details.proxyInfo) ? getUrlFromProxyInfo(details.proxyInfo)
: null; : null;

View File

@ -32,7 +32,7 @@ export default async function onProxyRequest(
} }
const host = getHostFromUrl(details.url); const host = getHostFromUrl(details.url);
if (!host) return { type: "DIRECT" }; if (!host) return { type: "direct" };
const documentHost = details.documentUrl const documentHost = details.documentUrl
? getHostFromUrl(details.documentUrl) ? getHostFromUrl(details.documentUrl)
@ -43,7 +43,7 @@ export default async function onProxyRequest(
!passportHostRegex.test(documentHost) && // Passport requests have a `passport.twitch.tv` document URL. !passportHostRegex.test(documentHost) && // Passport requests have a `passport.twitch.tv` document URL.
!twitchTvHostRegex.test(documentHost) !twitchTvHostRegex.test(documentHost)
) { ) {
return { type: "DIRECT" }; return { type: "direct" };
} }
const proxies = store.state.optimizedProxiesEnabled const proxies = store.state.optimizedProxiesEnabled
@ -92,12 +92,12 @@ export default async function onProxyRequest(
if (proxyUsherRequest && usherHostRegex.test(host)) { if (proxyUsherRequest && usherHostRegex.test(host)) {
if (details.url.includes("/vod/")) { if (details.url.includes("/vod/")) {
console.log(`✋ '${details.url}' is a VOD manifest.`); console.log(`✋ '${details.url}' is a VOD manifest.`);
return { type: "DIRECT" }; return { type: "direct" };
} }
const channelName = findChannelFromUsherUrl(details.url); const channelName = findChannelFromUsherUrl(details.url);
if (isChannelWhitelisted(channelName)) { if (isChannelWhitelisted(channelName)) {
console.log(`✋ Channel '${channelName}' is whitelisted.`); console.log(`✋ Channel '${channelName}' is whitelisted.`);
return { type: "DIRECT" }; return { type: "direct" };
} }
console.log( console.log(
`⌛ Proxying ${details.url} (${ `⌛ Proxying ${details.url} (${
@ -119,7 +119,7 @@ export default async function onProxyRequest(
findChannelFromTwitchTvUrl(tabUrl); findChannelFromTwitchTvUrl(tabUrl);
if (isChannelWhitelisted(channelName)) { if (isChannelWhitelisted(channelName)) {
console.log(`✋ Channel '${channelName}' is whitelisted.`); console.log(`✋ Channel '${channelName}' is whitelisted.`);
return { type: "DIRECT" }; return { type: "direct" };
} }
console.log( console.log(
`⌛ Proxying ${details.url} (${ `⌛ Proxying ${details.url} (${
@ -149,12 +149,12 @@ export default async function onProxyRequest(
return proxyInfoArray; return proxyInfoArray;
} }
return { type: "DIRECT" }; return { type: "direct" };
} }
function getProxyInfoArrayFromUrls(urls: string[]): ProxyInfo[] { function getProxyInfoArrayFromUrls(urls: string[]): ProxyInfo[] {
return [ return [
...urls.map(getProxyInfoFromUrl), ...urls.map(getProxyInfoFromUrl),
{ type: "DIRECT" } as ProxyInfo, // Fallback to direct connection if all proxies fail. { type: "direct" } as ProxyInfo, // Fallback to direct connection if all proxies fail.
]; ];
} }

View File

@ -171,7 +171,7 @@ function getProxyFromDetails(
return getUrlFromProxyInfo(possibleProxies[0]); return getUrlFromProxyInfo(possibleProxies[0]);
} else { } else {
const proxyInfo = details.proxyInfo; // Firefox only. const proxyInfo = details.proxyInfo; // Firefox only.
if (!proxyInfo || proxyInfo.type === "DIRECT") return null; if (!proxyInfo || proxyInfo.type === "direct") return null;
return getUrlFromProxyInfo(proxyInfo); return getUrlFromProxyInfo(proxyInfo);
} }
} }

View File

@ -1,36 +1,24 @@
import { Address6 } from "ip-address"; import { Address6 } from "ip-address";
import type { ProxyInfo } from "../../types"; import type { ProxyInfo, ProxyType } from "../../types";
export const proxySchemes = { const DEFAULT_PORTS: Readonly<Record<ProxyType, number>> = Object.freeze({
direct: "DIRECT", direct: 0,
http: "PROXY",
https: "HTTPS",
socks: "SOCKS",
socks4: "SOCKS4",
socks5: "SOCKS5",
quic: "QUIC",
} as const;
type Protocol = keyof typeof proxySchemes;
const defaultPorts: Partial<{
[key in keyof typeof proxySchemes]: number;
}> = {
http: 80, http: 80,
https: 443, https: 443,
socks: 1080, socks: 1080,
socks4: 1080, socks4: 1080,
socks5: 1080, });
quic: 443,
};
export function getProxyInfoFromUrl(url: string) { export function getProxyInfoFromUrl(
let protocol = ""; url: string
): ProxyInfo & { host: string; port: number } {
let type: ProxyType | undefined = undefined;
if (url.includes("://")) { if (url.includes("://")) {
const [_protocol, urlWithoutProtocol] = url.split("://"); const [protocol, urlWithoutProtocol] = url.split("://", 2);
protocol = _protocol; type = protocol as ProxyType;
url = urlWithoutProtocol; url = urlWithoutProtocol;
} }
const lastIndexOfAt = url.lastIndexOf("@"); const lastIndexOfAt = url.lastIndexOf("@");
const hostname = url.substring(lastIndexOfAt + 1, url.length); const hostname = url.substring(lastIndexOfAt + 1, url.length);
const lastIndexOfColon = getLastIndexOfColon(hostname); const lastIndexOfColon = getLastIndexOfColon(hostname);
@ -39,9 +27,7 @@ export function getProxyInfoFromUrl(url: string) {
let port: number | undefined = undefined; let port: number | undefined = undefined;
if (lastIndexOfColon === -1) { if (lastIndexOfColon === -1) {
host = hostname; host = hostname;
if (!protocol) { port = !type ? 3128 : DEFAULT_PORTS[type]; // Default port
port = 3128; // Default port
}
} else { } else {
host = hostname.substring(0, lastIndexOfColon); host = hostname.substring(0, lastIndexOfColon);
port = Number(hostname.substring(lastIndexOfColon + 1, hostname.length)); port = Number(hostname.substring(lastIndexOfColon + 1, hostname.length));
@ -59,11 +45,8 @@ export function getProxyInfoFromUrl(url: string) {
password = credentials.substring(indexOfColon + 1, credentials.length); password = credentials.substring(indexOfColon + 1, credentials.length);
} }
port = port ? port : defaultPorts[protocol as Protocol];
return { return {
type: proxySchemes[protocol as Protocol] ?? "PROXY", type: type ?? "http",
protocol,
host, host,
port, port,
username, username,

View File

@ -1,5 +1,5 @@
import store from "../../store"; import store from "../../store";
import { ProxyRequestType } from "../../types"; import { ProxyRequestType, ProxyType } from "../../types";
import isRequestTypeProxied from "./isRequestTypeProxied"; import isRequestTypeProxied from "./isRequestTypeProxied";
import { getProxyInfoFromUrl, getUrlFromProxyInfo } from "./proxyInfo"; import { getProxyInfoFromUrl, getUrlFromProxyInfo } from "./proxyInfo";
import { import {
@ -11,6 +11,14 @@ import {
} from "./regexes"; } from "./regexes";
import updateDnsResponses from "./updateDnsResponses"; import updateDnsResponses from "./updateDnsResponses";
const PROXY_TYPE_MAP: Readonly<Record<ProxyType, string>> = Object.freeze({
direct: "DIRECT",
http: "PROXY",
https: "HTTPS",
socks4: "SOCKS4",
socks: "SOCKS5",
});
export function updateProxySettings(requestFilter?: ProxyRequestType[]) { export function updateProxySettings(requestFilter?: ProxyRequestType[]) {
const { optimizedProxiesEnabled, passportLevel } = store.state; const { optimizedProxiesEnabled, passportLevel } = store.state;
@ -92,7 +100,7 @@ function getProxyInfoStringFromUrls(urls: string[]): string {
return [ return [
...urls.map(url => { ...urls.map(url => {
const proxyInfo = getProxyInfoFromUrl(url); const proxyInfo = getProxyInfoFromUrl(url);
return `${proxyInfo.type} ${getUrlFromProxyInfo({ return `${PROXY_TYPE_MAP[proxyInfo.type]} ${getUrlFromProxyInfo({
...proxyInfo, ...proxyInfo,
// Don't include username/password in PAC script. // Don't include username/password in PAC script.
username: undefined, username: undefined,

View File

@ -85,6 +85,9 @@ const optimizedProxiesListElement = $(
) as HTMLOListElement; ) as HTMLOListElement;
const normalProxiesInputElement = $("#normal") as HTMLInputElement; const normalProxiesInputElement = $("#normal") as HTMLInputElement;
const normalProxiesListElement = $("#normal-proxies-list") as HTMLOListElement; const normalProxiesListElement = $("#normal-proxies-list") as HTMLOListElement;
const otherProtocolsCheckboxElement = $(
"#other-protocols-checkbox"
) as HTMLInputElement;
// Ad log // Ad log
const adLogEnabledCheckboxElement = $( const adLogEnabledCheckboxElement = $(
"#ad-log-enabled-checkbox" "#ad-log-enabled-checkbox"
@ -219,6 +222,12 @@ function main() {
hidePromptMarker: true, hidePromptMarker: true,
insertMode: "both", insertMode: "both",
}); });
otherProtocolsCheckboxElement.checked = store.state.allowOtherProxyProtocols;
otherProtocolsCheckboxElement.addEventListener("change", () => {
store.state.allowOtherProxyProtocols =
otherProtocolsCheckboxElement.checked;
location.reload();
});
// Ad log // Ad log
adLogEnabledCheckboxElement.checked = store.state.adLogEnabled; adLogEnabledCheckboxElement.checked = store.state.adLogEnabled;
adLogEnabledCheckboxElement.addEventListener("change", () => { adLogEnabledCheckboxElement.addEventListener("change", () => {
@ -336,16 +345,18 @@ function isOptimizedProxyUrlAllowed(url: string): AllowedResult {
return [false, "TTV LOL PRO v1 proxies are not compatible"]; return [false, "TTV LOL PRO v1 proxies are not compatible"];
} }
const proxyInfo = getProxyInfoFromUrl(url); if (url.includes("://")) {
if (proxyInfo.host.includes("/")) { const [protocol, urlWithoutProtocol] = url.split("://", 2);
return [false, "Proxy URLs must not contain a path (e.g. '/path')"]; if (!store.state.allowOtherProxyProtocols) {
return [false, "Proxy URLs must not contain a protocol (e.g. 'http://')"];
} else if (!["http", "https", "socks", "socks4"].includes(protocol)) {
return [false, `'${protocol}' is not a supported protocol`];
}
url = urlWithoutProtocol;
} }
try { if (url.includes("/")) {
const host = url.substring(url.lastIndexOf("@") + 1, url.length); return [false, "Proxy URLs must not contain a path (e.g. '/path')"];
new URL(`http://${host}`); // Throws if the host is invalid.
} catch {
return [false, `'${url}' is not a valid proxy URL`];
} }
try { try {

View File

@ -200,6 +200,36 @@
>List of other proxies</a >List of other proxies</a
>" discussion on TTV LOL PRO's GitHub repository. >" discussion on TTV LOL PRO's GitHub repository.
</small> </small>
<br /><br />
<ul class="options-list">
<li>
<input
type="checkbox"
name="other-protocols-checkbox"
id="other-protocols-checkbox"
/>
<label for="other-protocols-checkbox">
Allow the use of other protocols for proxies
</label>
<br />
<small>
By default, only HTTP proxies are allowed. Enabling this option
will allow the use of other protocols like HTTPS, SOCKS4, and
SOCKS5.
</small>
<br />
<small>
To provide a protocol, use the format
<code>protocol://hostname:port</code>. Available protocols are
<code>http</code>, <code>https</code>, <code>socks</code>, and
<code>socks4</code>.
</small>
<br />
<small>
<b>ONLY ENABLE THIS OPTION IF YOU KNOW WHAT YOU ARE DOING</b>.
</small>
</li>
</ul>
</section> </section>
<!-- Ad log --> <!-- Ad log -->

View File

@ -7,6 +7,7 @@ export default function getDefaultState() {
adLog: [], adLog: [],
adLogEnabled: true, adLogEnabled: true,
adLogLastSent: 0, adLogLastSent: 0,
allowOtherProxyProtocols: false,
anonymousMode: true, anonymousMode: true,
chromiumProxyActive: false, chromiumProxyActive: false,
dnsResponses: [], dnsResponses: [],

View File

@ -10,6 +10,7 @@ export interface State {
adLog: AdLogEntry[]; adLog: AdLogEntry[];
adLogEnabled: boolean; adLogEnabled: boolean;
adLogLastSent: number; adLogLastSent: number;
allowOtherProxyProtocols: boolean;
anonymousMode: boolean; anonymousMode: boolean;
chromiumProxyActive: boolean; chromiumProxyActive: boolean;
dnsResponses: DnsResponse[]; dnsResponses: DnsResponse[];

View File

@ -3,19 +3,11 @@ export type KeyOfType<T, V> = keyof {
[P in keyof T as T[P] extends V ? P : never]: any; [P in keyof T as T[P] extends V ? P : never]: any;
}; };
// From https://chromium.googlesource.com/chromium/src/+/HEAD/net/docs/proxy.md#proxy-server-schemes export type ProxyType = "direct" | "http" | "https" | "socks" | "socks4";
export type ProxyScheme =
| "DIRECT"
| "PROXY"
| "HTTPS"
| "SOCKS"
| "SOCKS4"
| "SOCKS5"
| "QUIC";
// From https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/proxy/ProxyInfo // From https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/proxy/ProxyInfo
export interface ProxyInfo { export interface ProxyInfo {
type: ProxyScheme; type: ProxyType;
host?: string; host?: string;
port?: number; port?: number;
username?: string; username?: string;