0時に寝て6時に起きた。金曜日は非稼働日だけど、今週は月曜日が祝日だったから普通に働いてた。

spring-webflux とプロキシ

たまたま api client 周りを触っている。それらは spring の WebClient で実装されている。WebClient は spring-webflux プロジェクトが提供している http リクエストを扱うためのクライアントでリアクティブプログラミングを用いた設計になっている。リアクティブという言葉がピンとこなければ非同期フレームワークを用いた http クライアントと言い換えても大枠ではあっているのではないかと思う。spring-webflux プロジェクトそのものはノンブロッキング、バックプレッシャーといった機能をサポートする web アプリケーションフレームワークを提供するもの。

Reactor というコアライブラリを使って spring-webflux のフレームワークは実装されている。このデータ構造の1つに MonoFlux が出てくる。初見の開発者はこの名前のデータ構造がよくわからんというところから始まる。私がそうだった。ドキュメントの説明によると、Mono は0から1、Flux は0からNまでのデータ列の概念を扱うという。おそらく json のようなレスポンスを返す場合は Mono を使い、ストリームを返すレスポンスは Flux を使えばいいんじゃないかと思う。

Reactor is the reactive library of choice for Spring WebFlux. It provides the Mono and Flux API types to work on data sequences of 0..1 (Mono) and 0..N (Flux) through a rich set of operators aligned with the ReactiveX vocabulary of operators. Reactor is a Reactive Streams library and, therefore, all of its operators support non-blocking back pressure. Reactor has a strong focus on server-side Java. It is developed in close collaboration with Spring.

WebFlux requires Reactor as a core dependency but it is interoperable with other reactive libraries via Reactive Streams. As a general rule, a WebFlux API accepts a plain Publisher as input, adapts it to a Reactor type internally, uses that, and returns either a Flux or a Mono as output. So, you can pass any Publisher as input and you can apply operations on the output, but you need to adapt the output for use with another reactive library. Whenever feasible (for example, annotated controllers), WebFlux adapts transparently to the use of RxJava or another reactive library. See Reactive Libraries for more details.

https://docs.spring.io/spring-framework/docs/current/reference/html/web-reactive.html#webflux-reactive-api

いまローカルの開発環境では vpn 接続をしてプロキシ経由でアクセスするサーバーがいる。WebClient のプロキシ経由で通信できないという問題があることを同僚から教えてもらった。プロキシ経由でアクセスしようとすると認可エラーになってしまう。なにかしらプロキシ経由の接続に問題がある。

Caused by: io.netty.handler.proxy.HttpProxyHandler$HttpProxyConnectException: http, none, /10.100.101.10:8080 => /192.168.201.35:18980, status: 403 Forbidden

同僚は WebClient のデフォルトでは http の CONNECT メソッドを使って通信しようとするが、それを squid がサポートしていないか、設定を変更しないとダメなんじゃないかと話していた。その内容が正しいかどうか、私は未検証だけどデフォルト設定では通信できないことがわかった。ここで WebClient の設定の1つに ClientHttpConnector があり、任意の http client ライブラリに置き換えられる。ソースをみると次の4つの ClientHttpConnector が使えるらしい。デフォルトが ReactorClientHttpConnector になる。

  private ClientHttpConnector initConnector() {
    if (reactorClientPresent) {
      return new ReactorClientHttpConnector();
    }
    else if (jettyClientPresent) {
      return new JettyClientHttpConnector();
    }
    else if (httpComponentsClientPresent) {
      return new HttpComponentsClientHttpConnector();
    }
    else {
      return new JdkClientHttpConnector();
    }
  }

試しに jetty-reactive-httpclient を使って JettyClientHttpConnector に置き換えてみたところ、プロキシサーバー経由のアクセスができるようになった。

public WebClient create(String proxyIp, int proxyPort) {
    var httpClient = new HttpClient();
    httpClient.setFollowRedirects(true);
    httpClient.getProxyConfiguration().getProxies().add(new HttpProxy(proxyIp, proxyPort));
    var connector = new JettyClientHttpConnector(httpClient);
    return WebClient.builder().clientConnector(connector).build();
}