Browse Source
This commit introduces support for OkHttp (http://square.github.io/okhttp/) as a backing implementation for ClientHttpRequestFactory and AsyncClientHttpRequestFactory. Issue: SPR-12893pull/773/merge
11 changed files with 433 additions and 0 deletions
@ -0,0 +1,144 @@
@@ -0,0 +1,144 @@
|
||||
/* |
||||
* 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 java.io.IOException; |
||||
import java.net.URI; |
||||
import java.util.List; |
||||
import java.util.Map; |
||||
import java.util.concurrent.ExecutionException; |
||||
|
||||
import com.squareup.okhttp.Call; |
||||
import com.squareup.okhttp.Callback; |
||||
import com.squareup.okhttp.MediaType; |
||||
import com.squareup.okhttp.OkHttpClient; |
||||
import com.squareup.okhttp.Request; |
||||
import com.squareup.okhttp.RequestBody; |
||||
import com.squareup.okhttp.Response; |
||||
|
||||
import org.springframework.http.HttpHeaders; |
||||
import org.springframework.http.HttpMethod; |
||||
import org.springframework.util.concurrent.ListenableFuture; |
||||
import org.springframework.util.concurrent.SettableListenableFuture; |
||||
|
||||
/** |
||||
* {@link ClientHttpRequest} implementation that uses OkHttp to execute requests. |
||||
* |
||||
* <p>Created via the {@link OkHttpClientHttpRequestFactory}. |
||||
* |
||||
* @author Luciano Leggieri |
||||
* @author Arjen Poutsma |
||||
* @since 4.2 |
||||
*/ |
||||
class OkHttpClientHttpRequest extends AbstractBufferingAsyncClientHttpRequest |
||||
implements ClientHttpRequest { |
||||
|
||||
private final OkHttpClient client; |
||||
|
||||
private final URI uri; |
||||
|
||||
private final HttpMethod method; |
||||
|
||||
|
||||
public OkHttpClientHttpRequest(OkHttpClient client, URI uri, HttpMethod method) { |
||||
this.client = client; |
||||
this.uri = uri; |
||||
this.method = method; |
||||
} |
||||
|
||||
|
||||
@Override |
||||
public HttpMethod getMethod() { |
||||
return method; |
||||
} |
||||
|
||||
@Override |
||||
public URI getURI() { |
||||
return uri; |
||||
} |
||||
|
||||
@Override |
||||
protected ListenableFuture<ClientHttpResponse> executeInternal(HttpHeaders headers, |
||||
byte[] bufferedOutput) throws IOException { |
||||
RequestBody body = bufferedOutput.length > 0 ? |
||||
RequestBody.create(getContentType(headers), bufferedOutput) : null; |
||||
|
||||
Request.Builder builder = new Request.Builder(). |
||||
url(this.uri.toURL()). |
||||
method(this.method.name(), body); |
||||
|
||||
for (Map.Entry<String, List<String>> entry : headers.entrySet()) { |
||||
String headerName = entry.getKey(); |
||||
for (String headerValue : entry.getValue()) { |
||||
builder.addHeader(headerName, headerValue); |
||||
} |
||||
} |
||||
Request request = builder.build(); |
||||
|
||||
return new ListenableFutureCall(client.newCall(request)); |
||||
} |
||||
|
||||
private MediaType getContentType(HttpHeaders headers) { |
||||
org.springframework.http.MediaType contentType = headers.getContentType(); |
||||
return contentType != null ? MediaType.parse(contentType.toString()) : null; |
||||
} |
||||
|
||||
@Override |
||||
public ClientHttpResponse execute() throws IOException { |
||||
try { |
||||
return executeAsync().get(); |
||||
} |
||||
catch (InterruptedException ex) { |
||||
throw new IOException(ex.getMessage(), ex); |
||||
} |
||||
catch (ExecutionException ex) { |
||||
if (ex.getCause() instanceof IOException) { |
||||
throw (IOException) ex.getCause(); |
||||
} |
||||
else { |
||||
throw new IOException(ex.getMessage(), ex); |
||||
} |
||||
} |
||||
} |
||||
|
||||
private static class ListenableFutureCall extends |
||||
SettableListenableFuture<ClientHttpResponse> { |
||||
|
||||
private final Call call; |
||||
|
||||
public ListenableFutureCall(Call call) { |
||||
this.call = call; |
||||
this.call.enqueue(new Callback() { |
||||
@Override |
||||
public void onResponse(Response response) throws IOException { |
||||
set(new OkHttpClientHttpResponse(response)); |
||||
} |
||||
|
||||
@Override |
||||
public void onFailure(Request request, IOException ex) { |
||||
setException(ex); |
||||
} |
||||
}); |
||||
} |
||||
|
||||
@Override |
||||
protected void interruptTask() { |
||||
call.cancel(); |
||||
} |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,118 @@
@@ -0,0 +1,118 @@
|
||||
/* |
||||
* 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 java.net.URI; |
||||
import java.util.concurrent.TimeUnit; |
||||
|
||||
import com.squareup.okhttp.OkHttpClient; |
||||
|
||||
import org.springframework.beans.factory.DisposableBean; |
||||
import org.springframework.http.HttpMethod; |
||||
import org.springframework.util.Assert; |
||||
|
||||
/** |
||||
* {@link ClientHttpRequestFactory} implementation that uses |
||||
* <a href="http://square.github.io/okhttp/">OkHttp</a> to create requests. |
||||
* |
||||
* @author Luciano Leggieri |
||||
* @author Arjen Poutsma |
||||
* @since 4.2 |
||||
*/ |
||||
public class OkHttpClientHttpRequestFactory |
||||
implements ClientHttpRequestFactory, AsyncClientHttpRequestFactory, |
||||
DisposableBean { |
||||
|
||||
private final OkHttpClient client; |
||||
|
||||
private final boolean defaultClient; |
||||
|
||||
|
||||
/** |
||||
* Create a new {@code OkHttpClientHttpRequestFactory} with a default |
||||
* {@link OkHttpClient}. |
||||
*/ |
||||
public OkHttpClientHttpRequestFactory() { |
||||
client = new OkHttpClient(); |
||||
defaultClient = true; |
||||
} |
||||
|
||||
/** |
||||
* Create a new {@code OkHttpClientHttpRequestFactory} with the given |
||||
* {@link OkHttpClient}. |
||||
* @param okHttpClient the client to use |
||||
*/ |
||||
public OkHttpClientHttpRequestFactory(OkHttpClient okHttpClient) { |
||||
Assert.notNull(okHttpClient, "'okHttpClient' must not be null"); |
||||
client = okHttpClient; |
||||
defaultClient = false; |
||||
} |
||||
|
||||
|
||||
/** |
||||
* Sets the underlying read timeout (in milliseconds). |
||||
* A timeout value of 0 specifies an infinite timeout. |
||||
* @see OkHttpClient#setReadTimeout(long, TimeUnit) |
||||
*/ |
||||
public void setReadTimeout(int readTimeout) { |
||||
this.client.setReadTimeout(readTimeout, TimeUnit.MILLISECONDS); |
||||
} |
||||
|
||||
/** |
||||
* Sets the underlying write timeout (in milliseconds). |
||||
* A timeout value of 0 specifies an infinite timeout. |
||||
* @see OkHttpClient#setWriteTimeout(long, TimeUnit) |
||||
*/ |
||||
public void setWriteTimeout(int writeTimeout) { |
||||
this.client.setWriteTimeout(writeTimeout, TimeUnit.MILLISECONDS); |
||||
} |
||||
|
||||
/** |
||||
* Sets the underlying connect timeout (in milliseconds). |
||||
* A timeout value of 0 specifies an infinite timeout. |
||||
* @see OkHttpClient#setConnectTimeout(long, TimeUnit) |
||||
*/ |
||||
public void setConnectTimeout(int connectTimeout) { |
||||
this.client.setConnectTimeout(connectTimeout, TimeUnit.MILLISECONDS); |
||||
} |
||||
|
||||
|
||||
@Override |
||||
public ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod) { |
||||
return createRequestInternal(uri, httpMethod); |
||||
} |
||||
|
||||
@Override |
||||
public AsyncClientHttpRequest createAsyncRequest(URI uri, HttpMethod httpMethod) { |
||||
return createRequestInternal(uri, httpMethod); |
||||
} |
||||
|
||||
private OkHttpClientHttpRequest createRequestInternal(URI uri, HttpMethod httpMethod) { |
||||
return new OkHttpClientHttpRequest(this.client, uri, httpMethod); |
||||
} |
||||
|
||||
@Override |
||||
public void destroy() throws Exception { |
||||
if (defaultClient) { |
||||
// Clean up the client if we created it in the constructor
|
||||
if (this.client.getCache() != null) { |
||||
this.client.getCache().close(); |
||||
} |
||||
this.client.getDispatcher().getExecutorService().shutdown(); |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,86 @@
@@ -0,0 +1,86 @@
|
||||
/* |
||||
* 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 java.io.IOException; |
||||
import java.io.InputStream; |
||||
import java.util.Map; |
||||
|
||||
import com.squareup.okhttp.Response; |
||||
|
||||
import org.springframework.http.HttpHeaders; |
||||
import org.springframework.util.Assert; |
||||
|
||||
/** |
||||
* {@link org.springframework.http.client.ClientHttpResponse} implementation that uses |
||||
* OkHttp. |
||||
* |
||||
* @author Luciano Leggieri |
||||
* @author Arjen Poutsma |
||||
* @since 4.2 |
||||
*/ |
||||
class OkHttpClientHttpResponse extends AbstractClientHttpResponse { |
||||
|
||||
private final Response response; |
||||
|
||||
private HttpHeaders headers; |
||||
|
||||
|
||||
public OkHttpClientHttpResponse(Response response) { |
||||
Assert.notNull(response, "'response' must not be null"); |
||||
this.response = response; |
||||
} |
||||
|
||||
|
||||
@Override |
||||
public int getRawStatusCode() { |
||||
return response.code(); |
||||
} |
||||
|
||||
@Override |
||||
public String getStatusText() { |
||||
return response.message(); |
||||
} |
||||
|
||||
@Override |
||||
public InputStream getBody() throws IOException { |
||||
return response.body().byteStream(); |
||||
} |
||||
|
||||
@Override |
||||
public HttpHeaders getHeaders() { |
||||
if (this.headers == null) { |
||||
HttpHeaders headers = new HttpHeaders(); |
||||
for (String headerName : this.response.headers().names()) { |
||||
for (String headerValue : this.response.headers(headerName)) { |
||||
headers.add(headerName, headerValue); |
||||
} |
||||
} |
||||
this.headers = headers; |
||||
} |
||||
return this.headers; |
||||
} |
||||
|
||||
@Override |
||||
public void close() { |
||||
try { |
||||
response.body().close(); |
||||
} |
||||
catch (IOException ignored) { |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,40 @@
@@ -0,0 +1,40 @@
|
||||
/* |
||||
* 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.junit.Test; |
||||
import org.springframework.http.HttpMethod; |
||||
|
||||
/** |
||||
* @author Luciano Leggieri |
||||
*/ |
||||
public class OkHttpAsyncClientHttpRequestFactoryTests extends AbstractAsyncHttpRequestFactoryTestCase { |
||||
|
||||
@Override |
||||
protected AsyncClientHttpRequestFactory createRequestFactory() { |
||||
return new OkHttpClientHttpRequestFactory(); |
||||
} |
||||
|
||||
@Override |
||||
@Test |
||||
public void httpMethods() throws Exception { |
||||
super.httpMethods(); |
||||
assertHttpMethod("patch", HttpMethod.PATCH); |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,39 @@
@@ -0,0 +1,39 @@
|
||||
/* |
||||
* 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.junit.Test; |
||||
import org.springframework.http.HttpMethod; |
||||
|
||||
/** |
||||
* @author Luciano Leggieri |
||||
*/ |
||||
public class OkHttpClientHttpRequestFactoryTests extends AbstractHttpRequestFactoryTestCase { |
||||
|
||||
@Override |
||||
protected ClientHttpRequestFactory createRequestFactory() { |
||||
return new OkHttpClientHttpRequestFactory(); |
||||
} |
||||
|
||||
@Override |
||||
@Test |
||||
public void httpMethods() throws Exception { |
||||
assertHttpMethod("patch", HttpMethod.PATCH); |
||||
} |
||||
|
||||
} |
||||
Loading…
Reference in new issue