Git Product home page Git Product logo

xhook's Introduction

XHook

Easily intercept and modify XHR ("AJAX") request and response

With XHook, you could easily implement functionality to:

  • Cache requests in memory, localStorage, etc.
  • Insert authentication headers
  • Simulate responses
    • Create fake transparent backends for testing purposes
  • Sending Error statistics to Google Analytics
  • Create a client-side alternative to CORS by offloading requests to an iframe then splicing the response back in, see XDomain
  • Devious practical jokes
  • Supports RequiresJS and Browserify
  • Preflight GZip compression, see XZip (Incomplete)

Features

  • Intercept and modify XMLHttpRequest ("AJAX") request and response
  • Simulate responses transparently
  • Backwards compatible addEventListener removeEventListener
  • Backwards compatible user controlled progress (download/upload) events

Browser Support

Support Modern Browser.

Demos

Usage

⚠️ It's important to include XHook first as other libraries may store a reference to XMLHttpRequest before XHook can patch it

Using script link to load xhook and use it, like so:

<script src="//unpkg.com/xhook@latest/dist/xhook.min.js"></script>
<script>
  xhook.after(function (request, response) {
    if (request.url.match(/example\.txt$/))
      response.text = response.text.replace(/[aeiou]/g, "z");
  });
</script>

We can also install xhook via npm.

npm install xhook

Then use ESM syntax to load xhook.

import xhook from "xhook";
//modify 'responseText' of 'example2.txt'
xhook.after(function (request, response) {
  if (request.url.match(/example\.txt$/))
    response.text = response.text.replace(/[aeiou]/g, "z");
});

API

xhook.before(handler(request[, callback])[, index])

Modifying any property of the request object will modify the underlying XHR before it is sent.

To make the handler is asynchronous, just include the optional callback function, which accepts an optional response object.

To provide a fake response, return or callback() a response object.

xhook.after(handler(request, response[, callback]) [, index])

Modifying any property of the response object will modify the underlying XHR before it is received.

To make the handler is asynchronous, just include the optional callback function.

xhook.enable()

Enables XHook (swaps out the native XMLHttpRequest class). XHook is enabled be default.

xhook.disable()

Disables XHook (swaps the native XMLHttpRequest class back in)


request Object

response Object

Overview

The dark red before hook is returning a response object, which will trigger the after hooks, then trigger the appropriate events, so it appears as if response came from the server.

Reference

https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest

http://www.w3.org/TR/XMLHttpRequest/

http://www.w3.org/TR/XMLHttpRequest2/

Issues

  • XHook does not attempt to resolve any browser compatibility issues. Libraries like jQuery and https://github.com/ilinsky/xmlhttprequest will attempt to do this. XHook simply proxies to and from XMLHttpRequest, so you may use any library conjunction with XHook, just make sure to load XHook first.

  • You may use synchronous XHR, though this will cause asynchronous hooks to be skipped.

Contributing

See CONTRIBUTING for instructions on how to build and run XHook locally.

License

MIT License Copyright © 2022 Jaime Pillora [email protected]

xhook's People

Contributors

3y3 avatar abrissirba avatar avivasyuta avatar banyudu avatar ilyagelman avatar jhorlin avatar jiangts avatar jpillora avatar morsdyce avatar nowells avatar peterjosling avatar rajsite avatar sondremare avatar stevenyxu avatar trickypi avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

xhook's Issues

IE11 Support

It is not working for IE11 and most tests fail , is IE11 supported ?

xhook breaks jQuery's <script src> functionality

Hi!

It appears that xhook (and thus xdomain) messes with jQuery's ability to parse and execute <script> tags with a src parameter in dynamically-added markup. Here's a plunkr with reproduction: http://plnkr.co/edit/005F3745KFBQWgeFiZES?p=preview

(I'm including xdomain there but I repro'd with vanilla xhook as well so I figured that this was the right place to file a bug.)

The expected behavior here is that jQuery will parse out that <script> tag, request the script, and execute it, replacing the <h1> text with "hello, world". All of that happens if you comment out or remove xdomain.

