JettyのHttpClient機能、その1

2019年5月15日

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