it-swarm.com.de

HttpsUrlConnection und Keep-Alive

Ich verwende com.Sun.net.httpserver.HttpsServer in meinem aktuellen Projekt, das sich mit der Client-Authentifizierung usw. befasst. Derzeit werden nur die Adresse/der Port des Clients ausgedruckt, sodass ich prüfen kann, ob eine TCP-Verbindung für mehrere Anforderungen verwendet wird (keep-alive) oder ob eine neue Verbindung besteht Für jede Anfrage wird eine Verbindung hergestellt (und daher wird jedes Mal ein neuer SSL-Handshake erstellt). Wenn ich mit FireFox mehrere Anforderungen an den Server stelle, kann ich feststellen, dass Keep-Alive funktioniert. Der Server-Teil funktioniert also gut mit GET- und POST-Anfragen.

Wenn ich HttpURLConnection verwende, um eine Anfrage an den Server zu richten (in diesem Fall mit no SSL), funktioniert keep-alive auch: Für mehrere sequenziell gestartete Anforderungen wird nur eine Verbindung hergestellt.

Wenn ich jedoch HttpsURLConnection verwende (mit exakt demselben Code, aber using SSL), funktioniert keep-alive nicht mehr. Für jede Anfrage wird also eine neue Verbindung hergestellt, obwohl ich dieselbe SSLContext (und SSLSocketFactory) verwende:

// URL myUrl = ...
// SSLContext mySsl = ...
HttpsURLConnection conn = (HttpsURLConnection) myUrl.openConnection();
conn.setUseCaches(false);
conn.setSSLSocketFactory(mySsl.getSocketFactory());

conn.setRequestMethod("POST");
// send Data
// receive Data

Wie erzwinge ich, dass HttpsURLConnectionkeep-alive verwendet, da viele Anfragen zu vielen SSL-Handshakes führen, was ein echtes Leistungsproblem darstellt?

Update (2012-04-02): Statt jedes Mal mySsl.getSocketFactory() aufzurufen, habe ich versucht, die SSLSocketFactory zwischenzuspeichern. Aber nichts hat sich geändert. Das Problem besteht immer noch.

25
Biggie

Ich konnte es nicht mit HttpsUrlConnection zum Laufen bringen. Der HTTP-Client von Apache kann jedoch sehr gut mit SSL-Verbindungen umgehen.

5
Biggie

Ich bin auf genau dasselbe Problem gestoßen und habe nach ausführlichem Debugging endlich eine Lösung gefunden.

Http (s) UrlConnection verarbeitet standardmäßig Keep-Alive, aber Sockets müssen sich in einem bestimmten Zustand befinden, um wieder verwendet zu werden.

Diese sind:

  • Eingabeströme müssen vollständig verbraucht werden. Sie müssen read im Eingabestrom aufrufen, bis -1 zurückgegeben wird, und es auch schließen.
  • Die Einstellungen des zugrunde liegenden Sockets müssen exakt dieselben Objekte verwenden.
  • Wenn Sie damit fertig sind, sollten Sie die Verbindung zu Http (s) URLConnection (ja, dies ist kontraintuitiv) anrufen.

In dem obigen Code ist das Problem:

conn.setSSLSocketFactory(mySsl.getSocketFactory());

Wenn Sie das Ergebnis von getSocketFactory () während der Initialisierung in einer statischen Variablen speichern und dieses dann an conn.setSSLSocketFactory übergeben, sollte der Socket wieder verwendet werden können.

17
Bill Healey

Der Aufbau einer SSL-Verbindung ist sowohl für Serviceaufrufe als auch für das Abrufen vieler Ressourcen über einen Browser sehr teuer.

Java Http(s)UrlConnection behandelt standardmäßig HTTP (S) Keep Alive .

Ich habe den Quellcode der default SSLSocketFactory nicht gefunden und wahrscheinlich ist der Keep-Alive-Mechanismus dort implementiert. Deaktivieren Sie zur Bestätigung Ihre eigene SSLSocketFactory-Implementierung für einen Test mit einem benutzerdefinierten Truststore in javax.net.ssl.trustStore, damit Ihr selbstsigniertes Zertifikat akzeptiert wird.

Gemäß OpenJDK 7 ServerImpl - Implementierung, die ServerConfig verwendet, verwendet die HttpsServer ein Keep-Alive mit einem Timeout von 5 Minuten per Default.

Ich schlage vor, Sie setzen die Eigenschaft Sun.net.httpserver.debug auf true serverseitig, um Details zu erhalten.

Achten Sie darauf, dass Ihr Code nicht den Header Connection: close hinzufügt, wodurch der Keep-Alive-Mechanismus deaktiviert wird.

