确定由于不安全的响应或连接拒绝而导致ajax呼叫失败

我一直在做很多的研究,找不到解决这个问题的方法。 我试图从https服务器执行一个jQuery ajax调用到一个locahost https服务器运行与自定义自签名证书的docker。 我的问题是我无法确定响应是拒绝连接还是不安全响应(由于缺less证书接受)。 有没有办法来确定两种情况之间的差异? responseTextstatusCode在两种情况下都是一样的,即使在chrome控制台中我可以看到一个区别:

 net::ERR_INSECURE_RESPONSE net::ERR_CONNECTION_REFUSED 

在两种情况下, responseText始终为“”, statusCode始终为“0”。

我的问题是,我怎样才能确定是否由于ERR_INSECURE_RESPONSE或由于ERR_CONNECTION_REFUSED jQuery ajax调用失败?

一旦证书被接受,一切正常,但我想知道本地主机服务器是否closures,或者启动并运行,但证书尚未被接受。

 $.ajax({ type: 'GET', url: "https://localhost/custom/server/", dataType: "json", async: true, success: function (response) { //do something }, error: function (xhr, textStatus, errorThrown) { console.log(xhr, textStatus, errorThrown); //always the same for refused and insecure responses. } }); 

在这里输入图像说明

即使手动执行请求,我也会得到相同的结果:

 var request = new XMLHttpRequest(); request.open('GET', "https://localhost/custom/server/", true); request.onload = function () { console.log(request.responseText); }; request.onerror = function () { console.log(request.responseText); }; request.send(); 

没有办法将其与最新的Web浏览器区分开来。

W3C规范:

以下步骤描述了用户代理必须为简单的跨源请求做些什么

在提出请求的同时,应用请求步骤并遵守下面的请求规则。

如果手动redirect标志未设置,并且响应的HTTP状态码为301,302,303,307或308应用redirect步骤。

如果最终用户取消请求应用中止步骤。

如果出现networking错误如果出现 DNS错误,TLS协商失败或其他types的networking错误,则应用networking错误步骤 。 不要求任何types的最终用户交互。

注意:这不包括指示某种types的错误的HTTP响应,例如HTTP状态码410。

否则,请执行资源共享检查。 如果返回失败,则应用networking错误步骤。 否则,如果返回通过,则终止该algorithm并将交叉源请求状态设置为成功。 不要实际终止请求。

正如你可以读到的,networking错误不包括包含错误的HTTP响应,这就是为什么你总是会得到0作为状态码,而错误。

资源


注意 :以下示例是使用Google Chrome版本43.0.2357.130创build的,并针对我创build的用于模拟OP的环境。 代码的设置是在答案的底部。


我虽然这种方法解决这个问题将通过HTTP而不是HTTPS作为第二个请求作为这个答案,但我记得,这是不可能的,因为新版本的浏览器阻止混合内容。

这意味着如果您使用HTTPS,Web浏览器将不允许通过HTTP进行请求,反之亦然。

几年前就已经是这样了,但老版本的Web浏览器版本,比如Mozilla Firefox在23版本以下版本中就可以。

有关它的证据:

从HTTPS发出HTTP请求使用Web Broser控制台

 var request = new XMLHttpRequest(); request.open('GET', "http://localhost:8001", true); request.onload = function () { console.log(request.responseText); }; request.onerror = function () { console.log(request.responseText); }; request.send(); 

将导致以下错误:

混合内容:通过HTTPS加载“ https:// localhost:8000 / ”页面,但请求不安全的XMLHttpRequest端点“ http:// localhost:8001 / ”。 此请求已被阻止; 内容必须通过HTTPS提供。

如果您尝试以其他方式添加Iframe,将会在浏览器控制台中显示相同的错误。

 <iframe src="http://localhost:8001"></iframe> 

使用套接字连接也发表作为答案 ,我很确定,结果将是相同/相似的,但我已经试了一下。

尝试使用HTTPS将Web Broswer打开到非安全套接字端点的套接字连接将以混合内容错误结束。

 new WebSocket("ws://localhost:8001", "protocolOne"); 

1)混合内容:通过HTTPS加载“ https:// localhost:8000 / ”页面,但试图连接到不安全的WebSocket端点“ws:// localhost:8001 /”。 此请求已被阻止; 此端点必须通过WSS可用。

2)未被捕获的DOMException:未能构build“WebSocket”:不能从通过HTTPS加载的页面启动不安全的WebSocket连接。

然后我试图连接到一个wss端点也看到如果我可以读取有关networking连接错误的一些信息:

 var exampleSocket = new WebSocket("wss://localhost:8001", "protocolOne"); exampleSocket.onerror = function(e) { console.log(e); } 

在服务器closures的情况下执行上面的代码片段会导致:

到'wss:// localhost:8001 /'的WebSocket连接失败:连接build立时出错:net :: ERR_CONNECTION_REFUSED

在服务器打开的情况下执行上面的代码片段

WebSocket连接到'wss:// localhost:8001 /'失败:WebSocket打开握手被取消

但是,“误差函数”输出到控制台的错误没有任何提示来区分另一个错误。


使用代理作为这个答案build议可以工作,但只有当“目标”服务器具有公共访问。

这不是这种情况,所以在这种情况下试图实现一个代理将导致我们同样的问题。

创buildNode.js HTTPS服务器的代码

我创build了两个使用自签名证书的Nodejs HTTPS服务器:

targetServer.js:

 var https = require('https'); var fs = require('fs'); var options = { key: fs.readFileSync('./certs2/key.pem'), cert: fs.readFileSync('./certs2/key-cert.pem') }; https.createServer(options, function (req, res) { res.setHeader('Access-Control-Allow-Origin', '*'); res.setHeader('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE'); res.setHeader('Access-Control-Allow-Headers', 'Content-Type'); res.writeHead(200); res.end("hello world\n"); }).listen(8001); 

applicationServer.js:

 var https = require('https'); var fs = require('fs'); var options = { key: fs.readFileSync('./certs/key.pem'), cert: fs.readFileSync('./certs/key-cert.pem') }; https.createServer(options, function (req, res) { res.writeHead(200); res.end("hello world\n"); }).listen(8000); 

为了使其工作,您需要安装Nodejs,需要为每个服务器生成单独的证书,并相应地将其存储在文件夹certs和certs2中。

运行它只需在terminal(ubuntu示例)中执行node applicationServer.jsnode targetServer.js

截至目前: 浏览器之间没有办法区分这个事件。 由于浏览器不提供开发人员访问的事件。 (2015年7月)

这个答案仅仅是为了提供一个可能的,albiet hacky和不完整的解决scheme的想法。


免责声明:这个答案是不完整的,因为它没有完全解决OP的问题(由于交叉来源的政策)。 然而,这个想法本身确实有一些优点,可以进一步扩展: @artur grzesiak 在这里 ,使用代理和Ajax。


经过我自己的一些研究,似乎没有任何forms的错误检查连接被拒绝和不安全的响应之间的差异,至less就javascript提供了对两者之间的差异的回应。

我的研究普遍认为SSL证书是由浏览器处理的,所以直到用户接受自签名证书,浏览器才locking所有请求,包括状态码的请求。 浏览器可以(如果编码的话)发回它自己的状态代码,以确保不安全的响应,但是这对于任何事情都没有什么帮助,即使如此,浏览器的兼容性也会有问题(Chrome / Firefox / IE有不同的标准。 .. 再来一次)

由于你原来的问题是检查你的服务器之间的状态正在对比有一个不被接受的证书,你可以不这样做一个标准的HTTP请求?

 isUp = false; isAccepted = false; var isUpRequest = new XMLHttpRequest(); isUpRequest.open('GET', "http://localhost/custom/server/", true); //note non-ssl port isUpRequest.onload = function() { isUp = true; var isAcceptedRequest = new XMLHttpRequest(); isAcceptedRequest.open('GET', "https://localhost/custom/server/", true); //note ssl port isAcceptedRequest.onload = function() { console.log("Server is up and certificate accepted"); isAccepted = true; } isAcceptedRequest.onerror = function() { console.log("Server is up and certificate is not accepted"); } isAcceptedRequest.send(); }; isUpRequest.onerror = function() { console.log("Server is down"); }; isUpRequest.send(); 

当然,这确实需要一个额外的请求来validation服务器连接,但它应该通过淘汰过程完成工作。 尽pipe如此,我仍然觉得很尴尬,而且我也不是这个双重要求的忠实粉丝。

@Schultzie的答案非常接近,但是在浏览器环境中,通常情况下, http通常不起作用。

你可以做的是使用中间服务器(代理)代表你提出请求。 代理应允许从https来源转发http请求或从自签名来源加载内容。

拥有适当证书的自己的服务器可能是一个矫枉过正的情况 – 你可以使用这个设置,而不是使用自签名证书的机器 – 但是有大量的匿名开放代理服务

所以我想到的两个方法是:

  1. ajax请求 – 在这种情况下,代理必须使用适当的CORS设置
  2. 使用iframe – 您可以通过代理在iframe中加载脚本(可能包含在html中)。 一旦脚本加载它发送一个消息到它的.parentWindow 。 如果你的窗口收到一条消息,你可以确定服务器正在运行(或者更确切地说,是在运行几分之一秒之前)。

如果您只对本地环境感兴趣,则可以尝试使用--disable-web-security标志运行chrome。


另一个build议:你是否尝试加载一个图像编程,以确定是否有更多的信息存在?

查看jQuery.ajaxError()参考: jQuery AJAXerror handling(HTTP状态码)它捕获全局Ajax错误,您可以通过HTTP或HTTPS以多种方式处理错误:

 if (jqXHR.status == 501) { //insecure response } else if (jqXHR.status == 102) { //connection refused } 

不幸的是,现在的浏览器XHR API没有提供明确的指示,说明浏览器由于“不安全响应”而拒绝连接,以及不信任网站的HTTP / SSL证书。

但是这个问题有办法解决。

我想出了一个解决scheme来确定浏览器何时不信任HTTP / SSL证书,首先要检测是否发生了XHR错误(例如,使用jQuery error()callback),然后检查XHR调用是否然后检查XHR readyState是否为0,这意味着XHR连接甚至没有打开(当浏览器不喜欢证书时会发生这种情况)。

这里是我这样做的代码: https : //github.com/maratbn/RainbowPayPress/blob/e9e9472a36ced747a0f9e5ca9fa7d96959aeaf8a/rainbowpaypress/js/le_requirejs/public/model_info__transaction_details.js#L88

我不认为现在有办法检测这些错误消息,但是你可以做的一个黑客就是在你的应用程序服务器前面使用像nginx这样的服务器,这样如果应用程序服务器closures,你会得到一个坏的网关错误从Nginx的502状态码,你可以在JS中检测。 否则,如果证书是无效的,你仍然会得到与statusCode = 0相同的通用错误。