I tried a variety of versions of jQuery, seemed consistent across them.

Thanks!

CDN link 404

The CDN link in the ReadMe is broken:
<script src="//cdn.rawgit.com/jpillora/xhook/1.3.1/dist/xhook.min.js"></script>

Suggest to use this instead:
https://cdnjs.cloudflare.com/ajax/libs/xhook/1.3.5/xhook.min.js

[enhancement] Add missing bower.json.

Hey, maintainer(s) of jpillora/xhook!

We at VersionEye are working hard to keep up the quality of the bower's registry.

We just finished our initial analysis of the quality of the Bower.io registry:

7530 - registered packages, 224 of them doesnt exists anymore;

We analysed 7306 existing packages and 1070 of them don't have bower.json on the master branch ( that's where a Bower client pulls a data ).

Sadly, your library jpillora/xhook is one of them.

Can you spare 15 minutes to help us to make Bower better?

Just add a new file bower.json and change attributes.

{
  "name": "jpillora/xhook",
  "version": "1.0.0",
  "main": "path/to/main.css",
  "description": "please add it",
  "license": "Eclipse",
  "ignore": [
    ".jshintrc",
    "**/*.txt"
  ],
  "dependencies": {
    "<dependency_name>": "<semantic_version>",
    "<dependency_name>": "<Local_folder>",
    "<dependency_name>": "<package>"
  },
  "devDependencies": {
    "<test-framework-name>": "<version>"
  }
}

Read more about bower.json on the official spefication and nodejs semver library has great examples of proper versioning.

NB! Please validate your bower.json with jsonlint before commiting your updates.

Thank you!

Timo,
twitter: @versioneye
email: [email protected]
VersionEye - no more legacy software!

support jsonp

Does it support jsonp? If not, could you point out how to implement?

getAllResponseHeaders does not include trailing CRLF

According to spec, the getAllResponseHeaders should include trailing CRLF:
https://xhr.spec.whatwg.org/#the-getallresponseheaders()-method

Xdomains implementation does not contain trailing CRLF.
https://github.com/jpillora/xhook/blob/gh-pages/src/xhook.coffee#L478

This breaks behavior of superagent, which pops the trailing CRLF:
https://github.com/visionmedia/superagent/blob/master/lib/client.js#L203

With xdomain trailing CRLF is not present, so the last header gets stripped.

Angular 1 support broken with 1.4.0

This commit

6335390

breaks support for angular1 (the requests do not appear to be sent at all) . Does anybody know how to fix this quickly without having to remove this commit from my branch (it would be nice to support both!!)

If I get a chance to debug it I will but I'm not using angular 2 at all at the moment.

Thanks!

EmitFinal calls two time

Hi All,

I am not sure whether I am doing right thing or not, but when I am trying to debug XDomain code I have found that emitFinal method is getting calls two times for little longer running process (pages).

Because of this, a Timer event starts from XDomain and taking longer to display page.

This is happening only in IE.

Do you know a possible solution, it would really help us for resolving things of slowness of our Angular application in IE.

Thanks
Rushi

Error callbacks never fire on versions after 1.1.8

This one took me awhile to track down.

In version 0.6.8 of XDomain which uses version 1.1.8 of XHook there are no problems whatsoever.

However in later versions of XHook, I can't get any error callbacks to fire on failed AJAX events.

Done / Success callbacks work just fine, but if the server returns anything in the 4xx range, nothing happens. The AJAX event will just sit in the "pending" status and never moved into failed. readyState never reaches past 1.

Also I noticed the xhook after functions fire correctly, and I can inspect the response object and modify things in there, but I can't seem to make anything happen past that. If I manually change the status to 200, then success callbacks fire.

Fails with recent versions of jQuery

XHook doesn't work well with jQuery 2.2.4 and 3.1.1 - all requests returns undefined. I've been tracing down the issue, and found the problem was related to jQuerys onabort handling:

https://github.com/jquery/jquery/blob/master/src/ajax/xhr.js#L123

