From c187157ff48427c3c36ea459f474912bfd9b235d Mon Sep 17 00:00:00 2001 From: chenyumic Date: Thu, 30 Aug 2018 13:43:53 -0700 Subject: [PATCH] Added sample for handling CORS requests. (#721) * Added sample for handling CORS requests. * Added README * Moved samples to http/ * Minor fix * Fix nits --- functions/http/index.js | 54 +++++++++++++++++++++++++++++++ functions/http/test/index.test.js | 49 ++++++++++++++++++++++++++++ 2 files changed, 103 insertions(+) diff --git a/functions/http/index.js b/functions/http/index.js index bab3df85b5..2cdabb8471 100644 --- a/functions/http/index.js +++ b/functions/http/index.js @@ -231,3 +231,57 @@ exports.getSignedUrl = (req, res) => { } }; // [END functions_http_signed_url] + +// [START functions_http_cors] +/** + * HTTP function that supports CORS requests. + * + * @param {Object} req Cloud Function request context. + * @param {Object} res Cloud Function response context. + */ +exports.corsEnabledFunction = (req, res) => { + // Set CORS headers for preflight requests + // Allows GETs from any origin with the Content-Type header + // and caches preflight response for 3600s + + res.set('Access-Control-Allow-Origin', '*'); + + if (req.method === 'OPTIONS') { + // Send response to OPTIONS requests + res.set('Access-Control-Allow-Methods', 'GET'); + res.set('Access-Control-Allow-Headers', 'Content-Type'); + res.set('Access-Control-Max-Age', '3600'); + res.status(204).send(''); + } else { + // Set CORS headers for the main request + res.set('Access-Control-Allow-Origin', '*'); + res.send('Hello World!'); + } +}; +// [END functions_http_cors] + +// [START functions_http_cors_auth] +/** + * HTTP function that supports CORS requests with credentials. + * + * @param {Object} req Cloud Function request context. + * @param {Object} res Cloud Function response context. + */ +exports.corsEnabledFunctionAuth = (req, res) => { + // Set CORS headers for preflight requests + // Allows GETs from origin https://mydomain.com with Authorization header + + res.set('Access-Control-Allow-Origin', 'https://mydomain.com'); + res.set('Access-Control-Allow-Credentials', 'true'); + + if (req.method === 'OPTIONS') { + // Send response to OPTIONS requests + res.set('Access-Control-Allow-Methods', 'GET'); + res.set('Access-Control-Allow-Headers', 'Authorization'); + res.set('Access-Control-Max-Age', '3600'); + res.status(204).send(''); + } else { + res.send('Hello World!'); + } +}; +// [END functions_http_cors_auth] diff --git a/functions/http/test/index.test.js b/functions/http/test/index.test.js index 263d85ac2a..30da5d54fe 100644 --- a/functions/http/test/index.test.js +++ b/functions/http/test/index.test.js @@ -43,9 +43,20 @@ function getMocks () { }; sinon.spy(req, `get`); + const corsPreflightReq = { + method: 'OPTIONS' + }; + + const corsMainReq = { + method: 'GET' + }; + return { req: req, + corsPreflightReq: corsPreflightReq, + corsMainReq: corsMainReq, res: { + set: sinon.stub().returnsThis(), send: sinon.stub().returnsThis(), json: sinon.stub().returnsThis(), end: sinon.stub().returnsThis(), @@ -155,3 +166,41 @@ test.serial(`http:helloContent: should handle other`, (t) => { t.true(mocks.res.send.calledOnce); t.deepEqual(mocks.res.send.firstCall.args[0], `Hello World!`); }); + +test.serial(`http:cors: should respond to preflight request (no auth)`, (t) => { + const mocks = getMocks(); + const httpSample = getSample(); + + httpSample.sample.corsEnabledFunction(mocks.corsPreflightReq, mocks.res); + + t.true(mocks.res.status.calledOnceWith(204)); + t.true(mocks.res.send.called); +}); + +test.serial(`http:cors: should respond to main request (no auth)`, (t) => { + const mocks = getMocks(); + const httpSample = getSample(); + + httpSample.sample.corsEnabledFunction(mocks.corsMainReq, mocks.res); + + t.true(mocks.res.send.calledOnceWith(`Hello World!`)); +}); + +test.serial(`http:cors: should respond to preflight request (auth)`, (t) => { + const mocks = getMocks(); + const httpSample = getSample(); + + httpSample.sample.corsEnabledFunctionAuth(mocks.corsPreflightReq, mocks.res); + + t.true(mocks.res.status.calledOnceWith(204)); + t.true(mocks.res.send.calledOnce); +}); + +test.serial(`http:cors: should respond to main request (auth)`, (t) => { + const mocks = getMocks(); + const httpSample = getSample(); + + httpSample.sample.corsEnabledFunctionAuth(mocks.corsMainReq, mocks.res); + + t.true(mocks.res.send.calledOnceWith(`Hello World!`)); +});