Определить, если ajax вызов не удался из-за небезопасного ответа или соединения отказано



я делал много исследований, и не мог найти способ, чтобы справиться с этим. Я пытаюсь выполнить вызов jQuery ajax с сервера https на сервер locahost https, на котором работает причал с пользовательским самозаверяющим сертификатом. Моя проблема заключается в том, что я не могу определить, является ли ответ отказом от соединения или небезопасным ответом (Из-за отсутствия принятия сертификата). Есть ли способ определить разницу между обоими сценариями? Элемент responseText и statusCode всегда одинаковы в обоих случаях, хотя в консоли chrome я вижу разницу:



net::ERR_INSECURE_RESPONSE
net::ERR_CONNECTION_REFUSED


responseText - это всегда "" и statusCode всегда " 0 " для обоих случаев.



мой вопрос в том, как я могу определить, если вызов jQuery ajax не удался из-за ERR_INSECURE_RESPONSE или из-за ERR_CONNECTION_REFUSED?



как только сертификат принят, все работает нормально, но я хочу знать, выключен ли сервер localhost, или он работает, но сертификат еще не был общепринятый.



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


enter image description here



даже выполняя вручную запрос, я получаю тот же результат:



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

6 ответов:

нет никакого способа отличить его от новейших веб-браузеров.

спецификация W3C:

шаги ниже описывают, что должны делать агенты пользователей для простого запроса кросс-происхождения:

примените шаги сделать запрос и соблюдайте правила запроса ниже, делая запрос.

если флаг перенаправления вручную не установлен и ответ имеет код состояния HTTP 301, 302, 303, 307 или 308 Примените шаги перенаправления.

если пользователь отменяет запрос Применить меры отменить.

если есть сетевая ошибка В случае ошибок DNS, сбоя согласования TLS или других сетевых ошибок примените сетевые шаги ошибки. Не запрашивайте никакого взаимодействия с конечным пользователем.

Примечание: это не включает HTTP-ответы, которые указывают на некоторые тип ошибки, например код состояния HTTP 410.

иначе Выполните проверку общего доступа к ресурсам. Если он возвращает fail, примените шаги сетевой ошибки. В противном случае, если он возвращает pass, завершите этот алгоритм и установите состояние запроса перекрестного источника в success. Фактически не завершайте запрос.

как вы можете прочитать, сетевые ошибки не включают HTTP-ответ, который включает ошибки, поэтому вы всегда получите 0 как код состояния, и "" как ошибка.

источник


Примечание: следующие примеры были сделаны с использованием Google Chrome версии 43.0.2357.130 и против среды, которую я создал для эмуляции OP one. Код для установки его находится в нижней части ответа.


Я думал, что подход к работе вокруг этого будет сделать вторичный запрос по HTTP вместо HTTPS как ответ но я вспомнил, что это невозможно из-за того, что новые версии браузеров блокируют смешанный контент.

это означает, что веб-браузер не будет разрешать запрос по HTTP, если вы используете HTTPS и наоборот.

это было так с тех пор, как несколько лет назад, но старые версии веб-браузера, такие как Mozilla Firefox ниже его версии 23 позволяют это.

доказательства об этом:

выполнение HTTP-запроса от HTTPS usign 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://localhost:8000/ 'был загружен по HTTPS, но запросил небезопасную конечную точку XMLHttpRequest'http://localhost:8001/'. Этот запрос был заблокирован; содержимое должно обслуживаться по протоколу HTTPS.

та же ошибка появится в консоли браузера, если вы попытаетесь сделать это другими способами, как добавление Iframe.

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

использование сокета также было опубликовано в качестве ответа, я был уверен, что результат будет таким же / похожим, но я дал ему попробовать.

попытка открыть соединение сокета с веб-браузером с помощью HTTPS на небезопасную конечную точку сокета приведет к ошибкам смешанного содержимого.

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

1) смешанный контент: страница в 'https://localhost:8000/' был загружен через HTTPS, но попытался для подключения к небезопасной конечной точке WebSocket ' ws:/ / localhost: 8001/'. Этот запрос был заблокирован; эта конечная точка должна быть доступна через WSS.

2) Uncaught DOMException: не удалось построить "WebSocket": небезопасное соединение WebSocket не может быть инициировано со страницы, загруженной через HTTPS.

затем я попытался подключиться к конечной точке wss, чтобы узнать, могу ли я прочитать некоторую информацию об ошибках сетевого подключения:

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

выполнение фрагмент выше с отключенным сервером приводит к:

подключение WebSocket к 'wss: / / localhost:8001 /' не удалось: ошибка при установлении соединения: net:: ERR_CONNECTION_REFUSED

выполнение сниппета выше с включенным сервером

WebSocket соединение с 'wss://localhost:8001/' не удалось: WebSocket открытие рукопожатия было отменено

