16

I have this Chrome extension that modifies the header of requests before sending them. I now would like to be able, within the same extension, to check the header of the response. I searched throughout the Chrome Extension APIs but I couldn't find anything of interest.

This is the code I use for modifying the header of the request, maybe it's useful for you to know how I do it.

chrome.webRequest.onBeforeSendHeaders.addListener(
      function(details) {/*do something*/},
      {urls: ["<all_urls>"]},
      ["blocking", "requestHeaders"]);

Does anyone knows how to do that, or can point me to an interesting source? Thanks

ccpizza
  • 28,968
  • 18
  • 162
  • 169
Masiar
  • 20,450
  • 31
  • 97
  • 140
  • I am currently looking for a Chrome extension (I do not know how to write my own) which can modify a response header. Specifically I would like to dynamically change _Content-Type_ `image/x-png` to `image/png` because Chrome does not understand `x-png` (an old bug from early days, still unfixed). Have you succeeded in creating your extension, and if so, can it do what I need? – kriegaex Sep 02 '12 at 11:38
  • Update: Chrome extension "Redirector" does what I need. Thanks. – kriegaex Sep 02 '12 at 12:10

2 Answers2

42

I achieved capturing all HTTP requests and responses made by a website, by injecting a script to DOM. There are several methods of doing it depending on your needs and environment e.g. ManifestV3/V2. Here's the one I've used:

inject.js:

var s = document.createElement('script');
// must be listed in web_accessible_resources in manifest.json
s.src = chrome.runtime.getURL('injected.js');
s.onload = function() {
    this.remove();
};
(document.head || document.documentElement).appendChild(s);

This would inject injected.js in website(s) that match "content_scripts" "matches" in manifest.json. Mention contentscript.js and inject.js in "js". See manifest.json at the end of answer.

Now, the code in injected.js which does the actual capturing of requests and responses is inspired from How we captured AJAX requests from a website tab with a Chrome Extension. Also see the comment section in that article.

injected.js:

(function(xhr) {

    var XHR = XMLHttpRequest.prototype;

    var open = XHR.open;
    var send = XHR.send;
    var setRequestHeader = XHR.setRequestHeader;

    XHR.open = function(method, url) {
        this._method = method;
        this._url = url;
        this._requestHeaders = {};
        this._startTime = (new Date()).toISOString();

        return open.apply(this, arguments);
    };

    XHR.setRequestHeader = function(header, value) {
        this._requestHeaders[header] = value;
        return setRequestHeader.apply(this, arguments);
    };

    XHR.send = function(postData) {

        this.addEventListener('load', function() {
            var endTime = (new Date()).toISOString();

            var myUrl = this._url ? this._url.toLowerCase() : this._url;
            if(myUrl) {

                if (postData) {
                    if (typeof postData === 'string') {
                        try {
                            // here you get the REQUEST HEADERS, in JSON format, so you can also use JSON.parse
                            this._requestHeaders = postData;    
                        } catch(err) {
                            console.log('Request Header JSON decode failed, transfer_encoding field could be base64');
                            console.log(err);
                        }
                    } else if (typeof postData === 'object' || typeof postData === 'array' || typeof postData === 'number' || typeof postData === 'boolean') {
                            // do something if you need
                    }
                }

                // here you get the RESPONSE HEADERS
                var responseHeaders = this.getAllResponseHeaders();

                if ( this.responseType != 'blob' && this.responseText) {
                    // responseText is string or null
                    try {

                        // here you get RESPONSE TEXT (BODY), in JSON format, so you can use JSON.parse
                        var arr = this.responseText;

                        // printing url, request headers, response headers, response body, to console

                        console.log(this._url);
                        console.log(JSON.parse(this._requestHeaders));
                        console.log(responseHeaders);
                        console.log(JSON.parse(arr));                        

                    } catch(err) {
                        console.log("Error in responseType try catch");
                        console.log(err);
                    }
                }

            }
        });

        return send.apply(this, arguments);
    };

})(XMLHttpRequest);

manifest.json:

