From a894f2ed7068cbf15b475e69af9d9c766c251032 Mon Sep 17 00:00:00 2001 From: LavrovArtem Date: Tue, 24 Nov 2015 15:13:40 +0300 Subject: [PATCH] Correctly transform the "Origin" header (close #284) --- .../code-instrumentation/location/wrapper.js | 7 +-- src/client/sandbox/xhr/index.js | 3 ++ src/client/utils/destination-location.js | 3 ++ src/request-pipeline/context.js | 2 + src/request-pipeline/xhr/headers.js | 7 +-- test/client/config-qunit-server-app.js | 4 ++ test/client/fixtures/sandbox/xhr-test.js | 49 ++++++++++++++++++- test/client/fixtures/transport-test.js | 2 +- test/server/proxy-test.js | 21 +++++++- 9 files changed, 86 insertions(+), 12 deletions(-) diff --git a/src/client/sandbox/code-instrumentation/location/wrapper.js b/src/client/sandbox/code-instrumentation/location/wrapper.js index be5e0ba7d..284b59267 100644 --- a/src/client/sandbox/code-instrumentation/location/wrapper.js +++ b/src/client/sandbox/code-instrumentation/location/wrapper.js @@ -1,6 +1,7 @@ import createPropertyDesc from '../../../utils/create-property-desc'; import { get as getDestLocation, getParsed as getParsedDestLocation } from '../../../utils/destination-location'; import { IFRAME, getProxyUrl, changeDestUrlPart } from '../../../utils/url'; +import { getDomain } from '../../../../utils/url'; export default class LocationWrapper { constructor (window) { @@ -27,11 +28,7 @@ export default class LocationWrapper { })); Object.defineProperty(this, 'origin', createPropertyDesc({ - get: () => { - var parsedDestLocation = getParsedDestLocation(); - - return parsedDestLocation.protocol + '//' + parsedDestLocation.host; - }, + get: () => getDomain(getParsedDestLocation()), set: origin => origin })); diff --git a/src/client/sandbox/xhr/index.js b/src/client/sandbox/xhr/index.js index 58159ac43..616baa58d 100644 --- a/src/client/sandbox/xhr/index.js +++ b/src/client/sandbox/xhr/index.js @@ -3,6 +3,7 @@ import XMLHttpRequestWrapper from './xml-http-request-wrapper'; import nativeMethods from '../native-methods'; import { getProxyUrl } from '../../utils/url'; import XHR_HEADERS from '../../../request-pipeline/xhr/headers'; +import { getOrigin } from '../../utils/destination-location'; export default class XhrSandbox extends SandboxBase { constructor (sandbox) { @@ -90,6 +91,8 @@ export default class XhrSandbox extends SandboxBase { // Access-Control_Allow_Origin flag and skip "preflight" requests. xhr.setRequestHeader(XHR_HEADERS.requestMarker, 'true'); + xhr.setRequestHeader(XHR_HEADERS.origin, getOrigin()); + if (xhrSandbox.corsSupported) xhr.setRequestHeader(XHR_HEADERS.corsSupported, 'true'); diff --git a/src/client/utils/destination-location.js b/src/client/utils/destination-location.js index 8b08db5c5..47c968e7b 100644 --- a/src/client/utils/destination-location.js +++ b/src/client/utils/destination-location.js @@ -120,3 +120,6 @@ export function getParsed () { }; } +export function getOrigin () { + return sharedUrlUtils.getDomain(getParsed()); +} diff --git a/src/request-pipeline/context.js b/src/request-pipeline/context.js index 85bc6ed39..b58c555e9 100644 --- a/src/request-pipeline/context.js +++ b/src/request-pipeline/context.js @@ -127,6 +127,8 @@ export default class RequestPipelineContext { this.dest.referer = parsedReferer.dest.url; this.dest.reqOrigin = urlUtils.getDomain(parsedReferer.dest); } + else if (this.req.headers[XHR_HEADERS.origin]) + this.dest.reqOrigin = this.req.headers[XHR_HEADERS.origin]; this._initRequestNatureInfo(); diff --git a/src/request-pipeline/xhr/headers.js b/src/request-pipeline/xhr/headers.js index 681915cc3..1f4b9456d 100644 --- a/src/request-pipeline/xhr/headers.js +++ b/src/request-pipeline/xhr/headers.js @@ -4,7 +4,8 @@ // ------------------------------------------------------------- export default { - requestMarker: 'hammerhead|xhr|request-marker-header', - corsSupported: 'hammerhead|xhr|cors-supported-header', - withCredentials: 'hammerhead|xhr|with-credentials-header' + requestMarker: 'x-hammerhead|xhr|request-marker-header', + corsSupported: 'x-hammerhead|xhr|cors-supported-header', + withCredentials: 'x-hammerhead|xhr|with-credentials-header', + origin: 'x-hammerhead|xhr|origin' }; diff --git a/test/client/config-qunit-server-app.js b/test/client/config-qunit-server-app.js index 9eee17537..a9a7525af 100644 --- a/test/client/config-qunit-server-app.js +++ b/test/client/config-qunit-server-app.js @@ -54,4 +54,8 @@ module.exports = function (app) { res.send(req.originalUrl || req.url); }, delay); }); + + app.post('/xhr-origin-header-test/', function (req, res) { + res.send(req.headers['x-hammerhead|xhr|origin']); + }); }; diff --git a/test/client/fixtures/sandbox/xhr-test.js b/test/client/fixtures/sandbox/xhr-test.js index a49be999d..ffe2efde4 100644 --- a/test/client/fixtures/sandbox/xhr-test.js +++ b/test/client/fixtures/sandbox/xhr-test.js @@ -1,6 +1,17 @@ var sharedUrlUtils = hammerhead.get('../utils/url'); -var xhrSandbox = hammerhead.sandbox.xhr; +var xhrSandbox = hammerhead.sandbox.xhr; +var iframeSandbox = hammerhead.sandbox.iframe; +var browserUtils = hammerhead.utils.browser; + +QUnit.testStart(function () { + iframeSandbox.on(iframeSandbox.IFRAME_READY_TO_INIT_EVENT, initIframeTestHandler); + iframeSandbox.off(iframeSandbox.IFRAME_READY_TO_INIT_EVENT, iframeSandbox.iframeReadyToInitHandler); +}); + +QUnit.testDone(function () { + iframeSandbox.off(iframeSandbox.IFRAME_READY_TO_INIT_EVENT, initIframeTestHandler); +}); test('redirect requests to proxy', function () { jQuery.ajaxSetup({ async: false }); @@ -82,3 +93,39 @@ asyncTest('parameters must pass correctly in xhr event handlers (T239198)', func request.open('GET', '/xhr-large-response', true); request.send(null); }); + +if (!browserUtils.isIE9) { + asyncTest('send the origin header correctly (GH-284)', function () { + var xhrTestFunc = function () { + var xhr = new XMLHttpRequest(); + + xhr.open('POST', '/xhr-origin-header-test/', false); + xhr.send(); + + window.response = xhr.responseText; + }; + + xhrTestFunc(); + strictEqual(window.response, 'https://example.com', 'top window'); + + var iframe = document.createElement('iframe'); + + iframe.id = 'test'; + iframe.addEventListener('load', function () { + var script = document.createElement('script'); + + script.innerHTML = '(' + xhrTestFunc.toString() + ')()'; + + iframe.contentDocument.body.appendChild(script); + + strictEqual(iframe.contentWindow.response, 'https://example.com', 'iframe'); + + document.body.removeChild(iframe); + + expect(2); + start(); + }); + + document.body.appendChild(iframe); + }); +} diff --git a/test/client/fixtures/transport-test.js b/test/client/fixtures/transport-test.js index 0b465be79..dede4b73e 100644 --- a/test/client/fixtures/transport-test.js +++ b/test/client/fixtures/transport-test.js @@ -228,7 +228,7 @@ else { var callbackCount = 0; var value = 'testValue'; - ok(!window.localStorage.getItem(settings.sessionId)); + ok(!window.localStorage.getItem(settings.get().sessionId)); var onAjaxSend = function (xhr) { xhr.abort(); diff --git a/test/server/proxy-test.js b/test/server/proxy-test.js index d5d86e285..8db556f9e 100644 --- a/test/server/proxy-test.js +++ b/test/server/proxy-test.js @@ -122,7 +122,7 @@ describe('Proxy', function () { res.end(); }); - app.get('/B234325/reply-with-origin', function (req, res) { + app.get('/B234325,GH-284/reply-with-origin', function (req, res) { res.set('access-control-allow-origin', 'http://example.com'); res.end(req.headers['origin']); }); @@ -589,7 +589,7 @@ describe('Proxy', function () { describe('Regression', function () { it('Should force "Origin" header for the same-domain requests (B234325)', function (done) { var options = { - url: proxy.openSession('http://127.0.0.1:2000/B234325/reply-with-origin', session), + url: proxy.openSession('http://127.0.0.1:2000/B234325,GH-284/reply-with-origin', session), headers: { referer: proxy.openSession('http://example.com', session) } @@ -665,5 +665,22 @@ describe('Proxy', function () { done(); }); }); + + it('Should transform the "Origin" header for requests without the "Referer" header correctly (GH-284)', function (done) { + var options = { + url: proxy.openSession('http://127.0.0.1:2000/B234325,GH-284/reply-with-origin', session), + headers: { origin: 'http://127.0.0.1:1836' } + }; + + options.headers[XHR_HEADERS.origin] = 'http://example.com'; + options.headers[XHR_HEADERS.requestMarker] = 'true'; + options.headers[XHR_HEADERS.corsSupported] = 'true'; + + request(options, function (err, res, body) { + expect(body).eql('http://example.com'); + + done(); + }); + }); }); });