Skip to content

Commit 5fda331

Browse files
taeoldinlined
andauthored
Add debug feature to always enable cors for emulated v2 https functions (#1099)
* Add debug feature to always enable cors. * Add test cases. * Fix tests. * Add changelog. * Format. * Fix typo. Co-authored-by: Thomas Bouldin <inlined@users.noreply.github.com>
1 parent e720145 commit 5fda331

File tree

4 files changed

+85
-5
lines changed

4 files changed

+85
-5
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
- Add debug feature to enable cors option for v2 onRequest and onCall handlers. (#1099)

spec/v2/providers/https.spec.ts

Lines changed: 74 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,10 @@
2020
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
2121
// SOFTWARE.
2222

23+
import * as sinon from 'sinon';
2324
import { expect } from 'chai';
2425

26+
import * as debug from '../../../src/common/debug';
2527
import * as options from '../../../src/v2/options';
2628
import * as https from '../../../src/v2/providers/https';
2729
import {
@@ -166,7 +168,7 @@ describe('onRequest', () => {
166168
{
167169
'Access-Control-Request-Method': 'POST',
168170
'Access-Control-Request-Headers': 'origin',
169-
Origin: 'example.com',
171+
origin: 'example.com',
170172
}
171173
);
172174
req.method = 'OPTIONS';
@@ -181,6 +183,41 @@ describe('onRequest', () => {
181183
Vary: 'Origin, Access-Control-Request-Headers',
182184
});
183185
});
186+
187+
it('should add CORS headers if debug feature is enabled', async () => {
188+
sinon
189+
.stub(debug, 'isDebugFeatureEnabled')
190+
.withArgs('enableCors')
191+
.returns(true);
192+
193+
const func = https.onRequest((req, res) => {
194+
throw new Error('Should not reach here for OPTIONS preflight');
195+
});
196+
197+
const req = new MockRequest(
198+
{
199+
data: {},
200+
},
201+
{
202+
'Access-Control-Request-Method': 'POST',
203+
'Access-Control-Request-Headers': 'origin',
204+
origin: 'localhost',
205+
}
206+
);
207+
req.method = 'OPTIONS';
208+
209+
const resp = await runHandler(func, req as any);
210+
expect(resp.status).to.equal(204);
211+
expect(resp.body).to.be.undefined;
212+
expect(resp.headers).to.deep.equal({
213+
'Access-Control-Allow-Methods': 'GET,HEAD,PUT,PATCH,POST,DELETE',
214+
'Access-Control-Allow-Origin': 'localhost',
215+
'Content-Length': '0',
216+
Vary: 'Origin, Access-Control-Request-Headers',
217+
});
218+
219+
sinon.restore();
220+
});
184221
});
185222

186223
describe('onCall', () => {
@@ -317,7 +354,7 @@ describe('onCall', () => {
317354
{
318355
'Access-Control-Request-Method': 'POST',
319356
'Access-Control-Request-Headers': 'origin',
320-
Origin: 'example.com',
357+
origin: 'example.com',
321358
}
322359
);
323360
req.method = 'OPTIONS';
@@ -333,6 +370,41 @@ describe('onCall', () => {
333370
});
334371
});
335372

373+
it('overrides CORS headers if debug feature is enabled', async () => {
374+
sinon
375+
.stub(debug, 'isDebugFeatureEnabled')
376+
.withArgs('enableCors')
377+
.returns(true);
378+
379+
const func = https.onCall({ cors: 'example.com' }, (request) => {
380+
throw new Error('Should not reach here for OPTIONS preflight');
381+
});
382+
const req = new MockRequest(
383+
{
384+
data: {},
385+
},
386+
{
387+
'Access-Control-Request-Method': 'POST',
388+
'Access-Control-Request-Headers': 'origin',
389+
origin: 'localhost',
390+
}
391+
);
392+
req.method = 'OPTIONS';
393+
394+
const response = await runHandler(func, req as any);
395+
396+
expect(response.status).to.equal(204);
397+
expect(response.body).to.be.undefined;
398+
expect(response.headers).to.deep.equal({
399+
'Access-Control-Allow-Methods': 'POST',
400+
'Access-Control-Allow-Origin': 'localhost',
401+
'Content-Length': '0',
402+
Vary: 'Origin, Access-Control-Request-Headers',
403+
});
404+
405+
sinon.restore();
406+
});
407+
336408
it('adds CORS headers', async () => {
337409
const func = https.onCall((request) => 42);
338410
const req = new MockRequest(

src/common/debug.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ const debugMode = process.env.FIREBASE_DEBUG_MODE === 'true';
2525

2626
interface DebugFeatures {
2727
skipTokenVerification?: boolean;
28+
enableCors?: boolean;
2829
}
2930

3031
function loadDebugFeatures(): DebugFeatures {

src/v2/providers/https.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ import {
3939
import { ManifestEndpoint } from '../../runtime/manifest';
4040
import * as options from '../options';
4141
import { GlobalOptions, SupportedRegion } from '../options';
42+
import { isDebugFeatureEnabled } from '../../common/debug';
4243

4344
export { Request, CallableRequest, FunctionsErrorCode, HttpsError };
4445

@@ -218,12 +219,13 @@ export function onRequest(
218219
opts = optsOrHandler as HttpsOptions;
219220
}
220221

221-
if ('cors' in opts) {
222+
if (isDebugFeatureEnabled('enableCors') || 'cors' in opts) {
223+
const origin = isDebugFeatureEnabled('enableCors') ? true : opts.cors;
222224
const userProvidedHandler = handler;
223225
handler = (req: Request, res: express.Response): void | Promise<void> => {
224226
return new Promise((resolve) => {
225227
res.on('finish', resolve);
226-
cors({ origin: opts.cors })(req, res, () => {
228+
cors({ origin })(req, res, () => {
227229
resolve(userProvidedHandler(req, res));
228230
});
229231
});
@@ -319,7 +321,11 @@ export function onCall<T = any, Return = any | Promise<any>>(
319321
opts = optsOrHandler as HttpsOptions;
320322
}
321323

322-
const origin = 'cors' in opts ? opts.cors : true;
324+
const origin = isDebugFeatureEnabled('enableCors')
325+
? true
326+
: 'cors' in opts
327+
? opts.cors
328+
: true;
323329

324330
// onCallHandler sniffs the function length to determine which API to present.
325331
// fix the length to prevent api versions from being mismatched.

0 commit comments

Comments
 (0)