-
-
Save tbroyer/509aeae1e0738bcdd28a to your computer and use it in GitHub Desktop.
/* | |
* Copyright 2015 Thomas Broyer | |
* | |
* 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. | |
*/ | |
import com.squareup.okhttp.Headers; | |
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 java.io.IOException; | |
import java.io.InputStream; | |
import java.util.List; | |
import java.util.Map; | |
import javax.annotation.Nullable; | |
import javax.inject.Inject; | |
import javax.net.ssl.HostnameVerifier; | |
import javax.net.ssl.SSLContext; | |
import javax.ws.rs.ProcessingException; | |
import javax.ws.rs.core.HttpHeaders; | |
import javax.ws.rs.core.MultivaluedMap; | |
import okio.Buffer; | |
import okio.BufferedSink; | |
import org.jboss.resteasy.client.jaxrs.ClientHttpEngine; | |
import org.jboss.resteasy.client.jaxrs.internal.ClientInvocation; | |
import org.jboss.resteasy.client.jaxrs.internal.ClientResponse; | |
import org.jboss.resteasy.util.CaseInsensitiveMap; | |
/** | |
* @author Thomas Broyer <[email protected]> | |
*/ | |
public class OkHttpClientEngine implements ClientHttpEngine { | |
private final OkHttpClient client; | |
private SSLContext sslContext; | |
@Inject | |
public OkHttpClientEngine(OkHttpClient client) { | |
this.client = client; | |
} | |
@Override | |
public SSLContext getSslContext() { | |
return sslContext; | |
} | |
public void setSslContext(SSLContext sslContext) { | |
this.sslContext = sslContext; | |
} | |
@Override | |
public HostnameVerifier getHostnameVerifier() { | |
return client.getHostnameVerifier(); | |
} | |
@Override | |
public ClientResponse invoke(ClientInvocation request) { | |
Request req = createRequest(request); | |
Response response; | |
try { | |
response = client.newCall(req).execute(); | |
} catch (IOException e) { | |
throw new ProcessingException("Unable to invoke request", e); | |
} | |
return createResponse(request, response); | |
} | |
private Request createRequest(ClientInvocation request) { | |
Request.Builder builder = new Request.Builder() | |
.method(request.getMethod(), createRequestBody(request)) | |
.url(request.getUri().toString()); | |
for (Map.Entry<String, List<String>> header : request.getHeaders().asMap().entrySet()) { | |
String headerName = header.getKey(); | |
for (String headerValue : header.getValue()) { | |
builder.addHeader(headerName, headerValue); | |
} | |
} | |
return builder.build(); | |
} | |
@Nullable | |
private RequestBody createRequestBody(final ClientInvocation request) { | |
if (request.getEntity() == null) { | |
return null; | |
} | |
// NOTE: this will invoke WriterInterceptors which can possibly change the request, | |
// so it must be done first, before reading any header. | |
final Buffer buffer = new Buffer(); | |
try { | |
request.writeRequestBody(buffer.outputStream()); | |
} catch (IOException e) { | |
throw new RuntimeException(e); | |
} | |
javax.ws.rs.core.MediaType mediaType = request.getHeaders().getMediaType(); | |
final MediaType contentType = (mediaType == null) ? null : MediaType.parse(mediaType.toString()); | |
return new RequestBody() { | |
@Override | |
public long contentLength() throws IOException { | |
return buffer.size(); | |
} | |
@Override | |
public MediaType contentType() { | |
return contentType; | |
} | |
@Override | |
public void writeTo(BufferedSink sink) throws IOException { | |
sink.write(buffer, buffer.size()); | |
} | |
}; | |
} | |
private ClientResponse createResponse(ClientInvocation request, final Response response) { | |
ClientResponse clientResponse = new ClientResponse(request.getClientConfiguration()) { | |
private InputStream stream; | |
@Override | |
protected InputStream getInputStream() { | |
if (stream == null) { | |
try { | |
stream = response.body().byteStream(); | |
} catch (IOException e) { | |
throw new RuntimeException(e); | |
} | |
} | |
return stream; | |
} | |
@Override | |
protected void setInputStream(InputStream is) { | |
stream = is; | |
} | |
@Override | |
protected void releaseConnection() throws IOException { | |
// Stream might have been entirely replaced, so we need to close it independently from response.body() | |
Throwable primaryExc = null; | |
try { | |
if (stream != null) { | |
stream.close(); | |
} | |
} catch (Throwable t) { | |
primaryExc = t; | |
throw t; | |
} finally { | |
if (primaryExc != null) { | |
try { | |
response.body().close(); | |
} catch (Throwable suppressedExc) { | |
primaryExc.addSuppressed(suppressedExc); | |
} | |
} else { | |
response.body().close(); | |
} | |
} | |
} | |
}; | |
clientResponse.setStatus(response.code()); | |
clientResponse.setHeaders(transformHeaders(response.headers())); | |
return clientResponse; | |
} | |
private MultivaluedMap<String, String> transformHeaders(Headers headers) { | |
MultivaluedMap<String, String> ret = new CaseInsensitiveMap<>(); | |
for (int i = 0, l = headers.size(); i < l; i++) { | |
ret.add(headers.name(i), headers.value(i)); | |
} | |
return ret; | |
} | |
@Override | |
public void close() { | |
// no-op | |
} | |
} |
I have found a serious bug in this implementation: when POSTing JSON data, the encoding is somehow broken - the remote Server won't accept it in some instances (the Mailchimp API in my case). I had to use the default Adapter... I have tested it by building a raw call with plain OkHttp and that worked, so it can't be OkHttps fault. Not wanting to say that this is a bad project though - i'd love to use OkHttp for all my Java Networking
You might want to use resteasy-client-okhttp or resteasy-client-okhttp3 from https://github.com/tbroyer/jaxrs-utils (https://search.maven.org/search?q=g:net.ltgt.jaxrs); that said, the code there is mostly the same as it is here (only difference AFAICT: tbroyer/jaxrs-utils@6a5ee07#diff-2f027da64d17f1a994b1a6e39f11c65e) so it would have the same bug; but it also has tests so maybe you could do a PR with a failing test (and/or even better, a fix for it 😉; but at a minimum please file an issue with more details about your failing request)
I must say I moved all my projects to using plain OkHttp so I can't promise anything wrt when I could fix the issue.
To use it: