From ce2de8c79a08250e89ff6932aa1f80e999579c5e Mon Sep 17 00:00:00 2001 From: Ruslan Zavacky Date: Mon, 4 Dec 2017 18:59:17 +0000 Subject: [PATCH 1/7] Ability to specify custom headers for request (#1166) --- src/raven.js | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/raven.js b/src/raven.js index d85f893e27c0..eae0135588e0 100644 --- a/src/raven.js +++ b/src/raven.js @@ -1960,9 +1960,20 @@ Raven.prototype = { } request.open('POST', url); + this._appendHeadersToRequest(request, opts.options.headers); request.send(stringify(opts.data)); }, + _appendHeadersToRequest: function(request, headers) { + if (!headers) { + return; + } + + each(headers, function(key, value) { + request.setRequestHeader(key, typeof value === 'function' ? value() : value); + }); + }, + _logDebug: function(level) { if (this._originalConsoleMethods[level] && this.debug) { // In IE<10 console methods do not have their own 'apply' method From 2320e537251d0cbd98fdbf08967d08855a2a4ca4 Mon Sep 17 00:00:00 2001 From: Ruslan Zavacky Date: Mon, 4 Dec 2017 19:30:46 +0000 Subject: [PATCH 2/7] Add headers for fetch() method (#1166) --- src/raven.js | 42 +++++++++++++++++------- test/raven.test.js | 81 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 112 insertions(+), 11 deletions(-) diff --git a/src/raven.js b/src/raven.js index eae0135588e0..5d9876b89666 100644 --- a/src/raven.js +++ b/src/raven.js @@ -1897,12 +1897,23 @@ Raven.prototype = { // Auth is intentionally sent as part of query string (NOT as custom HTTP header) to avoid preflight CORS requests var url = opts.url + '?' + urlencode(opts.auth); + var evaluatedHeaders = null; + if (opts.options.headers) { + evaluatedHeaders = this._evaluateHeaders(opts.options.headers); + } + if (supportsFetch()) { + var fetchOptions = { + method: 'POST', + body: stringify(opts.data) + }; + + if (evaluatedHeaders) { + fetchOptions.headers = evaluatedHeaders; + } + return _window - .fetch(url, { - method: 'POST', - body: stringify(opts.data) - }) + .fetch(url, fetchOptions) .then(function(response) { if (response.ok) { opts.onSuccess && opts.onSuccess(); @@ -1960,18 +1971,27 @@ Raven.prototype = { } request.open('POST', url); - this._appendHeadersToRequest(request, opts.options.headers); + + if (evaluatedHeaders) { + each(evaluatedHeaders, function(key, value) { + request.setRequestHeader(key, value); + }); + } + request.send(stringify(opts.data)); }, - _appendHeadersToRequest: function(request, headers) { - if (!headers) { - return; + _evaluateHeaders: function(headers) { + var evaluatedHeaders = {}; + + for (var key in headers) { + if (headers.hasOwnProperty(key)) { + var value = headers[key]; + evaluatedHeaders[key] = typeof value === 'function' ? value() : value; + } } - each(headers, function(key, value) { - request.setRequestHeader(key, typeof value === 'function' ? value() : value); - }); + return evaluatedHeaders; }, _logDebug: function(level) { diff --git a/test/raven.test.js b/test/raven.test.js index 718fc859eaa0..6a51aad4c442 100644 --- a/test/raven.test.js +++ b/test/raven.test.js @@ -1392,6 +1392,87 @@ describe('globals', function() { }); }); + it('should apply globalOptions.headers if specified', function() { + this.sinon.stub(Raven, 'isSetup').returns(true); + this.sinon.stub(Raven, '_getHttpData').returns({ + url: 'http://localhost/?a=b', + headers: {'User-Agent': 'lolbrowser'} + }); + + var globalOptions = { + logger: 'javascript', + maxMessageLength: 100, + transport: sinon.stub(), + options: { + headers: { + 'custom-header': 'value' + } + } + }; + + Raven._globalProject = '2'; + Raven._globalOptions = globalOptions; + + Raven._send({message: 'bar'}); + + assert.deepEqual(globalOptions.transport.lastCall.args[0].data, { + project: '2', + logger: 'javascript', + platform: 'javascript', + request: { + url: 'http://localhost:9876/?a=b', + headers: { + 'User-Agent': 'lolbrowser', + 'custom-header': 'value' + } + }, + event_id: 'abc123', + message: 'bar', + extra: {'session:duration': 100} + }); + }); + + it('should apply globalOptions.headers with function value if specified', function() { + this.sinon.stub(Raven, 'isSetup').returns(true); + this.sinon.stub(Raven, '_getHttpData').returns({ + url: 'http://localhost/?a=b', + headers: {'User-Agent': 'lolbrowser'} + }); + + var globalOptions = { + logger: 'javascript', + maxMessageLength: 100, + transport: sinon.stub(), + options: { + headers: { + 'custom-header': function() { + return 'computed-header-value'; + } + } + } + }; + + Raven._globalProject = '2'; + Raven._globalOptions = globalOptions; + + Raven._send({message: 'bar'}); + assert.deepEqual(globalOptions.transport.lastCall.args[0].data, { + project: '2', + logger: 'javascript', + platform: 'javascript', + request: { + url: 'http://localhost/?a=b', + headers: { + 'User-Agent': 'lolbrowser', + 'custom-header': 'computed-header-value' + } + }, + event_id: 'abc123', + message: 'bar', + extra: {'session:duration': 100} + }); + }); + it('should check `Raven.isSetup`', function() { this.sinon.stub(Raven, 'isSetup').returns(false); this.sinon.stub(Raven, '_makeRequest'); From 77ac3660f361b76822879ccf184db18887ca73a5 Mon Sep 17 00:00:00 2001 From: Ruslan Zavacky Date: Tue, 5 Dec 2017 11:25:42 +0000 Subject: [PATCH 3/7] Update tests for the evaluating headers (#1166) --- test/raven.test.js | 71 +++++++++++----------------------------------- 1 file changed, 16 insertions(+), 55 deletions(-) diff --git a/test/raven.test.js b/test/raven.test.js index 6a51aad4c442..fd865ce3c173 100644 --- a/test/raven.test.js +++ b/test/raven.test.js @@ -1394,82 +1394,43 @@ describe('globals', function() { it('should apply globalOptions.headers if specified', function() { this.sinon.stub(Raven, 'isSetup').returns(true); - this.sinon.stub(Raven, '_getHttpData').returns({ - url: 'http://localhost/?a=b', - headers: {'User-Agent': 'lolbrowser'} - }); + this.sinon.stub(window, 'fetch').resolves(true); - var globalOptions = { + Raven._globalProject = '2'; + Raven._globalOptions = { logger: 'javascript', maxMessageLength: 100, - transport: sinon.stub(), - options: { - headers: { - 'custom-header': 'value' - } + headers: { + 'custom-header': 'value' } }; - Raven._globalProject = '2'; - Raven._globalOptions = globalOptions; - Raven._send({message: 'bar'}); - assert.deepEqual(globalOptions.transport.lastCall.args[0].data, { - project: '2', - logger: 'javascript', - platform: 'javascript', - request: { - url: 'http://localhost:9876/?a=b', - headers: { - 'User-Agent': 'lolbrowser', - 'custom-header': 'value' - } - }, - event_id: 'abc123', - message: 'bar', - extra: {'session:duration': 100} + assert.deepEqual(window.fetch.lastCall.args[1].headers, { + 'custom-header': 'value' }); }); it('should apply globalOptions.headers with function value if specified', function() { this.sinon.stub(Raven, 'isSetup').returns(true); - this.sinon.stub(Raven, '_getHttpData').returns({ - url: 'http://localhost/?a=b', - headers: {'User-Agent': 'lolbrowser'} - }); + this.sinon.stub(window, 'fetch').resolves(true); - var globalOptions = { + Raven._globalProject = '2'; + Raven._globalOptions = { logger: 'javascript', maxMessageLength: 100, - transport: sinon.stub(), - options: { - headers: { - 'custom-header': function() { - return 'computed-header-value'; - } + headers: { + 'custom-header': function() { + return 'computed-header-value'; } } }; - Raven._globalProject = '2'; - Raven._globalOptions = globalOptions; - Raven._send({message: 'bar'}); - assert.deepEqual(globalOptions.transport.lastCall.args[0].data, { - project: '2', - logger: 'javascript', - platform: 'javascript', - request: { - url: 'http://localhost/?a=b', - headers: { - 'User-Agent': 'lolbrowser', - 'custom-header': 'computed-header-value' - } - }, - event_id: 'abc123', - message: 'bar', - extra: {'session:duration': 100} + + assert.deepEqual(window.fetch.lastCall.args[1].headers, { + 'custom-header': 'computed-header-value' }); }); From a2e5943487f8c29bddaa45fc226b1669b339354f Mon Sep 17 00:00:00 2001 From: Ruslan Zavacky Date: Tue, 5 Dec 2017 11:32:07 +0000 Subject: [PATCH 4/7] Add TS type for headers (#1166) --- typescript/raven.d.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/typescript/raven.d.ts b/typescript/raven.d.ts index 0c2dec761eca..222a873a5186 100644 --- a/typescript/raven.d.ts +++ b/typescript/raven.d.ts @@ -63,6 +63,11 @@ declare module Raven { /** Override the default HTTP data transport handler. */ transport?: (options: RavenTransportOptions) => void; + /** Append headers to the fetch or XMLHttpRequest request. Should be in a form of hash, were value can be string or function */ + headers?: { + [key: string]: (string | Function); + }; + /** Allow use of private/secretKey. */ allowSecretKey?: boolean; From 6c6d139ec83121f0b0d990c4895426461afe9df9 Mon Sep 17 00:00:00 2001 From: Ruslan Zavacky Date: Tue, 5 Dec 2017 12:05:11 +0000 Subject: [PATCH 5/7] Add `headers` to documentation (#1166) --- docs/config.rst | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/docs/config.rst b/docs/config.rst index a20052f2e1cd..28b26fbf6ee3 100644 --- a/docs/config.rst +++ b/docs/config.rst @@ -275,6 +275,32 @@ Those configuration options are documented below: onError Callback to be invoked upon a failed request. +.. describe:: headers + + Pass custom headers to server requests for ``fetch`` or ``XMLHttpRequest``. + + .. code-block:: javascript + + { + headers: { + 'CSRF-TOKEN': '12345' + } + } + + Headers value can be in form of a function, to compute value in time of a request: + + .. code-block:: javascript + + { + headers: { + 'CSRF-TOKEN': function() { + // custom logic that will be computed on every request + return new Date(); + } + } + } + + .. describe:: allowDuplicates By default, Raven.js attempts to suppress duplicate captured errors and messages that occur back-to-back. Such events are often triggered by rogue code (e.g. from a `setInterval` callback in a browser extension), are not actionable, and eat up your event quota. From 5e663df6bedc11a58abc8e2b203794fe50d006ef Mon Sep 17 00:00:00 2001 From: Ruslan Zavacky Date: Thu, 7 Dec 2017 10:35:58 +0000 Subject: [PATCH 6/7] Add `headers: null` to global options default definition (#1166) --- src/raven.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/raven.js b/src/raven.js index 5d9876b89666..a2efabe0a1d0 100644 --- a/src/raven.js +++ b/src/raven.js @@ -74,6 +74,7 @@ function Raven() { ignoreUrls: [], whitelistUrls: [], includePaths: [], + headers: null, collectWindowErrors: true, maxMessageLength: 0, From 09eeaed06e1f192be9469666b14c6d3c88551061 Mon Sep 17 00:00:00 2001 From: Ruslan Zavacky Date: Thu, 7 Dec 2017 10:36:39 +0000 Subject: [PATCH 7/7] Describe `options` for transport method (#1166) --- docs/config.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/config.rst b/docs/config.rst index 28b26fbf6ee3..ec230662bfb5 100644 --- a/docs/config.rst +++ b/docs/config.rst @@ -269,6 +269,9 @@ Those configuration options are documented below: sentry_key Your public client key (DSN). + options + Include all top-level options described. + onSuccess Callback to be invoked upon a successful request.