The underlying issue seems to be that XHook calls onreadystatechange with readyState = 4 synchronously, while onload is triggered asynchronously. This makes jQuery assume that the request was aborted, because it's onreadystatechange handler is called without onload being called.

I managed to solve the issue on my end by adding facade.onabort = null, which means jQuery will not try to fallback to onreadystatechange. I only need to support IE10+, so that's fine for me - but I suppose XHook should expose onabort = null if the browser supports the native event, and generally ensure that onreadystatechange with readyState = 4 becomes asynchronous (ie. triggered at the same time as onload)?

Add api for storing a reference of a non-toxic XmlHttpRequest before implementation

there are enough reasons where the modification of ALL requests might not be wanted, especially for extremely large SPA's.

When xhook/xdomain are initiated, create/clone so one instance uses the postMessage api, and the other uses default behavior, which can be configured through the xdomain setup. (suggesting an enhancement for that api based on this).

Both async and sync requests?

In the README it says: You may use synchronous XHR, though this will cause asynchronous hooks to be skipped., is there any workaround for this or a way to resolve this in the library? I need this as I am proxying the requests of a browser library which fetches separate components using both sync and async requests.

Causing errors with FormData when disabled.

I encountered this issue when using xdomain but I'm pretty sure the issue fits better here. Our app uses FormData for uploading files, and with xhook enabled everything works fine. But when xhook is disabled it breaks, getting an error response from the server. I believe this is because xhook.disable() swaps the native XMLHttpRequest but does not swap back in the native FormData.

I added that swap for enable/disable the same way it saves and swaps the native XMLHttpRequest and that seemed to make it work.

Safari 6 bug: "SYNTAX_ERR: DOM Exception 12: An invalid or illegal string was specified."

Hi there,

Great work on xhook and xdomain. Really like the idea, and we're switching to it in an app I work on, to get rid of annoying CORS pre-flight requests for our JSON API.

Unfortunately, the switch has killed our app in Safari 6 with the following error:

SYNTAX_ERR: DOM Exception 12: An invalid or illegal string was specified.

I've debugged this error to this part of xhook:

    send = function() {
      var header, value, _j, _len1, _ref1, _ref2;
      transiting = true;
      xhr.open(request.method, request.url, true, request.user, request.pass);
      _ref1 = ['type', 'timeout'];
      for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) {
        k = _ref1[_j];
        modk = k === "type" ? "responseType" : k;
        xhr[modk] = request[k];    // <------------ this line
      }
      _ref2 = request.headers;
      for (header in _ref2) {
        value = _ref2[header];
        xhr.setRequestHeader(header, value);
      }
      if (request.body instanceof window[FormData]) {
        request.body = request.body.fd;
      }
      xhr.send(request.body);
    };

k is indeed "type", but request.type is undefined, and Safari 6 rejects setting xhr.responseType to undefined.

It seems to work fine in Safari 7 (Mavericks), iOS 7 Mobile Safari, and Chrome.

We're on the latest xdomain 0.6.1 which contains xhook 1.1.4. We're using xdomain with jQuery 1.10, but not making Ajax calls directly; we're a Backbone app, so it's doing that automatically / under the hood.

Any ideas? Thanks for the help!

change response data later

I want to change response data asynchronous, does it possible? something like following:
xhook.after(function(request, response){
response.text = asyncData; // asyncData will return later from other place
})

Open method does not reset readystate after xhr is used more than once

The W3 spec for both XHR and XHR2 state that the open method should change the state to OPENED (state 1).

xhook's open method does not reset the state to open if the xhr object has been used before which breaks any code that checks readyState after opening and before sending, for example - https://github.com/scottjehl/Respond/blob/master/src/respond.js#L34-44

The internal currentState variable should reset when open is called.

npm package

Could you please publish the library on npm?

using xhook to intercept requests in Salesforce

I injected the xhook.js into the web page of Salesforce in order to intercept the requests. However, I found that xhook.js affects the normal upload file function.
First, I execute xhook.js in the console of the chrome after Salesforce is loaded as Figure 1. Then I upload a file to Salesforce and the upload file progress is stuck. Figure 3 presents the upload requests after disabling the xhook. In Figure 2, there exists only two requests after uploading a file under the xhook, which means xhook.js causes the third request of upload function not to be sent.

