确定 ajax 调用是否由于不安全响应或连接被拒绝而失败 [英] Determine if ajax call failed due to insecure response or connection refused

查看:45
本文介绍了确定 ajax 调用是否由于不安全响应或连接被拒绝而失败的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我一直在进行大量研究,但找不到解决此问题的方法.我正在尝试从 https 服务器到使用自定义自签名证书运行 jetty 的 locahost https 服务器执行 jQuery ajax 调用.我的问题是我无法确定响应是连接被拒绝还是不安全响应(由于缺乏证书接受).有没有办法确定两种情况之间的区别?responseTextstatusCode 在这两种情况下总是相同的,即使在 Chrome 控制台中我可以看到不同:

net::ERR_INSECURE_RESPONSEnet::ERR_CONNECTION_REFUSED

responseText 总是 "" 并且 statusCode 在这两种情况下总是 "0".

我的问题是,如何确定 jQuery ajax 调用是由于 ERR_INSECURE_RESPONSE 还是由于 ERR_CONNECTION_REFUSED 而失败?

一旦证书被接受,一切正常,但我想知道 localhost 服务器是否已关闭,或者它已启动并正在运行但证书尚未被接受.

$.ajax({类型:'获取',url: "https://localhost/custom/server/",数据类型:json",异步:真,成功:功能(响应){//做一点事},错误:函数(xhr,textStatus,errorThrown){console.log(xhr, textStatus, errorThrown);//对于拒绝和不安全的响应总是相同的.}});

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

var request = new XMLHttpRequest();request.open('GET', "https://localhost/custom/server/", true);request.onload = 函数(){控制台日志(请求.响应文本);};request.onerror = 函数 () {控制台日志(请求.响应文本);};request.send();

解决方案

无法将其与最新的 Web 浏览器区分开来.

W3C 规范:

<块引用>

以下步骤描述了用户代理必须为一个简单的跨域请求执行的操作:

应用发出请求步骤并在发出请求时遵守以下请求规则.

如果手动重定向标志未设置且响应的 HTTP 状态代码为 301、302、303、307 或 308应用重定向步骤.

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

如果出现网络错误如果出现 DNS 错误、TLS 协商失败或其他类型的网络错误,请应用 网络错误步骤.不要请求任何类型的最终用户交互.

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

否则执行资源共享检查.如果返回失败,请应用网络错误步骤.否则,如果返回pass,则终止该算法并将跨域请求状态设置为成功.不要实际终止请求.

如您所见,网络错误不包括包含错误的 HTTP 响应,这就是为什么您将始终获得 0 作为状态代码,而"作为错误.

来源

<小时>

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

<小时>

我认为解决这个问题的方法是通过 HTTP 而不是 HTTPS 作为这个答案但我记得这是不可能的,因为较新版本的浏览器会阻止混合内容.

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

几年前就一直是这样,但旧的 Web 浏览器版本(例如低于 23 版的 Mozilla Firefox)允许这样做.

关于它的证据:

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

var request = new XMLHttpRequest();request.open('GET', "http://localhost:8001", true);request.onload = 函数(){控制台日志(请求.响应文本);};request.onerror = 函数 () {控制台日志(请求.响应文本);};request.send();

将导致以下错误:

<块引用>

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

如果您尝试以添加 Iframe 的其他方式执行此操作,则浏览器控制台中将出现相同的错误.

<小时>

使用套接字连接也是 作为答案发布,我很确定结果会相同/相似但我已经试过了.

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

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

<块引用>

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

2) 未捕获的 DOMException:无法构建WebSocket":可能无法从通过 HTTPS 加载的页面启动不安全的 WebSocket 连接.

然后我也尝试连接到 wss 端点,看看是否可以阅读有关网络连接错误的一些信息:

var exampleSocket = new WebSocket("wss://localhost:8001", "protocolOne");exampleSocket.onerror = function(e) {控制台日志(e);}

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

<块引用>

WebSocket 连接到 'wss://localhost:8001/' 失败:连接建立错误:net::ERR_CONNECTION_REFUSED

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

<块引用>

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

但同样,onerror 函数"输出到控制台的错误没有任何提示来区分另一个错误.

<小时>

使用代理作为 这个答案建议 可以工作,但前提是目标"服务器有公共访问.

这里不是这种情况,所以在这种情况下尝试实现代理会导致我们遇到同样的问题.

用于创建 Node.js HTTPS 服务器的代码:

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

目标服务器.js:

var https = require('https');var fs = require('fs');变量选项 = {密钥:fs.readFileSync('./certs2/key.pem'),证书: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("你好世界
");}).听(8001);

applicationServer.js:

var https = require('https');var fs = require('fs');变量选项 = {密钥:fs.readFileSync('./certs/key.pem'),证书:fs.readFileSync('./certs/key-cert.pem')};https.createServer(options, function (req, res) {res.writeHead(200);res.end("你好世界
");}).听(8000);

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

要运行它,只需在终端(ubuntu 示例)中执行 node applicationServer.jsnode targetServer.js.

I've been doing a lot of research and could not find a way to handle this. I'm trying to perform a jQuery ajax call from an https server to a locahost https server running jetty with a custom self signed certificate. My problem is that I cannot determine whether the response is a connection refused or a insecure response (due to the lack of the certificate acceptance). Is there a way to determine the difference between both scenarios? The responseText, and statusCode are always the same in both cases, even though in the chrome console I can see a difference:

net::ERR_INSECURE_RESPONSE
net::ERR_CONNECTION_REFUSED

responseText is always "" and statusCode is always "0" for both cases.

My question is, how can I determine if a jQuery ajax call failed due to ERR_INSECURE_RESPONSE or due to ERR_CONNECTION_REFUSED?

Once the certificate is accepted everything works fine, but I want to know whether the localhost server is shut down, or its up and running but the certificate has not yet been accepted.

$.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.
    }
});

Even performing manually the request I get the same result:

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();

解决方案

There is no way to differentiate it from newest Web Browsers.

W3C Specification:

The steps below describe what user agents must do for a simple cross-origin request:

Apply the make a request steps and observe the request rules below while making the request.

If the manual redirect flag is unset 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 abort steps.

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

Note: This does not include HTTP responses that indicate some type of error, such as HTTP status code 410.

Otherwise Perform a resource sharing check. If it returns fail, apply the network error steps. Otherwise, if it returns pass, terminate this algorithm and set the cross-origin request status to success. Do not actually terminate the request.

As you can read, network errors does not include HTTP response that include errors, that is why you will get always 0 as status code, and "" as error.

Source


Note: The following examples were made using Google Chrome Version 43.0.2357.130 and against an environment that I've created to emulate OP one. Code to the set it up is at the bottom of the answer.


I though that an approach To work around this would be make a secondary request over HTTP instead of HTTPS as This answer but I've remembered that is not possible due that newer versions of browsers block mixed content.

That means that the Web Browser will not allow a request over HTTP if you are using HTTPS and vice versa.

This has been like this since few years ago but older Web Browser versions like Mozilla Firefox below it versions 23 allow it.

Evidence about it:

Making a HTTP request from HTTPS usign Web Broser console

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();

will result in the following error:

Mixed Content: The page at 'https://localhost:8000/' was loaded over HTTPS, but requested an insecure XMLHttpRequest endpoint 'http://localhost:8001/'. This request has been blocked; the content must be served over HTTPS.

Same error will appear in the browser console if you try to do this in other ways as adding an Iframe.

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


Using Socket connection was also Posted as an answer, I was pretty sure that the result will be the same / similar but I've give it a try.

Trying to Open a socket connection from the Web Broswer using HTTPS to a non Secure socket endpoint will end in mixed content errors.

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

1) Mixed Content: The page at 'https://localhost:8000/' was loaded over HTTPS, but attempted to connect to the insecure WebSocket endpoint 'ws://localhost:8001/'. This request has been blocked; this endpoint must be available over WSS.

2) Uncaught DOMException: Failed to construct 'WebSocket': An insecure WebSocket connection may not be initiated from a page loaded over HTTPS.

Then I've tried to connect to a wss endpoint too see If I could read some information about network connection errors:

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

Executing snippet above with Server turned off results in:

WebSocket connection to 'wss://localhost:8001/' failed: Error in connection establishment: net::ERR_CONNECTION_REFUSED

Executing snippet above with Server turned On

WebSocket connection to 'wss://localhost:8001/' failed: WebSocket opening handshake was canceled

But again, the error that the "onerror function" output to the console have not any tip to differentiate one error of the other.


Using a proxy as this answer suggest could work but only if the "target" server has public access.

This was not the case here, so trying to implement a proxy in this scenario will lead Us to the same problem.

Code to create Node.js HTTPS server:

I've created two Nodejs HTTPS servers, that use self signed certificates:

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
");
}).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
");
}).listen(8000);

To make it work you need to have Nodejs Installed, Need to generate separated certificates for each server and store it in the folders certs and certs2 accordingly.

To Run it just execute node applicationServer.js and node targetServer.js in a terminal (ubuntu example).

这篇关于确定 ajax 调用是否由于不安全响应或连接被拒绝而失败的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