Chrome fails to download response body if HTTP status is an error code - http

I have a Node.js Express web server that returns an HTTP response JSON payload along with an error status (4xx or 5xx) when something goes wrong.
res.status(500).json({ error: 'message' });
From the Chrome browser developer console's Timing section, I can see a lot of time (up to 5 minutes) spent in the "Content Download" segment and ultimately I am getting "Failed to load response data" in the Response section after download fails.
Chrome developer console timing output
Other browsers like Firefox and Opera are able to successfully download the JSON payload successfully and display them in their respective developer consoles.
If I send back the HTTP status as 200, Chrome has no trouble downloading the payload.
Also, if I do not set the Cache-Control HTTP headers to "no-store, no cache...", Chrome is able to successfully download the payload with 4xx/5xx status. However, I would like to set this header as a good practice against cache misuse.
HTTP Response Headers in the success and failure case
Is there something specific I need to do for Chrome?
Thank you!

I just had a similar problem. For the request I used the fetch API and in case of an error I did not read the stream of the response body.
Neither the content of the response body was displayed in the devtools nor was the request included in the HAR export.
After debugging this in the console I noticed that the content is displayed in the response or preview tab as soon as I was reading the stream. (e.g.: await response.text())
Strangely enough the behavior changes as you described when the corresponding cache headers are not set.

In my case, it happened when nothing was awaiting the response body.
Before (no body is rendered in the preview tab):
async request(context: RequestOpts): Promise<Response> {
// ...
const response = await this.fetchApi(url, init);
if (response.status >= 200 && response.status < 300) {
return response;
}
throw new Error();
}
After:
async request(context: RequestOpts): Promise<Response> {
// ...
const response = await this.fetchApi(url, init);
if (response.status >= 200 && response.status < 300) {
return response;
}
throw await response.json();
}

Related

File upload : who is responsible for setting HTTP headers

I'm trying to understand how HTTP file uploads work.
For instance, my VueJS app is calling a REST API (with Axios). When calling axios.request, no headers are set. There is just a FormData object containing the file to upload.
When the request arrives to the backend, I see that a Content-Type: multipart/form-data; ... header has been added to the request.
At which moment is this header created? Who is responsible for creating the header?
If it is a file upload Ajax request, in Axios, it's the browser that set the Content-Type: multipart/form-data;... header.
In Axios source code lib/adapters/xhr.js (the one that take charge of XMLHttpRequest), the HTTP request data will be checked. If it is an instance of FormData, then Content-Type header would be deleted and let browser do the job.
In lib/adapters/xhr.js (look at the comment in the source code):
if (utils.isFormData(requestData)) {
delete requestHeaders['Content-Type']; // Let the browser set it
}
For utils.isFormData(), the logic is:
// code in lib/utils.js
function isFormData(val) {
return (typeof FormData !== 'undefined') && (val instanceof FormData);
}

Temporary Servlet response from siteminder : "This page is used to hold your data "

