Create a gist now

Instantly share code, notes, and snippets.

What would you like to do?
This OkHttp application interceptor will replace the destination hostname in the request URL.
import java.io.IOException;
import okhttp3.HttpUrl;
import okhttp3.Interceptor;
import okhttp3.OkHttpClient;
import okhttp3.Request;
/** An interceptor that allows runtime changes to the URL hostname. */
public final class HostSelectionInterceptor implements Interceptor {
private volatile String host;
public void setHost(String host) {
this.host = host;
}
@Override public okhttp3.Response intercept(Chain chain) throws IOException {
Request request = chain.request();
String host = this.host;
if (host != null) {
HttpUrl newUrl = request.url().newBuilder()
.host(host)
.build();
request = request.newBuilder()
.url(newUrl)
.build();
}
return chain.proceed(request);
}
public static void main(String[] args) throws Exception {
HostSelectionInterceptor interceptor = new HostSelectionInterceptor();
OkHttpClient okHttpClient = new OkHttpClient.Builder()
.addInterceptor(interceptor)
.build();
Request request = new Request.Builder()
.url("http://www.coca-cola.com/robots.txt")
.build();
okhttp3.Call call1 = okHttpClient.newCall(request);
okhttp3.Response response1 = call1.execute();
System.out.println("RESPONSE FROM: " + response1.request().url());
System.out.println(response1.body().string());
interceptor.setHost("www.pepsi.com");
okhttp3.Call call2 = okHttpClient.newCall(request);
okhttp3.Response response2 = call2.execute();
System.out.println("RESPONSE FROM: " + response2.request().url());
System.out.println(response2.body().string());
}
}

Plastix commented Apr 18, 2016

Has the OkHttp 3 api changed? I'm currently getting the following error when trying to use this interceptor:

java.lang.IllegalArgumentException: unexpected host: http://localhost:63820/

    at okhttp3.HttpUrl$Builder.host(HttpUrl.java:754)
    at <mypackage>.HostSelectionInterceptor.intercept(HostSelectionInterceptor.java:26)
    at okhttp3.RealCall$ApplicationInterceptorChain.proceed(RealCall.java:187)
    at okhttp3.RealCall.getResponseWithInterceptorChain(RealCall.java:160)
    at okhttp3.RealCall.execute(RealCall.java:57)
    at retrofit2.OkHttpCall.execute(OkHttpCall.java:174)
    at retrofit2.adapter.rxjava.RxJavaCallAdapterFactory$CallOnSubscribe.call(RxJavaCallAdapterFactory.java:144)
    at retrofit2.adapter.rxjava.RxJavaCallAdapterFactory$CallOnSubscribe.call(RxJavaCallAdapterFactory.java:125)
    at rx.Observable$2.call(Observable.java:233)
    at rx.Observable$2.call(Observable.java:225)
    at rx.Observable$2.call(Observable.java:233)
    at rx.Observable$2.call(Observable.java:225)
    at rx.Observable$2.call(Observable.java:233)
    at rx.Observable$2.call(Observable.java:225)
    at rx.Observable$2.call(Observable.java:233)
    at rx.Observable$2.call(Observable.java:225)
    at rx.Observable.subscribe(Observable.java:8834)

It appears to me that OkHttp3's HttpUrl builder can't correctly handle parsing the port?

Edit: I fixed my problem by switching the interceptor to parse the URL like so:
HttpUrl newUrl = HttpUrl.parse(host)
@swankjesse is there benefit to using the HttpUrl.Builder as shown in your gist?

@Plastix I was getting this same error when the host string started with http:// or had a trailing slash.
Using the HttpUrl.Builder you can change only the host of the URL while keeping the rest of your request URL.

mdzht commented Jun 6, 2016

I've got a strange behavior. scheme changes from https to http, but doesn't changes from https to http

public class HostSelectionInterceptor implements Interceptor {

