Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -821,7 +821,7 @@
"@azure/storage-file-datalake": "^12.1.1",
"@azure/storage-file-share": "^12.25.0-beta.1",
"@azure/storage-queue": "^12.7.0",
"@microsoft/vscode-azext-azureutils": "^4.0.1",
"@microsoft/vscode-azext-azureutils": "^4.3.0",
"@microsoft/vscode-azext-utils": "^4.0.4",
"@microsoft/vscode-azureresources-api": "^3.1.0",
"mime": "^2.4.4",
Expand Down
7 changes: 4 additions & 3 deletions src/BlobContainerFS.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import { createStorageClient } from './utils/azureClients';
import { BlobPathUtils } from './utils/blobPathUtils';
import { createBlockBlobClient } from './utils/blobUtils';
import { localize } from './utils/localize';
import { withProxyOptions } from './utils/proxyUtils';
import { nonNullValue } from "./utils/nonNull";
import { StorageAccountKeyWrapper } from './utils/storageWrappers';

Expand Down Expand Up @@ -136,7 +137,7 @@ export class BlobContainerFS implements vscode.FileSystemProvider {
throw Error("Unable to get key credential.");
}

const serviceClient = new BlobServiceClient(blobEndpoint, credential);
const serviceClient = new BlobServiceClient(blobEndpoint, credential, withProxyOptions(blobEndpoint));
const containerClient = serviceClient.getContainerClient(containerName);
const blobClient = containerClient.getBlobClient(blobPath);

Expand All @@ -160,12 +161,12 @@ export class BlobContainerFS implements vscode.FileSystemProvider {
throw Error("Unable to get key credential.");
}

const serviceClient = new DataLakeServiceClient(dfsEndpoint, credential);
const serviceClient = new DataLakeServiceClient(dfsEndpoint, credential, withProxyOptions(dfsEndpoint));
const fileSystemClient = serviceClient.getFileSystemClient(containerName);

const pathUrl = new URL(dfsEndpoint);
pathUrl.pathname = `${containerName}/${blobPath}`;
const pathClient = new DataLakePathClient(pathUrl.toString(), credential);
const pathClient = new DataLakePathClient(pathUrl.toString(), credential, withProxyOptions(dfsEndpoint));

