Skip to content

Move shared HTTP client into extension-toolkit#22539

Open
aasimkhan30 wants to merge 3 commits into
mainfrom
aasim/feat/sharedHttp
Open

Move shared HTTP client into extension-toolkit#22539
aasimkhan30 wants to merge 3 commits into
mainfrom
aasim/feat/sharedHttp

Conversation

@aasimkhan30

Copy link
Copy Markdown
Contributor

Summary

Both the mssql and sql-database-projects extensions carried near-identical copies of the HTTP client, proxy resolution, and file download logic. This PR consolidates them into a single implementation owned by extension-toolkit.

Net effect: 978 insertions, 1731 deletions — one HTTP client instead of two.

What moved

New location Replaces
extension-toolkit/baseHttpClient extensions/mssql/src/http/httpClientCore.ts, extensions/sql-database-projects/src/http/httpClientCore.ts
extension-toolkit/vscodeVscodeHttpClient extensions/mssql/src/http/httpClient.ts, extensions/sql-database-projects/src/http/httpClient.ts
extension-toolkit/basegetErrorMessage formerly injected by each caller

Design notes

  • base is VS Code independent. HttpClient takes an optional IHttpClientDependencies for proxy config, URI parsing, and warning display. VscodeHttpClient is the thin adapter that wires those to http.proxy / http.proxyStrictSSL and vscode.window.showWarningMessage.
  • axios is not part of the public API. The toolkit exposes its own IHttpResponse<T> / IHttpHeaders so consumers aren't coupled to the implementation library.
  • Localization stays in the extensions. Warning strings are injected via messages; the toolkit never calls l10n.
  • Downloads report progress generically. downloadFile takes an onProgress callback delivering { downloadedBytes, totalBytes?, percentage? }, so an extension can render it however it likes (status bar, output channel, notification). The old onHeaders/onData pair and the per-extension content-length parsing are gone — totalBytes is now undefined rather than 0 when the size is unknown, so "unknown size" is distinguishable from "empty".
  • File descriptor ownership is preserved. Path destinations are opened and closed by the toolkit; caller-supplied descriptors are left open (autoClose: false) and completion resolves on finish.
  • axios / tunnel / @types/tunnel moved to the toolkit's package.json and dropped from both extensions.

Testing

  • extensions/mssql/test/unit/httpClient.test.ts — 18/18 passing, now the single suite covering the shared client (the duplicate DB Projects copy was deleted).
  • extensions/mssql/test/unit/fabricHelper.test.ts — 8/8 passing; unsafe as unknown as IHttpResponse<...> casts replaced with a typed factory.
  • Builds and lint pass for extension-toolkit, mssql, and sql-database-projects.

Both the mssql and sql-database-projects extensions carried near-identical
copies of the HTTP client, proxy resolution and file download logic. This
consolidates them into a single implementation owned by extension-toolkit.

- Add `extension-toolkit/base` HttpClient: portable HTTP + download with
  proxy/tunnel support, a generic `onProgress` callback and typed
  `IHttpResponse` / `IDownloadFileResult` results.
- Add `extension-toolkit/vscode` VscodeHttpClient adapter wiring VS Code's
  `http.proxy` / `http.proxyStrictSSL` settings and warning messages.
- Add `extension-toolkit/base/common/errors.ts` with `getErrorMessage`, so
  the client no longer needs a formatter injected by callers.
- Keep axios out of the public API; the toolkit owns its own response types.
- Localized strings stay in the extensions and are injected as `messages`.
- Move axios/tunnel dependencies to the toolkit and drop the duplicated
  http/ folders and tests from both extensions.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR consolidates duplicated HTTP client / proxy validation / file download logic from the mssql and sql-database-projects extensions into a single shared implementation in packages/extension-toolkit, with a VS Code adapter (VscodeHttpClient) and a VS Code-independent core (HttpClient).

Changes:

  • Introduces extension-toolkit/base HttpClient (proxy + download + progress callbacks) and extension-toolkit/vscode VscodeHttpClient adapter.
  • Updates both extensions (and unit tests) to consume the shared client and shared response/progress shapes.
  • Moves axios / tunnel dependencies into extension-toolkit and removes per-extension copies and tests.

Reviewed changes