{
  "manifest_version": 3,
  "name": "Extension Name",
  "description": "Some Desc.",
  "version": "1.1",
  "content_scripts": [{
      "matches": ["*://website.com/*"],
      "run_at": "document_start",
      "js": ["contentscript.js", "inject.js"]
  }],
  "web_accessible_resources": [{
      "resources": ["injected.js"],
      "matches": ["*://website.com/*"]
  }]
}

For MV2 the last block is simply "web_accessible_resources": ["injected.js"]

wOxxOm
  • 65,848
  • 11
  • 132
  • 136
user5155835
  • 4,392
  • 4
  • 53
  • 97
  • Hi, what's about inject.js? – Nammen8 Oct 05 '18 at 15:57
  • 1
    @user5155835 thanks, so now what is contentscript.js? Can you help me here: https://stackoverflow.com/questions/52669328/chrome-extension-intercept-http-response?noredirect=1#comment92268497_52669328 ? Thanks – Nammen8 Oct 08 '18 at 07:47
  • Content Script is used to communicate with your injected.js. I've also added this as the answer in your asked question. – user5155835 Oct 08 '18 at 09:51
  • 1
    @user5155835 what about contentscript.js file ? – Er KK Chopra Dec 08 '19 at 10:28
  • @ErKKChopra It is here: https://stackoverflow.com/a/52699831/5155835 – user5155835 Dec 09 '19 at 10:45
  • @user5155835 will this work with latest manifest v3? They have some restrictions regarding inline javascript – Andrew Apr 10 '21 at 16:19
  • What's the first snippet file name? What are "contentscript.js", "inject.js"? This was helpful, I got it to work but it needed a few modifications, in particular, you can't try to access `this.responseText` when `this.responseType === 'json'`. – Justin Harris Apr 26 '21 at 02:59
  • 1
    Thanks! This was helpful but I corrected some issues and simplified this. Here's the summary of my changes and there's a link to my full example: https://stackoverflow.com/a/67390377/1226799 – Justin Harris May 04 '21 at 18:43
  • Thanks for your answer. I was trying to do it for sometime. – rbansal Sep 24 '21 at 14:00
  • This works for reddit but it does not work for other websites like youtube.com Is it possible those websites are using there own implementation of the XMLHttp making it hard to monkey patch them? – rbansal Sep 25 '21 at 04:09
  • but you cant read document image css... response body, only xhr type response – walkman May 17 '22 at 11:16
  • How does one capture the Response from injected.js? I can see that it changes the XHR.prototype but do I need to dispatch an event, or how would my content-script be able to make use of the HTTP Response data? – dani Mar 21 '23 at 18:31
0

See the live-headers example.

http://code.google.com/chrome/extensions/examples/api/debugger/live-headers.zip

EDIT: For posterity you can find a version of live-headers.zip on their archived bug/patch site https://chromiumcodereview.appspot.com/9289057

With the latest revision (2021) no longer including the zip, but here's the dir https://src.chromium.org/viewvc/chrome/trunk/src/chrome/common/extensions/docs/examples/api/debugger/live-headers/?pathrev=226223

A T
  • 13,008
  • 21
  • 97
  • 158
  • Correct me if I'm wrong, I should use `chrome.experimental.debugger.onEvent.addListener(aFunction);` and then in `aFunction` use `params.response` in which I have everything, right? – Masiar Jan 20 '12 at 10:16
  • 1
    Or if your using the web request api couldnt you record the requestId when you modify the headers and then check for that requestId in a onHeadersReceived event. – PAEz Jan 20 '12 at 16:55
  • I used the same thing but the returned params response doesnt have the body. It has the headers and other information but the content itself is not accessible. – Milo Cabs Apr 25 '13 at 03:23
  • I'm trying to accomplish the same thing here, and just seeing the live-headers example I couldn't understand what I should do. – fiatjaf May 06 '16 at 19:19
  • same here, if anyone could help w/ web request api that’d be great – Vitali Pom Aug 18 '17 at 15:38
  • the link is now dead – OroshiX Mar 27 '21 at 16:04
  • 1
    @OroshiX - Fixed – A T Mar 30 '21 at 03:23