From a999ccba25c22bde73e2d527dbebecfb02b99d22 Mon Sep 17 00:00:00 2001 From: Todd Williams Date: Fri, 24 Jun 2016 15:26:57 -0700 Subject: [PATCH] Forward cookies to browser --- package.json | 2 +- src/lib/getHttpClient.js | 18 +++++++- test/lib/getHttpClient.test.js | 75 +++++++++++++++++++++++++--------- 3 files changed, 72 insertions(+), 23 deletions(-) diff --git a/package.json b/package.json index 57fd656..28c8633 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "gluestick-shared", - "version": "0.3.11", + "version": "0.4.0", "description": "This contains the shared code in the GlueStick CLI and the apps that it generates.", "main": "./build/index.js", "scripts": { diff --git a/src/lib/getHttpClient.js b/src/lib/getHttpClient.js index 300c06d..eeccd94 100644 --- a/src/lib/getHttpClient.js +++ b/src/lib/getHttpClient.js @@ -7,7 +7,7 @@ import axios from "axios"; * https://github.com/mzabriskie/axios#request-config * @param {axios} [httpClient] optionally override axios (used for tests/mocking) */ -export default function getHttpClient (options={}, req, httpClient=axios) { +export default function getHttpClient (options={}, req, serverResponse, httpClient=axios) { if (!req) { return httpClient.create(options); } @@ -17,7 +17,7 @@ export default function getHttpClient (options={}, req, httpClient=axios) { // If a request object is provided, then we want to merge the custom headers // with the headers that we sent from the browser in the request. - return httpClient.create({ + const client = httpClient.create({ baseURL: protocol + req.headers.host, headers: { ...req.headers, @@ -25,5 +25,19 @@ export default function getHttpClient (options={}, req, httpClient=axios) { }, ...httpConfig }); + + client.interceptors.response.use((response) => { + // @TODO: This will append all of the cookies sent back from server side + // requests in the initial page load. There is a potential issue if you are + // hitting 3rd party APIs. If site A sets a cookie and site B sets a cookie + // with the same key, then it will overwrite A's cookie and possibly create + // undesired effects. Currently, the suggested solution for dealing with + // this problem is to make the API requests to A or B in the browser and + // not in gsBeforeRoute for apps where that is an issue. + serverResponse.append("Set-Cookie", response.headers["set-cookie"]); + return response; + }); + + return client; } diff --git a/test/lib/getHttpClient.test.js b/test/lib/getHttpClient.test.js index 3071eeb..895e306 100644 --- a/test/lib/getHttpClient.test.js +++ b/test/lib/getHttpClient.test.js @@ -2,7 +2,30 @@ import getHttpClient from "../../src/lib/getHttpClient"; import sinon from "sinon"; import { expect } from "chai"; + describe("lib/getHttpClient", () => { + let axiosMock, mockAxiosInstance; + + beforeEach(() => { + mockAxiosInstance = { + interceptors: { + response: { + use: (middleware) => { + mockAxiosInstance.interceptors.response.middleware.push(middleware); + }, + middleware: [], + } + }, + get: (fakeResponse) => { + return mockAxiosInstance.interceptors.response.middleware.forEach(m => m(fakeResponse)); + } + }; + + axiosMock = { + create: sinon.stub().returns(mockAxiosInstance) + }; + }); + it("should call create with passed params", () => { const options = { headers: { @@ -11,11 +34,8 @@ describe("lib/getHttpClient", () => { }, rewriteRequest: [() => {}] }; - const mockAxios = { - create: sinon.spy() - }; - const client = getHttpClient(options, undefined, mockAxios); - expect(mockAxios.create.calledWith(options)).to.equal(true); + const client = getHttpClient(options, undefined, undefined, axiosMock); + expect(axiosMock.create.calledWith(options)).to.equal(true); }); it("should merge request headers if request object is passed", () => { @@ -32,14 +52,11 @@ describe("lib/getHttpClient", () => { "host": "hola.com:332211" } }; - const mockAxios = { - create: sinon.spy() - }; - const client = getHttpClient(options, req, mockAxios); - expect(mockAxios.create.calledWith(options)).to.equal(false); + const client = getHttpClient(options, req, {}, axiosMock); + expect(axiosMock.create.calledWith(options)).to.equal(false); const { headers, ...config } = options; - expect(mockAxios.create.lastCall.args[0]).to.deep.equal({ + expect(axiosMock.create.lastCall.args[0]).to.deep.equal({ baseURL: `http://${req.headers.host}`, headers: { ...req.headers, @@ -57,11 +74,8 @@ describe("lib/getHttpClient", () => { }, secure: true }; - const mockAxios = { - create: sinon.spy() - }; - const client = getHttpClient({}, req, mockAxios); - expect(mockAxios.create.lastCall.args[0].baseURL).to.equal(`https://${req.headers.host}`); + const client = getHttpClient({}, req, {}, axiosMock); + expect(axiosMock.create.lastCall.args[0].baseURL).to.equal(`https://${req.headers.host}`); }); it("should set baseURL with http if req.secure is false", () => { @@ -72,11 +86,32 @@ describe("lib/getHttpClient", () => { }, secure: false }; - const mockAxios = { - create: sinon.spy() + const client = getHttpClient({}, req, {}, axiosMock); + expect(axiosMock.create.lastCall.args[0].baseURL).to.equal(`http://${req.headers.host}`); + }); + + it("should forward along cookies back to the browser", () => { + const req = { + headers: { + "cookie": "name=Lincoln", + "host": "hola.com:332211" + }, + secure: false }; - const client = getHttpClient({}, req, mockAxios); - expect(mockAxios.create.lastCall.args[0].baseURL).to.equal(`http://${req.headers.host}`); + + const mockServerResponse = { + append: sinon.spy() + }; + + const client = getHttpClient({}, req, mockServerResponse, axiosMock); + const response = client.get({ + headers: { + "set-cookie": "oh hai" + } + }); + + expect(mockServerResponse.append.lastCall.args[0]).to.equal("Set-Cookie"); + expect(mockServerResponse.append.lastCall.args[1]).to.equal("oh hai"); }); });