0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Retrofitをproxyに対応させる

Posted at

Retrofitをproxyに対応させるには

kotlinでサーバサイドアプリを作っていて、そこからインターネット上のサイトにretrofitでデータを取りに行く必要がありました。
在宅で仕事していると、最近の光回線はproxyなしのダイレクトなので、proxyのことは、全く考慮にはなく、out of 眼中でした。
社内のproxyの中にあるネットワークに持って行くと・・・あれ?繋がらない?

Windowsの環境変数も試してみましたが、retrofit、okHttp3は環境変数は見てくれないようで、独自に対応が必要です。

proxyを設定する。

retrofitでproxyに対応させるには、okHttp3のクライアントでproxyに対応させる必要があります。

val proxyHost = "192.168.10.1"
val proxyPort = 8080
val logging = HttpLoggingInterceptor()
logging.setLevel(HttpLoggingInterceptor.Level.BODY)
val client = OkHttpClient.Builder()
    .addInterceptor(logging)
    .proxy(Proxy(Proxy.Type.HTTP, InetSocketAddress(proxyHost, proxyPort))))
    .build()
val moshi = Moshi.Builder()
    .add(KotlinJsonAdapterFactory())
    .build()
return Retrofit.Builder()
    .baseUrl("http://localhost/")
    .client(client)
    .addConverterFactory(MoshiConverterFactory.create(moshi))
    .build()
    .create(MyInterface::class.java)

ミソはこの部分です。

    .proxy(Proxy(Proxy.Type.HTTP, InetSocketAddress(proxyHost, proxyPort))))

これでめでたく、proxyを通して外のインターネットサイトと通信できます。

あれ?ローカルなLAN内も、proxyに飛んじゃう?

ところが、これでめでたしめでたしとはなりませんでした。
同一LAN内のマシンにもretrofitでアクセスが必要で、それも含めて全部がproxyに向かってしまうようになってしまいます。

Windowsのproxyの設定とかでよくある、proxy除外ホストのリストですね。これはどうやって実現したらいいのでしょうか?

java.net.ProxySelector を使う

ProxySelectorを使ってretrofitでアクセスするサイトによって、proxyを通すのか、ダイレクトなのかを判定してやります。ProxySelectorを継承してクラスを1個作ります。

class CustomProxySelector(
    private val httpProxyHost: String,
    private val httpProxyPort: Int,
    private val noProxy: String) : ProxySelector() {

    private val logger = LoggerFactory.getLogger(CustomProxySelector::class.java)

    override fun select(uri: URI?): MutableList<Proxy> {
        val noProxyList = noProxy.split(",").toList()
        logger.debug("uri.host = ${uri?.host}")
        logger.debug("noProxyList = {}", noProxyList)
        val proxyList = mutableListOf<Proxy>()
        if (httpProxyHost.isBlank()) {
            // proxy未設定
            logger.debug("proxy not set")
            proxyList.add(Proxy.NO_PROXY)
        } else if (uri != null && noProxyList.contains(uri.host)) {
            // proxy除外ホスト
            logger.debug("no proxy: ${uri.host}")
            proxyList.add(Proxy.NO_PROXY)
        } else {
            // proxyを通してアクセス
            logger.debug("to proxy: ${uri?.host}")
            proxyList.add(Proxy(Proxy.Type.HTTP, InetSocketAddress(httpProxyHost, httpProxyPort)))
        }
        return proxyList
    }

    override fun connectFailed(uri: URI?, sa: SocketAddress?, ioe: IOException?) {
        throw UnsupportedOperationException("Not support, yet")
    }
}

このクラスのコンストラクタの引数は3つを想定しています。

  • proxyのhost(String)
  • proxyのポート(Int)
  • proxy除外ホスト、カンマ区切りで複数(String)

このクラスの中の処理は

  • proxyのhostが未設定の場合は全部、ダイレクトです。
  • アクセスするURIのホストがproxy除外ホストの中にあれば、ダイレクト
  • アクセスするURIのホストがproxy除外ホストの中になけば、proxyアクセス

となります。これをopkHttp3のクライアントに渡してやります

val proxyHost = "192.168.10.1"
val proxyPort = 8080
val noProxy = "localhost"

val logging = HttpLoggingInterceptor()
logging.setLevel(HttpLoggingInterceptor.Level.BODY)
val client = OkHttpClient.Builder()
    .addInterceptor(logging)
    .proxySelector(CustomProxySelector(proxyHost, proxyPort, noProxy))
    .build()
val moshi = Moshi.Builder()
    .add(KotlinJsonAdapterFactory())
    .build()
return Retrofit.Builder()
    .baseUrl("http://localhost/")
    .client(client)
    .addConverterFactory(MoshiConverterFactory.create(moshi))
    .build()
    .create(MyInterface::class.java)

ミソはこの部分です。

    .proxySelector(CustomProxySelector(proxyHost, proxyPort, noProxy))

これで同一LAN内のホスト、proxyを通してインターネット上のホスト、両方にアクセスできます。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?