Skip to content

Commit

Permalink
Avoid setting (#237890)
Browse files Browse the repository at this point in the history
  • Loading branch information
chrmarti committed Jan 21, 2025
1 parent 57e5f4b commit d2627ea
Show file tree
Hide file tree
Showing 2 changed files with 78 additions and 36 deletions.
95 changes: 72 additions & 23 deletions extensions/vscode-api-tests/src/singlefolder-tests/proxy.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@

import * as https from 'https';
import 'mocha';
import { assertNoRpc, delay } from '../utils';
import { assertNoRpc } from '../utils';
import { pki } from 'node-forge';
import { AddressInfo } from 'net';
import { resetCaches } from '@vscode/proxy-agent';
import * as vscode from 'vscode';
import { middleware, Straightforward } from 'straightforward';
import { Straightforward, Middleware, RequestContext, ConnectContext, isRequest, isConnect } from 'straightforward';
import assert from 'assert';

(vscode.env.uiKind === vscode.UIKind.Web ? suite.skip : suite)('vscode API - network proxy support', () => {
Expand Down Expand Up @@ -85,7 +85,8 @@ import assert from 'assert';

const sf = new Straightforward();
let authEnabled = false;
const auth = middleware.auth({ user, pass });
const authOpts: AuthOpts = { user, pass };
const auth = middlewareAuth(authOpts);
sf.onConnect.use(async (context, next) => {
if (authEnabled) {
return auth(context, next);
Expand Down Expand Up @@ -132,27 +133,17 @@ import assert from 'assert';
.on('error', reject);
});

for (let i = 0; i < 3; i++) {
await vscode.workspace.getConfiguration().update('integration-test.http.proxyAuth', `${user}:${pass}`, vscode.ConfigurationTarget.Global);
await delay(1000); // Wait for the configuration change to propagate.
try {
await new Promise<void>((resolve, reject) => {
https.get(url, res => {
if (res.statusCode === 204) {
resolve();
} else {
reject(new Error(`Unexpected status code (expected 204): ${res.statusCode}`));
}
})
.on('error', reject);
});
break; // Exit the loop if the request is successful
} catch (err) {
if (i === 2) {
throw err; // Rethrow the error if it's the last attempt
authOpts.realm = Buffer.from(JSON.stringify({ username: user, password: pass })).toString('base64');
await new Promise<void>((resolve, reject) => {
https.get(url, res => {
if (res.statusCode === 204) {
resolve();
} else {
reject(new Error(`Unexpected status code (expected 204): ${res.statusCode}`));
}
}
}
})
.on('error', reject);
});
} finally {
sf.close();
const change = waitForConfigChange('http.proxy');
Expand Down Expand Up @@ -219,3 +210,61 @@ import assert from 'assert';
});
}
});

// Added 'realm'. From MIT licensed https://github.com/berstend/straightforward/blob/84a4cb88024cffce37a05870da7d9d0aba7dcca8/src/middleware/auth.ts

export interface AuthOpts {
realm?: string;
user?: string;
pass?: string;
dynamic?: boolean;
}

export interface RequestAdditionsAuth {
locals: { proxyUser: string; proxyPass: string };
}

/**
* Authenticate an incoming proxy request
* Supports static `user` and `pass` or `dynamic`,
* in which case `ctx.req.locals` will be populated with `proxyUser` and `proxyPass`
* This middleware supports both onRequest and onConnect
*/
export const middlewareAuth =
(opts: AuthOpts): Middleware<
RequestContext<RequestAdditionsAuth> | ConnectContext<RequestAdditionsAuth>
> =>
async (ctx, next) => {
const { realm, user, pass, dynamic } = opts;
const sendAuthRequired = () => {
const realmStr = realm ? ` realm="${realm}"` : '';
if (isRequest(ctx)) {
ctx.res.writeHead(407, { 'Proxy-Authenticate': `Basic${realmStr}` });
ctx.res.end();
} else if (isConnect(ctx)) {
ctx.clientSocket.end(
'HTTP/1.1 407\r\n' + `Proxy-Authenticate: basic${realmStr}\r\n` + '\r\n'
);
}
};
const proxyAuth = ctx.req.headers['proxy-authorization'];
if (!proxyAuth) {
return sendAuthRequired();
}
const [proxyUser, proxyPass] = Buffer.from(
proxyAuth.replace('Basic ', ''),
'base64'
)
.toString()
.split(':');

if (!dynamic && !!(!!user && !!pass)) {
if (user !== proxyUser || pass !== proxyPass) {
return sendAuthRequired();
}
}
ctx.req.locals.proxyUser = proxyUser;
ctx.req.locals.proxyPass = proxyPass;

return next();
};
19 changes: 6 additions & 13 deletions src/vs/platform/native/electron-main/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -139,20 +139,13 @@ export class ProxyAuthService extends Disposable implements IProxyAuthService {

// For testing.
if (this.environmentMainService.extensionTestsLocationURI) {
const credentials = this.configurationService.getValue<string>('integration-test.http.proxyAuth');
if (credentials) {
const j = credentials.indexOf(':');
if (j !== -1) {
return {
username: credentials.substring(0, j),
password: credentials.substring(j + 1)
};
} else {
return {
username: credentials,
password: ''
};
try {
const decodedRealm = Buffer.from(authInfo.realm, 'base64').toString('utf-8');
if (decodedRealm.startsWith('{')) {
return JSON.parse(decodedRealm);
}
} catch {
// ignore
}
return undefined;
}
Expand Down

0 comments on commit d2627ea

Please sign in to comment.