Skip to content

Commit

Permalink
fix(sdk): failed to transform proxy port between sdk and renderer (#8175
Browse files Browse the repository at this point in the history
)

* fix: update the proxy transforming between sdk and inso

* fix: lint error

* fix: smoke test

* chore: enable sentry for the hidden window

* chore: add tag for sentry reporting
  • Loading branch information
ihexxa authored Nov 20, 2024
1 parent fe151cd commit 12cb2fc
Show file tree
Hide file tree
Showing 7 changed files with 121 additions and 66 deletions.
29 changes: 20 additions & 9 deletions packages/insomnia-sdk/src/objects/__tests__/proxy-configs.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { describe, expect, it } from 'vitest';

import { ProxyConfig, ProxyConfigList } from '../proxy-configs';
import { ProxyConfig, ProxyConfigList, transformToSdkProxyOptions } from '../proxy-configs';
import { Url } from '../urls';

describe('test ProxyConfig object', () => {
Expand All @@ -15,6 +15,7 @@ describe('test ProxyConfig object', () => {
authenticate: true,
username: 'proxy_username',
password: 'proxy_password',
protocol: 'https:',
});

expect(
Expand All @@ -23,15 +24,8 @@ describe('test ProxyConfig object', () => {
['http', 'https']
);

proxyConfig.updateProtocols(['http']);
expect(
proxyConfig.getProtocols()
).toEqual(
['http']
);

expect(proxyConfig.getProxyUrl()).toEqual(
'proxy_username:[email protected]:8080'
'https://proxy_username:[email protected]:8080'
);

expect(
Expand All @@ -49,9 +43,26 @@ describe('test ProxyConfig object', () => {
authenticate: true,
username: 'proxy_username',
password: 'proxy_password',
protocol: 'https:',
}));

const matchedProxyConfigDef = configList.resolve(new Url('http://sub.example.com:80/path'));
expect(matchedProxyConfigDef?.host).toEqual('proxy.com');
});

const proxyUrls = [
'http://wormhole',
'http://wormhole:0',
'https://localhost',
'http://user:pass@localhost:666',
'http://user:pass@localhost:0',
'http://user:pass@localhost',
];

proxyUrls.forEach(url => {
it(`test proxy transforming: ${url}`, () => {
const proxy = new ProxyConfig(transformToSdkProxyOptions(url, '', true, ''));
expect(proxy.getProxyUrl()).toEqual(url);
});
});
});
1 change: 1 addition & 0 deletions packages/insomnia-sdk/src/objects/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@ export * from './request-info';
export * from './async_objects';
export * from './test';
export * from './execution';
export * from './proxy-configs';
40 changes: 7 additions & 33 deletions packages/insomnia-sdk/src/objects/insomnia.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { CookieObject } from './cookies';
import { Environment, Variables } from './environments';
import { Execution } from './execution';
import type { RequestContext } from './interfaces';
import { transformToSdkProxyOptions } from './proxy-configs';
import { Request as ScriptRequest, type RequestOptions, toScriptRequestBody } from './request';
import { RequestInfo } from './request-info';
import { Response as ScriptResponse } from './response';
Expand Down Expand Up @@ -179,39 +180,12 @@ export async function initInsomniaObject(
} :
{ disabled: true };

const bestProxy = rawObj.settings.httpsProxy || rawObj.settings.httpProxy;
const enabledProxy = rawObj.settings.proxyEnabled && bestProxy !== '';
const bypassProxyList = rawObj.settings.noProxy ?
rawObj.settings.noProxy
.split(',')
.map(urlStr => urlStr.trim()) :
[];
const proxy = {
disabled: !enabledProxy,
match: '<all_urls>',
bypass: bypassProxyList,
host: '',
port: 0,
tunnel: false,
authenticate: false,
username: '',
password: '',
};
if (bestProxy !== '') {
const portStartPos = bestProxy.indexOf(':');
if (portStartPos > 0) {
proxy.host = bestProxy.slice(0, portStartPos);
const port = bestProxy.slice(portStartPos + 1);
try {
proxy.port = parseInt(port);
} catch (e) {
throw Error(`Invalid proxy port: ${bestProxy}`);
}
} else {
proxy.host = bestProxy;
proxy.port = 0;
}
}
const proxy = transformToSdkProxyOptions(
rawObj.settings.httpProxy,
rawObj.settings.httpsProxy,
rawObj.settings.proxyEnabled,
rawObj.settings.noProxy,
);

