Feignに認証ありのProxyを設定する方法

## FeignのProxy対応（認証あり）
インターネットに接続する際、認証が必要な`proxy`を経由しなければならない会社等もあるかと思います。
そのような環境でAPIクライアントである`Feign`を利用して、インターネット上に公開されているAPIを呼び出す際の`proxy`の設定方法について説明したいと思います。

`Feign`はHTTPリクエストを発行する処理を別のライブラリにデリゲートしています。
つまり、利用するライブラリの`proxy`対応を行えばよいということです。

今回は`Feign`の[GitHubページで説明されているGitHubの指定したリポジトリのコントリビュータを取得するサンプル](https://github.com/OpenFeign/feign#basics)を`proxy`対応に変更してみます。

なお、`Feign`の説明ついては「[インターフェースだけでAPIクライアントを実装するFeignがすごく便利！](https://qiita.com/5zm/items/f7a48e80ad5ba4316748)」に書いていますのでそちらを参照ください。

```java:GitHubDemo.java
package com.example.feign.demo.github;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.util.List;
import java.util.concurrent.TimeUnit;

import feign.Feign;
import feign.jackson.JacksonDecoder;
import okhttp3.Authenticator;
import okhttp3.Credentials;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.Route;

/**
 * modify Feign example(https://github.com/OpenFeign/feign) to corresponded proxy
 */
public class GitHubDemo {

    public static void main(String[] args) {

        // create instance of okhttp3.OkHttpClient corresponded proxy 
        OkHttpClient client = createOkHttpClientCorrespondedProxy("127.0.0.1",
                8080, "userId", "pass");

        // feign use proxy with authentication
        GitHub github = Feign.builder()
                // set instance of feign.Client.OkHttpClient
                .client(new feign.okhttp.OkHttpClient(client))
                // use Jackson
                .decoder(new JacksonDecoder())
                .target(GitHub.class, "https://api.github.com");

        // call api [GET /repos/{owner}/{repo}/contributors]
        List<Contributor> contributors = github.contributors("OpenFeign",
                "feign");
        for (Contributor contributor : contributors) {
            System.out.println(
                    contributor.login + " (" + contributor.contributions + ")");
        }
    }

    /**
     * create instance of okhttp3.OkHttpClient corresponded proxy
     * @param proxyHost proxy's host url or ipAddress
     * @param proxyPort proxy's port number
     * @param userId userId of proxy's authentication
     * @param pass pass of proxy's authentication
     * @return instance of okhttp3.OkHttpClient
     */
    private static OkHttpClient createOkHttpClientCorrespondedProxy(
            String proxyHost, int proxyPort, String userId, String pass) {

        // okhttp3.Authenticator : correct
        // java.net.Authenticator : incorrect
        Authenticator proxyAuthenticator = new Authenticator() {
            @Override
            public Request authenticate(Route route,
                    Response response) throws IOException {
                String credential = Credentials.basic(userId, pass);
                return response.request().newBuilder()
                        .header("Proxy-Authorization", credential).build();
            }
        };

        // okhttp3.OkHttpClient : correct
        // feign.Client.OkHttpClient : incorrect
        return new OkHttpClient.Builder().connectTimeout(30, TimeUnit.SECONDS)
                .writeTimeout(30, TimeUnit.SECONDS)
                .readTimeout(30, TimeUnit.SECONDS)
                .proxy(new Proxy(Proxy.Type.HTTP,
                        new InetSocketAddress(proxyHost, proxyPort)))
                .proxyAuthenticator(proxyAuthenticator).build();
    }
}
```

注意すべきポイントを以下に示します。

* `feign.okhttp.OkHttpClient`のインスタンスを生成する際、`okhttp3.OkHttpClient`を引数にとるコンストラクタを利用する
* `okhttp3.OkHttpClient`のインスタンスを生成する際、`proxy`に対応するようにコンフィグレーションする
* 同じクラス名のものがあるので`import`する際は注意すること
 * `okhttp3.Authenticator`が正しく、`java.net.Authenticator`は誤り
 * `okhttp3.OkHttpClient`が正しく、`feign.Client.OkHttpClient`は誤り（proxy対応のokhttpのインスタンスを生成する）

（参考）
https://stackoverflow.com/questions/35554380/okhttpclient-proxy-authentication-how-to