Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
148 changes: 118 additions & 30 deletions background.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
(function() {
/**
* A dictionary of header settings, keyed using the URLs supplied in the Options page
*/
Expand All @@ -18,6 +19,11 @@ var alteredCount = 0;
*/
var displayCount = true;

/**
* Cache with of request specific data
*/
var requestIds;

/**
* Returns the index of a given header object in the provided array
* @param headerArray The Array to search in
Expand All @@ -36,18 +42,31 @@ function getHeaderIndex(headerArray, newHeader) {
return -1;
}

function mergeNewHeaders(originalHeaders, newHeaders) {
function mergeNewHeaders(originalHeaders, newHeaders, origin) {
//copy the headers for our own usage
var mergedHeaders = originalHeaders.slice();
for (var i = 0, len = newHeaders.length; i < len; i++) {
var index = getHeaderIndex(mergedHeaders, newHeaders[i]);

//if a matching header is defined, replace it
//if not, add the new header to the end
if (index > -1) {
mergedHeaders[index] = newHeaders[i];
} else {
mergedHeaders.push(newHeaders[i]);
var thisHeader = newHeaders[i];

//skip if both are blank
if (thisHeader.name || thisHeader.value) {
var index = getHeaderIndex(mergedHeaders, thisHeader);

//issue #9 - fix - use new object to not over-write passed in reference
if (thisHeader.value === "HTTP_ORIGIN") {
thisHeader = {
name: thisHeader.name,
value: origin
};
}

//if a matching header is defined, replace it
//if not, add the new header to the end
if (index > -1) {
mergedHeaders[index] = thisHeader;
} else {
mergedHeaders.push(thisHeader);
}
}
}

Expand Down Expand Up @@ -101,8 +120,80 @@ function onHeadersReceivedHandler(info) {
chrome.browserAction.setBadgeText({ text:(++alteredCount).toString()});
}

return { responseHeaders:mergeNewHeaders(info.responseHeaders, desiredHeaders) };
var request = getRequestAndCleanUp(info);
var origin = request ? request.origin : "*"; // default to all if nothing is found

return { responseHeaders: mergeNewHeaders(info.responseHeaders, desiredHeaders, origin) };
}

/**
* Responds to Chrome's onBeforeSendHeaders event and finds 'origin' or 'referer' header for use later
* @param info {Object} Contains the request info
* @see http://code.google.com/chrome/extensions/webRequest.html#event-onBeforeSendHeaders
*/
function onBeforeSendHeadersHandler(info) {
var origin = null;

for (var i = 0, len = info.requestHeaders.length; i < len; i++) {
var header = info.requestHeaders[i];
if (header.name === "Origin") { // be done
origin = header.value;
break;
}
if (header.name === "Referer") { // keep going
origin = header.value;
}
}

//push on to stack
if (origin) {
requestIds.push({
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd recommend using an object hash instead of an array here. That way we won't need to iterate/filter the array later, we can just pull out the request by its ID:

requestIds = {};
requestIds[ 'ID_'+info.requestId ] = { origin: origin, expiration:1234 };

//when retrieving value
var request = requestIds[ 'ID_' + info.requestId ];

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I started with a hash - but then you'd have to use delete() to remove items. Since the list isn't every going to be huge the looping speed will be small. Using an array - you can use the Array methods like '.find' and '.filter' ...

I don't like to remove items in a loop like this:

for (var id in hash) {
    delete(hash[id]);
}

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's no need to loop through the IDs, you'll have the specific ID you need to delete:

delete requestIds[ 'ID_' + info.requestId ];
//or if you don't care about having an empty key
requestIds[ 'ID_' + info.requestId ] = null;

I don't see any situation in the code where you would iterate all of the keys...

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about when you have a request but no response? Your requestIds will grow ...

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You would have to loop through the Object - looking for expired (non-response) - so since the list will be at most 100 (100 requests in 10s) - looping isn't a big deal ... using a hash seems like a good idea until you do for (var id in hash) - then looping is better in an array.

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

valid point, the request should be removed from the requestIds inside the getRequestAndCleanUp method then.

Did you intend to periodically clean out 'expired' requests? I don't see that logic here

requestId: info.requestId,
origin: origin,
expiration: Math.round(info.timeStamp) + 10E3
});
}

return {requestHeaders: info.requestHeaders};
};

/**
* Find the request and garbage collect the other requests. This should clean up orphan requests.
* @param info {Object} Contains the request info
*/
function getRequestAndCleanUp(info) {

var requestId = info.requestId;
var now = Math.ceil(info.timeStamp);

var request = null;

// Array.prototype.filter method
// - callback ~ return true (keep) or false (remove)
// - finds the current element - fitlered
// - return true (keep) if now is less than expiration
//
requestIds = requestIds.filter(function(elem) {
if (elem.requestId === requestId) {
request = elem;
return false;
}
return (now < elem.expiration);
});

return request;
}

/**
* Responds to Chrome's onErrorOccurred event
* @param info {Object} Contains the request info
* @see http://code.google.com/chrome/extensions/webRequest.html#event-onErrorOccurred
*/
function onErrorOccurredHandler(info) {
//still works but Chrome still complains ... this is the only property that is different from a normal request
if (info.type !== "xmlhttprequest") {
console.log('ForceCORS was unable to modify headers for: '+info.url +' - '+info.error);
}
}

/**
Expand All @@ -116,8 +207,6 @@ function showOptionsPage() {
);
}



/**
* Initializes the background page by retrieving settings and establishing the onHeadersReceived listener.
* This method is called upon initialization, and also when the user changes settings on the Options page.
Expand All @@ -127,6 +216,7 @@ function init() {
var settings = retrieveSettings();
headersPerUrl = {};
urlsToAlter = [];
requestIds = [];

if (settings) {
for (var l = settings.length, i = 0; i < l; i++) {
Expand All @@ -150,36 +240,34 @@ function init() {
if (chrome.webRequest.onHeadersReceived.hasListener(onHeadersReceivedHandler)) {
chrome.webRequest.onHeadersReceived.removeListener(onHeadersReceivedHandler)
}
chrome.webRequest.onHeadersReceived.addListener(onHeadersReceivedHandler, {urls: urlsToAlter}, ["blocking", "responseHeaders"]);

chrome.webRequest.onHeadersReceived.addListener(
onHeadersReceivedHandler,
// filters
{
urls:urlsToAlter
},
// extraInfoSpec
["blocking", "responseHeaders"]
);
//same logic
if (chrome.webRequest.onBeforeSendHeaders.hasListener(onBeforeSendHeadersHandler)) {
chrome.webRequest.onBeforeSendHeaders.removeListener(onBeforeSendHeadersHandler)
}
chrome.webRequest.onBeforeSendHeaders.addListener(onBeforeSendHeadersHandler, {urls: urlsToAlter}, ["requestHeaders"]);

chrome.webRequest.onErrorOccurred.addListener(
function(info){console.log('ForceCORS was unable to modify headers for: '+info.url +' - '+info.error)},
{
urls:urlsToAlter
}
);
//dido
if (chrome.webRequest.onErrorOccurred.hasListener(onErrorOccurredHandler)) {
chrome.webRequest.onErrorOccurred.removeListener(onErrorOccurredHandler);
}
chrome.webRequest.onErrorOccurred.addListener(onErrorOccurredHandler, {urls: urlsToAlter});

//let chrome know
chrome.webRequest.handlerBehaviorChanged();
}

//establish a listener to respond to changes from the Options page
chrome.extension.onRequest.addListener(function (request, sender, sendResponse) {
chrome.extension.onRequest.addListener(function (request_not_used, sender_not_used, sendResponse) {
//retrigger the init method to load the new settings
init();

//respond that we got the message
sendResponse();
});


//make rocket go now!
init();
init();

})();