Skip to content

Commit bb25189

Browse files
Merge branch 'master' into AllowCacheHeaders
2 parents ea2a8c7 + 9aa7d71 commit bb25189

File tree

3 files changed

+132
-5
lines changed

3 files changed

+132
-5
lines changed

lib/remote-storage.js

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@ const fs = require('fs-extra')
1717
const joi = require('joi')
1818
const klaw = require('klaw')
1919
const http = require('http')
20+
// Proxy support for AWS SDK v3 (inspired by PR #224 by pat-lego, with compatibility fixes)
21+
const { NodeHttpHandler } = require('@smithy/node-http-handler')
22+
const { ProxyAgent } = require('proxy-agent')
2023
const { codes, logAndThrow } = require('./StorageError')
2124

2225
const fileExtensionPattern = /\*\.[0-9a-zA-Z]+$/
@@ -71,8 +74,20 @@ module.exports = class RemoteStorage {
7174
}
7275
this.bucket = creds.params.Bucket
7376

77+
// Configure proxy support for AWS SDK v3
78+
// ProxyAgent automatically handles proxy environment variables via proxy-from-env
79+
const agent = new ProxyAgent()
80+
const s3Config = {
81+
credentials,
82+
region,
83+
requestHandler: new NodeHttpHandler({
84+
httpAgent: agent,
85+
httpsAgent: agent
86+
})
87+
}
88+
7489
// https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/clients/client-s3/classes/s3.html#constructor
75-
this.s3 = new S3({ credentials, region })
90+
this.s3 = new S3(s3Config)
7691
}
7792

