All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.alibaba.dashscope.protocol.okhttp.OkHttpHttpClient Maven / Gradle / Ivy

// Copyright (c) Alibaba, Inc. and its affiliates.

package com.alibaba.dashscope.protocol.okhttp;

import com.alibaba.dashscope.base.HalfDuplexParamBase;
import com.alibaba.dashscope.common.DashScopeResult;
import com.alibaba.dashscope.common.ErrorType;
import com.alibaba.dashscope.common.ResultCallback;
import com.alibaba.dashscope.common.Status;
import com.alibaba.dashscope.exception.ApiException;
import com.alibaba.dashscope.exception.NoApiKeyException;
import com.alibaba.dashscope.protocol.HalfDuplexClient;
import com.alibaba.dashscope.protocol.HalfDuplexRequest;
import com.alibaba.dashscope.protocol.HttpMethod;
import com.alibaba.dashscope.protocol.HttpRequest;
import com.alibaba.dashscope.protocol.NetworkResponse;
import com.alibaba.dashscope.protocol.Protocol;
import com.alibaba.dashscope.utils.ApiKeywords;
import com.alibaba.dashscope.utils.JsonUtils;
import com.google.gson.JsonObject;
import io.reactivex.BackpressureStrategy;
import io.reactivex.Flowable;
import io.reactivex.FlowableEmitter;
import java.io.IOException;
import java.util.Map;
import lombok.extern.slf4j.Slf4j;
import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.Headers;
import okhttp3.HttpUrl;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Request.Builder;
import okhttp3.RequestBody;
import okhttp3.Response;
import okhttp3.ResponseBody;
import okhttp3.sse.EventSource;
import okhttp3.sse.EventSourceListener;
import okhttp3.sse.EventSources;

@Slf4j
public final class OkHttpHttpClient implements HalfDuplexClient {
  private final OkHttpClient client;
  private static final MediaType MEDIA_TYPE_APPLICATION_JSON =
      MediaType.parse("application/json; charset=utf-8");

  private Status parseStreamEventData(String data) {
    try {
      JsonObject jsonResponse = JsonUtils.parse(data);
      String code = "";
      String message = "";
      String requestId = "";
      if (jsonResponse.has(ApiKeywords.REQUEST_ID)) {
        requestId = jsonResponse.get(ApiKeywords.REQUEST_ID).getAsString();
      }
      if (jsonResponse.has(ApiKeywords.CODE)) {
        code = jsonResponse.get(ApiKeywords.CODE).getAsString();
      }
      if (jsonResponse.has(ApiKeywords.MESSAGE)) {
        message = jsonResponse.get(ApiKeywords.MESSAGE).getAsString();
      }
      return Status.builder()
          .statusCode(400)
          .code(code)
          .message(message)
          .requestId(requestId)
          .isJson(true)
          .build();
    } catch (Throwable e) {
      return Status.builder()
          .statusCode(400)
          .code(ErrorType.RESPONSE_ERROR.getValue())
          .message(data)
          .isJson(false)
          .build();
    }
  }

  private Status parseFailed(Response response) {
    boolean isJson = false;
    String contentType = response.header("Content-Type");
    if (contentType != null
        && (contentType.toLowerCase().contains("application/json")
            || contentType.toLowerCase().contains("text/event-stream"))) {
      isJson = true;
      try {
        JsonObject jsonResponse = JsonUtils.parse(response.body().string());
        String code = "";
        String message = "";
        String requestId = "";
        if (jsonResponse.has(ApiKeywords.REQUEST_ID)) {
          requestId = jsonResponse.get(ApiKeywords.REQUEST_ID).getAsString();
        }
        if (jsonResponse.has(ApiKeywords.CODE)) {
          code = jsonResponse.get(ApiKeywords.CODE).getAsString();
        }
        if (jsonResponse.has(ApiKeywords.MESSAGE)) {
          message = jsonResponse.get(ApiKeywords.MESSAGE).getAsString();
        }
        return Status.builder()
            .statusCode(response.code())
            .code(code)
            .message(message)
            .requestId(requestId)
            .isJson(isJson)
            .build();
      } catch (Throwable e) {
        return Status.builder()
            .statusCode(response.code())
            .code(ErrorType.RESPONSE_ERROR.getValue())
            .message(response.message())
            .isJson(isJson)
            .build();
      }
    } else {
      return Status.builder()
          .statusCode(response.code())
          .code(ErrorType.RESPONSE_ERROR.getValue())
          .message(response.message())
          .isJson(isJson)
          .build();
    }
  }

  public OkHttpHttpClient(OkHttpClient client) {
    this.client = client;
  }

  private  Request buildRequest(HttpRequest req)
      throws NoApiKeyException, ApiException {
    Request request = null;
    if (req.getHttpMethod() == HttpMethod.GET) {
      HttpUrl.Builder httpBuilder = HttpUrl.parse(req.getUrl()).newBuilder();
      if (req.getParameters() != null) {
        for (Map.Entry entry : req.getParameters().entrySet()) {
          String key = entry.getKey();
          String value = entry.getValue().toString();
          httpBuilder.addQueryParameter(key, value);
        }
      }
      request =
          new Request.Builder()
              .url(httpBuilder.build())
              .headers(Headers.of(req.getHeaders()))
              .build();
    } else if (req.getHttpMethod() == HttpMethod.POST) {
      Builder requestBuilder = new Request.Builder();
      requestBuilder.url(req.getUrl()).headers(Headers.of(req.getHeaders()));
      if (req.getBody() != null) {
        requestBuilder.post(
            RequestBody.create((String) (req.getBody()), MEDIA_TYPE_APPLICATION_JSON));
      }
      request = requestBuilder.build();
    } else if (req.getHttpMethod() == HttpMethod.DELETE) {
      Builder requestBuilder = new Request.Builder();
      requestBuilder.url(req.getUrl()).headers(Headers.of(req.getHeaders()));
      if (req.getBody() != null) {
        requestBuilder.delete(
            RequestBody.create((String) (req.getBody()), MEDIA_TYPE_APPLICATION_JSON));
      }
      request = requestBuilder.build();
    } else {
      Status status =
          Status.builder()
              .statusCode(400)
              .code("BadRequest")
              .message(String.format("Unsupported method: %s", req.getHttpMethod()))
              .build();
      throw new ApiException(status);
    }
    return request;
  }