но опять же, ошибка, которую" функция onerror " выводит на консоль не имейте никаких подсказок, чтобы отличить одну ошибку от другой.


через прокси, как этот ответ предлагаю может работать, но только если "целевой" сервер имеет открытый доступ.

здесь этого не было, поэтому попытка реализовать прокси в этом сценарии приведет нас к той же проблеме.

код для создания узла.JS HTTPS server:

Я создал два сервера 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 соответственно.

, чтобы запустить его просто выполнить node applicationServer.js и node targetServer.js в терминале (пример ubuntu).

сейчас: нет никакого способа отличить это событие от брауэров. Поскольку браузеры не предоставляют разработчикам доступ к событию. (июль 2015 года)

этот ответ просто стремится предоставить идеи для потенциального, albiet hacky и неполного решения.


отказ от ответственности: этот ответ является неполным, так как не полностью решить проблемы OP (из-за политики кросс-происхождения). Однако сама идея имеет некоторые заслуги, которые далее расширяются: @artur grzesiak здесь, используя прокси и ajax.


после довольно большого количества исследований, кажется, нет никакой формы проверки ошибок на разницу между отказом от соединения и небезопасным ответом, по крайней мере, в том, что касается javascript, предоставляющего ответ на разницу между ними.

общий консенсус моего исследования заключается в том, что SSL-сертификаты обрабатываются браузер, поэтому до тех пор, пока пользователь не примет самозаверяющий сертификат, браузер блокирует все запросы, включая запросы на код состояния. Браузер может (если закодирован) отправить обратно свой собственный код состояния для небезопасного ответа, но это действительно ничего не помогает, и даже тогда у вас будут проблемы с совместимостью браузера (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();

конечно, это требует дополнительного запроса для проверки подключения к серверу, но он должен получить работу, выполненную процессом устранения. Тем не менее, я все еще чувствую себя хакером, и я не большой поклонник удвоенного запроса.

ответ@Schultzie довольно близок, но явно http - В общем - не получится из https в среде браузера.

что вы можете сделать, это использовать промежуточный сервер (Прокси), чтобы сделать запрос от вашего имени. Прокси должен позволить либо переслать http запрос от https origin или загрузить содержимое из самоподписанный происхождение.

наличие собственного сервера с надлежащим сертификатом, вероятно, является излишним в вашем случае -- как вы могли бы использовать эту настройку вместо машины с самозаверяющим сертификатом - но есть много анонимные открытые прокси-сервисов там.

Итак, два подхода, которые приходят мне на ум:

  1. ajax запрос -- в таком случае прокси должен использовать соответствующие настройки CORS
  2. использование iframe -- вы загружаете свой скрипт (вероятно, завернутый в html) внутри iframe через прокси. Однажды загруженный скрипт отправляет сообщение своему .parentWindow. Если ваше окно получило сообщение, вы можете быть уверены, что сервер работает (или точнее был запущен за долю секунды до этого).

если вас интересует только ваша локальная среда, Вы можете попробовать запустить chrome с --disable-web-security флаг.


еще одно предложение: вы пытались загрузить изображение программно, чтобы узнать, если больше информации присутствует там?

проверить jQuery.ajaxError() Взяты ссылки из : обработка ошибок jQuery AJAX (коды состояния HTTP) Он ловит глобальные ошибки Ajax, которые вы можете обрабатывать любым количеством способов через HTTP или HTTPS:

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

к сожалению, современный браузер XHR API не предоставляет явного указания на то, когда браузер отказывается подключаться из-за "небезопасного ответа", а также когда он не доверяет сертификату HTTP/SSL веб-сайта.

но есть способы обойти эту проблему.

одно из решений, которое я придумал, чтобы определить, когда браузер не доверяет сертификату HTTP / SSL, - это сначала определить, произошла ли ошибка XHR (используя jQuery error() обратный звонок для экземпляр), затем проверьте, является ли вызов XHR URL-адресом " https://", а затем проверьте, является ли XHR readyState равно 0, что означает, что соединение XHR даже не было открыто (что происходит, когда браузеру не нравится сертификат).

вот код, где я это делаю: https://github.com/maratbn/RainbowPayPress/blob/e9e9472a36ced747a0f9e5ca9fa7d96959aeaf8a/rainbowpaypress/js/le_requirejs/public/model_info__transaction_details.js#L88

Я не думаю, что в настоящее время есть способ обнаружить эти сообщения об ошибках, но хак, который вы можете сделать, это использовать сервер, такой как nginx, перед вашим сервером приложений, так что если сервер приложений не работает, вы получите плохую ошибку шлюза от nginx с 502 код состояния, который вы можете обнаружить в JS. В противном случае, если сертификат недействителен, вы все равно получите ту же общую ошибку с statusCode = 0.

Comments

    Ничего не найдено.