    private volatile String host;
    private volatile String scheme;
    private volatile int port;

    public void setHost(String host) {
        this.host = host;
    }
    public void setScheme(String scheme) {
        this.scheme = scheme;
    }
    public void setPort(int port) {
        this.port = port;
    }

    @Override
    public okhttp3.Response intercept(Chain chain) throws IOException {
        Request request = chain.request();
        String host = this.host;
        String scheme = this.scheme;
        int port = this.port;
        if(host != null && scheme != null) {
            HttpUrl newUrl = request.url().newBuilder()
                    .scheme(scheme)
                    .host(host)
                    .port(port)
                    .build();
            request = request.newBuilder()
                    .url(newUrl)
                    .build();
        }

        return chain.proceed(request);
    }
}
@Provides
 @Singleton
Retrofit provideRetrofitHttps(final OkHttpClient okHttpClient){
        return new Retrofit.Builder()
                .baseUrl("https://bombi-bom:776/")
                .callFactory(okHttpClient)
                .addConverterFactory(GsonConverterFactory.create())
                .build();
    }
private static okhttp3.OkHttpClient createApiClient(HostSelectionInterceptor hostSelectionInterceptor) {
        try {
            OkHttpClient okHttpClient = new okhttp3.OkHttpClient();
            return okHttpClient.newBuilder().sslSocketFactory(createBadSslSocketFactory())
                    .hostnameVerifier(new HostnameVerifier() {
                        @Override
                        public boolean verify(String hostname, SSLSession session) {
                            return true;
                        }
                    })
                    .addInterceptor(hostSelectionInterceptor)
                    .build();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
 hostSelectionInterceptor.setScheme("http://");
 hostSelectionInterceptor.setHost("bombi-bom");
 hostSelectionInterceptor.setPort(80);

it works, my url changes from: https://bombi-bom:776/ to http://bombi-bom:80/

but when I'm trying to get it back

hostSelectionInterceptor.setScheme("https://");
  hostSelectionInterceptor.setHost("bombi-bom");
  hostSelectionInterceptor.setPort(776);

I get this url: http://bombi-bom:80

Don't suppose anyone could give an example of the race condition that volatile host here prevents? Would like to understand the concurrency wizardry that's going on.

It did not like ASP.NET web application URLs such as http://domain.com/MyWebApp, so I had to change setHost slightly:

public void setHost(String host) { // Store the actual host as "domain.com" this.host = HttpUrl.parse(host).host(); }

Has something changed with the okhttp3 API? When I try to change the host I get this:

network interceptor must retain the same host and port

crm416 commented Oct 23, 2016

My guess is that this has to be used with addInterceptor rather than addNetworkInterceptor. As @joshafeinberg points out, if you try to change the host or port with an Interceptor added via addNetworkInterceptor, you get an error (with okhttp3 at least). I've had success altering those fields with addInterceptor, though.

How this work with retrofit2? Seems like its just replaces whole request url

This seems to be working for me.
Thanks!

class ChangeableBaseUrlInterceptor : Interceptor {
    @Volatile private var host: HttpUrl? = null

    fun setHost(url: String) {
        this.host = HttpUrl.parse(url)
    }

    @Throws(IOException::class)
    override fun intercept(chain: Interceptor.Chain): Response {
        val newRequest = host?.let {
            val newUrl = chain.request().url().newBuilder()
                    .scheme(it.scheme())
                    .host(it.url().toURI().host)
                    .port(it.port())
                    .build()

            return@let chain.request().newBuilder()
                    .url(newUrl)
                    .build()
        }

        return chain.proceed(newRequest)
    }
}

This solution will affect @get (full path) @post (full path) and @url so that they can not work,try my solution: https://github.com/JessYanCoding/RetrofitUrlManager

@JessYanCoding was using this class until I ran into same issue you mentioned. My @url's base url got replaced with intercepted host. Thanks for creating your library.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment