How to detect Cross Origin (CORS) error and other types of errors for XMLHttpRequest () in Javascript

I am trying to detect when XMLHttpRequest () fails due to a Cross Origin error, as opposed to a bad request. For example:

ajaxObj=new XMLHttpRequest() ajaxObj.open("GET", url, true); ajaxObj.send(null); 

Consider 4 cases for url:

Example 1: A URL is a valid address where access control-allow-origin is correctly allowed

  • Example: http://192.168.8.35 , where I have a server with Access-Control-Allow-Origin: * installed in the header
  • This is easy to detect as ajaxObj.readyState == 4 and ajaxObj.status == 200

Case 2: The URL is an invalid address on an existing server

  • Example: http://xyz.google.com , where the server is responding, but this is not a valid request
  • The result is ajaxObj.readyState == 4 and ajaxObj.status == 0

Example 3: URL of a non-existent server IP

  • Example: http://192.168.8.6 on my local network, where there is nothing to answer
  • The result is ajaxObj.readyState == 4 and ajaxObj.status == 0

Example 4: A URL is a valid address in which access control-allow NOT is set

  • Example: http://192.168.8.247 , where I have a server without Access-Control-Allow-Origin: * installed in the header
  • The result is ajaxObj.readyState == 4 and ajaxObj.status == 0

The problem is this: How to distinguish between case 4 (error accessing permission-permission) and cases 2 & 3?

In case 4, the Chrome debugging console shows an error:

XMLHttpRequest cannot load http://192.168.8.247/. Origin http://localhost is not allowed by Access-Control-Allow-Origin.

How to make this error known in Javascript?

I tried to find some indication in ajaxObj , but there was nothing similar compared to Case 2 & 3.

