Why does my XMLHttpRequest have readystate 4 but status 0? - javascript

I have a content script in my Chrome extension which runs on some HTTPS page. It is trying to send a POST request to an HTTP page (by means of a background script) which is a route for an API that I have set up. I am trying to send JSON data over. However I am getting status 0, even though the ready state is 4. I used Postman to perform the same post and it worked. This leads me to believe it is a HTTPS protocol issue, however I am performing a GET on an HTTP page in the same background script and that is working fine. What might be the issue then? Here is my POST code:
var string = json;
xhr = new XMLHttpRequest();
xhr.open("POST", url, true);
xhr.setRequestHeader("Content-Type", "application/json");
xhr.onreadystatechange = function () {
if (xhr.readyState == 4 && xhr.status == 200) {
var json = JSON.parse(xhr.responseText);
}
};
xhr.send(string);
Thanks!
UPDATE:
I used the chrome developer tools to debug the background script and I found the error, which was "Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.". I guess background script errors do not print to the main console.
UPDATE:
I had to add the site I was posting to to the permissions field in my manifest. It works now.

I have a similar issue and, after debugging it for days, the only solution I found was to make the XMLHttpRequest synchronous by setting set the async param in the XMLHttpRequest open method to false.

The readyState value of 4 means the operation completed successfully or failed. The status property is initialized to 0 and will remain at 0 if an error occurs. Assign an event handler to the xhr.onerror property and I bet you'll see that handler fire. Unfortunately, the error event doesn't give any useful information about what caused the error.
To find out what caused the error, I would use the debug tools found in Chrome. Menu => More tools => Developer Tools. Then go to the "Network" tab. There you can see all the HTTP requests your webpage has made. It will show better details on any errors there.

What did you do?
I had to add the site I was posting to the permissions field in my manifest. It works now.
XMLHttpRequest.setRequestHeader("Access-Control-Allow-Origin", "the api website"); ?

Related

Chrome not adding headers in ajax requests from iframe, Firefox ok

I've got a situation where I'm sending ajax requests from iframe, to same domain as original page, this Iframe loaded from same domain. Say, original page address is http://server/client and iframe src is http://server/client/addin1/view.html
From Iframe I make initial xhr request to api: POST http://server/api/connect which returns 201 with token in response header and a cookie.
Now I make next call to different api method, say, GET http://server/api/status, but in this case I add the token header, and I assume the received cookie will be included by browser - it's HttpOnly, my xhr has withCredentials: true.
The magic is: in FF it works ok, both token and cookie are set and sent, in chrome the token header is not added and cookie is not sent. I've verified that in both cases xhr.setRequestHeader(...) gets called, and just to be 100% sure I've verified with wireshark what gets actually sent.
Any idea why chrome, behaves differently from FF? Maybe I'm missing sth simple.
thanks,
Ɓukasz
I had a similar problem to yours and I was able to fix my CORS issue following this MDN article CORS (MDN), see the section on Requests with credentials. There you'll find that you have to set the xhr options withCredentials. Here's the example they use:
var invocation = new XMLHttpRequest();
var url = 'http://bar.other/resources/credentialed-content/';
function callOtherDomain(){
if(invocation) {
invocation.open('GET', url, true);
invocation.withCredentials = true;
invocation.onreadystatechange = handler; // Needs to be implemented
invocation.send();
}
}
Hope that helps!

getting status code from resource with No 'Access-Control-Allow-Origin' header present

I'm using good 'ol XMLHttpRequest to make a GET request to https://www.instagram.com/USERNAME. My goal is to confirm that a the instagram username entered by my user actually exists, and it would be great if I could confirm this on the client side.
For instance, try to make a GET request to https://www.instagram.com/9gag and you get a 200 back. https://www.instagram.com/sakjafkhdsafd and you get a 404 back.
Now, now... seems like Instagram does not allow CORS... because when I run XMLHttpRequest.send() I get the following error:
XMLHttpRequest cannot load https://www.instagram.com/. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:8080' is therefore not allowed access.
Ok, guess the server is going to have to take care of this... BUT! if I check my network tab, I actually see that the request is being made and I am getting a response back with the expected status code. I also get all the html, everything. What the...?
How is it that the browser (chrome in my case) is able to capture this but not my application?
Adding my code as requested:
var xhr = new XMLHttpRequest();
xhr.open("GET", "https://www.instagram.com/" + username, false);
xhr.onreadystatechange = function () {
if (xhr.readyState == 4 && xhr.status == 200) {
// do something...
}
}
xhr.send()
EDIT:
I just tested this on Firefox and I get a 301 back. I guess my question now is "what is chrome doing to get the expected status code on the network tab?"
Although the browser has successfully retrieved content, it is forbidding you to read any of it due to CORS.
CORS is a little confusing in itself - if the server was okay fulfilling the request, why does the browser block it? But okay, that's a security restriction that was pasted on in a backward-compatible way.
What is more surprising is that this is true even for errors: the server must add a special header to its response even to allow the client to read a status code or error message!
But the browser will display those in the browser console and network tab.