  /*
   * Send blocking and get
   */
  @Override
  public DashScopeResult send(HalfDuplexRequest req) throws NoApiKeyException, ApiException {
    Request request = buildRequest(req.getHttpRequest());
    try (Response response = client.newCall(request).execute()) {
      if (!response.isSuccessful()) {
        Status status = parseFailed(response);
        throw new ApiException(status);
      }
      return new DashScopeResult()
          .fromResponse(
              Protocol.HTTP, NetworkResponse.builder().message(response.body().string()).build());
    } catch (Throwable e) {
      throw new ApiException(e);
    }
  }

  @Override
  public void send(HalfDuplexRequest req, ResultCallback callback)
      throws NoApiKeyException, ApiException {
    Request request = buildRequest(req.getHttpRequest());
    client
        .newCall(request)
        .enqueue(
            new Callback() {
              @Override
              public void onFailure(Call call, IOException e) {
                callback.onError(e);
              }

              @Override
              public void onResponse(Call call, Response response) throws IOException {
                try (ResponseBody responseBody = response.body()) {
                  if (!response.isSuccessful()) {
                    Status status = parseFailed(response);
                    callback.onError(new ApiException(status));
                  } else {
                    callback.onEvent(
                        new DashScopeResult()
                            .fromResponse(
                                Protocol.HTTP,
                                NetworkResponse.builder()
                                    .message(response.body().string())
                                    .build()));
                    callback.onComplete();
                  }
                }
              }
            });
  }

  private void handleSSEEvent(
      FlowableEmitter emitter, String id, String type, String data) {
    log.debug(String.format("Event: id %s, type: %s, data: %s", id, type, data));
    if (SSEEventType.ERROR.equals(type)) {
      Status st = parseStreamEventData(data);
      emitter.onError(new ApiException(st));
    } else if (SSEEventType.DATA.equals(type) || SSEEventType.RESULT.equals(type)) {
      emitter.onNext(
          new DashScopeResult()
              .fromResponse(Protocol.HTTP, NetworkResponse.builder().message(data).build()));
    } else if (type == null) {
      emitter.onNext(
          new DashScopeResult()
              .fromResponse(Protocol.HTTP, NetworkResponse.builder().message(data).build()));
    }
  }

  @Override
  public Flowable streamOut(HalfDuplexRequest req)
      throws NoApiKeyException, ApiException {
    Flowable flowable =
        Flowable.create(
            emitter -> {
              Request request = buildRequest(req.getHttpRequest());
              EventSources.createFactory(client)
                  .newEventSource(
                      request,
                      new EventSourceListener() {
                        @java.lang.Override
                        public void onEvent(
                            EventSource eventSource,
                            java.lang.String id,
                            java.lang.String type,
                            java.lang.String data) {
                          handleSSEEvent(emitter, id, type, data);
                        }

                        @java.lang.Override
                        public void onOpen(EventSource eventSource, Response response) {
                          super.onOpen(eventSource, response);
                        }

                        @java.lang.Override
                        public void onFailure(
                            EventSource eventSource, java.lang.Throwable t, Response response) {
                          super.onFailure(eventSource, t, response);
                          emitter.onError(new ApiException(parseFailed(response)));
                        }

                        @java.lang.Override
                        public void onClosed(EventSource eventSource) {
                          super.onClosed(eventSource);
                          emitter.onComplete();
                        }
                      });
            },
            BackpressureStrategy.BUFFER);
    return flowable;
  }

  private class SSEEventType {
    public static final String ERROR = "error";
    public static final String DATA = "data";
    public static final String DONE = "DONE";
    public static final String RESULT = "result";
  }

  @Override
  public void streamOut(HalfDuplexRequest req, ResultCallback callback)
      throws NoApiKeyException, ApiException {
    Request request = buildRequest(req.getHttpRequest());
    EventSources.createFactory(client)
        .newEventSource(
            request,
            new EventSourceListener() {
              @java.lang.Override
              public void onEvent(
                  EventSource eventSource,
                  java.lang.String id,
                  java.lang.String type,
                  java.lang.String data) {
                log.debug(String.format("Event: id %s, type: %s, data: %s", id, type, data));
                if (SSEEventType.ERROR.equals(type)) {
                  Status st = parseStreamEventData(data);
                  callback.onError(new ApiException(st));
                } else if (SSEEventType.DATA.equals(type) || SSEEventType.RESULT.equals(type)) {
                  callback.onEvent(
                      new DashScopeResult()
                          .fromResponse(
                              Protocol.HTTP, NetworkResponse.builder().message(data).build()));
                } else if (type == null) {
                  callback.onEvent(
                      new DashScopeResult()
                          .fromResponse(
                              Protocol.HTTP, NetworkResponse.builder().message(data).build()));
                }
              }

              @java.lang.Override
              public void onOpen(EventSource eventSource, Response response) {
                callback.onOpen(null);
              }

              @java.lang.Override
              public void onFailure(
                  EventSource eventSource, java.lang.Throwable t, Response response) {
                callback.onError(new ApiException(parseFailed(response)));
              }

              @java.lang.Override
              public void onClosed(EventSource eventSource) {
                callback.onComplete();
              }
            });
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy