HttpClient – Advanced

In der letzten Folge der Java Perlen habe wir die Grundlagen des HttpClient APIs angeschaut. In dieser Folge möchten wird uns die Konfiguration und einige Advanced Features des HttpClients im Detail anschauen.

1. Vorarbeiten

Zur Veranschaulichung bzw. Verifizierung der Anpassungen der Konfiguration machen wir zwei Vorarbeiten:
• einen Echo Service suchen
• und einen lokalen Proxy installieren

Damit wir keine lokale (Http)Server Installation machen müssen, verwenden wir einen Echo Service. Einen solchen Service kann man mit einer URL anrufen und bekommt als Antwort genau das zurück, was man zum Service gesendet hat. Wir verwenden in den Beispielen den Service von http://ivanzuzak.info/urlecho. Diesen kann man mit der URL aufrufen.

Zusätzlich installieren wir einen lokalen Proxy. Damit kann man dann genau prüfen, wie ein HttpRequest (oder auch die Response) aussieht. Wir verwenden den Proxy von https://mitmproxy.org. Dieser lässt sich für Mac/Win/Linux einfach und schnell installieren. Starten kann man den Proxy indem man in der Shell “mitmweb” aufruft. Standardmässig hört der Proxy dann auf Port 8080.

$ mitmweb
Web server listening at
Proxy server listening at http://*:8080

Es wird automatisch ein Browser Fenster geöffnet, in dem wir den Http “Traffic” im Detail anschauen können.

2. Nutzung des lokalen Proxy

Damit der lokale Proxy mit dem HttpClient API genutzt werden kann, muss man beim Aufruf den HttpClient für den Proxy konfigurieren. Wir lagern das in eine Hilfsmethode aus.

Mittels proxy() kann ein Proxy gesetzt werden. Mit der Factory Method ProxySelector.of() können wir die Parameter für unseren lokalen Proxy setzen.

private void processRequestUsingProxy(HttpRequest request) throws Exception {
  HttpResponse response = HttpClient.newBuilder() 
      .proxy(ProxySelector.of(new InetSocketAddress("localhost", 8080))) 
      .build() 
      .send(request, HttpResponse.BodyHandler.asString());

  System.out.println(ResponseHelper.toString(response));
}

In den weiteren Beispielen können wir die HttpRequest Objekte der Methode zur Ausführung übergeben und dann Request und Response im Browser überprüfen.

3. Konfiguration HttpRequest

Der HttpRequest ist nach dem Builder Pattern implementiert. Im Builder hat es verschiedene Methoden um den Request zu konfigurieren.

Support für Headers

Mit der Methode headers() können Header Parameter als Key Value Pairs angegeben werden.

HttpRequest request = HttpRequest.newBuilder() //
    .uri(new URI(pageUrl)) //
    .headers("key1", "value1", "key2", "value2") //
    .GET() //
    .build();

processRequestUsingProxy(request);

Die Ausgabe im Proxy zeigt, dass wir den Server mit zusätzlichen Header Parametern aufgerufen haben.

Support für Parameter

Erstaunlicherweise gibt es keinen direkten Support für die Anreicherung einer URL mit Parametern, wie z.B. http://www.myurl.ch?a=b. Sicherlich kann man das manuell machen… man muss sich dann aber um Dinge wie eine saubere Codierung von Sonderzeichen etc. selbst kümmern.

Es gibt aber die Möglichkeit auf Libraries von Drittanbietern zurückzugreifen. Das folgende Beispiel verwendet den URIBuilder von der Apache HttpComponenents Library. Zu finden ist diese unter https://hc.apache.org. Statt eine URI manuell mit Parametern zu erstellen, nutzen wir den URIBuilder, der mittels build() eine URI erstellt, die wir direkt im HttpRequest verwenden können.

URIBuilder uriWithParameters = new URIBuilder(pageUrl);
uriWithParameters.addParameter("t", "search");
uriWithParameters.addParameter("q", "apples");

HttpRequest request = HttpRequest.newBuilder()
    .uri(uriWithParameters.build()) 
    .GET() 
    .build();

processRequestUsingProxy(request);

Wie man in der Proxy Ausgabe sieht, haben wir die URL mit Parametern t und q erstellt und eine Request an den Server gemacht.

Das Zusammenspiel mit der Apache Library funktioniert gut, man hat aber eine Abhängigkeit zu einer weiteren Library.

Anmerkung: Die Einbindung der HttpComponenents Library hat einen Stolperstein. Mit dem HttpClient sind wir in der neuen Welt der Java Module. Leider ist aber HttpComponenents selbst noch nicht modularisiert. Wir brauchen also einen Weg eine “Legacy” Library in unser Modul einzubinden. Und das geht ziemlich leicht, indem man die Library als “Automatic Module” einbindet. Dazu passen wir den Modul Descriptor folgendermassen an:

module puzzle.blog.module.http {
	exports ch.puzzle.http2;
	requires jdk.incubator.httpclient;
	requires httpclient; // automatic module
}

Support für POST

Um von GET auf POST zu wechseln, kann man im HttpRequest einfach POST() verwenden. POST() braucht als Parameter einen BodyProcessor. Mittels diesem können wir die zu übermittelnden Daten angeben. Da wir einen String übergeben möchten, verwenden wir die BodyProcessor.fromString() Methode, die schon standardmässig implementiert ist.

