Skip to content
This repository has been archived by the owner on Feb 5, 2020. It is now read-only.

Commit

Permalink
Reduce OPTIONS preflight requests in browsers.
Browse files Browse the repository at this point in the history
Each request with an Accept-Datetime header requires
an OPTIONS preflight request in the browser.
To avoid this, only the first request to a "root" resource
(e.g., http://example.org/fragments) gets the header;
subsequent requests to "related" resources
(e.g., http://example.org/fragments?subject=bar)
will not carry this header anymore.
  • Loading branch information
RubenVerborgh committed Jun 20, 2016
1 parent e799227 commit a043853
Showing 1 changed file with 28 additions and 1 deletion.
29 changes: 28 additions & 1 deletion lib/util/Request-browser.js
Expand Up @@ -3,19 +3,29 @@

var EventEmitter = require('events').EventEmitter,
SingleIterator = require('../../lib/iterators/Iterator').SingleIterator,
parseLink = require('parse-link-header'),
_ = require('lodash');

require('setimmediate');

// Headers we cannot send (see https://www.w3.org/TR/XMLHttpRequest/#the-setrequestheader()-method)
var UNSAFE_REQUEST_HEADERS = ['accept-encoding', 'user-agent', 'referer'];
// Headers we need to obtain
var RESPONSE_HEADERS = ['content-type', 'link', 'memento-datetime'];
var RESPONSE_HEADERS = ['content-type', 'content-location', 'link', 'memento-datetime'];

// Resources that were already time-negotiated
var negotiatedResources = Object.create(null);

// Creates an HTTP request with the given settings
function createRequest(settings) {
var request = new EventEmitter();

// PERFORMANCE HACK:
// Reduce OPTIONS preflight requests by removing the Accept-Datetime header
// on requests for resources that are presumed to have been time-negotiated
if (negotiatedResources[removeQuery(settings.url)])
delete settings.headers['accept-datetime'];

// Delegate the request to jQuery's AJAX module
var jqXHR = jQuery.ajax({
url: settings.url,
Expand All @@ -29,6 +39,18 @@ function createRequest(settings) {
response.statusCode = jqXHR.status;
response.headers = _.object(RESPONSE_HEADERS, RESPONSE_HEADERS.map(jqXHR.getResponseHeader));
request.emit('response', response);

// If the resource was time-negotiated, store its "base" URI (= no query string)
if (settings.headers['accept-datetime'] && response.headers['memento-datetime']) {
var resource = removeQuery(response.headers['content-location'] || settings.url);
if (!negotiatedResources[resource]) {
// Ensure the resource is not a timegate
var links = response.headers.link && parseLink(response.headers.link),
timegate = removeQuery(links && links.timegate && links.timegate.url);
if (resource !== timegate)
negotiatedResources[resource] = true;
}
}
})
// Emit an error if the request fails
.fail(function () {
Expand All @@ -40,4 +62,9 @@ function createRequest(settings) {
return request;
}

// Removes the query string from a URL
function removeQuery(url) {
return url ? url.replace(/\?.*$/, '') : '';
}

module.exports = createRequest;

0 comments on commit a043853

Please sign in to comment.