Copilot reviewed 28 out of 31 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
packages/extension-toolkit/src/vscode/index.ts Re-exports VS Code HTTP adapter entrypoint.
packages/extension-toolkit/src/vscode/http/vscodeHttpClient.ts Adds VscodeHttpClient wrapper wiring VS Code proxy settings + warnings.
packages/extension-toolkit/src/vscode/http/index.ts Barrel export for VS Code HTTP adapter.
packages/extension-toolkit/src/base/index.ts Exposes new common + http modules from toolkit base.
packages/extension-toolkit/src/base/http/index.ts Barrel export for base HTTP client.
packages/extension-toolkit/src/base/http/httpClient.ts New shared HTTP client + download/progress APIs.
packages/extension-toolkit/src/base/common/index.ts Barrel export for shared base helpers.
packages/extension-toolkit/src/base/common/errors.ts Adds shared getErrorMessage() helper.
packages/extension-toolkit/package.json Adds axios + tunnel dependencies (and @types/tunnel).
packages/extension-toolkit/package-lock.json Lockfile updates for new toolkit dependencies.
extensions/sql-database-projects/test/httpClient.test.ts Deletes duplicate HTTP client tests (migrated to mssql suite).
extensions/sql-database-projects/test/buildHelper.test.ts Updates tests to stub VscodeHttpClient.downloadFile.
extensions/sql-database-projects/src/tools/buildHelper.ts Switches downloads to shared VscodeHttpClient and new progress callback.
extensions/sql-database-projects/src/http/httpClientCore.ts Deletes duplicated core HTTP client.
extensions/sql-database-projects/src/http/httpClient.ts Deletes duplicated VS Code HTTP client wrapper.
extensions/sql-database-projects/src/controllers/mainController.ts Switches proxy warning call to VscodeHttpClient.
extensions/sql-database-projects/src/common/logger.ts Updates logger documentation to reflect new HTTP client usage.
extensions/sql-database-projects/package.json Removes direct axios/tunnel deps; relies on toolkit.
extensions/sql-database-projects/package-lock.json Lockfile updates reflecting dependency move.
extensions/mssql/test/unit/mainController.test.ts Updates proxy-warning test to spy on VscodeHttpClient.
extensions/mssql/test/unit/languageService.test.ts Updates download progress test to use toolkit progress type.
extensions/mssql/test/unit/httpClient.test.ts Migrates HTTP client unit tests to toolkit HttpClient API.
extensions/mssql/test/unit/fabricHelper.test.ts Updates Fabric tests to use toolkit response type and client stub.
extensions/mssql/test/unit/extension.test.ts Updates proxy-warning stub to VscodeHttpClient.
extensions/mssql/src/languageservice/downloadHelper.ts Uses toolkit HttpClient.downloadFile with unified progress callback.
extensions/mssql/src/fabric/fabricHelper.ts Switches Fabric REST calls to VscodeHttpClient + toolkit response types.
extensions/mssql/src/controllers/mainController.ts Switches activation-time proxy warning to VscodeHttpClient.
extensions/mssql/src/azure/utils.ts Switches Graph calls to VscodeHttpClient.
extensions/mssql/src/azure/msal/msalAzureAuth.ts Switches tenant calls to VscodeHttpClient.
extensions/mssql/package.json Removes direct axios/tunnel deps; relies on toolkit.
extensions/mssql/package-lock.json Lockfile updates reflecting dependency move.
Files not reviewed (3)
  • extensions/mssql/package-lock.json: Generated file
  • extensions/sql-database-projects/package-lock.json: Generated file
  • packages/extension-toolkit/package-lock.json: Generated file
Comments suppressed due to low confidence (7)

packages/extension-toolkit/src/base/http/httpClient.ts:159

  • makeGetRequest returns the Axios response object via a type cast to IHttpResponse<T>, but IHttpResponse.headers is declared as Record<string, string>. Axios headers are not guaranteed to already be normalized strings, so this is an unsound contract and can break consumers relying on the type.

This issue also appears on line 187 of the same file.
packages/extension-toolkit/src/base/http/httpClient.ts:187

  • makePostRequest returns the Axios response object via a type cast to IHttpResponse<T>, but IHttpResponse.headers is declared as Record<string, string>. Axios headers are not guaranteed to already be normalized strings, so this is an unsound contract and can break consumers relying on the type.
    packages/extension-toolkit/src/base/http/httpClient.ts:283
  • downloadToFileDescriptor uses getContentLength() which currently returns undefined when content-length is 0. That makes an empty download indistinguishable from an unknown size, contradicting the API intent of totalBytes representing a known header value when available.
    packages/extension-toolkit/src/base/http/httpClient.ts:322
  • Progress percentage calculation divides by totalBytes without guarding against 0. If content-length is present and 0 (valid empty response), this results in a division-by-zero bug (Infinity/NaN).
    packages/extension-toolkit/src/base/http/httpClient.ts:292
  • IDownloadFileResult.headers is typed as Record<string, string>, but this returns Axios headers via a type cast. This can leak non-string header values (or arrays) through a string-only contract.

This issue also appears on line 340 of the same file.
packages/extension-toolkit/src/base/http/httpClient.ts:343

  • IDownloadFileResult.headers is typed as Record<string, string>, but this returns Axios headers via a type cast. This can leak non-string header values (or arrays) through a string-only contract.
    packages/extension-toolkit/src/vscode/http/vscodeHttpClient.ts:40
  • parseUriScheme uses vscode.Uri.parse(value).scheme, which returns a non-empty scheme for values like localhost:3128 (e.g. localhost). That can prevent warnOnInvalidProxySettings() from warning about a missing protocol even though later proxy agent creation will throw on new URL(proxy).

Comment on lines 48 to 50
* Logger that writes formatted, timestamped messages to a VS Code OutputChannel.
* Implements ILogger so it can be passed directly to HttpClient / HttpClientCore.
* Implements the logger contract used by the shared HttpClient.
*
@github-actions

