diff --git a/CHANGELOG.md b/CHANGELOG.md index 0272e7d8..db04fd49 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,18 +2,20 @@ ## Unreleased -## [v1.4.1](https://github.com/coder/vscode-coder/releases/tag/v1.3.9) (2025-02-19) +- Remove agent singleton so that client TLS certificates are reloaded on every API request. ### Fixed +## [v1.4.1](https://github.com/coder/vscode-coder/releases/tag/v1.4.1) (2025-02-19) + - Recreate REST client in spots where confirmStart may have waited indefinitely. -## [v1.4.0](https://github.com/coder/vscode-coder/releases/tag/v1.3.9) (2025-02-04) +## [v1.4.0](https://github.com/coder/vscode-coder/releases/tag/v1.4.0) (2025-02-04) - Recreate REST client after starting a workspace to ensure fresh TLS certificates. - Use `coder ssh` subcommand in place of `coder vscodessh`. -## [v1.3.10](https://github.com/coder/vscode-coder/releases/tag/v1.3.9) (2025-01-17) +## [v1.3.10](https://github.com/coder/vscode-coder/releases/tag/v1.3.10) (2025-01-17) - Fix bug where checking for overridden properties incorrectly converted host name pattern to regular expression. diff --git a/src/api.ts b/src/api.ts index 217a3d67..ba7eda2f 100644 --- a/src/api.ts +++ b/src/api.ts @@ -50,36 +50,6 @@ async function createHttpAgent(): Promise { }) } -// The agent is a singleton so we only have to listen to the configuration once -// (otherwise we would have to carefully dispose agents to remove their -// configuration listeners), and to share the connection pool. -let agent: Promise | undefined = undefined - -/** - * Get the existing agent or create one if necessary. On settings change, - * recreate the agent. The agent on the client is not automatically updated; - * this must be called before every request to get the latest agent. - */ -async function getHttpAgent(): Promise { - if (!agent) { - vscode.workspace.onDidChangeConfiguration((e) => { - if ( - // http.proxy and coder.proxyBypass are read each time a request is - // made, so no need to watch them. - e.affectsConfiguration("coder.insecure") || - e.affectsConfiguration("coder.tlsCertFile") || - e.affectsConfiguration("coder.tlsKeyFile") || - e.affectsConfiguration("coder.tlsCaFile") || - e.affectsConfiguration("coder.tlsAltHost") - ) { - agent = createHttpAgent() - } - }) - agent = createHttpAgent() - } - return agent -} - /** * Create an sdk instance using the provided URL and token and hook it up to * configuration. The token may be undefined if some other form of @@ -101,7 +71,7 @@ export async function makeCoderSdk(baseUrl: string, token: string | undefined, s // Configure proxy and TLS. // Note that by default VS Code overrides the agent. To prevent this, set // `http.proxySupport` to `on` or `off`. - const agent = await getHttpAgent() + const agent = await createHttpAgent() config.httpsAgent = agent config.httpAgent = agent config.proxy = false diff --git a/src/remote.ts b/src/remote.ts index ace2a378..e587a70d 100644 --- a/src/remote.ts +++ b/src/remote.ts @@ -51,11 +51,10 @@ export class Remote { * Try to get the workspace running. Return undefined if the user canceled. */ private async maybeWaitForRunning( + restClient: Api, workspace: Workspace, label: string, binPath: string, - baseUrlRaw: string, - token: string, ): Promise { const workspaceName = `${workspace.owner_name}/${workspace.name}` @@ -95,7 +94,6 @@ export class Remote { title: "Waiting for workspace build...", }, async () => { - let restClient = await makeCoderSdk(baseUrlRaw, token, this.storage) const globalConfigDir = path.dirname(this.storage.getSessionTokenPath(label)) while (workspace.latest_build.status !== "running") { ++attempts @@ -111,9 +109,6 @@ export class Remote { if (!(await this.confirmStart(workspaceName))) { return undefined } - // Recreate REST client since confirmStart may have waited an - // indeterminate amount of time for confirmation. - restClient = await makeCoderSdk(baseUrlRaw, token, this.storage) writeEmitter = initWriteEmitterAndTerminal() this.storage.writeToCoderOutputChannel(`Starting ${workspaceName}...`) workspace = await startWorkspaceIfStoppedOrFailed( @@ -131,9 +126,6 @@ export class Remote { if (!(await this.confirmStart(workspaceName))) { return undefined } - // Recreate REST client since confirmStart may have waited an - // indeterminate amount of time for confirmation. - restClient = await makeCoderSdk(baseUrlRaw, token, this.storage) writeEmitter = initWriteEmitterAndTerminal() this.storage.writeToCoderOutputChannel(`Starting ${workspaceName}...`) workspace = await startWorkspaceIfStoppedOrFailed( @@ -324,16 +316,13 @@ export class Remote { // If the workspace is not in a running state, try to get it running. if (workspace.latest_build.status !== "running") { - if (!(await this.maybeWaitForRunning(workspace, parts.label, binaryPath, baseUrlRaw, token))) { + const updatedWorkspace = await this.maybeWaitForRunning(workspaceRestClient, workspace, parts.label, binaryPath) + if (!updatedWorkspace) { // User declined to start the workspace. await this.closeRemote() - } else { - // Start over with a fresh REST client because we may have waited an - // indeterminate amount amount of time for confirmation to start the - // workspace. - await this.setup(remoteAuthority) + return } - return + workspace = updatedWorkspace } this.commands.workspace = workspace