GET query to URL using XMLHttpRequest in Chrome

I'm attempting to retrieve a url using XMLHttpRequest directly:
req = new XMLHttpRequest
req.onreadystatechange = ->
console.log req.readyState
if req.readyState == 1
console.log "sending..."
req.send
if req.readyState == 4
handler(req.response, req.status)
req.open("GET", info.srcUrl, true)
req.responseType = "arraybuffer"
But I never see the object transitioning beyond the 1 readyState. What am I missing?
If you are attempting to retrieve an arbitrary resource from another source than the server from which you received the running script, you are more than likely hitting a security issue related to Cross Site Scripting.
Except under very limited circumstances, you cannot retrieve resources from any other site but the one that served the page you are currently viewing.
For an explanation, see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Same_origin_policy_for_JavaScript?redirectlocale=en-US&redirectslug=JavaScript%2FSame_origin_policy_for_JavaScript
For the limited circumstances I mentioned above, see https://developer.mozilla.org/en-US/docs/HTTP/Access_control_CORS?redirectlocale=en-US&redirectslug=HTTP_access_control
Further, since you do not appear to provide an error handler for your XMLHTTPRequest, you're more than likely missing the error message which would have informed you why your request had failed.
Update
A quick tutorial on XMLHTTPRequest, including how to handle an onError event can be found at https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/Using_XMLHttpRequest?redirectlocale=en-US&redirectslug=DOM%2FXMLHttpRequest%2FUsing_XMLHttpRequest
Coming from Ruby, I did not realize that there is a subtle but important difference between req.send and req.send(). As #RobW pointed out in the comments, this method should also not be called in the event handler but at the end of the code.

The very first XMLHttpRequest fails but only on IE9

I have a site that I enter a username/password and click a login button. The login button makes an XMLHttpRequest object, and fires it off.
On Chrome, Firefox, Opera, Safari, Android devices, iOS devices this works fine.
IE9 will work fin so long as I am on an HTTP address and not using HTTPS.
On HTTPS, IE9 behaves as follows:
The first login request never returns anything back. The F12 screen does show my login request in the network tab and all looks correct. The scripting tab never throws an error. Simply nothing happens.
Here's the crazy part:
- If I click login a second time, it actually works.
- If I click refresh on the browser, and then login, that will work as well!
I am making the request as follows:
var x = new XMLHttpRequest();
x.open("POST", "/Relative/URL/Path", true);
x.setRequestHeader("Content-Type", "text/plain");
x.onreadystatechange = function () {
if ((x.readyState == 4) && (x.status == 200)) {
// handle callback
}
}
x.send(my request);
When this fails, the debugger will go from the x.send() line into the onreadystatechange code. The readyState will be 1. This will be the last I can debug because nothing else happens.
Any ideas would be extremely appreciated.
[EDIT]: I let one request go to see what would happen. The onreadystatechange event fired again with readyState = 4 and status = 12152. The network view in IE9's F12 screen shows the result as Aborted and the time taken as 1589.07s. A Google search shows this means the connection was closed on the server.
[EDIT 2]: Based on a comment below I redid this code to just use jQuery's ajax() method. I thought this might have a chance at eliminating bad code on my part. No such luck. The same behavior occurs.
$.ajax({
"url": sUrl,
"success": function (data, textStatus, x) {
workerCallback(data, id, "");
},
"error": function (x, testStatus, errorThrown) {
workerCallback("nc", id, errorThrown);
},
"contentType": "text/plain",
"data": JSON.stringify(req),
"dataType": "json",
"timeout": 1600000,
"type": "POST"
});
[FINAL UPDATE:] I've updated the code. If a timeout occurs, I simply repost the same request - one time only. Quite the hack but it works. Unless anyone finds the solution I'll split the bounty between a few helpful ideas people have had below.
This seems like a strange issue and it's hard to test it without poking around the code on an https site.
If you want a quick fix you could try doing an initial (dummy) request then abort it right away with a short setTimeout and make a second (real) request.
As per your description it should work.
during debug on the first request this came through
There is a related post to this exact error...
IE 9 Javascript error c00c023f
The author put the following in the onreadystatechange handler
if (xmlHttpRequest.aborted==true) {
stopLoadAnimation();
return;
}
This may help point you in the right direction.
Timeouts prevents the request from being terminated at readyState 1, and it succeeds afterwards due to content sniffing.
Configure SSL client authentication on the login form using the web server config
Insert a hidden element (such as an image) that references an URL that requires SSL client authentication
Use a protocol relative gif hyperlink, such as //example.com/image.gif, to avoid the SEC7111 mixed content vulnerability
The URL of the open method matches the domain when using HTTP, but not HTTPS, which causes the request to fail, but subsequent requests fallback to the security zone policy
Use a comparison between window.location.protocol and document.location.protocol to check whether the script is executing in the same context as the page
Sending JSON as MIME type text/plain may trigger content sniffing
Compare the Accept header between the requests that fail versus those that succeed
HTTPS Caching may be an issue
The Connection header may need to be set
Proxy configuration may be an issue
The initial header response values may be too large (e.g. HTTP status description has a limit of 512 characters)
document.readystate may not be complete on the initial request, which causes premature execution problems
Certificate revocation checks may block the initial JSON POST, but allow subsequent requests after the GET callback
readyState and status properties should be referenced using the callback scope rather than the variable x to avoid using the closure scope:
function cb()
{
if ( (this.readyState === 4) && (this.status === 200) )
{
// handle callback
}
}
onreadystatechange = cb;

