JettyのHttpClient機能、その1
jettyについての全投稿は/tag/jettyにあるので参照されたい
HttpClient機能というのは、あらゆるライブラリがあるのだが、jetty-allライブラリを入れると、jetty-clientというライブラリも入ることに気がついた。これを使ってみたのだが、非常に簡単に使えることがわかった。
マニュアル
公式マニュアルは以下にある。
※これは9.4.x用とのことなので、バージョン更新でURLが変わるかもしれない。
HttpClientの作成と開始・停止
org.eclipse.jetty.client.HttpClientというクラスがあり、これは「ブラウザのように」使えるとのことだ。つまり、このオブジェクトを一つ作れば、あちこちのURLにアクセスすることができる。
import org.eclipse.jetty.client.*;
import org.eclipse.jetty.client.api.*;
....
// HttpClientのインスタンスを作る。ただし、
HttpClient httpClient = new HttpClient();
// 後述するが、以下にした方がよい。
HttpClient httpClient = new HttpClient(new SslContextFactory());
// HttpClientを設定する。例えば、
httpClient.setFollowRedirects(false);
// HttpClientを開始する
httpClient.start();
開始すると、内部で専用スレッドが作られるらしい。stop()をしないと、このスレッドが動作し続けることになる。アプリを停止するときには、stop()せよとのことだ。
httpClient.stop();
GETリクエストとレスポンス
単純なGETリクエストとそのレスポンスを得る方法は以下だ。
Request request = httpClient.newRequest("http:/something.com/anything");
request.cookie(new HttpCookie("sid", sid));
ContentResponse res = request.send();
int status = res.getStatus();
System.out.println("status:" + status);
if (status == 200) {
System.out.println(res.getContentAsString());
}
SSLへのアクセスの場合
https、SSLのアクセスの場合は以下にしなければならないという。
HttpClient httpClient = new HttpClient(new SslContextFactory());
これを忘れると以下のエラーになる。ひどいことに平気でNullPointerExceptionを出している。もう何とかならなかったのだろうか?
ちなみに、「new HttpClient(new SslContextFactory())」のままでもhttpへのアクセスは問題無いようだ。始めからこれをデフォルトにしてくれれば良いものを。。。。
java.util.concurrent.ExecutionException: java.lang.NullPointerException
at org.eclipse.jetty.client.util.FutureResponseListener.getResult(FutureResponseListener.java:118)
at org.eclipse.jetty.client.util.FutureResponseListener.get(FutureResponseListener.java:101)
at org.eclipse.jetty.client.HttpRequest.send(HttpRequest.java:653)
............................
Caused by: java.lang.NullPointerException
at org.eclipse.jetty.io.ssl.SslClientConnectionFactory.newConnection(SslClientConnectionFactory.java:56)
at org.eclipse.jetty.client.AbstractHttpClientTransport$ClientSelectorManager.newConnection(AbstractHttpClientTransport.java:170)
at org.eclipse.jetty.io.SelectorManager$ManagedSelector.createEndPoint(SelectorManager.java:745)
at org.eclipse.jetty.io.SelectorManager$ManagedSelector.processConnect(SelectorManager.java:681)
at org.eclipse.jetty.io.SelectorManager$ManagedSelector.processKey(SelectorManager.java:644)
at org.eclipse.jetty.io.SelectorManager$ManagedSelector.select(SelectorManager.java:611)
at org.eclipse.jetty.io.SelectorManager$ManagedSelector.run(SelectorManager.java:549)
at org.eclipse.jetty.util.thread.NonBlockingThread.run(NonBlockingThread.java:52)
at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:635)
at org.eclipse.jetty.util.thread.QueuedThreadPool$3.run(QueuedThreadPool.java:555)
at java.lang.Thread.run(Thread.java:745)
大きなファイルをダウンロードする場合
前述したのは、リクエストもレスポンスも比較的小さい場合なのだが、レスポンスが大きい場合はどうすれば良いだろうか?
HttpClient httpClient = new HttpClient(new SslContextFactory());
httpClient.start();
try {
InputStreamResponseListener listener = new InputStreamResponseListener();
httpClient.newRequest("https://foobar.com/download").send(listener);
Response response = listener.get(5, TimeUnit.SECONDS);
if (response.getStatus() != HttpStatus.OK_200) {
throw new RuntimeException("Could not getOK);
}
try (InputStream in = listener.getInputStream(); OutputStream out = new FileOutputStream(outputFile)) {
// inからoutへコピー
}
} finally {
httpClient.stop();
}