sinonjs / nise

Fake XHR and fake server
Other
109 stars 47 forks source link

[HELP] can't respond to xhr: INVALID_STATE_ERR - 4 #148

Closed davidjohnbell closed 4 years ago

davidjohnbell commented 4 years ago

Im writing a chrome extension that is essentially a proxy. I'm getting an invalid state error when trying to respond to a fake xhr request. When the proxyRequest method executes I'm seeing in the console that onload is triggered before I call respond. Thank you in advance for any help.

import { fakeXhr, FakeXMLHttpRequest, fakeServer, FakeServer } from 'nise'
import { isM3u8Url, isTsUrl, PROXY_REQUEST, Message, PROXY_RESPONSE } from './common'
import { generateId } from 'utils-and-helpers/helpers'
import { HttpMethod } from 'utils-and-helpers/enums'
import axios from 'axios'

let server: FakeServer
function createShim() {
    fakeXhr.FakeXMLHttpRequest.useFilters = true
    fakeXhr.FakeXMLHttpRequest.addFilter(xhrFilter)
    server = fakeServer.create({
        autoRespond: true
    })
    server.respondWith(proxyRequest)
}

function xhrFilter(method: string, url: string): boolean {
    if((method.toLocaleUpperCase() == HttpMethod.GET) && (isM3u8Url(url) || isTsUrl(url))) return false
    else return true
}

function proxyRequest(xhr: FakeXMLHttpRequest) {
    let id = generateId(25)
    let message: Message = {
        id,
        action: PROXY_REQUEST,
        async: xhr.async,
        method: xhr.method,
        requestBody: xhr.requestBody,
        requestHeaders: xhr.requestHeaders,
        url: xhr.url,
        username: xhr.username,
        withCredentials: xhr.withCredentials
    }
    xhr.onload = () => console.log(`${id}: load start`)
    chrome.runtime.sendMessage(message, response => {
        switch(response.action) {
            case PROXY_RESPONSE:
                console.log(`${id}: trying to respond`)
                xhr.respond(response.status, response.responseHeaders, response.responseBody)
                break
            default:
                xhr.error()
        }
    })
}

createShim()

//test
setInterval(() => {
    axios.get('https://5g9s179kfgx9wn8tqwj9-cul47i.p5cdn.com/dlive/transcode-93-29/dlive-47692714/1583938810/src/live.m3u8')
    .then(res => console.log('res: ' + res))
    .catch(error => console.error('error: ' + error))
}, 3000)
fatso83 commented 4 years ago

We are trying to keep the GitHub issues list tidy and focused on bugs and feature discussions. This ticket looks like a usage question; please post it to StackOverflow and tag it with sinon, so the bigger community can help answer your questions.

If you feel that your topic is an issue with Sinon, please open a new ticket and follow the guidelines for reporting an issue.

drzraf commented 3 years ago

I think there is a clear lack of documentation. If xhr.error() is used, one could thing of an HTTP error, but the fact is that the client xhr object is changed in such a way that any further request to that server will trigger this error. This is unexpected and most people testing a timeout followed by a connection success (and using .error() to simulate the timeout) will encounter this.

A workaround is to use server.respond([400, {}, 'Error']); which does not "corrupt" the state of the xhr client. But its resulting state may not actually mimic a real network error.

fatso83 commented 3 years ago

@drzraf You are probably right that we could need some improved docs! If you feel like getting it done, we do accept pull requests 👍 The docs are automatically generated from the repo.

In this case, I think it's quite clear what this does from the docs.

request.error(); Simulates a network error on the request. The onerror handler will be called and the status will be 0.

A network error is just that: an error on the network level. If you return a HTTP status code, that implies the network is working, so that would not make much sense. Do feel free to elaborate on the doc examples, though!

drzraf commented 3 years ago

Yes, but the problem I (and possibly OP) experience, is that we can't use the fake-server afterwards. Sending a new XMLHttpRequest will (likely?) provoke an INVALID_STATE_ERR failure (0 in my case). This is not documented, I ignore the reason of this behavior and whether it's a bug or not.

fatso83 commented 3 years ago

Ah, good point! I did not get that it made the server unusable for the following requests. That both needs documenting and/or potentially a fix. Would you care to create a feature request describing the manner in which you think this should work? Perhaps with an example use case that does not work today.

drzraf commented 3 years ago

I want to simulate a temporary network failure to see how my code react to it (For example, it should retry X times). In case of xhr.error(), my code handles the state of the XMLHttpRequest and will trigger new ones. Again, I can't tell in which internal state nise's XHR server must be left but I believe it should be capable of accepting new requests.