String data = "Lorem ipsum dolor sit amet ....";
HttpRequest request = HttpRequest.newBuilder() 
    .uri(new URI(pageUrl))
    .POST(HttpRequest.BodyProcessor.fromString(data))
    .build();

processRequestUsingProxy(request);

Die Daten des POST Requests sind im Proxy zu sehen.

Support für HTTP 2

Der HttpClient bietet direkt support für HTTP 2 an. Um von HTTP 1.1 auf 2 zu wechseln, muss nur die Version im HttpRequest angepasst werden.

HttpRequest request = HttpRequest.newBuilder() 
    .uri(new URI(pageUrl)) 
    .version(HttpClient.Version.HTTP_2) 
    .GET() 
    .build();

processRequestUsingProxy(request);

4. Konfiguration HttpRequest

Neben dem HttpRequest kann auch der HttpClient konfiguriert werden. Wir haben bereits gesehen, wie man den HttpClient konfigurieren kann, damit er einen lokalen Proxy verwenden kann.

Support für URL Redirection

Häufig wird man beim Aufruf einer Webseite automatisch auf eine andere Seite weitergeleitet. Ein Beispiel dafür ist die URL von Sun. Da Sun von Oracle gekauft wurde, wird man beim Aufruf von http://www.sun.com in einem Browser direkt auf eine Seite von Oracle weitergeleitet. Diese Verhalten wird auch vom HttpClient API unterstützt.

Wenn wir einen Request machen und die Response loggen, erhalten wir folgende Information:

Url : http://www.sun.com
Status : 301
Version: HTTP_1_1
Body :
Headers: „connection“:“[close]“
„content-length“:“[0]“
„location“:“[http://www.oracle.com/us/sun/index.htm]“

d.h. statt dem Inhalt der Webseite erhalten wir einen Status von 301 (oder auch 302) und die neue URL.

Mittels der Methode followRedirects() kann man die URL Redirection einschalten. followRedirects() nimmt einen Parameter vom Typ Redirect. Gültige Werte sind:
• NEVER: keine Redirection
• ALWAYS: immer Redirection
• SAME_PROTOCOL: Redirection von http auf http oder https auf https
• SECURE: keine Redirection von https auf http

String pageUrl = “http://www.sun.com"”;
HttpRequest request = 
    HttpRequest.newBuilder().uri(new URI(pageUrl)).GET().build();

HttpResponse response = HttpClient.newBuilder() 
    .followRedirects(HttpClient.Redirect.SAME_PROTOCOL) 
    .build() 
    .send(request, HttpResponse.BodyHandler.asString());

System.out.println(ResponseHelper.toString(response));

String pageUrl = “http://www.sun.com“”;
HttpRequest request =
HttpRequest.newBuilder().uri(new URI(pageUrl)).GET().build();

HttpResponse response = HttpClient.newBuilder()
.followRedirects(HttpClient.Redirect.SAME_PROTOCOL)
.build()
.send(request, HttpResponse.BodyHandler.asString());

System.out.println(ResponseHelper.toString(response));

Support für Cookies

Das HttpClient API unterstützt auch Cookies. Als Setup erstellen wir einen CookieManager, setzen eine Policy und registrieren diesen CookieManager cookieManager = new CookieManager();
cookieManager.setCookiePolicy(CookiePolicy.ACCEPT_ALL);
CookieHandler.setDefault(cookieManager);

Wenn man die Cookies auflisten möchte, kann man sich über den CookieManager den CookieStore holen und mittels diesem über alle HttpCookies iterieren.

CookieStore cookieStore = cookieManager.getCookieStore();
for (HttpCookie cookie : cookieStore.getCookies()) {  
  System.out.println(toString(cookie));
}

Nur so am Rande: Wenn wir noch keine Requests gemacht haben, ist der CookieStore leer und es werden keine Cookies aufgelistet werden.

Als Beispiel machen wir einen GET Request auf www.google.com. Wichtig ist dabei, dass wir bei jedem Request den CookieManager mitgeben.

HttpClient.newBuilder()
    .cookieManager(cookieManager) 
    .build()
    .send(request, HttpResponse.BodyHandler.asString());

Wenn wir einen lokalen Proxy dazwischen schalten sehen wir, dass wir einen simplen GET Request gemacht haben.

Beim Iterieren über den CookieStore können wir das “google” Cookie sehen.

cookies for http://www.google.com:
domain : .google.com
max age : 15811199
name : NID
server path : /
is secure : false
value : 133=A2igKfCKZ53wcND1_..

Wenn wir den gleichen GET Request nochmals zu google machen sehen wir im Proxy, dass das google Cookie dem neuen Request mitgegeben wird.

Das Ganze funktioniert genau wie erwartet.

Fazit: In dieser Folge der Java Perlen habe wir uns angeschaut, wie HttpClient API erlaubt, den HttpRequest und den HttpClient zu konfigurieren und somit eine Vielzahl von Anwendungen ermöglicht. Wer sich für das Thema interessiert, sollte mal einen Blick in die Javadoc werfen. Denn es gibt noch weitere Dinge, die man konfigurieren kann.

Referenzen:
• Echo Service: http://ivanzuzak.info/urlecho
• Proxy: https://mitmproxy.org
• baeldung.com –
• javadevjournal: https://www.javadevjournal.com/java/java-9-http-client
• Source Code: https://github.com/puzzle/java9_blog/tree/master/ch/puzzle/http

Kommentare sind geschlossen.