Hope my issue can help you to improve the xhook.

Figure 1 :
image
Figure 2:
image
Figure 3:
image

Synchronous AJAX call fail in Firefox

It fails with

A parameter or an operation is not supported by the underlying object
window[XMLHTTP]/facade.send/send@http://localhost:3000/dist/xhook.js:468:11
window[XMLHTTP]/facade.send/process@http://localhost:3000/dist/xhook.js:487:1
window[XMLHTTP]/facade.send@http://localhost:3000/dist/xhook.js:517:5
...

It fails on https://github.com/jpillora/xhook/blob/gh-pages/dist/xhook.js#L451 when you try to set withCredentails property to XHR object.

This seems to be a documented behavior.

According to https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest

Note: Starting with Gecko 11.0 (Firefox 11.0 / Thunderbird 11.0 / SeaMonkey 2.8), Gecko no longer lets you use the withCredentials attribute when performing synchronous requests. Attempting to do so throws an NS_ERROR_DOM_INVALID_ACCESS_ERR exception.

Patching withCredentials causes Access Denied errors in IE9

I've noticed that XHook patches the XHR object in IE9 to have the key: withCredentials. While this works just fine in most cases, it conflicts with 3rd party services like Airbrake or Raygun or even services like Pusher which attempt to do feature detection on CORS support.

It seems the canonical way to check whether to use the XHR object or the XDomainRequest is by checking whether withCredentials is present in an instance of the XHR object.

This article highlights what I'm referring to.