Copy link
Copy Markdown

PR Changes

Category Target Branch PR Branch Difference
vscode-mssql VSIX 79298 KB 79304 KB ⚪ 6 KB ( 0% )
sql-database-projects VSIX 2950 KB 2953 KB ⚪ 3 KB ( 0% )
data-workspace VSIX 188 KB 255 KB 🔴 67 KB ( 35% )
keymap VSIX 7 KB 7 KB ⚪ 0 KB ( 0% )

@codecov-commenter

codecov-commenter commented Jul 25, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 60.82474% with 38 lines in your changes missing coverage. Please review.
✅ Project coverage is 75.74%. Comparing base (27cafce) to head (fa8e783).

Files with missing lines Patch % Lines
...ons/sql-database-projects/src/tools/buildHelper.ts 36.36% 21 Missing ⚠️
extensions/mssql/src/fabric/fabricHelper.ts 46.15% 7 Missing ⚠️
...nsions/mssql/src/languageservice/downloadHelper.ts 77.41% 7 Missing ⚠️
extensions/mssql/src/azure/utils.ts 40.00% 3 Missing ⚠️
Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             main   #22539      +/-   ##
==========================================
- Coverage   75.78%   75.74%   -0.04%     
==========================================
  Files         407      402       -5     
  Lines      130542   129266    -1276     
  Branches     8382     8351      -31     
==========================================
- Hits        98933    97917    -1016     
+ Misses      31609    31349     -260     
Flag Coverage Δ
data-workspace 78.37% <ø> (ø)
mssql 75.41% <71.66%> (-0.04%) ⬇️
sqlproj 78.94% <43.24%> (+0.07%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
extensions/mssql/src/azure/msal/msalAzureAuth.ts 45.68% <100.00%> (+0.20%) ⬆️
extensions/mssql/src/controllers/mainController.ts 39.29% <100.00%> (+0.04%) ⬆️
...atabase-projects/src/controllers/mainController.ts 82.78% <100.00%> (+0.09%) ⬆️
extensions/mssql/src/azure/utils.ts 47.82% <40.00%> (-0.28%) ⬇️
extensions/mssql/src/fabric/fabricHelper.ts 63.05% <46.15%> (-0.10%) ⬇️
...nsions/mssql/src/languageservice/downloadHelper.ts 53.10% <77.41%> (+5.15%) ⬆️
...ons/sql-database-projects/src/tools/buildHelper.ts 81.25% <36.36%> (-6.36%) ⬇️

... and 5 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copilot AI review requested due to automatic review settings July 25, 2026 07:11

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 50 out of 53 changed files in this pull request and generated 3 comments.

Files not reviewed (3)
  • extensions/mssql/package-lock.json: Generated file
  • extensions/sql-database-projects/package-lock.json: Generated file
  • packages/extension-toolkit/package-lock.json: Generated file

Comment on lines +9 to +17
export function getErrorMessage(error: unknown): string {
return error instanceof Error
? typeof error.message === "string"
? error.message
: ""
: typeof error === "string"
? error
: `${JSON.stringify(error, undefined, "\t")}`;
}
Comment on lines +458 to +463
const parsedRetryAfter = retryAfter === undefined ? Number.NaN : parseInt(retryAfter, 10);
const retryAfterInMs =
Number.isFinite(parsedRetryAfter) && parsedRetryAfter > 0
? parsedRetryAfter
: this.defaultRetryInMs;

Comment on lines +71 to +82
/** Progress reported while a download is being written. */
export interface IDownloadProgress {
/** Number of bytes received so far. */
readonly downloadedBytes: number;

/**
* Total number of bytes to expect.
*
* `undefined` means the length is unknown; `0` means the response is known to be empty.
*/
readonly totalBytes: number | undefined;
}
Copilot AI review requested due to automatic review settings July 25, 2026 07:20

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 50 out of 53 changed files in this pull request and generated 4 comments.

Files not reviewed (3)
  • extensions/mssql/package-lock.json: Generated file
  • extensions/sql-database-projects/package-lock.json: Generated file
  • packages/extension-toolkit/package-lock.json: Generated file
Comments suppressed due to low confidence (1)

packages/extension-toolkit/src/base/common/errors.ts:16

  • getErrorMessage falls back to JSON.stringify for non-Error, non-string values, but JSON.stringify itself can throw (e.g., circular structures or BigInt). That can cause logging/cleanup paths to throw while handling another error.

Comment on lines +755 to +763
function createFileDescriptorWritable(destinationFd: number): Writable {
return new Writable({
write(chunk: Buffer, _encoding, callback) {
fs.write(destinationFd, chunk, 0, chunk.length, null, (error) => {
callback(error ?? null);
});
},
});
}
Comment thread packages/extension-toolkit/test/base/http/httpClient.test.ts
Comment thread packages/extension-toolkit/test/base/http/httpClient.test.ts
Comment thread extensions/mssql/test/unit/languageService.test.ts
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants