Determina se la chiamata ajax è fallita a causa di una risposta non sicura o di una connessione rifiutata

Ho fatto molte ricerche e non sono riuscito a trovare un modo per gestirlo. Sto cercando di eseguire una chiamata jQuery ajax da un server https a un server https locahost che esegue un molo con un certificato autofirmato personalizzato. Il mio problema è che non riesco a determinare se la risposta è una connessione rifiutata o una risposta non sicura (a causa della mancanza dell’accettazione del certificato). C’è un modo per determinare la differenza tra entrambi gli scenari? responseText e statusCode sono sempre gli stessi in entrambi i casi, anche se nella console di Chrome posso notare una differenza:

 net::ERR_INSECURE_RESPONSE net::ERR_CONNECTION_REFUSED 

responseText è sempre “” e statusCode è sempre “0” per entrambi i casi.

La mia domanda è, come posso determinare se una chiamata jQuery ajax non è riuscita a causa di ERR_INSECURE_RESPONSE o a causa di ERR_CONNECTION_REFUSED ?

Una volta accettato il certificato, tutto funziona correttamente, ma voglio sapere se il server localhost è spento o se è attivo e funzionante, ma il certificato non è ancora stato accettato.

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

inserisci la descrizione dell'immagine qui

Anche eseguendo manualmente la richiesta ottengo lo stesso risultato:

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

Non c’è modo di differenziarlo dai nuovi Web Browser.

Specifica W3C:

I passaggi seguenti descrivono cosa devono fare gli interpreti per una semplice richiesta di cross-origine :

Applicare la procedura di richiesta e osservare le regole di richiesta di seguito mentre si effettua la richiesta.

Se il flag di reindirizzamento manuale non è impostato e la risposta ha un codice di stato HTTP 301, 302, 303, 307 o 308, applicare i passaggi di reindirizzamento.

Se l’utente finale annulla la richiesta, applica i passaggi di interruzione.

Se si verifica un errore di rete In caso di errori DNS, errore di negoziazione TLS o altri tipi di errori di rete, applicare i passaggi di errore di rete . Non richiedere alcun tipo di interazione con l’utente finale.

Nota: questo non include le risposte HTTP che indicano un qualche tipo di errore, come il codice di stato HTTP 410.

Altrimenti eseguire un controllo di condivisione delle risorse. Se restituisce un errore, applica i passaggi di errore di rete. Altrimenti, se restituisce il passaggio, termina questo algoritmo e imposta lo stato della richiesta di origine incrociata su successo. In realtà non terminare la richiesta.

Come puoi leggere, gli errori di rete non includono la risposta HTTP che include errori, ecco perché otterrai sempre 0 come codice di stato e “” come errore.

fonte


Nota : i seguenti esempi sono stati realizzati utilizzando Google Chrome versione 43.0.2357.130 e un ambiente che ho creato per emulare l’OP uno. Il codice per l’impostazione è in fondo alla risposta.


Ho pensato che un approccio Per aggirare questo sarebbe fare una richiesta secondaria su HTTP invece di HTTPS come questa risposta, ma ho ricordato che non è ansible a causa del fatto che le versioni più recenti di browser bloccano il contenuto misto.

Ciò significa che il browser Web non consente una richiesta tramite HTTP se si utilizza HTTPS e viceversa.

Questo è stato così da pochi anni, ma le versioni precedenti del browser Web come Mozilla Firefox al di sotto delle versioni 23 lo consentono.

Prova a riguardo:

Esecuzione di una richiesta HTTP da 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(); 

comporterà il seguente errore:

Contenuto misto: la pagina di ” https: // localhost: 8000 / ” è stata caricata su HTTPS, ma ha richiesto un endpoint XMLHttpRequest non sicuro ” http: // localhost: 8001 / “. Questa richiesta è stata bloccata; il contenuto deve essere pubblicato su HTTPS.

Lo stesso errore verrà visualizzato nella console del browser se si tenta di farlo in altri modi aggiungendo un Iframe.

  

Anche l’uso della connessione Socket è stato postato come risposta , ero piuttosto sicuro che il risultato sarebbe stato lo stesso / simile ma ho provato.

Provare ad aprire una connessione socket da Web Broswer utilizzando HTTPS a un endpoint socket non sicuro terminerà con errori di contenuto misto.

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

1) Contenuto misto: la pagina di ” https: // localhost: 8000 / ” è stata caricata su HTTPS, ma ha tentato di connettersi all’endpoint WebSocket non sicuro “ws: // localhost: 8001 /”. Questa richiesta è stata bloccata; questo endpoint deve essere disponibile su WSS.

2) DOMException non rilevata: Imansible creare ‘WebSocket’: una connessione WebSocket non sicura potrebbe non essere avviata da una pagina caricata su HTTPS.

Poi ho provato a connettermi a un endpoint wss anche vedere Se potessi leggere alcune informazioni sugli errori di connessione di rete:

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

L’esecuzione dello snippet sopra con Server distriggersto ha come risultato:

Connessione WebSocket a “wss: // localhost: 8001 /” non riuscita: errore nella creazione della connessione: net :: ERR_CONNECTION_REFUSED

Esecuzione dello snippet precedente con il Server triggersto

Connessione WebSocket a “wss: // localhost: 8001 /” non riuscita: l’handshake di apertura di WebSocket è stato annullato

Ma ancora, l’errore che l’uscita “onerror function” alla console non ha alcun suggerimento per differenziare un errore dell’altro.


L’utilizzo di un proxy come suggerito da questa risposta potrebbe funzionare ma solo se il server “target” ha accesso pubblico.

Questo non era il caso, quindi provare a implementare un proxy in questo scenario ci porterà allo stesso problema.

Codice per creare il server HTTPS Node.js :

Ho creato due server HTTPS Nodejs, che utilizzano certificati autofirmati:

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

Per farlo funzionare è necessario avere Nodejs installato, necessario generare certificati separati per ogni server e memorizzarlo nelle cartelle certs e certs2 di conseguenza.

Per eseguirlo basta eseguire il node applicationServer.js e node targetServer.js in un terminale (esempio di ubuntu).

A partire da ora: non c’è modo di differenziare questo evento tra i brower. Poiché i browser non forniscono un evento a cui gli sviluppatori possono accedere. (Luglio 2015)

Questa risposta cerca semplicemente di fornire idee per una potenziale soluzione albiet e incompleta.


Disclaimer: questa risposta è incompleta in quanto non risolve completamente i problemi dell’OP (a causa di politiche di origine incrociata). Tuttavia l’idea in sé ha qualche merito che è ulteriormente ampliato da: @artur grzesiak qui , usando un proxy e ajax.


Dopo un po ‘di ricerche, non sembra esserci alcuna forma di controllo degli errori per la differenza tra la connessione rifiutata e una risposta insicura, almeno per quanto javascript che fornisce una risposta per la differenza tra i due.

Il consenso generale della mia ricerca è che i certificati SSL sono gestiti dal browser, quindi finché un certificato autofirmato non viene accettato dall’utente, il browser blocca tutte le richieste, incluse quelle per un codice di stato. Il browser potrebbe (se codificato per) restituire il proprio codice di stato per una risposta non sicura, ma ciò non aiuta veramente nulla, e anche in quel caso, avresti problemi con la compatibilità del browser (chrome / firefox / IE con standard diversi. .. di nuovo)

Dal momento che la tua domanda iniziale era quella di verificare lo stato del tuo server tra l’alto e l’avere un certificato non accettato, non potresti fare una richiesta HTTP standard in questo modo?

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

A condizione che ciò richieda una richiesta aggiuntiva per verificare la connettività del server, ma dovrebbe portare a termine il processo tramite il processo di eliminazione. Resta comunque l’hacky e non sono un grande fan della richiesta raddoppiata.

@ La risposta di Schultzie è abbastanza vicina, ma chiaramente http – in generale – non funzionerà da https nell’ambiente del browser.

Quello che puoi fare è usare un server intermedio (proxy) per fare la richiesta a tuo nome. Il proxy dovrebbe consentire di inoltrare la richiesta http dall’origine https o di caricare il contenuto da origini autofirmate .

Avere il proprio server con un certificato adeguato è probabilmente un eccesso nel tuo caso, dato che potresti usare questa impostazione al posto della macchina con certificato autofirmato, ma c’è un sacco di servizi anonimi di proxy aperto .

Quindi, due approcci che mi vengono in mente sono:

  1. richiesta ajax : in questo caso il proxy deve utilizzare le impostazioni CORS appropriate
  2. uso di un iframe – si carica lo script (probabilmente racchiuso in html) all’interno di un iframe tramite il proxy. Una volta caricato lo script, invia un messaggio alla sua .parentWindow . Se la tua finestra ha ricevuto un messaggio puoi essere sicuro che il server è in esecuzione (o più precisamente stava eseguendo una frazione di secondo prima).

Se sei interessato solo al tuo ambiente locale puoi provare a eseguire chrome con il flag --disable-web-security .


Un altro suggerimento: hai provato a caricare un’immagine in modo programmatico per scoprire se sono presenti più informazioni?

Controlla jQuery.ajaxError () Preso riferimento da: jQuery AJAX Gestione degli errori (codici di stato HTTP) Rileva gli errori globali Ajax che puoi gestire in un numero qualsiasi di modi su HTTP o HTTPS:

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

Sfortunatamente l’API XHR del browser attuale non fornisce un’indicazione esplicita per quando il browser rifiuta di connettersi a causa di una “risposta non sicura” e anche quando non si fida del certificato HTTP / SSL del sito web.

Ma ci sono modi per aggirare questo problema.

Una soluzione che ho individuato per determinare quando il browser non si fida del certificato HTTP / SSL, è innanzitutto quello di rilevare se si è verificato un errore XHR (utilizzando la callback jQuery error() per esempio), quindi verificare se la chiamata XHR è quella un URL ‘https: //’, quindi controlla se XHR readyState è 0, il che significa che la connessione XHR non è stata nemmeno aperta (che è ciò che accade quando il browser non apprezza il certificato).

Ecco il codice in cui faccio questo: https://github.com/maratbn/RainbowPayPress/blob/e9e9472a36ced747a0f9e5ca9fa7d96959aeaf8a/rainbowpaypress/js/le_requirejs/public/model_info__transaction_details.js#L88

Non penso che al momento ci sia un modo per rilevare questi messaggi di errore, ma un trucco che puoi fare è usare un server come nginx davanti al tuo server delle applicazioni, così che se il server delle applicazioni è inattivo ne riceverai una brutta errore del gateway da nginx con codice di stato 502 che è ansible rilevare in JS. Altrimenti, se il certificato non è valido, otterrai lo stesso errore generico con statusCode = 0 .