7893
/**

package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@adobe/aio-lib-web",
3-
"version": "7.0.4",
3+
"version": "7.0.6",
44
"description": "Utility tooling library to build and deploy Adobe I/O Project Firefly app static sites to CDN",
55
"main": "index.js",
66
"directories": {
@@ -33,13 +33,15 @@
3333
"@adobe/aio-lib-core-logging": "^3",
3434
"@adobe/aio-lib-core-tvm": "^4",
3535
"@aws-sdk/client-s3": "^3.624.0",
36+
"@smithy/node-http-handler": "^4.0.2",
3637
"core-js": "^3.25.1",
3738
"fs-extra": "^11",
3839
"joi": "^17.2.1",
3940
"klaw": "^4",
4041
"lodash.clonedeep": "^4.5.0",
4142
"mime-types": "^2.1.24",
4243
"parcel": "^2.15.4",
44+
"proxy-agent": "^6.3.0",
4345
"regenerator-runtime": "^0.13.7"
4446
},
4547
"devDependencies": {

test/lib/remote-storage.test.js

Lines changed: 113 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,8 @@ describe('RemoteStorage', () => {
4949
sessionToken: global.fakeTVMResponse.sessionToken,
5050
expiration: new Date(global.fakeTVMResponse.expiration)
5151
},
52-
region: 'us-east-1'
52+
region: 'us-east-1',
53+
requestHandler: expect.any(Object)
5354
})
5455
rs.bucket = global.fakeTVMResponse.Bucket
5556
})
@@ -59,13 +60,122 @@ describe('RemoteStorage', () => {
5960
expect(S3).toHaveBeenCalledWith({
6061
credentials: {
6162
accessKeyId: global.fakeTVMResponse.accessKeyId,
62-
secretAccessKey: global.fakeTVMResponse.secretAccessKey
63+
secretAccessKey: global.fakeTVMResponse.secretAccessKey,
64+
sessionToken: undefined,
65+
expiration: undefined
6366
},
64-
region: 'us-east-1'
67+
region: 'us-east-1',
68+
requestHandler: expect.any(Object)
6569
})
6670
rs.bucket = global.fakeTVMResponse.Bucket
6771
})
6872

73+
describe('Proxy configuration', () => {
74+
const originalEnv = process.env
75+
76+
beforeEach(() => {
77+
// Clear environment variables before each test
78+
delete process.env.https_proxy
79+
delete process.env.HTTPS_PROXY
80+
delete process.env.http_proxy
81+
delete process.env.HTTP_PROXY
82+
})
83+
84+
afterAll(() => {
85+
// Restore original environment
86+
process.env = originalEnv
87+
})
88+
89+
test('Constructor uses HTTPS_PROXY when set (uppercase)', async () => {
90+
process.env.HTTPS_PROXY = 'http://proxy.example.com:8080'
91+
// eslint-disable-next-line no-new
92+
new RemoteStorage(global.fakeTVMResponse)
93+
94+
expect(S3).toHaveBeenCalledWith(expect.objectContaining({
95+
requestHandler: expect.any(Object),
96+
credentials: expect.any(Object),
97+
region: 'us-east-1'
98+
}))
99+
})
100+
101+
test('Constructor uses https_proxy when set (lowercase)', async () => {
102+
process.env.https_proxy = 'http://proxy.example.com:3128'
103+
// eslint-disable-next-line no-new
104+
new RemoteStorage(global.fakeTVMResponse)
105+
106+
expect(S3).toHaveBeenCalledWith(expect.objectContaining({
107+
requestHandler: expect.any(Object),
108+
credentials: expect.any(Object),
109+
region: 'us-east-1'
110+
}))
111+
})
112+
113+
test('Constructor uses HTTP_PROXY when HTTPS_PROXY not set', async () => {
114+
process.env.HTTP_PROXY = 'http://proxy.example.com:8080'
115+
// eslint-disable-next-line no-new
116+
new RemoteStorage(global.fakeTVMResponse)
117+
118+
expect(S3).toHaveBeenCalledWith(expect.objectContaining({
119+
requestHandler: expect.any(Object),
120+
credentials: expect.any(Object),
121+
region: 'us-east-1'
122+
}))
123+
})
124+
125+
test('Constructor uses http_proxy when other proxy vars not set', async () => {
126+
process.env.http_proxy = 'http://proxy.example.com:3128'
127+
// eslint-disable-next-line no-new
128+
new RemoteStorage(global.fakeTVMResponse)
129+
130+
expect(S3).toHaveBeenCalledWith(expect.objectContaining({
131+
requestHandler: expect.any(Object),
132+
credentials: expect.any(Object),
133+
region: 'us-east-1'
134+
}))
135+
})
136+
137+
test('Constructor prioritizes HTTPS_PROXY over HTTP_PROXY', async () => {
138+
process.env.HTTPS_PROXY = 'http://https-proxy.example.com:8080'
139+
process.env.HTTP_PROXY = 'http://http-proxy.example.com:8080'
140+
// eslint-disable-next-line no-new
141+
new RemoteStorage(global.fakeTVMResponse)
142+
143+
expect(S3).toHaveBeenCalledWith(expect.objectContaining({
144+
requestHandler: expect.any(Object),
145+
credentials: expect.any(Object),
146+
region: 'us-east-1'
147+
}))
148+
})
149+
150+
test('Constructor prioritizes https_proxy over HTTP_PROXY', async () => {
151+
process.env.https_proxy = 'http://https-proxy.example.com:3128'
152+
process.env.HTTP_PROXY = 'http://http-proxy.example.com:8080'
153+
// eslint-disable-next-line no-new
154+
new RemoteStorage(global.fakeTVMResponse)
155+
156+
expect(S3).toHaveBeenCalledWith(expect.objectContaining({
157+
requestHandler: expect.any(Object),
158+
credentials: expect.any(Object),
159+
region: 'us-east-1'
160+
}))
161+
})
162+
163+
test('Constructor always includes requestHandler with ProxyAgent', async () => {
164+
// eslint-disable-next-line no-new
165+
new RemoteStorage(global.fakeTVMResponse)
166+
167+
expect(S3).toHaveBeenCalledWith({
168+
credentials: expect.any(Object),
169+
region: 'us-east-1',
170+
requestHandler: expect.any(Object)
171+
})
172+
173+
// ProxyAgent handles proxy detection automatically via proxy-from-env
174+
const s3CallArgs = S3.mock.calls[S3.mock.calls.length - 1][0]
175+
expect(s3CallArgs).toHaveProperty('requestHandler')
176+
})
177+
})
178+
69179
test('folderExists missing prefix', async () => {
70180
const rs = new RemoteStorage(global.fakeTVMResponse)
71181
await expect(rs.folderExists()).rejects.toEqual(expect.objectContaining({ message: 'prefix must be a valid string' }))

0 commit comments

Comments
 (0)