[Java] Introduction to the JDK HTTP Client

原文はこちら。
https://blogs.oracle.com/java/jdk-http-client

Chris Hegartyによるエントリです。

JDK HTTP ClientがJDK 9に追加されました。これを使ってHTTPリソースをリクエストできます。
  • HTTP/1.1、HTTP/2のサポート
  • 同期/非同期プログラミングモデルの両方をサポート
  • reactive-streamsとしてリクエスト及びレスポンスを処理
  • ビルダーパターンの利用
reactive-streams
http://reactive-streams.org/
以下は文字列としてレスポンスボディを出力する基本的なリクエストの例です。
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
                                 .uri(URI.create("http://openjdk.java.net/"))
                                 .build();
client.sendAsync(request, asString())
      .thenApply(HttpResponse::body)
      .thenAccept(System.out::println)
      .join();

Incubation

APIは現在、Incubatingの状態にあり、モジュールとパッケージ名に反映されています。これは、APIを育てて、将来のバージョンのJava SEで標準に取り込むことを目的としています。インキュベーションに関する詳細はJEP 11にありますが、現時点では、パッケージ名を認識し、そのパッケージに含まれているモジュールjdk.incubator.httpclientをコマンドラインオプション--add-modulesで指定するとAPIにアクセスできると知っていれば十分です。
JEP 11: Incubator Modules
http://openjdk.java.net/jeps/11
以下のようにMavenコンパイラとsurefireプラグインを設定できます。
<build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.7.0</version>
                <configuration>
                    <source>9</source>
                    <target>9</target>
                    <compilerArgument>--add-modules=jdk.incubator.httpclient</compilerArgument>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>2.20.1</version>
                <configuration>
                    <argLine>--add-modules=jdk.incubator.httpclient</argLine>
                </configuration>
            </plugin>
        </plugins>
    </build>

HttpClient

リクエストを送信する場合、まずビルダーでHttpClientを作成します。ビルダーを使ってクライアントの状態毎に以下のように構成します。
  • 所望のプロトコルバージョン(HTTP/1.1 もしくは HTTP/2)
  • リダイレクトに従うか否かの指定
  • プロキシの設定
  • authenticatorの設定
HttpClient client = HttpClient.newBuilder()
                              .version(Version.HTTP_2)
                              .followRedirects(Redirect.SAME_PROTOCOL)
                              .proxy(ProxySelector.of(new InetSocketAddress("www-proxy.com", 8080)))
                              .authenticator(Authenticator.getDefault())
                              .build();
ビルダーを使ってHttpClientを作成後、HttpClientを使って複数のリクエストを送信できます。

HttpRequest

HttpRequestはビルダーを使って作成します。リクエストビルダーを使って以下の構成を設定できます。
  • リクエストURI
  • リクエストメソッド(GET、PUT、POST)
  • リクエスト本体(必要であれば)
  • タイムアウト
  • リクエスト・ヘッダー
HttpRequest request = HttpRequest.newBuilder()
                                 .uri(URI.create("http://openjdk.java.net/"))
                                 .timeout(Duration.ofMinutes(1))
                                 .header("Content-Type", "application/json")
                                 .POST(BodyPublisher.fromFile(Paths.get("file.json")))
                                 .build()
ビルダーを使って作成したHttpRequestはイミュータブルなので、複数回送信できます。

Synchronous or Asynchronous

リクエストを同期・非同期のいずれの方法でも送信できます。同期APIは想定通りHttpResponseが利用可能になるまでブロックします。
HttpResponse<String> response = client.send(request, BodyHandler.asString());
System.out.println(response.statusCode());
System.out.println(response.body());
非同期APIは、使用可能になったときにHttpResponseで完了するCompletableFutureですぐに戻ります。CompletableFutureはJava 8で追加され、構成可能な非同期プログラミングをサポートしています。
client.sendAsync(request, BodyHandler.asString())
      .thenApply(response -> { 
                                 System.out.println(response.statusCode());
                                 return response;
                             } )
      .thenApply(HttpResponse::body)
      .thenAccept(System.out::println);

Data as reactive-streams

