Skip to content

SPR-12893 Support OkHttp as an implementation for ClientHttpRequestFactory / AsyncClientHttpRequestFactory #772

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 8 commits into from
Closed
1 change: 1 addition & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -704,6 +704,7 @@ project("spring-web") {
optional("com.googlecode.protobuf-java-format:protobuf-java-format:1.2")
optional("com.google.protobuf:protobuf-java:${protobufVersion}")
optional("javax.mail:javax.mail-api:1.5.2")
optional("com.squareup.okhttp:okhttp:2.2.0")
testCompile(project(":spring-context-support")) // for JafMediaTypeFactory
testCompile("xmlunit:xmlunit:${xmlunitVersion}")
testCompile("org.slf4j:slf4j-jcl:${slf4jVersion}")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package org.springframework.http.client;

import com.squareup.okhttp.Call;
import org.springframework.util.concurrent.SettableListenableFuture;

public final class ListenableFutureCall extends SettableListenableFuture<ClientHttpResponse> {

private final Call call;

public ListenableFutureCall(Call c) {
call = c;
call.enqueue(new OkHttpCallback2SettableFutureAdapter(this));
}

@Override
protected void interruptTask() {
call.cancel();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
/*
* Copyright 2002-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.springframework.http.client;

import com.squareup.okhttp.OkHttpClient;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.StreamingHttpOutputMessage;
import org.springframework.util.concurrent.ListenableFuture;

import java.io.IOException;
import java.io.OutputStream;
import java.net.URI;

import static org.springframework.http.client.OkHttpRequestHelper.buildRequest;
import static org.springframework.http.client.OkHttpRequestHelper.getRequestBody;

/**
* @author Luciano Leggieri
*/
public final class OkHttpAsyncClientHttpRequest extends AbstractAsyncClientHttpRequest
implements StreamingHttpOutputMessage {

private final OkHttpClient client;
private final URI uri;
private final HttpMethod httpMethod;
private final OkHttpRequestBody body;

public OkHttpAsyncClientHttpRequest(OkHttpClient okHttpClient, URI u, HttpMethod method) {
this.client = okHttpClient;
uri = u;
httpMethod = method;
body = new OkHttpRequestBody();
}

@Override
public HttpMethod getMethod() {
return httpMethod;
}

@Override
public URI getURI() {
return uri;
}

@Override
public void setBody(Body b) {
assertNotExecuted();
body.setBody(b);
}

@Override
protected OutputStream getBodyInternal(HttpHeaders ignored) {
return body.getAsBuffer();
}

@Override
protected ListenableFuture<ClientHttpResponse> executeInternal(HttpHeaders headers)
throws IOException {
return new ListenableFutureCall(
client.newCall(buildRequest(httpMethod, uri, headers, getRequestBody(body))));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package org.springframework.http.client;

import com.squareup.okhttp.Callback;
import com.squareup.okhttp.Request;
import com.squareup.okhttp.Response;
import org.springframework.util.concurrent.SettableListenableFuture;

import java.io.IOException;

/**
* @author Luciano Leggieri
*/
public final class OkHttpCallback2SettableFutureAdapter implements Callback {

private final SettableListenableFuture<ClientHttpResponse> delegate;

public OkHttpCallback2SettableFutureAdapter(SettableListenableFuture<ClientHttpResponse> future) {
delegate = future;
}

@Override
public void onFailure(Request request, IOException e) {
delegate.setException(e);
}

@Override
public void onResponse(Response response) {
delegate.set(new OkHttpClientHttpResponse(response));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/*
* Copyright 2002-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.springframework.http.client;

import com.squareup.okhttp.OkHttpClient;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.StreamingHttpOutputMessage;

import java.io.IOException;
import java.io.OutputStream;
import java.net.URI;

import static org.springframework.http.client.OkHttpRequestHelper.buildRequest;
import static org.springframework.http.client.OkHttpRequestHelper.getRequestBody;

/**
* @author Luciano Leggieri
*/
public final class OkHttpClientHttpRequest extends AbstractClientHttpRequest implements StreamingHttpOutputMessage {

private final OkHttpClient client;
private final URI uri;
private final HttpMethod httpMethod;
private final OkHttpRequestBody body;

public OkHttpClientHttpRequest(OkHttpClient okHttpClient, URI u, HttpMethod method) {
client = okHttpClient;
uri = u;
httpMethod = method;
body = new OkHttpRequestBody();
}

@Override
public HttpMethod getMethod() {
return httpMethod;
}

@Override
public URI getURI() {
return uri;
}

@Override
public void setBody(Body b) {
assertNotExecuted();
body.setBody(b);
}

@Override
protected OutputStream getBodyInternal(HttpHeaders ignored) {
return body.getAsBuffer();
}

@Override
protected ClientHttpResponse executeInternal(HttpHeaders headers) throws IOException {
return new OkHttpClientHttpResponse(
client.newCall(buildRequest(httpMethod, uri, headers, getRequestBody(body))).execute());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/*
* Copyright 2002-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.springframework.http.client;

import com.squareup.okhttp.OkHttpClient;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.http.HttpMethod;
import org.springframework.util.Assert;

import java.net.URI;

/**
* @author Luciano Leggieri
*/
public final class OkHttpClientHttpRequestFactory implements
ClientHttpRequestFactory, AsyncClientHttpRequestFactory, DisposableBean {

private final OkHttpClient client;
private final boolean locallyBuilt;

public OkHttpClientHttpRequestFactory() {
client = new OkHttpClient();
locallyBuilt = true;
}

public OkHttpClientHttpRequestFactory(OkHttpClient okHttpClient) {
Assert.notNull(okHttpClient, "'okHttpClient' must not be null");
client = okHttpClient;
locallyBuilt = false;
}

@Override
public ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod) {
return new OkHttpClientHttpRequest(client, uri, httpMethod);
}

@Override
public AsyncClientHttpRequest createAsyncRequest(URI uri, HttpMethod httpMethod) {
return new OkHttpAsyncClientHttpRequest(client, uri, httpMethod);
}

@Override
public void destroy() throws Exception {
if (locallyBuilt) {
if (this.client.getCache() != null) {
this.client.getCache().close();
}
this.client.getDispatcher().getExecutorService().shutdown();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/*
* Copyright 2002-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.springframework.http.client;

import com.squareup.okhttp.Response;
import org.springframework.http.HttpHeaders;

import java.io.IOException;
import java.io.InputStream;

/**
* @author Luciano Leggieri
*/
public final class OkHttpClientHttpResponse extends AbstractClientHttpResponse {

private final Response response;
private final HttpHeaders httpHeaders;

public OkHttpClientHttpResponse(Response r) {
response = r;
httpHeaders = new HttpHeaders();
for (String key : response.headers().names()) {
for (String value : response.headers(key)) {
httpHeaders.add(key, value);
}
}
}

@Override
public int getRawStatusCode() {
return response.code();
}

@Override
public String getStatusText() {
return response.message();
}

@Override
public void close() {
try {
response.body().close();
} catch (IOException ignored) {
}
}

@Override
public InputStream getBody() {
return response.body().byteStream();
}

@Override
public HttpHeaders getHeaders() {
return httpHeaders;
}
}
Loading