2
Yves Martin

Wir können einen Apache-Webserver einrichten und folgende Anweisungen hinzufügen, um festzustellen, ob das access.log des Apache eine Keep-Alive-Verbindung für den http-Client hat.

LogFormat "%k %v %h %l %u %t \"%r\" %>s %b" common
CustomLog "logs/access.log" common 

http://httpd.Apache.org/docs/current/mod/mod_log_config.html

"% k" Anzahl der Keepalive-Anforderungen, die für diese Verbindung bearbeitet werden. Interessant, wenn KeepAlive verwendet wird, so dass zum Beispiel eine '1' die erste Keepalive-Anforderung nach der ersten Anforderung bedeutet, '2' die zweite, usw. Andernfalls ist dies immer 0 (zeigt die ursprüngliche Anforderung an).

0
WENPIN

versuchen Sie den folgenden Code hinzuzufügen:

con.setRequestProperty("Connection", "Keep-Alive");
con.setRequestProperty("Keep-Alive", "header");
0
UVM

Soweit ich verstehen kann, ist HTTP/1.1 und HTTPS Protokoll, auch dokumentiert hier , Keep-Alive kein end-to-end - Header, sondern ein hop -to-hop -Kopfzeile. Da SSL für jede neue Verbindung mehrere Schritte des Handshakes zwischen verschiedenen Hops (z. B. CA und Server) umfasst, denke ich, dass Keep-Alive möglicherweise nicht im SSL-Kontext anwendbar ist. das kann sein, warumKeep-Alive-Header von HTTPS-Verbindungen ignoriert wird. Basierend auf dieser dieser Frage müssen Sie möglicherweise sicherstellen, dass one Instanz der HTTP-Verbindung verwendet wird, um Keep-Alive-Beobachtung zu gewährleisten. In der Frage scheint Apache HTTPClient eine bessere Lösung gewesen zu sein.

0
nobeh

Ich hatte das gleiche Problem und Bill Healey hat Recht ... Ich habe meinen Beispielcode unten mit ein paar https-Bibliotheken getestet fast dasselbe Verhalten ... Ich hoffe, das wird etwas helfen.

public class SampleActivity extends Activity implements OnClickListener {

    // Keep default context and factory
    private SSLContext mDefaultSslContext;
    private SSLSocketFactory mDefaultSslFactory;

    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        findViewById(R.id.button_id).setOnClickListener(this);

        try {
            // Initialize context and factory
            mDefaultSslContext = SSLContext.getInstance("TLS");
            mDefaultSslContext.init(null, null, null);
            mDefaultSslFactory = mDefaultSslContext.getSocketFactory();
        } catch (NoSuchAlgorithmException | KeyManagementException e) {
            Log.e(TAG, e.getMessage(), e);
        }

    }

    @Override
    public void onClick(View v){
        SSLContext sslcontext;
        SSLSocketFactory sslfactory;

        try {
            // If using this factory, enable Keep-Alive
            sslfactory = mDefaultSslFactory;

            // If using this factory, enable session resumption (abbreviated handshake)
            sslfactory = mDefaultSslContext.getSocketFactory();

            // If using this factory, enable full handshake each time
            sslcontext = SSLContext.getInstance("TLS");
            sslcontext.init(null, null, null);
            sslfactory = sslcontext.getSocketFactory();
        } catch (NoSuchAlgorithmException | KeyManagementException e) {
            Log.e(TAG, e.getMessage(), e);
        }

        URL url = new URL("https://example.com");
        HttpsURLConnection = conn = (HttpsURLConnection) url.openConnection();
        conn.setSSLSocketFactory(sslfactory);
        conn.connect();
    }
}

Aktualisieren:

Durch die gemeinsame Nutzung von SSLSocketFactory wird Keep Alive aktiviert. Wenn Sie SSLContext freigeben und jede Anforderung abrufen, wird die Sitzung wieder aufgenommen. Ich weiß nicht, wie der TLS-Stack funktioniert, aber ich habe gerade dieses Verbindungsverhalten mit einigen mobilen Geräten bestätigt. 

Wenn Sie Keep-Alive für mehrere Klassen aktivieren möchten, sollten Sie die Instanz von SSLSocketFactory mithilfe eines Singleton-Patterns gemeinsam nutzen. 

Wenn Sie die Wiederaufnahme der Sitzung aktivieren möchten, stellen Sie sicher, dass die Einstellungen für das Sitzungszeitlimit auf der Serverseite lang genug sind, beispielsweise SSLSessionCacheTimeout (Apache), ssl_session_timeout (nginx).

0
forte916