リクエストおよびレスポンス本体は、reactive stream(ノンブロッキングのバックプレッシャーを持つ非同期データストリーム)として利用できます。HttpClientは、事実上、リクエスト本体のサブスクライバであり、レスポンス本体のパブリッシャです。BodyHandlerインターフェイスを使って、実際のレスポンス本体を受信する前にレスポンスコードとヘッダーを検査し、レスポンスBodySubscriberを作成する必要があります。
public abstract class HttpRequest {
        ...
    public interface BodyPublisher
                    extends Flow.Publisher<ByteBuffer> { ... }
}

public abstract class HttpResponse<T> {
        ...
    public interface BodyHandler<T> {
            BodySubscriber<T> apply(int statusCode, HttpHeaders responseHeaders);
    }

    public interface BodySubscriber<T>
                    extends Flow.Subscriber<List<ByteBuffer>> { ... }
} 
HttpRequestとHttpResponseでは、リクエスト・パブリッシャとレスポンス・サブスクライバを作成するための便利なファクトリメソッドを多数提供しており、ファイル、文字列、バイトといった、よく使われる本体のデータtタイプを処理できます。これらの便利な実装では、Stringのように高水準のJavaタイプを作成できるようになるまでデータを蓄積するか、ファイルの場合はデータをストリーミングします。BodySubscriberとBodyPublisherインターフェースを実装し、カスタムのリアクティブストリームとしてデータを処理することも可能です。
HttpRequest.BodyPublisher::fromByteArray(byte[])
HttpRequest.BodyPublisher::fromByteArrays(Iterable)
HttpRequest.BodyPublisher::fromFile(Path)
HttpRequest.BodyPublisher::fromString(String)
HttpRequest.BodyPublisher::fromInputStream(Supplier< InputStream>)

HttpResponse.BodyHandler::asByteArray()
HttpResponse.BodyHandler::asString()
HttpResponse.BodyHandler::asFile(Path)
HttpResponse.BodyHandler::discard()
java.util.concurrent.FlowのPublisher/SubscriberタイプとHTTP ClientのBodyPublisher/BodySubscriberタイプとの間のAdapterがJDK 10で追加されました。詳細はJDK-8193366を参照してください。
Improve interoperability between HTTP Client's BodyPublisher/BodySubscriber and Flow.Subscriber/Publisher
https://bugs.openjdk.java.net/browse/JDK-8193366

HTTP/2

JDK HTTP Clientは、HTTP/1.1とHTTP/2の両方をサポートしています。デフォルトでは、クライアントはHTTP/2を使用してリクエストを送信します。まだHTTP/2をサポートしていないサーバーに送信されたリクエストは自動的にHTTP/1.1にダウングレードされます。HTTP/2がもたらす主な改善点の概要は以下の通りです。

  • ヘッダー圧縮:HTTP/2ではHPACK圧縮を使用しているため、オーバーヘッドが減少します。
  • サーバーへの1個の接続:複数のTCP接続をセットアップするために必要なラウンドトリップが減ります。
  • 多重化:同一接続で同時に複数リクエストを送信できます。
  • サーバープッシュ:必要なりソースをクライアントに送信可能な追加機能
  • バイナリフォーマット:テキスト形式に比べてメッセージサイズが小さくなります

HTTP/2はデフォルトの優先プロトコルであり、必要に応じてシームレスにHTTP/1.1にフォールバックします。将来HTTP/2がより広く普及すれば、JDK HTTP Clientはよい状況になることでしょう。

The Future

incubation状態のJDK HTTP Clientは、コミュニティからのフィードバックに基づいてJDK 10で更新されました。便利なリクエスト本体のパブリッシャおよびレスポンス本体のサブスクライバが追加されました。実装は完全に非同期に書き直されました。
Java SEでHTTP Clientを標準化するためにJEP 321を作成しました。
JEP 321: HTTP Client (Standard)
http://openjdk.java.net/jeps/321
これはつまり、jdk.incubatorというモジュールとパッケージの接頭辞を削除して、java.net名前空間の何かに変わるということです。--add-modulesという追加コマンドラインオプションは不要になり、モジュールはデフォルトで利用可能になる予定です。

References

0 件のコメント:

コメントを投稿