From 62e34f55b05f7ee88268cfc53370effffc2724c4 Mon Sep 17 00:00:00 2001 From: Tim Date: Sat, 1 Jun 2024 12:23:56 +0100 Subject: [PATCH 1/3] feat: add support for additional proxy protocols --- .../handlers/onBeforeVideoWeaverRequest.ts | 2 +- src/background/handlers/onResponseStarted.ts | 2 +- src/common/ts/proxyInfo.ts | 33 ++++++++++++++----- src/common/ts/proxySettings.ts | 2 +- src/options/options.ts | 15 +++++---- src/types.ts | 14 +++++++- 6 files changed, 50 insertions(+), 18 deletions(-) diff --git a/src/background/handlers/onBeforeVideoWeaverRequest.ts b/src/background/handlers/onBeforeVideoWeaverRequest.ts index 9eba3c7..deec2f2 100644 --- a/src/background/handlers/onBeforeVideoWeaverRequest.ts +++ b/src/background/handlers/onBeforeVideoWeaverRequest.ts @@ -38,7 +38,7 @@ export default function onBeforeVideoWeaverRequest( textLower.includes("https://example.com") && textLower.includes("https://help.twitch.tv/"); const proxy = - details.proxyInfo && details.proxyInfo.type !== "direct" + details.proxyInfo && details.proxyInfo.type !== "DIRECT" ? getUrlFromProxyInfo(details.proxyInfo) : null; diff --git a/src/background/handlers/onResponseStarted.ts b/src/background/handlers/onResponseStarted.ts index a02b436..e188386 100644 --- a/src/background/handlers/onResponseStarted.ts +++ b/src/background/handlers/onResponseStarted.ts @@ -171,7 +171,7 @@ function getProxyFromDetails( return getUrlFromProxyInfo(possibleProxies[0]); } else { const proxyInfo = details.proxyInfo; // Firefox only. - if (!proxyInfo || proxyInfo.type === "direct") return null; + if (!proxyInfo || proxyInfo.type === "DIRECT") return null; return getUrlFromProxyInfo(proxyInfo); } } diff --git a/src/common/ts/proxyInfo.ts b/src/common/ts/proxyInfo.ts index a053a22..9b06313 100644 --- a/src/common/ts/proxyInfo.ts +++ b/src/common/ts/proxyInfo.ts @@ -1,18 +1,34 @@ import { Address6 } from "ip-address"; -import type { ProxyInfo } from "../../types"; +import type { ProxyInfo, ProxyScheme } from "../../types"; -export function getProxyInfoFromUrl( - url: string -): ProxyInfo & { type: "http"; host: string; port: number } { +export const proxySchemes: { [key: string]: ProxyScheme } = { + direct: "DIRECT", + http: "PROXY", + https: "HTTPS", + socks: "SOCKS", + socks4: "SOCKS4", + socks5: "SOCKS5", + quic: "QUIC", +}; + +export function getProxyInfoFromUrl(url: string) { + let protocol = ""; + if (url.includes("://")) { + let [proto, urlWithoutProtocol] = url.split("://"); + protocol = proto; + url = urlWithoutProtocol; + } const lastIndexOfAt = url.lastIndexOf("@"); - const hostname = url.substring(lastIndexOfAt + 1, url.length); + let hostname = url.substring(lastIndexOfAt + 1, url.length); const lastIndexOfColon = getLastIndexOfColon(hostname); - + hostname; let host: string | undefined = undefined; let port: number | undefined = undefined; if (lastIndexOfColon === -1) { host = hostname; - port = 3128; // Default port + if (!protocol) { + port = 3128; // Default port + } } else { host = hostname.substring(0, lastIndexOfColon); port = Number(hostname.substring(lastIndexOfColon + 1, hostname.length)); @@ -31,7 +47,8 @@ export function getProxyInfoFromUrl( } return { - type: "http", + type: proxySchemes[protocol] ?? "PROXY", + protocol, host, port, username, diff --git a/src/common/ts/proxySettings.ts b/src/common/ts/proxySettings.ts index 2269791..d493f5f 100644 --- a/src/common/ts/proxySettings.ts +++ b/src/common/ts/proxySettings.ts @@ -92,7 +92,7 @@ function getProxyInfoStringFromUrls(urls: string[]): string { return [ ...urls.map(url => { const proxyInfo = getProxyInfoFromUrl(url); - return `PROXY ${getUrlFromProxyInfo({ + return `${proxyInfo.type} ${getUrlFromProxyInfo({ ...proxyInfo, // Don't include username/password in PAC script. username: undefined, diff --git a/src/options/options.ts b/src/options/options.ts index 078e0b4..640a648 100644 --- a/src/options/options.ts +++ b/src/options/options.ts @@ -16,10 +16,9 @@ import sendAdLog from "../common/ts/sendAdLog"; import store from "../store"; import getDefaultState from "../store/getDefaultState"; import type { State } from "../store/types"; -import { KeyOfType, ProxyRequestType } from "../types"; +import { AllowedResult, KeyOfType, ProxyRequestType } from "../types"; //#region Types -type AllowedResult = [boolean, string?]; type InsertMode = "append" | "prepend" | "both"; type StoreStringArrayKey = KeyOfType; type ListOptions = { @@ -323,12 +322,16 @@ function isOptimizedProxyUrlAllowed(url: string): AllowedResult { return [false, "TTV LOL PRO v1 proxies are not compatible"]; } - if (/^https?:\/\//i.test(url)) { - return [false, "Proxy URLs must not contain a protocol (e.g. 'http://')"]; + const proxyInfo = getProxyInfoFromUrl(url); + if (proxyInfo.host.includes("/")) { + return [false, "Proxy URLs must not contain a path (e.g. '/path')"]; } - if (url.includes("/")) { - return [false, "Proxy URLs must not contain a path (e.g. '/path')"]; + try { + const host = url.substring(url.lastIndexOf("@") + 1, url.length); + new URL(`http://${host}`); // Throws if the host is invalid. + } catch { + return [false, `'${url}' is not a valid proxy URL`]; } try { diff --git a/src/types.ts b/src/types.ts index ca7d7a4..ce657e3 100644 --- a/src/types.ts +++ b/src/types.ts @@ -3,9 +3,21 @@ export type KeyOfType = keyof { [P in keyof T as T[P] extends V ? P : never]: any; }; +export type AllowedResult = [boolean, string?]; + +// From https://chromium.googlesource.com/chromium/src/+/HEAD/net/docs/proxy.md#proxy-server-schemes +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 export interface ProxyInfo { - type: "direct" | "http" | "https" | "socks" | "socks4"; + type: ProxyScheme; host?: string; port?: number; username?: string; From a7e7aa0e9312e8d975a8d36c2be128bac84747b3 Mon Sep 17 00:00:00 2001 From: Tim Date: Sat, 1 Jun 2024 12:23:56 +0100 Subject: [PATCH 2/3] fix: fallback to protocols default port for onAuthRequired --- src/common/ts/proxyInfo.ts | 27 +++++++++++++++++++++------ src/options/options.ts | 3 ++- src/types.ts | 2 -- 3 files changed, 23 insertions(+), 9 deletions(-) diff --git a/src/common/ts/proxyInfo.ts b/src/common/ts/proxyInfo.ts index 9b06313..4e8b482 100644 --- a/src/common/ts/proxyInfo.ts +++ b/src/common/ts/proxyInfo.ts @@ -1,7 +1,7 @@ import { Address6 } from "ip-address"; -import type { ProxyInfo, ProxyScheme } from "../../types"; +import type { ProxyInfo } from "../../types"; -export const proxySchemes: { [key: string]: ProxyScheme } = { +export const proxySchemes = { direct: "DIRECT", http: "PROXY", https: "HTTPS", @@ -9,19 +9,32 @@ export const proxySchemes: { [key: string]: ProxyScheme } = { socks4: "SOCKS4", socks5: "SOCKS5", quic: "QUIC", +} as const; + +type Protocol = keyof typeof proxySchemes; + +const defaultPorts: Partial<{ + [key in keyof typeof proxySchemes]: number; +}> = { + http: 80, + https: 443, + socks: 1080, + socks4: 1080, + socks5: 1080, + quic: 443, }; export function getProxyInfoFromUrl(url: string) { let protocol = ""; if (url.includes("://")) { - let [proto, urlWithoutProtocol] = url.split("://"); - protocol = proto; + let [_protocol, urlWithoutProtocol] = url.split("://"); + protocol = _protocol; url = urlWithoutProtocol; } const lastIndexOfAt = url.lastIndexOf("@"); let hostname = url.substring(lastIndexOfAt + 1, url.length); const lastIndexOfColon = getLastIndexOfColon(hostname); - hostname; + let host: string | undefined = undefined; let port: number | undefined = undefined; if (lastIndexOfColon === -1) { @@ -46,8 +59,10 @@ export function getProxyInfoFromUrl(url: string) { password = credentials.substring(indexOfColon + 1, credentials.length); } + port = port ? port : defaultPorts[protocol as Protocol]; + return { - type: proxySchemes[protocol] ?? "PROXY", + type: proxySchemes[protocol as Protocol] ?? "PROXY", protocol, host, port, diff --git a/src/options/options.ts b/src/options/options.ts index 640a648..0737953 100644 --- a/src/options/options.ts +++ b/src/options/options.ts @@ -16,9 +16,10 @@ import sendAdLog from "../common/ts/sendAdLog"; import store from "../store"; import getDefaultState from "../store/getDefaultState"; import type { State } from "../store/types"; -import { AllowedResult, KeyOfType, ProxyRequestType } from "../types"; +import { KeyOfType, ProxyRequestType } from "../types"; //#region Types +type AllowedResult = [boolean, string?]; type InsertMode = "append" | "prepend" | "both"; type StoreStringArrayKey = KeyOfType; type ListOptions = { diff --git a/src/types.ts b/src/types.ts index ce657e3..2502934 100644 --- a/src/types.ts +++ b/src/types.ts @@ -3,8 +3,6 @@ export type KeyOfType = keyof { [P in keyof T as T[P] extends V ? P : never]: any; }; -export type AllowedResult = [boolean, string?]; - // From https://chromium.googlesource.com/chromium/src/+/HEAD/net/docs/proxy.md#proxy-server-schemes export type ProxyScheme = | "DIRECT" From f2aee154ba68558344df40ff9604f6ee4500f225 Mon Sep 17 00:00:00 2001 From: Tim Date: Sat, 1 Jun 2024 20:14:56 +0100 Subject: [PATCH 3/3] fix: types --- src/background/handlers/onProxyRequest.ts | 14 +++++++------- src/common/ts/proxyInfo.ts | 4 ++-- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/background/handlers/onProxyRequest.ts b/src/background/handlers/onProxyRequest.ts index 5dbaf5f..a27e60d 100644 --- a/src/background/handlers/onProxyRequest.ts +++ b/src/background/handlers/onProxyRequest.ts @@ -32,7 +32,7 @@ export default async function onProxyRequest( } const host = getHostFromUrl(details.url); - if (!host) return { type: "direct" }; + if (!host) return { type: "DIRECT" }; const documentHost = 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. !twitchTvHostRegex.test(documentHost) ) { - return { type: "direct" }; + return { type: "DIRECT" }; } const proxies = store.state.optimizedProxiesEnabled @@ -92,12 +92,12 @@ export default async function onProxyRequest( if (proxyUsherRequest && usherHostRegex.test(host)) { if (details.url.includes("/vod/")) { console.log(`✋ '${details.url}' is a VOD manifest.`); - return { type: "direct" }; + return { type: "DIRECT" }; } const channelName = findChannelFromUsherUrl(details.url); if (isChannelWhitelisted(channelName)) { console.log(`✋ Channel '${channelName}' is whitelisted.`); - return { type: "direct" }; + return { type: "DIRECT" }; } console.log( `⌛ Proxying ${details.url} (${ @@ -119,7 +119,7 @@ export default async function onProxyRequest( findChannelFromTwitchTvUrl(tabUrl); if (isChannelWhitelisted(channelName)) { console.log(`✋ Channel '${channelName}' is whitelisted.`); - return { type: "direct" }; + return { type: "DIRECT" }; } console.log( `⌛ Proxying ${details.url} (${ @@ -149,12 +149,12 @@ export default async function onProxyRequest( return proxyInfoArray; } - return { type: "direct" }; + return { type: "DIRECT" }; } function getProxyInfoArrayFromUrls(urls: string[]): ProxyInfo[] { return [ ...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. ]; } diff --git a/src/common/ts/proxyInfo.ts b/src/common/ts/proxyInfo.ts index 4e8b482..7f42552 100644 --- a/src/common/ts/proxyInfo.ts +++ b/src/common/ts/proxyInfo.ts @@ -27,12 +27,12 @@ const defaultPorts: Partial<{ export function getProxyInfoFromUrl(url: string) { let protocol = ""; if (url.includes("://")) { - let [_protocol, urlWithoutProtocol] = url.split("://"); + const [_protocol, urlWithoutProtocol] = url.split("://"); protocol = _protocol; url = urlWithoutProtocol; } const lastIndexOfAt = url.lastIndexOf("@"); - let hostname = url.substring(lastIndexOfAt + 1, url.length); + const hostname = url.substring(lastIndexOfAt + 1, url.length); const lastIndexOfColon = getLastIndexOfColon(hostname); let host: string | undefined = undefined;