Iam making an POST call using fetch from javascript to a servlet which returns a json response .
fetch('upload', {
method: 'post',
//body: data
body: formData
}).then(function (response) {
// console.log("response.text()--> ",response.text());
//return response.text();
if(response != '' && response != undefined){
return response.json();
}else{
return response;
}
}).then(function (result) {
if(result != '' && result != undefined){
failure=result.failure;
}
It works fine in IE but the same request when made from Chrome or Firefox browsers i get below response from Server.As this is considered as 200 response by fetch , it's using this response for further processing in the promise which is causing issues.
<HTML>
<HEAD>
<TITLE></TITLE>
</HEAD>
<BODY onLoad="document.AUTOSUBMIT.submit();">
This page is used to hold your data while you are being authorized for
your request.
<BR>
<BR>You will be forwarded to continue the authorization process.If this does not happen automatically, please click the Continue button
below.
Below are the request headers from chrome.
Request URL:https://devhost.com/dev/upload
Request Method:POST
Status Code:200 OK
Remote Address:XXX.XXX.XXX.XXX:443
Referrer Policy:no-referrer-when-downgrade
Cache-Control:no-store, max-age=0
Connection:close
Content-Length:2541
Content-Type:text/html; charset=iso-8859-1
Date:Wed, 16 Aug 2017 22:26:32 GMT
Expires:Wed, 16 Aug 2017 22:26:32 GMT
It seems this is a problem with the Siteminder setting , which is taking more time to validate a use This message is being sent back when server needs more time to validate and send response to the user(client), it sends a temporary reply to the client display the following message to the user.
Is there anyway to handle this from Siteminder or from Client side.
It looks like you are seeing the post preservation page, which is probably because SiteMinder wants to update your session or you aren't authorized. What is the action of the form element?

Angular 2 http service. Get detailed error information

Executing Angular2 http call to the offline server doesn't provide much info in it's "error response" object I'm getting in the Observable's .catch(error) operator or subscription error delegate (they are both share the same info actually). But as you can see on the screen shot of the console there's actual error was displayed by zone.js somehow.
So, how can I get this specific error info (net::ERR_CONNECTION_REFUSED)?
Thanks.
Whenever server do not respond, response.status will be always equal to 0 (zero)
{
type: 3, //ResponseType.Error
status: 0, // problem connecting endpoint
}
Also note, when you are performing CORS request, but origin (url in browser) is not authorized (not in allowed list of host names configured in remote endpoint) the response would be similar to above, with exception to type attribute which will be equal to 4 = ResponseType.Opaque...
This means, connection was made, but for instance, OPTIONS request returned with headers which do not contain origin or HTTPS request was performed from HTTP origin.
You can handle the error messages so they are easier to read. This can definitely be expanded on too:
public Get() {
return this.http.get(this.URL).map(this.extractData)
.catch(this.handleError);
}
public extractData(res: Response) {
let body = res.json();
return body || {};
}
public handleError(error: any) {
let errMsg = (error.message) ? error.message :
error.status ? `${error.status} - ${error.statusText}` : 'Server error';
console.error(errMsg);
return Observable.throw(errMsg);
}
Check out this part of the docs on error handling.
Without digging in the code, my expectation is that if the server is unreachable, then no response can be returned from the server. Therefore the Response object remains its initialized state.

Angular2 : detect error from HTTP post

I cannot interecept error from http post
a part of my mservice (http post method)
addApplicationLink(applicationLink: ApplicationLink){
let body = JSON.stringify(applicationLink);
let requestHeaders = new Headers();
var headers = new Headers();
headers.set('Content-Type', ['application/json; charset=utf-8']);
let reqoptions = new RequestOptions({
headers: headers
});
return this._http.post(this._applicationLinksUrl + this._linkServicePath,body,{headers: headers});
in my component :
addApplicationLink() {
//todo
this.addNewLink = false;
/* check if must be done after call real rest service */
//this.applicationLinks.push(this.applicationLinkAdd);
this._applicationLinkService.addApplicationLink(this.applicationLinkAdd)
.subscribe(data => {
this.applicationLinks.push(this.applicationLinkAdd)
},
error => {
// handle error
console.error('this an erreor ' + error.status)
}
)
When user tries to add two same applicationlinks , the backend returns an error 409
But when I execute , error.status displays 200 in browser console
I see also in browser console
XMLHttpRequest cannot load http://localhost:7001...... No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:3000' is therefore not allowed access. The response had HTTP status code 409.
rem : Http post is made with json , thus there is a prefligth call
Have you an idea to intercept error 409 ?
In fact, your server doesn't send back the CORS header (Access-Control-Allow-Origin' header is missing). This prevent the browser from providing the actual 409 error to the Angular2 application within the browser.
You need to fix first the problem on the server and you will be able to see this 409 error.
For more details about how CORS works, you could have a look at this article:
http://restlet.com/blog/2015/12/15/understanding-and-using-cors/

How to return error pages with body using HttpListener/HttpListenerResponse

I'm in the process of creating a REST API using HttpListener in .NET (C#). This all works out great, except for one slight issue.
I'm trying to return responses with Status Codes other than OK (200), for instance ResourceNotFound (404).
When I set the StatusCode of the HttpListenerResponse to something other than 200, and create a response body (using HttpListenerResponse.OutputStream), it seems to be resetting the status code to 200. I'm not able to send a response with StatusCode 404 and a message body. However, this should be possible according to the HTTP specs. I'm checking the requests and responses with Fiddler, but I'm not able to get what I'm looking for.
I've had the same problem and found the source of the problem :
If you write the body in the OutputStream before set the StatusCode (or any other property), the response will be sent before the modification is applied !
So, you have to proceed in this order :
public void Send(HttpListenerContext context, byte[] body)
{
// First, set a random status code and other stuffs
context.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
context.Response.ContentType = "text/plain";
// Write to the stream IN LAST (will send request)
context.Response.OutputStream.Write(body, 0, body.Length);
}

Resources