Here is a simple test that I used:

 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>CORS Test</title> <script type="text/javascript"> function PgBoot() { // doCORS("http://192.168.8.35"); // Case 1 // doCORS("http://xyz.google.com"); // Case 2 doCORS("http://192.168.8.6"); // Case 3 // doCORS("http://192.168.8.247"); // Case 4 } function doCORS(url) { document.getElementById("statusDiv").innerHTML+="Processing url="+url+"<br>"; var ajaxObj=new XMLHttpRequest(); ajaxObj.overrideMimeType('text/xml'); ajaxObj.onreadystatechange = function() { var stat=document.getElementById("statusDiv"); stat.innerHTML+="readyState="+ajaxObj.readyState; if(ajaxObj.readyState==4) stat.innerHTML+=", status="+ajaxObj.status; stat.innerHTML+="<br>"; } ajaxObj.open("GET", url, true); ajaxObj.send(null); } </script> </head> <body onload="PgBoot()"> <div id="statusDiv"></div> </body> </html> 

Results using Chrome:

 Processing url=http://192.168.8.35 readyState=1 readyState=2 readyState=3 readyState=4, status=200 



 Processing url=http://xyz.google.com readyState=1 readyState=4, status=0 



 Processing url=http://192.168.8.6 readyState=1 readyState=4, status=0 



 Processing url=http://192.168.8.247 readyState=1 readyState=4, status=0 
+31
javascript ajax cors
Oct 11 '13 at
source share
3 answers

No, you can’t tell the difference according to the W3C specification.

Here's how the CORS specification specifies a simple cross-origin request procedure:

Apply the make request steps and follow the request rules below when making a request.

If the redirect flag is manually disabled and the response has an HTTP status code of 301, 302, 303, 307, or 308: Apply the redirect steps.

If the end user cancels the request: Apply the cancellation steps.

If there is a network error: In the case of DNS errors, TLS negotiation failure, or other types of network errors, the network error steps apply . Do not request any interaction with the end user ...

Otherwise: perform a resource access check. If it returns an error, apply the network error steps ...

In the event of a failed network connection or a failed CORS exchange, network error measures are applied , so there is literally no way to distinguish between the two cases.

Why? One of the advantages is that it does not allow an attacker to check the network topology of a local network. For example, a malicious script web page can find the IP address of your router by requesting its HTTP interface and, therefore, find out a few things about your network topology (for example, how big your private IP block is, /8 or /16 ). Since your router does not send (or should not) send CORS headers, the script knows nothing.

+27
Oct 11 '13 at 19:22
source

Perhaps in case this helps someone ... another way to deal with the difference between cors and network error ... might work with chrome or firefox ... (although this is not an ideal solution)

 var external = 'your url'; if (window.fetch) { // must be chrome or firefox which have native fetch fetch(external, {'mode':'no-cors'}) .then(function () { // external is reachable; but failed due to cors // fetch will pass though if it a cors error }) .catch(function () { // external is _not_ reachable }); } else { // must be non-updated safari or older IE... // I don't know how to find error type in this case } 
+3
Nov 18 '16 at 18:25
source

To distinguish a CORS violation from other failed AJAX requests, you can check the response headers of the HEAD request using server-side code and submit the results to your client page. For example, if an AJAX request failed (status 0), you can call this script (call it cors.php ) and you probably know if the response headers contain Access-Control-* headers.

Examples:

cors.php URL = HTTP://ip.jsontest.com
cors.php URL = HTTP://www.google.com
cors.php URL = HTTP://10.0.0.1

returns

HTTP / 1.1 200 OK Access-Control-Allow-Origin: *
HTTP / 1.1 302 Found
Invalid request




cors.php - Customize if necessary

 <?php /* cors.php */ $url = $_GET["url"]; if(isset($url)) { $headers = getHeaders($url); header("Access-Control-Allow-Origin: *"); if(count($headers) == 0) { die("Invalid request"); // cURL returns no headers on bad urls } else { echo $headers[0]; // echo the HTTP status code } // Include any CORS headers foreach($headers as $header) { if(strpos($header, "Access-Control") !== false) { echo " " . $header; } } } function getHeaders($url, $needle = false) { $headers = array(); $ch = curl_init($url); curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 4); // Timeout in seconds curl_setopt($ch, CURLOPT_TIMEOUT, 4); // Timeout in seconds curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_VERBOSE, true); curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'HEAD'); // HEAD request only curl_setopt($ch, CURLOPT_HEADER, true); curl_setopt($ch, CURLOPT_HEADERFUNCTION, function($curl, $header) use(&$headers) { array_push($headers, $header); return strlen($header); }); curl_exec($ch); return $headers; } /* Drakes, 2015 */ 

Client side wiring harness:

 function testCORS(url, $elem) { $.ajax({ url: url, timeout: 4000 }) .fail(function(jqXHR, textStatus) { if(jqXHR.status === 0) { // Determine if this was a CORS violation or not $.ajax({ context: url, url: "http://myserver.com/cors.php?url=" + escape(this.url), }) .done(function(msg) { if(msg.indexOf("HTTP") < 0) { $elem.text(url + " - doesn't exist or timed out"); } else if(msg.indexOf("Access-Control-Allow-Origin") >= 0) { $elem.text(url + " - CORS violation because '" + msg + "'"); } else { $elem.text(url + " - no Access-Control-Allow-Origin header set"); } }); } else { // Some other failure (eg 404), but not CORS-related $elem.text(url + " - failed because '" + responseText + "'"); } }) .done(function(msg) { // Successful ajax request $elem.text(this.url + " - OK"); }); /* Drakes, 2015 */ } 

Wiring harness driver:

 // Create a div and append the results of the URL calls $div = $("<div>"); $("body").append($div); var urls = ["http://ip.jsontest.com", "http://google.com", "http://10.0.0.1"]; urls.map( function(url) { testCORS(url, $div.append("<h4>").children().last()); }); 

Results:

http://ip.jsontest.com - OK

http://google.com - no Access-Control-Allow-Origin header set

http://10.0.0.1 - does not exist or is not running

+1
May 02 '15 at 15:35
source



All Articles