Skip to content

Instantly share code, notes, and snippets.

@paour
Created October 14, 2016 07:40
An OkHttp MockWebServer dispatcher that makes it possible to proxy requests to an upstream server while checking the request and response in tests.
interface ReverseProxyValidator {
void validate(RecordedRequest request, Response response);
}
class ReverseProxyDispatcher extends Dispatcher {
private final OkHttpClient client;
private final HttpUrl serverUrl;
private final ReverseProxyValidator validator;
public ReverseProxyDispatcher(HttpUrl url, ReverseProxyValidator validator) {
serverUrl = url;
this.validator = validator;
client = new OkHttpClient.Builder()
.addInterceptor(new CurlInterceptor(new Loggable() {
@Override
public void log(String message) {
System.out.println(message);
}
}))
.build();
}
@Override
public MockResponse dispatch(final RecordedRequest request) throws InterruptedException {
HttpUrl proxiedUri = HttpUrl.parse("http://example.com" + request.getPath())
.newBuilder()
.scheme(serverUrl.scheme())
.host(serverUrl.host())
.build();
Request.Builder requestBuilder = new Request.Builder()
.url(proxiedUri)
.headers(request.getHeaders())
.removeHeader("Host");
if (request.getBodySize() != 0) {
requestBuilder.method(request.getMethod(), new RequestBody() {
@Override
public MediaType contentType() {
return MediaType.parse(request.getHeader("Content-Type"));
}
@Override
public void writeTo(BufferedSink sink) throws IOException {
request.getBody().clone().readAll(sink);
}
@Override
public long contentLength() throws IOException {
return request.getBodySize();
}
});
}
Response response = null;
try {
response = client.newCall(requestBuilder.build()).execute();
validator.validate(request, response);
} catch (IOException ignored) {}
if (response == null) {
return new MockResponse()
.setStatus("Reverse proxy error")
.setResponseCode(500);
}
return new MockResponse()
.setHeaders(response.headers())
.setBody(response.body().source().buffer())
.setResponseCode(response.code());
}
}
// https://github.com/square/okhttp/issues/2533
@Implements(NetworkSecurityPolicy.class)
public static class NetworkSecurityPolicyWorkaround {
@Implementation
public static NetworkSecurityPolicy getInstance() {
//noinspection OverlyBroadCatchBlock
try {
Class<?> shadow = Class.forName("android.security.NetworkSecurityPolicy");
return (NetworkSecurityPolicy) shadow.newInstance();
} catch (Exception e) {
throw new AssertionError();
}
}
@Implementation
public boolean isCleartextTrafficPermitted(String hostname) {
return true;
}
}
@paour
Copy link
Author

paour commented Oct 14, 2016

To use in Robolectric tests, you may need to use the following class annotation to work around this OkHttp issue.

@Config(constants = BuildConfig.class, sdk = 23, shadows = ServerTest.NetworkSecurityPolicyWorkaround.class)

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