return { fileSystemClient, pathClient };
}
Expand Down
13 changes: 7 additions & 6 deletions src/tree/StorageAccountTreeItem.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import { createActivityContext } from '../utils/activityUtils';
import { createStorageClient } from '../utils/azureClients';
import { localize } from '../utils/localize';
import { nonNullProp } from '../utils/nonNull';
import { withProxyOptions } from '../utils/proxyUtils';
import { StorageAccountKeyWrapper, StorageAccountWrapper } from '../utils/storageWrappers';
import { IStorageRoot } from './IStorageRoot';
import { IStorageTreeItem } from './IStorageTreeItem';
Expand Down Expand Up @@ -289,7 +290,7 @@ export class StorageAccountTreeItem implements ResolvedStorageAccount, IStorageT
if (this.allowSharedKeyAccess) {
try {
const credential = new StorageSharedKeyCredentialBlob(this.storageAccount.name, ((await this.getKey()).value));
client = new BlobServiceClient(nonNullProp(this.storageAccount.primaryEndpoints, 'blob'), credential);
client = new BlobServiceClient(nonNullProp(this.storageAccount.primaryEndpoints, 'blob'), credential, withProxyOptions(nonNullProp(this.storageAccount.primaryEndpoints, 'blob')));
await client.getProperties(); // Trigger a request to validate the key
} catch {
// ignore and try scoped token
Expand All @@ -298,7 +299,7 @@ export class StorageAccountTreeItem implements ResolvedStorageAccount, IStorageT

if (!client) {
const token = await this._subscription.createCredentialsForScopes(['https://storage.azure.com/.default']);
client = new BlobServiceClient(nonNullProp(this.storageAccount.primaryEndpoints, 'blob'), token);
client = new BlobServiceClient(nonNullProp(this.storageAccount.primaryEndpoints, 'blob'), token, withProxyOptions(nonNullProp(this.storageAccount.primaryEndpoints, 'blob')));
await client.getProperties(); // Trigger a request to validate the token
}

Expand All @@ -309,15 +310,15 @@ export class StorageAccountTreeItem implements ResolvedStorageAccount, IStorageT
if (this.allowSharedKeyAccess) {
try {
const credential = new StorageSharedKeyCredentialFileShare(this.storageAccount.name, ((await this.getKey()).value));
client = new ShareServiceClient(nonNullProp(this.storageAccount.primaryEndpoints, 'file'), credential);
client = new ShareServiceClient(nonNullProp(this.storageAccount.primaryEndpoints, 'file'), credential, withProxyOptions(nonNullProp(this.storageAccount.primaryEndpoints, 'file')));
await client.getProperties(); // Trigger a request to validate the key
} catch {
// ignore and try scoped token
}
}
if (!client) {
const token = await this._subscription.createCredentialsForScopes(['https://storage.azure.com/.default']);
client = new ShareServiceClient(nonNullProp(this.storageAccount.primaryEndpoints, 'file'), token, { fileRequestIntent: 'backup' });
client = new ShareServiceClient(nonNullProp(this.storageAccount.primaryEndpoints, 'file'), token, withProxyOptions(nonNullProp(this.storageAccount.primaryEndpoints, 'file'), { fileRequestIntent: 'backup' }));
await client.getProperties(); // Trigger a request to validate the token
}

Expand All @@ -328,15 +329,15 @@ export class StorageAccountTreeItem implements ResolvedStorageAccount, IStorageT
if (this.allowSharedKeyAccess) {
try {
const credential = new StorageSharedKeyCredentialQueue(this.storageAccount.name, ((await this.getKey()).value));
client = new QueueServiceClient(nonNullProp(this.storageAccount.primaryEndpoints, 'queue'), credential);
client = new QueueServiceClient(nonNullProp(this.storageAccount.primaryEndpoints, 'queue'), credential, withProxyOptions(nonNullProp(this.storageAccount.primaryEndpoints, 'queue')));
await client.getProperties(); // Trigger a request to validate the key
} catch {
// ignore and try scoped token
}
}
if (!client) {
const token = await this._subscription.createCredentialsForScopes(['https://storage.azure.com/.default']);
client = new QueueServiceClient(nonNullProp(this.storageAccount.primaryEndpoints, 'queue'), token);
client = new QueueServiceClient(nonNullProp(this.storageAccount.primaryEndpoints, 'queue'), token, withProxyOptions(nonNullProp(this.storageAccount.primaryEndpoints, 'queue')));
await client.getProperties(); // Trigger a request to validate the token
}

Expand Down
26 changes: 26 additions & 0 deletions src/utils/proxyUtils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.md in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import { getProxySettings, type ProxySettings } from '@microsoft/vscode-azext-azureutils';

/**
* Merges VS Code's proxy configuration (`http.proxy` / `http.noProxy`, with the standard proxy
* environment variables as a fallback) into the pipeline options used to construct a storage
* data-plane service client, so requests route through a corporate proxy.
*
* The storage SDKs (`@azure/storage-blob`, `-file-share`, `-queue`) do not consult VS Code's
* `http.*` settings on their own, so operations fail on networks that require an explicit proxy.
* Returns `options` unchanged when no proxy applies to `endpointUrl` (for example when the host is
* matched by `http.noProxy`/`NO_PROXY`, or proxy support is turned off).
*/
export function withProxyOptions(endpointUrl: string): { proxyOptions?: ProxySettings } | undefined;
export function withProxyOptions<T extends object>(endpointUrl: string, options: T): (T & { proxyOptions?: ProxySettings }) | undefined;
export function withProxyOptions<T extends object>(endpointUrl: string, options?: T): (T & { proxyOptions?: ProxySettings }) | { proxyOptions?: ProxySettings } | undefined {
const proxyOptions = getProxySettings(endpointUrl);
if (!proxyOptions) {
return options;
}
return options ? { ...options, proxyOptions } : { proxyOptions };
}