const reqUrl = toUrlObject(rawObj.request.url);
reqUrl.addQueryParams(
Expand Down
93 changes: 76 additions & 17 deletions packages/insomnia-sdk/src/objects/proxy-configs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,15 @@ import { Url, UrlMatchPattern, UrlMatchPatternList } from './urls';
export interface ProxyConfigOptions {
match: string;
host: string;
port: number;
port?: number;
tunnel: boolean;
disabled?: boolean;
authenticate: boolean;
username: string;
password: string;
// follows are for compatibility with Insomnia
bypass?: string[];
protocol: string;
}

export class ProxyConfig extends Property {
Expand All @@ -19,21 +21,23 @@ export class ProxyConfig extends Property {

host: string;
match: string;
port: number;
port?: number;
tunnel: boolean;
authenticate: boolean;
username: string;
password: string;
bypass: string[]; // it is for compatibility with Insomnia's bypass list
protocol: string;

static authenticate: boolean = false;
static bypass: UrlMatchPatternList<UrlMatchPattern> = new UrlMatchPatternList<UrlMatchPattern>(undefined, []);
static host: string = '';
static match: string = '';
static password: string = '';
static port: number = 0;
static port?: number = undefined;
static tunnel: boolean = false; // unsupported
static username: string = '';
static protocol: string = 'https:';

constructor(def: {
id?: string;
Expand All @@ -42,13 +46,14 @@ export class ProxyConfig extends Property {

match: string;
host: string;
port: number;
port?: number;
tunnel: boolean;
disabled?: boolean;
authenticate: boolean;
username: string;
password: string;
bypass?: string[];
protocol: string;
}) {
super();

Expand All @@ -65,6 +70,7 @@ export class ProxyConfig extends Property {
this.username = def.username;
this.password = def.password;
this.bypass = def.bypass || [];
this.protocol = def.protocol;
}

static override _index: string = 'key';
Expand All @@ -81,11 +87,13 @@ export class ProxyConfig extends Property {

getProxyUrl(): string {
// http://proxy_username:proxy_password@proxy.com:8080
// TODO: check if port is not given
const portSegment = this.port === undefined ? '' : `:${this.port}`;

if (this.authenticate) {
return `${this.username}:${this.password}@${this.host}:${this.port}`;
return `${this.protocol}//${this.username}:${this.password}@${this.host}${portSegment}`;
}
return `${this.host}:${this.port}`;
return `${this.protocol}//${this.host}${portSegment}`;

}

test(url?: string) {
Expand All @@ -104,7 +112,7 @@ export class ProxyConfig extends Property {
update(options: {
host: string;
match: string;
port: number;
port?: number;
tunnel: boolean;
authenticate: boolean;
username: string;
Expand All @@ -119,17 +127,15 @@ export class ProxyConfig extends Property {
this.password = options.password;
}

updateProtocols(protocols: string[]) {
const protoSeparator = this.match.indexOf('://');
if (protoSeparator <= 0 || protoSeparator >= this.match.length) {
throw Error('updateProtocols: invalid protocols, no protocol is detected');
}

this.match = protocols.join('+') + this.match.slice(protoSeparator);
// eslint-disable-next-line @typescript-eslint/no-unused-vars
updateProtocols(_protocols: string[]) {
// In Insomnia there is no whitelist while there is a blacklist
throw Error('updateProtocols is not supported in Insomnia');
}
}

// myProxyConfig = new ProxyConfigList({}, [
// example:
// myProxyConfigs = new ProxyConfigList({}, [
// {match: 'https://example.com/*', host: 'proxy.com', port: 8080, tunnel: true},
// {match: 'http+https://example2.com/*', host: 'proxy2.com'},
// ]);
Expand Down Expand Up @@ -165,6 +171,59 @@ export class ProxyConfigList<T extends ProxyConfig> extends PropertyList<T> {
}
return null;
}
}

export function transformToSdkProxyOptions(
httpProxy: string,
httpsProxy: string,
proxyEnabled: boolean,
noProxy: string,
) {
const bestProxy = httpsProxy || httpProxy || '';
const enabledProxy = proxyEnabled && bestProxy.trim() !== '';
const bypassProxyList = noProxy ?
noProxy
.split(',')
.map(urlStr => urlStr.trim()) :
[];
const proxy: ProxyConfigOptions = {
disabled: !enabledProxy,
match: '<all_urls>',
bypass: bypassProxyList,
host: '',
port: undefined,
tunnel: false,
authenticate: false,
username: '',
password: '',
protocol: 'http',
};

if (bestProxy !== '') {
let sanitizedProxy = bestProxy;
if (bestProxy.indexOf('://') === -1) {
console.warn(`The protocol is missing and adding 'https:' protocol: ${bestProxy}`);
sanitizedProxy = 'https://' + bestProxy;
}

try {
const sanitizedProxyUrlOptions = new URL(sanitizedProxy); // it should just work in node and browser

if (sanitizedProxyUrlOptions.port !== '') {
proxy.port = parseInt(sanitizedProxyUrlOptions.port, 10);
}

proxy.protocol = sanitizedProxyUrlOptions.protocol;
proxy.host = sanitizedProxyUrlOptions.hostname;
proxy.username = sanitizedProxyUrlOptions.username;
proxy.password = sanitizedProxyUrlOptions.password;
if (proxy.username || proxy.password) {
proxy.authenticate = true;
}
} catch (e) {
throw `Failed to parse proxy (${sanitizedProxy}): ${e.message}`;
}
}

// toObject(excludeDisabledopt, nullable, caseSensitiveopt, nullable, multiValueopt, nullable, sanitizeKeysopt) → {Object}
return proxy;
}
1 change: 1 addition & 0 deletions packages/insomnia-sdk/src/objects/request.ts
Original file line number Diff line number Diff line change
Expand Up @@ -402,6 +402,7 @@ export class Request extends Property {
authenticate: this.proxy.authenticate,
username: this.proxy.username,
password: this.proxy.password,
protocol: this.proxy.protocol,
} : undefined,
certificate: this.certificate ? {
name: this.certificate?.name,
Expand Down
5 changes: 4 additions & 1 deletion packages/insomnia-sdk/src/objects/urls.ts
Original file line number Diff line number Diff line change
Expand Up @@ -461,7 +461,10 @@ export class UrlMatchPattern extends Property {
// TODO: the url can not start with -

getProtocols(): string[] {
// the pattern could be <all_urls>
if (this.pattern === '<all_urls>') {
return ['http', 'https', 'file'];
}

const protocolEndPos = this.pattern.indexOf('://');
if (protocolEndPos < 0) {
return [];
Expand Down
18 changes: 12 additions & 6 deletions packages/insomnia/src/hidden-window.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import * as Sentry from '@sentry/electron/renderer';
import { SENTRY_OPTIONS } from 'insomnia/src/common/sentry';
import { initInsomniaObject, InsomniaObject } from 'insomnia-sdk';
import { Console, mergeClientCertificates, mergeCookieJar, mergeRequests, mergeSettings, type RequestContext } from 'insomnia-sdk';
import * as _ from 'lodash';
Expand All @@ -6,9 +8,13 @@ export interface HiddenBrowserWindowBridgeAPI {
runScript: (options: { script: string; context: RequestContext }) => Promise<RequestContext>;
};

Sentry.init({
...SENTRY_OPTIONS,
});

window.bridge.onmessage(async (data, callback) => {
window.bridge.setBusy(true);
console.log('[hidden-browser-window] recieved message', data);

try {
const timeout = data.context.timeout || 5000;
const timeoutPromise = new window.bridge.Promise(resolve => {
Expand All @@ -19,8 +25,12 @@ window.bridge.onmessage(async (data, callback) => {
const result = await window.bridge.Promise.race([timeoutPromise, runScript(data)]);
callback(result);
} catch (err) {
console.error('error', err);
const errMessage = err.message ? `message: ${err.message}; stack: ${err.stack}` : err;
Sentry.captureException(errMessage, {
tags: {
source: 'hidden-window',
},
});
callback({ error: errMessage });
} finally {
window.bridge.setBusy(false);
Expand All @@ -32,7 +42,6 @@ window.bridge.onmessage(async (data, callback) => {
const runScript = async (
{ script, context }: { script: string; context: RequestContext },
): Promise<RequestContext> => {
console.log(script);
const scriptConsole = new Console();

const executionContext = await initInsomniaObject(context, scriptConsole.log);
Expand Down Expand Up @@ -78,9 +87,6 @@ const runScript = async (

await window.bridge.appendFile(context.timelinePath, scriptConsole.dumpLogs());

console.log('mutatedInsomniaObject', mutatedContextObject);
console.log('context', context);

return {
...context,
environment: {
Expand Down

0 comments on commit 12cb2fc

Please sign in to comment.