/* * Copyright 2016 LINE Corporation * * LINE Corporation licenses this file to you 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 com.linecorp.armeria.client.http.retrofit2; import java.io.IOException; import org.reactivestreams.Subscriber; import org.reactivestreams.Subscription; import com.google.common.base.Strings; import com.linecorp.armeria.client.http.retrofit2.ArmeriaCallFactory.ArmeriaCall; import com.linecorp.armeria.common.http.HttpData; import com.linecorp.armeria.common.http.HttpHeaderNames; import com.linecorp.armeria.common.http.HttpHeaders; import com.linecorp.armeria.common.http.HttpObject; import com.linecorp.armeria.common.http.HttpStatusClass; import okhttp3.Callback; import okhttp3.MediaType; import okhttp3.Protocol; import okhttp3.Request; import okhttp3.Response; import okhttp3.ResponseBody; import okio.Buffer; final class ArmeriaCallSubscriber implements Subscriber<HttpObject> { private final ArmeriaCall armeriaCall; private final Callback callback; private final Request request; private final Response.Builder responseBuilder = new Response.Builder(); private final Buffer responseDataBuffer = new Buffer(); private Subscription subscription; private HttpHeaders headers; private boolean callbackCalled; ArmeriaCallSubscriber(ArmeriaCall armeriaCall, Callback callback, Request request) { this.armeriaCall = armeriaCall; this.callback = callback; this.request = request; } @Override public void onSubscribe(Subscription subscription) { this.subscription = subscription; if (armeriaCall.isCanceled()) { safeOnFailure(newCanceledException()); subscription.cancel(); return; } subscription.request(Long.MAX_VALUE); } @Override public void onNext(HttpObject httpObject) { if (armeriaCall.isCanceled()) { safeOnFailure(newCanceledException()); subscription.cancel(); return; } if (httpObject instanceof HttpHeaders) { if (((HttpHeaders) httpObject).status().codeClass() == HttpStatusClass.INFORMATIONAL) { return; } if (headers != null) { return; } headers = (HttpHeaders) httpObject; String contentType = headers.get(HttpHeaderNames.CONTENT_TYPE); headers.forEach(header -> responseBuilder.addHeader(header.getKey().toString(), header.getValue())); responseBuilder.code(headers.status().code()); responseBuilder.message(headers.status().reasonPhrase()); responseBuilder.body(ResponseBody.create( Strings.isNullOrEmpty(contentType) ? null : MediaType.parse(contentType), headers.getLong(HttpHeaderNames.CONTENT_LENGTH, -1L), responseDataBuffer)); return; } HttpData data = (HttpData) httpObject; responseDataBuffer.write(data.array(), data.offset(), data.length()); } @Override public void onError(Throwable throwable) { if (armeriaCall.tryFinish()) { safeOnFailure(new IOException(throwable.getMessage(), throwable)); } else { safeOnFailure(newCanceledException()); } } @Override public void onComplete() { if (armeriaCall.tryFinish()) { responseBuilder.request(request); responseBuilder.protocol(Protocol.HTTP_1_1); safeOnResponse(responseBuilder.build()); } else { safeOnFailure(newCanceledException()); } } private void safeOnFailure(IOException e) { if (callbackCalled) { return; } callbackCalled = true; callback.onFailure(armeriaCall, e); } private void safeOnResponse(Response response) { if (callbackCalled) { return; } callbackCalled = true; try { callback.onResponse(armeriaCall, response); } catch (IOException e) { callback.onFailure(armeriaCall, e); } } private static IOException newCanceledException() { return new IOException("Canceled"); } }