The problem with XHook patching withCredentials for browsers that don't natively handle it, is when you're not sending requests between iframes, i.e. XDomain. For instance with Raygun, anytime there is an error, they generate an XHR out to their domain. Same deal with Pusher when it does fallback for IE (which don't support web sockets).

Their code is forced to believe we're not on IE and that it supports CORS. When it attempts to make a request, it receives Access Denied.

So I guess my question is.... why are we setting withCredentials by default?

XHookHttpRequest does not implement the XMLHttpRequest standard

XHookHttpRequest does not implement the XMLHttpRequest standard.
The specific issue I encountered was that the responseText property is undefined until the resource is loaded, whereas the spec specifies that it should never be undefined.

This makes xhook incompatible with some 3rd party libraries.

More generally, if xhook is to function as a drop-in replacement for XMLHttpRequest, then it should adhere to the standard.

Ensure `getResponseHeader` is case insensitive

First of all, thanks for all the great work in XHook and XDomain 😄 I am using it with superagent and ran into a bug where superagent can’t automatically decode the response body because the response content-type is undefined. I tracked it down to a small bug in XHook as outlined below.

The getResponseHeader method should not treat headers as case sensitive. I’d suggest [normalizing, e.g. lowercasing, all response headers](:

xhook/src/xhook.coffee

Lines 121 to 134 in bb38b1e

convertHeaders = xhook.headers = (h, dest = {}) ->
switch typeof h
when "object"
headers = []
for k,v of h
headers.push "#{k}:\t#{v}"
return headers.join '\n'
when "string"
headers = h.split '\n'
for header in headers
if /([^:]+):\s*(.+)/.test(header)
dest[RegExp.$1] = RegExp.$2 if not dest[RegExp.$1]
return dest
return
) before storing them in headers.

W3C compatibility tests: https://github.com/w3c/web-platform-tests/blob/master/XMLHttpRequest/getresponseheader-case-insensitive.htm

Let me know if you need any more info and have a great day!

Refactor: Remove CoffeeScript, Cleanup resulting JavaScript

Things we need to do to ensure this transition will go on smoothly as possible with no breaking changes:

  • Refactor to ES6
  • Choose a new build system
  • Refactor to separate modules instead of 1 large file with everything
  • Convert all existing test cases to actual tests #32
  • Make sure all test cases pass on all supported browsers
  • Confirm there are no breaking changes

Aborted requests fails miserably in IE9

If a request is aborted (i.e timeout), IE9 will report this error: 'SCRIPT575: Could not complete the operation due to error c00c023f.'

This is a IE9 problem which basically crashes my entire app when xhook tries to access status and statusText from the XHR object. There should be a check if the request has been aborted/timed out, and prevent reding status and statusText in function readHead().

Chrome Unverified URL issue on iOS

Hi, just like this issue on XDomain (jpillora/xdomain#76) I'm just using xhook and I'm also having the "Unverified URL" issue on Chrome on iOS.

If I remove xhook altogether, the issue disappears. But I need xhook, since it is the only way I could make my web app work on Windows Mobile 7 browser.

Cheers.

Header-case and duplicate headers

jpillora/xdomain#95 is actually an XHook issue, so we're continuing that thread here.

Currently XHook only allows unique headers and forces lowercase. According to the HTTP spec (HTTP 1.1 Section 4.2):

Multiple message-header fields with the same field-name MAY be present in a message if and only if the entire field-value for that header field is defined as a comma-separated list [i.e., #(values)]. It MUST be possible to combine the multiple header fields into one "field-name: field-value" pair, without changing the semantics of the message, by appending each subsequent field-value to the first, each separated by a comma. The order in which header fields with the same field-name are received is therefore significant to the interpretation of the combined field value, and thus a proxy MUST NOT change the order of these field values when a message is forwarded.

So XHook needs to be updated to reflect this

Check for a cached response in before hook

I am trying to use a before hook to intercept a request, see if I have a response cached for that request, and if so return that body. If the request is not cached, I would like to have it 'pass through' to the network (and I would intercept the response with an after hook). Unfortunately, I cannot figure out how to pass the request through to the network in the before hook when when a callback param has been added. Is this possible?

For example:

xhook.before(function (request, callback) {
        if (isCached(request.url)) {
            console.log('xhook - use cached value');
                callback({
                    status: 200,
                    text: 'cachedData',
                    headers: {
                        Foo: 'Bar'
                    }
                });
        } else {
            console.log('xhook - not cached; send to network');
        }
});

Setup travis

Should:

  • run tests on commit
  • publish to npm on tag

Implementation design

Hi,

I'm comparing xhook and ajax-interceptor and wondering why this library decided to provide a completely separate facade for the XMLHttpRequest implementation.

I may not have looked into it enough, but it looks like it's more work than simply overriding some functions on top of the original object.

I only ask because I believe the facade is doing something incorrectly. We've deployed a javascript SDK onto a customer's browser, and it's causing errors in the following function from the papaparse library.

Any help would be much appreciated!

Storing XHR from Request Object

Hi,

From my understanding, the Request object is different from XMLHttpRequest(XHR) object. Is it possible to extract the XHR from the request object, store it locally, abort current request, and send this XHR later?

Add API for listening for abort and manually firing events

This is required for jpillora/xdomain#20 (listen for abort, send to iframe, then fire abort event).

request and response need on(event, callback)

request and response need fire(eventName, eventData)

onabort needs to fire the standard addEventListener listener

Ideas:

//watch for user calls to abort
xhook.before(function(request, callback) {

  request.on('abort', function(event) {
    console.log('xhr has been aborted');

    //asynchronously react to abort
    doSomeThing(function() {
      //resolve xhr
      callback({ status: 0, statusText: 'aborted' });
    });

  });

});

and

//polyfill upload/download events
xhook.before(function(request, callback) {

  setTimeout(function() {
    request.fire('progress', { ... }); //event object needs to conform to the spec...
  }, 1000);

  //more progress

  //then fake response
     callback({status:200});
  //or normal send
     callback();

});

Test suite

XHook needs a test suite for preventing regressions, etc.

  • Using a free saucelabs account
  • See XDomains test/ directory

dynamic hack ajax

It seems we should invoke xhook.before and xhook.after before any XMLHTTPRequest send. But the common scenario is we may do some before or after in another js. Like this way:

// run.js
var record = {send: function(data){console.log("send to server data: ", data);}};
xhook.after(function(request, response){
    record.send(response.text);
})

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.