Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to handle AJAX response when broken pipe happens?

I have a (Spring Boot 1.5) REST server handling file uploads (multipart/form-data) and a JS client using jQuery fileupload. In most cases it works fine but when I want to upload a larger file (about 4MB), the server figures out it is above the limit and sends back a response including the error message.

However it seems the server stops reading the request (which is of course right) which results in a broken pipe on client side. In this case the response is not handled. The fail callback is called with the following content of data.jqXHR (data.response is undefined):

{"readyState":0,"responseText":"","status":0,"statusText":"error"}

When doing the call using curl, the result is:

HTTP/1.1 200
Content-Type: application/json;charset=UTF-8

curl: (55) Send failure: Broken pipe
{"files":[{"size":3863407,"error":"upload_uploadSizeExceeded"}]}

So there is a response returned but it seems to be ignored by the JS client. Is there any option to make jQuery handle the response even if the request is sent only partially?

BTW: even more strange, I see the request repeated several times in the server log in such a case, maybe a kind of retry mechanism in JS?

like image 539
Arne Burmeister Avatar asked Aug 22 '18 11:08

Arne Burmeister


People also ask

How do you handle ajax failure?

The best way to bubble that error from the server side (using php) to the client side is to send a header through the Ajax request somewhere in the 400's (which is always associated with errors). Once the Ajax request receives this it will trigger your error function.

How does error handle response in ajax?

When there is an AJAX error response or the AJAX request times out, you'll want to log as much information as you have, including the error message that jQuery gives you, the url and the request data. $. ajax(url, { "data": requestData, "type": "POST", "timeout": 5000 }) .

Is there any way to wait for ajax response and halt execution?

Cut and paste whatever code you need to execute in the callback function passed to success . Some good answer is already provided.

How do I return a response from ajax?

What you need to do is pass a callback function to the somefunction as a parameter. This function will be called when the process is done working (ie, onComplete): somefunction: function(callback){ var result = ""; myAjax = new Ajax.


1 Answers

Is there any option to make jQuery handle the response even if the request is sent only partially?

Short Answer

No, browsers use XMLHttpRequest and Fetch API, and this is considered a network error, by the specification, and network errors are intentionally empty.

CURL does not handle responses according to XMLHttpRequest specification.

Long Answer

Simulated Server

Read the request's ReadableStream and cancel mid-way:

const http = require('http');

const server = http.createServer((req, res) => {
    res.setHeader('Access-Control-Allow-Origin', '*');
    res.setHeader('Content-Type', 'text/plain');

    //Close Request ReadableStream Prematurely
    //to simulate close pipe on exceed file size
    if (req.url === '/data' && req.method === 'POST') {
        console.log('simulating...');

        let i = 0;
        req.on('data', chunk => {
            if (i++ === 10)
                req.destroy('broken pipe');
        });
    }

    res.end('fooby\n');
}).listen(8080);

Client Tests

Method 1: XMLHttpRequests

I carefully inspected each event and there is no indication of Sent bytes. If we did have a Sent bytes value, which should be in loaded, we could know if the request was cancelled midway to handle this case without the response:

let req = new XMLHttpRequest();
req.open('POST', 'http://localhost:8080/data');
    req.onloadstart = function (event) {
            console.log(event);
    };
    req.onprogress = function (event) {
            console.log(event);
    };
    req.onabort = function (event) {
            console.log(event);
    };
    req.onerror = function (event) {
            console.log(event);
    };
    req.onload = function (event) {
            console.log(event);
    };
    req.ontimeout = function (event) {
            console.log(event);
    };
    req.onloadend = function (event) {
            console.log(event);
    };
    req.onreadystatechange = function (event) {
            console.log(event);
    };
req.send(new ArrayBuffer(100000000));

Unfortunately, nothing.

The read-only XMLHttpRequest.status property returns the numerical status code of the response of the XMLHttpRequest. status will be an unsigned short. Before the request is complete, the value of status will be 0. It is worth noting that browsers report a status of 0 in case of XMLHttpRequest errors too.

From the XMLHttpRequest specification:

A response whose type is "error" is known as a network error.

A network error is a response whose status is always 0, status message is always the empty byte sequence, header list is always empty, body is always null, and trailer is always empty.

Method 2: Fetch API

I was hoping to intercept a low-level ReadableStream and get something, but, unfortunately, the resolve callback is not called on network errors:

fetch('http://localhost:8080/data', {
        method: 'POST',
        body: new ArrayBuffer(100000000),
        mode: 'cors'
}).then(resp => {
        console.log(resp);
        //hopefully we can get readable stream here
        //...nope, networkerrors do not trigger resolve
}).catch(error => {
        console.log(error);//TypeError: "NetworkError when attempting to fetch resource."
}).then(retry => {
        console.log(retry);
});

A fetch() promise rejects with a TypeError when a network error is encountered, although this usually means a permissions issue or similar. An accurate check for a successful fetch() would include checking that the promise resolved, then checking that the Response.ok property has a value of true. An HTTP status of 404 does not constitute a network error.

Fetch Documentation

Fetch Specification

Browsers do not treat this as an HTTP error, but a network error, and therefore do not forward anything HTTP related to user code.

Conclusion

XHR and Fetch specification states that network errors are handled as empty responses.

like image 155
Rafael Avatar answered Oct 10 '22 07:10

Rafael