Empty responseText from XMLHttpRequest

I have written an XMLHttpRequest which runs fine but returns an empty responseText.
The javascript is as follows:
var anUrl = "http://api.xxx.com/rates/csv/rates.txt";
var myRequest = new XMLHttpRequest();
callAjax(anUrl);
function callAjax(url) {
myRequest.open("GET", url, true);
myRequest.onreadystatechange = responseAjax;
myRequest.setRequestHeader("Cache-Control", "no-cache");
myRequest.send(null);
}
function responseAjax() {
if(myRequest.readyState == 4) {
if(myRequest.status == 200) {
result = myRequest.responseText;
alert(result);
alert("we made it");
} else {
alert( " An error has occurred: " + myRequest.statusText);
}
}
}
The code runs fine. I can walk through and I get the readyState == 4 and a status == 200 but the responseText is always blank.
I am getting a log error (in Safari debug) of Error dispatching: getProperties which I cannot seem to find reference to.
I have run the code in Safari and Firefox both locally and on a remote server.
The URL when put into a browser will return the string and give a status code of 200.
I wrote similar code to the same URL in a Mac Widget which runs fine, but the same code in a browser never returns a result.
Is http://api.xxx.com/ part of your domain? If not, you are being blocked by the same origin policy.
You may want to check out the following Stack Overflow post for a few possible workarounds:
Ways to circumvent the same-origin policy
PROBLEM RESOLVED
In my case the problem was that I do the ajax call (with $.ajax, $.get or $.getJSON methods from jQuery) with full path in the url param:
url: "http://mydomain.com/site/cgi-bin/serverApp.php"
But the correct way is to pass the value of url as:
url: "site/cgi-bin/serverApp.php"
Some browser don't conflict and make no distiction between one text or another, but in Firefox 3.6 for Mac OS take this full path as "cross site scripting"... another thing, in the same browser there is a distinction between:
http://mydomain.com/site/index.html
And put
http://www.mydomain.com/site/index.html
In fact it is the correct point view, but most implementations make no distinction, so the solution was to remove all the text that specify the full path to the script in the methods that do the ajax request AND.... remove any BASE tag in the index.html file
base href="http://mydomain.com/" <--- bad idea, remove it!
If you don't remove it, this version of browser for this system may take your ajax request like if it is a cross site request!
I have the same problem but only on the Mac OS machine. The problem is that Firefox treat the ajax response as an "cross site" call, in any other machine/browser it works fine. I didn't found any help about this (I think that is a firefox implementation issue), but I'm going to prove the next code at the server side:
header('Content-type: application/json');
to ensure that browser get the data as "json data" ...
The browser is preventing you from cross-site scripting.
If the url is outside of your domain, then you need to do this on the server side or move it into your domain.
This might not be the best way to do it. But it somehow worked for me, so i'm going to run with it.
In my php function that returns the data, one line before the return line, I add an echo statement, echoing the data I want to send.
Now sure why it worked, but it did.
Had a similar problem to yours. What we had to do is use the document.domain solution found here:
Ways to circumvent the same-origin policy
We also needed to change thins on the web service side. Used the "Access-Control-Allow-Origin" header found here:
https://developer.mozilla.org/En/HTTP_access_control

Categories

Resources