mirror of
https://github.com/samuelthomas2774/nxapi.git
synced 2026-03-21 18:04:10 -05:00
Add support for HTTP proxies
This commit is contained in:
parent
d7a64b9807
commit
0457746e5e
|
|
@ -30,6 +30,7 @@ export type Menu = import('electron').Menu;
|
|||
export type MenuItem = import('electron').MenuItem;
|
||||
export type MessageBoxOptions = import('electron').MessageBoxOptions;
|
||||
export type Notification = import('electron').Notification;
|
||||
export type Session = import('electron').Session;
|
||||
export type Settings = import('electron').Settings;
|
||||
export type ShareMenu = import('electron').ShareMenu;
|
||||
export type SharingItem = import('electron').SharingItem;
|
||||
|
|
|
|||
|
|
@ -1,14 +1,15 @@
|
|||
import { app, BrowserWindow, ipcMain, LoginItemSettingsOptions } from './electron.js';
|
||||
import { app, BrowserWindow, ipcMain, LoginItemSettingsOptions, session } from './electron.js';
|
||||
import process from 'node:process';
|
||||
import * as path from 'node:path';
|
||||
import { EventEmitter } from 'node:events';
|
||||
import { setGlobalDispatcher } from 'undici';
|
||||
import * as persist from 'node-persist';
|
||||
import MenuApp from './menu.js';
|
||||
import { handleOpenWebServiceUri } from './webservices.js';
|
||||
import { EmbeddedPresenceMonitor, PresenceMonitorManager } from './monitor.js';
|
||||
import { createModalWindow, createWindow } from './windows.js';
|
||||
import { setupIpc } from './ipc.js';
|
||||
import { askUserForUri, showErrorDialog } from './util.js';
|
||||
import { askUserForUri, buildElectronProxyAgent, showErrorDialog } from './util.js';
|
||||
import { setAppInstance } from './app-menu.js';
|
||||
import { handleAuthUri } from './na-auth.js';
|
||||
import { DiscordPresenceConfiguration, LoginItem, LoginItemOptions, WindowType } from '../common/types.js';
|
||||
|
|
@ -119,6 +120,11 @@ export async function init() {
|
|||
initGlobals();
|
||||
addUserAgent('nxapi-app (Chromium ' + process.versions.chrome + '; Electron ' + process.versions.electron + ')');
|
||||
|
||||
const agent = buildElectronProxyAgent({
|
||||
session: session.defaultSession,
|
||||
});
|
||||
setGlobalDispatcher(agent);
|
||||
|
||||
app.setAboutPanelOptions({
|
||||
applicationName: 'nxapi-app',
|
||||
applicationVersion: process.platform === 'darwin' ? version : version +
|
||||
|
|
|
|||
|
|
@ -1,11 +1,15 @@
|
|||
import { BrowserWindow, dialog, Menu, MenuItem, MessageBoxOptions, nativeImage } from './electron.js';
|
||||
import { BrowserWindow, dialog, Menu, MenuItem, MessageBoxOptions, nativeImage, Session } from './electron.js';
|
||||
import path from 'node:path';
|
||||
import { Buffer } from 'node:buffer';
|
||||
import createDebug from '../../util/debug.js';
|
||||
import { fetch } from 'undici';
|
||||
import { dir } from '../../util/product.js';
|
||||
import { App, Store } from './index.js';
|
||||
import { SavedToken } from '../../common/auth/coral.js';
|
||||
import { ErrorDescription } from '../../util/errors.js';
|
||||
import { buildProxyAgent, ProxyAgentOptions } from '../../util/undici-proxy.js';
|
||||
|
||||
const debug = createDebug('app:main:util');
|
||||
|
||||
export const bundlepath = path.resolve(dir, 'dist', 'app', 'bundle');
|
||||
|
||||
|
|
@ -87,3 +91,35 @@ export function showErrorDialog(options: ErrorBoxOptions) {
|
|||
dialog.showMessageBox(window, message_box_options) :
|
||||
dialog.showMessageBox(message_box_options);
|
||||
}
|
||||
|
||||
export function buildElectronProxyAgent(options: ProxyAgentOptions & {
|
||||
session: Session;
|
||||
}) {
|
||||
let warned_proxy_unsupported: string | null = null;
|
||||
|
||||
return buildProxyAgent({
|
||||
...options,
|
||||
resolveProxy: async origin => {
|
||||
// https://chromium.googlesource.com/chromium/src/+/HEAD/net/docs/proxy.md
|
||||
const proxies = await options.session.resolveProxy(origin);
|
||||
const proxy = proxies.split(';')[0].trim();
|
||||
|
||||
if (proxy === 'DIRECT') return null;
|
||||
|
||||
if (proxy.startsWith('PROXY ')) {
|
||||
return new URL('http://' + proxy.substr(6));
|
||||
}
|
||||
if (proxy.startsWith('HTTPS ')) {
|
||||
return new URL('https://' + proxy.substr(6));
|
||||
}
|
||||
|
||||
if (warned_proxy_unsupported !== proxy) {
|
||||
warned_proxy_unsupported = proxy;
|
||||
|
||||
debug('Unsupported proxy', proxy);
|
||||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import process from 'node:process';
|
||||
import Yargs from 'yargs';
|
||||
import { setGlobalDispatcher } from 'undici';
|
||||
import * as commands from './cli/index.js';
|
||||
import { checkUpdates } from './common/update.js';
|
||||
import createDebug from './util/debug.js';
|
||||
|
|
@ -9,11 +10,15 @@ import { YargsArguments } from './util/yargs.js';
|
|||
import { addUserAgent } from './util/useragent.js';
|
||||
import { USER_AGENT_INFO_URL } from './common/constants.js';
|
||||
import { init as initGlobals } from './common/globals.js';
|
||||
import { buildEnvironmentProxyAgent } from './util/undici-proxy.js';
|
||||
|
||||
const debug = createDebug('cli');
|
||||
|
||||
initGlobals();
|
||||
|
||||
const agent = buildEnvironmentProxyAgent();
|
||||
setGlobalDispatcher(agent);
|
||||
|
||||
export function createYargs(argv: string[]) {
|
||||
const yargs = Yargs(argv).option('data-path', {
|
||||
describe: 'Data storage path',
|
||||
|
|
|
|||
|
|
@ -20,12 +20,12 @@ export class ErrorDescription {
|
|||
|
||||
if (description) {
|
||||
return description.message +
|
||||
(err instanceof Error ? '\n\n--\n\n' + (err.stack ?? err.message) : '');
|
||||
(err instanceof Error ? '\n\n--\n\n' + util.inspect(err) : '');
|
||||
}
|
||||
}
|
||||
|
||||
if (err instanceof Error) {
|
||||
return err.stack || err.message;
|
||||
return util.inspect(err);
|
||||
}
|
||||
|
||||
return util.inspect(err, {compact: true});
|
||||
|
|
|
|||
94
src/util/undici-proxy.ts
Normal file
94
src/util/undici-proxy.ts
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
import { Agent, buildConnector, Dispatcher, errors } from 'undici';
|
||||
import createDebug from './debug.js';
|
||||
|
||||
const debug = createDebug('nxapi:util:undici-proxy');
|
||||
|
||||
function defaultProtocolPort(protocol: string) {
|
||||
return protocol === 'https:' ? 443 : 80;
|
||||
}
|
||||
|
||||
export interface ProxyAgentOptions {
|
||||
agent?: Agent;
|
||||
requestTls?: buildConnector.BuildOptions;
|
||||
}
|
||||
|
||||
export function buildProxyAgent(options: ProxyAgentOptions & {
|
||||
resolveProxy: (origin: string) => Promise<URL | null>;
|
||||
}) {
|
||||
const agent = options.agent ?? new Agent();
|
||||
const connectEndpoint = buildConnector(options.requestTls ?? {});
|
||||
|
||||
return new Agent({
|
||||
connect: async (opts, callback) => {
|
||||
let requestedHost = opts.host!;
|
||||
|
||||
if (!opts.port) {
|
||||
requestedHost += `:${defaultProtocolPort(opts.protocol)}`;
|
||||
}
|
||||
|
||||
try {
|
||||
const request_origin = opts.protocol + '//' + opts.hostname +
|
||||
(opts.port ? ':' + opts.port : '');
|
||||
|
||||
const proxy = await options.resolveProxy.call(null, request_origin);
|
||||
|
||||
debug('resolved proxy for %s as %s', request_origin, proxy?.toString());
|
||||
|
||||
if (!proxy) {
|
||||
connectEndpoint(opts, callback);
|
||||
return;
|
||||
}
|
||||
|
||||
const { origin, port, host } = proxy;
|
||||
|
||||
const { socket, statusCode } = await agent.connect({
|
||||
// @ts-expect-error
|
||||
origin,
|
||||
port,
|
||||
path: requestedHost,
|
||||
// @ts-expect-error
|
||||
signal: opts.signal,
|
||||
headers: {
|
||||
host,
|
||||
},
|
||||
}) as unknown as Dispatcher.ConnectData;
|
||||
|
||||
if (statusCode !== 200) {
|
||||
socket.on('error', () => {}).destroy();
|
||||
callback(new errors.RequestAbortedError('Proxy response !== 200 when HTTP Tunneling'), null);
|
||||
}
|
||||
|
||||
if (opts.protocol !== 'https:') {
|
||||
// @ts-expect-error
|
||||
callback(null, socket);
|
||||
return;
|
||||
}
|
||||
|
||||
// @ts-expect-error
|
||||
connectEndpoint({ ...opts, httpSocket: socket }, callback);
|
||||
} catch (err) {
|
||||
callback(err as Error, null);
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function buildEnvironmentProxyAgent(options?: ProxyAgentOptions) {
|
||||
return buildProxyAgent({
|
||||
...options,
|
||||
resolveProxy: resolveProxyFromEnvironment,
|
||||
});
|
||||
}
|
||||
|
||||
export async function resolveProxyFromEnvironment(origin: string) {
|
||||
const { protocol } = new URL(origin);
|
||||
|
||||
if (protocol === 'http:' && process.env.HTTP_PROXY) {
|
||||
return new URL(process.env.HTTP_PROXY);
|
||||
}
|
||||
if (protocol === 'https:' && process.env.HTTPS_PROXY) {
|
||||
return new URL(process.env.HTTPS_PROXY);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user