/**
* Copyright 2015-2017 The OpenZipkin 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 zipkin.storage.elasticsearch.http.internal.client;
import java.io.Closeable;
import java.io.IOException;
import okhttp3.Call;
import okhttp3.HttpUrl;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.ResponseBody;
import okhttp3.internal.http.HttpHeaders;
import okio.BufferedSource;
import okio.GzipSource;
import okio.Okio;
import zipkin.internal.CallbackCaptor;
import zipkin.storage.Callback;
import static zipkin.internal.Util.propagateIfFatal;
public final class HttpCall<V> {
public interface BodyConverter<V> {
V convert(BufferedSource content) throws IOException;
}
public static class Factory implements Closeable {
final OkHttpClient ok;
public final HttpUrl baseUrl;
public Factory(OkHttpClient ok, HttpUrl baseUrl) {
this.ok = ok;
this.baseUrl = baseUrl;
}
public <V> HttpCall<V> newCall(Request request, BodyConverter<V> bodyConverter) {
return new HttpCall(this, request, bodyConverter);
}
public <V> V execute(Request request, BodyConverter<V> bodyConverter) {
CallbackCaptor<V> response = new CallbackCaptor<>();
newCall(request, bodyConverter).submit(response);
return response.get();
}
@Override public void close() {
ok.dispatcher().executorService().shutdownNow();
}
}
final Call.Factory ok;
final Request request;
final BodyConverter<V> bodyConverter;
HttpCall(Factory factory, Request request, BodyConverter<V> bodyConverter) {
this.ok = factory.ok;
this.request = request;
this.bodyConverter = bodyConverter;
}
public void submit(Callback<V> delegate) {
ok.newCall(request).enqueue(new CallbackAdapter<>(bodyConverter, delegate));
}
static class CallbackAdapter<V> implements okhttp3.Callback {
final BodyConverter<V> bodyConverter;
final Callback<V> delegate;
CallbackAdapter(BodyConverter<V> bodyConverter, Callback<V> delegate) {
this.bodyConverter = bodyConverter;
this.delegate = delegate;
}
@Override public void onFailure(Call call, IOException e) {
delegate.onError(e);
}
/** Note: this runs on the {@link okhttp3.OkHttpClient#dispatcher() dispatcher} thread! */
@Override public void onResponse(Call call, Response response) {
if (!HttpHeaders.hasBody(response)) {
if (response.isSuccessful()) {
delegate.onSuccess(null);
} else {
delegate.onError(new IllegalStateException("response failed: " + response));
}
return;
}
try (ResponseBody responseBody = response.body()) {
BufferedSource content = responseBody.source();
if ("gzip".equalsIgnoreCase(response.header("Content-Encoding"))) {
content = Okio.buffer(new GzipSource(responseBody.source()));
}
if (response.isSuccessful()) {
delegate.onSuccess(bodyConverter.convert(content));
} else {
delegate.onError(new IllegalStateException("response failed: " + content.readUtf8()));
}
} catch (Throwable t) {
propagateIfFatal(t);
delegate.onError(t);
}
}
}
}