2016-03-24 5 views
1

Ich habe meine eigene Implementierung eines Webservers, der openssl verwendet.SSL_write meldet Erfolg aber schlägt in Safari fehl

Ich verwende die blockierende Version von SSL_write, um Daten und die neueste Safari auf OSX zu übertragen.

Meine Logik ist wie folgt:

num_bytes = 0; 
while(num_bytes < bytes_to_send) { 
    int n = SSL_write(...); 
    if (n <= 0) { 
    ... // Handle error, break loop 
    } 
    num_bytes += n; 
} 
// Close the socket 

Es ist ein seltenes Problem, das mir begegnet ist, wo Safari Failed to load resource: The network connection was lost.

berichtet Wenn ich die Ressource in dem Entwicklerbereich unter Safari überprüfen, zeigt es, dass es weniger Bytes als erwartet empfangen.

Ich habe meinen Code debugged und festgestellt SSL_write meldet, dass alle Bytes übertragen wurden, bevor ich die Verbindung schließe.

Wenn ich die Verbindung nicht schließe, tritt dieser Fehler nicht mehr auf.

Dies führt mich zu dem Schluss:

SSL_write meldet fälschlicherweise eine Anzahl von Bytes erfolgreich übertragen wurden, bevor sie tatsächlich stattgefunden hat, und dass die Verbindung geschlossen wird unmittelbar nach schneidet die Übertragung kurz.

Bin ich richtig, oder gibt es ein Detail über SSL_write, dass ich vermisse?

Update:

Nach der Art und Weise der Prüfung ich verarbeiten SSL_shutdown, ich ein paar SSL_ERROR_SYSCALL Fehler sehe berichtet. Dies kann etwas mit meinem Problem zu tun haben oder auch nicht, da diese Fehler nicht direkt nach den betroffenen HTTP-Übertragungen zurückgegeben werden. Seltsamerweise werden während der betroffenen HTTP-Übertragungen keine anderen Fehler als SSL_shutdown als der Wiederholungsfehler zum Blockieren von Anrufen gemeldet.

Shutdown-Code

// First shutdown SSL 
SSL_CTX_free(ctx); 
if (SSL_shutdown(ssl) == 0) { 
    // Call SSL Shutdown again 
    SSL_shutdown(ssl); 
} 
SSL_free(ssl); 

// Now shutdown port 
struct linger linger; 
linger.l_onoff = 1; 
linger.l_linger = 10; 
setsockopt(socket, SOL_SOCKET, SO_LINGER, (char *)&linger, 
      sizeof(linger)); 

shutdown(socket, SHUT_WR); 
while(recv(...) > 0) { } 
closesocket(socket); 

Update # 2

I wireshark mit ssl Entschlüsselung, den Fehler zu erfassen einzurichten. Hier ist, wie es aussieht: Safari Error Message: PatientSearch.js Wireshark Capture: See PatientSearch.js Successfully Transferred

Beachten Sie die erste grüne Linie, die für PatientSearch.js die HTTP-Anforderung von Safari zeigt. Jetzt zeigt die zweite grüne Zeile die HTTP-Antwort von meinem Webserver an. Diese Antwort enthält die Datei in ihrer Gesamtheit, gefolgt vom Trennungsprotokoll.

Update # 3

Dies wird durch die zweite mysteriöse bekommen. Nach dem Untersuchen einer Wireshark-Ablaufverfolgung für eine erfolgreiche Übertragung und es gibt keinen Unterschied in dem Protokollmuster in der erfolgreichen Übertragung gegenüber der unterbrochenen Übertragung. Völlig verwirrt jetzt.

+0

Vorausgesetzt, dass es funktioniert, wenn Sie die Verbindung offen halten, meine Vermutung ist, dass der Fehler tatsächlich woanders ist, d.h. dass Sie die Anfrage vom Client nicht richtig lesen, bevor Sie den Socket schließen. Siehe http://stackoverflow.com/a/33947985/3081018. –

+0

@SteffenUllrich Nachdem ich Ihren Kommentar gelesen hatte, ging ich zurück und überprüfte meinen Header-Verarbeitungscode und bestätigte, dass die gesamte Anfrage tatsächlich aus dem Socket abgerufen wurde. Ich habe den gesamten HTTP-Header gelesen/gedruckt, die Daten mit SSL_write an den Client zurückgegeben und dann den Socket geschlossen. Safari ist nach wie vor unterbrochen. – hofan41

+0

können Sie Ihren Shutdown-Code posten? Sowohl für SSL als auch für den zugrunde liegenden Socket? –

Antwort

2

Ihre Sendeschleife verfolgt nicht korrekt die Anzahl der gesendeten Bytes.Es sollte mehr so ​​aussehen statt:

unsigned char *data = ...; 
while (bytes_to_send > 0) { 
    int num_sent = SSL_write(ssl, data, bytes_to_send); 
    if (num_sent <= 0) { 
    // handle error as needed... 
    break; 
    } 
    data += num_sent; 
    bytes_to_send -= num_sent; 
} 
// Close the socket 

Oder diese:

unsigned char *data = ...; 
int num_total_sent = 0; 
while (num_total_sent < bytes_to_send) { 
    int num_sent = SSL_write(ssl, &data[num_total_sent], bytes_to_send - num_total_sent); 
    if (num_sent <= 0) { 
    // handle error as needed... 
    break; 
    } 
    num_total_sent += num_sent; 
} 
// Close the socket 

Update:

SSL_write meldet fälschlicherweise eine Anzahl von Bytes erfolgreich übertragen wurden, bevor es tatsächlich aufgetreten

Der Rückgabewert gibt die Anzahl der Bytes an, die er von Ihnen akzeptiert hat. Das garantiert nicht, dass diese Bytes tatsächlich tatsächlich übertragen wurden. Sie werden verschlüsselt und verwendet, um einen ausgehenden SSL-Frame zu erstellen, der im Ausgangspuffer des zugrunde liegenden Sockets sitzt und darauf wartet, dass der Kernel ihn im Hintergrund überträgt. Diese Übertragung kann einige Zeit dauern, besonders wenn der Nagle-Algorithmus für den Socket aktiviert ist (was normalerweise der Fall ist).

Schließen der Verbindung sofort nach dem Kürzung der Übertragung kurz.

das möglich ist, je nachdem, wie Sie die Verbindung schließen, wie die linger Option des Sockets konfiguriert ist, usw. Standardmäßig sollte eine Buchse schließen anmutig der Kernel lassen einige zusätzliche Momente im Hintergrund nehmen Schreiben Sie alle ausstehenden Daten aus, bevor sie die Verbindung tatsächlich schließen. In der Regel müssen Sie zusätzliche Maßnahmen ergreifen, um dieses Verhalten zu ändern.

+0

Entschuldigung dafür, meinen Pseudocode nicht korrekt zu posten. Ich verwende tatsächlich eine Schleife, die den von Ihnen geposteten ähnlich ist. Ich habe meine Frage aktualisiert, um meine Schleife genauer widerzuspiegeln. – hofan41

Verwandte Themen