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

com.parse.ParseOkHttpClient Maven / Gradle / Ivy

Go to download

A library that gives you access to the powerful Parse cloud platform from your Android app.

There is a newer version: 1.17.3
Show newest version
/*
 * Copyright (c) 2015-present, Parse, LLC.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 */
package com.parse;

import android.net.SSLSessionCache;

import com.parse.http.ParseHttpBody;
import com.parse.http.ParseHttpRequest;
import com.parse.http.ParseHttpResponse;
import com.parse.http.ParseNetworkInterceptor;

import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import bolts.Capture;
import okhttp3.Call;
import okhttp3.Headers;
import okhttp3.Interceptor;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import okhttp3.ResponseBody;
import okio.BufferedSink;
import okio.BufferedSource;
import okio.Okio;

/** package */ class ParseOkHttpClient extends ParseHttpClient {

  private final static String OKHTTP_GET = "GET";
  private final static String OKHTTP_POST = "POST";
  private final static String OKHTTP_PUT = "PUT";
  private final static String OKHTTP_DELETE = "DELETE";

  private OkHttpClient okHttpClient;

  public ParseOkHttpClient(int socketOperationTimeout, SSLSessionCache sslSessionCache) {

    OkHttpClient.Builder builder = new OkHttpClient.Builder();

    builder.connectTimeout(socketOperationTimeout, TimeUnit.MILLISECONDS);
    builder.readTimeout(socketOperationTimeout, TimeUnit.MILLISECONDS);

    // Don't handle redirects. We copy the setting from AndroidHttpClient.
    // For detail, check https://quip.com/Px8jAxnaun2r
    builder.followRedirects(false);

    okHttpClient = builder.build();
  }

  @Override
  /* package */ ParseHttpResponse executeInternal(ParseHttpRequest parseRequest) throws IOException {
    Request okHttpRequest = getRequest(parseRequest);
    Call okHttpCall = okHttpClient.newCall(okHttpRequest);

    Response okHttpResponse = okHttpCall.execute();

    return getResponse(okHttpResponse);
  }

  @Override
  /* package */ ParseHttpResponse getResponse(Response okHttpResponse)
      throws IOException {
    // Status code
    int statusCode = okHttpResponse.code();

    // Content
    InputStream content = okHttpResponse.body().byteStream();

    // Total size
    int totalSize = (int)okHttpResponse.body().contentLength();

    // Reason phrase
    String reasonPhrase = okHttpResponse.message();

    // Headers
    Map headers = new HashMap<>();
    for (String name : okHttpResponse.headers().names()) {
      headers.put(name, okHttpResponse.header(name));
    }

    // Content type
    String contentType = null;
    ResponseBody body = okHttpResponse.body();
    if (body != null && body.contentType() != null) {
      contentType = body.contentType().toString();
    }

    return new ParseHttpResponse.Builder()
        .setStatusCode(statusCode)
        .setContent(content)
        .setTotalSize(totalSize)
        .setReasonPhrase(reasonPhrase)
        .setHeaders(headers)
        .setContentType(contentType)
        .build();
  }

  @Override
  /* package */ Request getRequest(ParseHttpRequest parseRequest) throws IOException {
    Request.Builder okHttpRequestBuilder = new Request.Builder();
    ParseHttpRequest.Method method = parseRequest.getMethod();
    // Set method
    switch (method) {
      case GET:
        okHttpRequestBuilder.get();
        break;
      case DELETE:
        okHttpRequestBuilder.delete();
        break;
      case POST:
      case PUT:
        // Since we need to set body and method at the same time for POST and PUT, we will do it in
        // the following.
        break;
      default:
        // This case will never be reached since we have already handled this case in
        // ParseRequest.newRequest().
        throw new IllegalStateException("Unsupported http method " + method.toString());
    }
    // Set url
    okHttpRequestBuilder.url(parseRequest.getUrl());

    // Set Header
    Headers.Builder okHttpHeadersBuilder = new Headers.Builder();
    for (Map.Entry entry : parseRequest.getAllHeaders().entrySet()) {
      okHttpHeadersBuilder.add(entry.getKey(), entry.getValue());
    }
    // OkHttp automatically add gzip header so we do not need to deal with it
    Headers okHttpHeaders = okHttpHeadersBuilder.build();
    okHttpRequestBuilder.headers(okHttpHeaders);

    // Set Body
    ParseHttpBody parseBody = parseRequest.getBody();
    ParseOkHttpRequestBody okHttpRequestBody = null;
    if(parseBody instanceof ParseByteArrayHttpBody) {
      okHttpRequestBody = new ParseOkHttpRequestBody(parseBody);
    }
    switch (method) {
      case PUT:
        okHttpRequestBuilder.put(okHttpRequestBody);
        break;
      case POST:
        okHttpRequestBuilder.post(okHttpRequestBody);
        break;
    }
    return okHttpRequestBuilder.build();
  }

  private ParseHttpRequest getParseHttpRequest(Request okHttpRequest) {
    ParseHttpRequest.Builder parseRequestBuilder = new ParseHttpRequest.Builder();
    // Set method
    switch (okHttpRequest.method()) {
       case OKHTTP_GET:
           parseRequestBuilder.setMethod(ParseHttpRequest.Method.GET);
           break;
       case OKHTTP_DELETE:
           parseRequestBuilder.setMethod(ParseHttpRequest.Method.DELETE);
           break;
       case OKHTTP_POST:
           parseRequestBuilder.setMethod(ParseHttpRequest.Method.POST);
           break;
       case OKHTTP_PUT:
           parseRequestBuilder.setMethod(ParseHttpRequest.Method.PUT);
           break;
       default:
           // This should never happen
           throw new IllegalArgumentException(
               "Invalid http method " + okHttpRequest.method());
     }

    // Set url
    parseRequestBuilder.setUrl(okHttpRequest.url().toString());

    // Set Header
    for (Map.Entry> entry : okHttpRequest.headers().toMultimap().entrySet()) {
      parseRequestBuilder.addHeader(entry.getKey(), entry.getValue().get(0));
    }

    // Set Body
    ParseOkHttpRequestBody okHttpBody = (ParseOkHttpRequestBody) okHttpRequest.body();
    if (okHttpBody != null) {
      parseRequestBuilder.setBody(okHttpBody.getParseHttpBody());
    }
    return parseRequestBuilder.build();
  }

  /**
   * For OKHttpClient, since it does not expose any interface for us to check the raw response
   * stream, we have to use OKHttp networkInterceptors. Instead of using our own interceptor list,
   * we use OKHttp inner interceptor list.
   * @param parseNetworkInterceptor
   */
  @Override
  /* package */ void addExternalInterceptor(final ParseNetworkInterceptor parseNetworkInterceptor) {
    OkHttpClient.Builder builder = okHttpClient.newBuilder();
    builder.networkInterceptors().add(new Interceptor() {
      @Override
      public Response intercept(final Chain okHttpChain) throws IOException {
        Request okHttpRequest = okHttpChain.request();
        // Transfer OkHttpRequest to ParseHttpRequest
        final ParseHttpRequest parseRequest = getParseHttpRequest(okHttpRequest);
        // Capture OkHttpResponse
        final Capture okHttpResponseCapture = new Capture<>();
        final ParseHttpResponse parseResponse =
            parseNetworkInterceptor.intercept(new ParseNetworkInterceptor.Chain() {
          @Override
          public ParseHttpRequest getRequest() {
            return parseRequest;
          }

          @Override
          public ParseHttpResponse proceed(ParseHttpRequest parseRequest) throws IOException {
            // Use OKHttpClient to send request
            Request okHttpRequest = ParseOkHttpClient.this.getRequest(parseRequest);
            Response okHttpResponse = okHttpChain.proceed(okHttpRequest);
            okHttpResponseCapture.set(okHttpResponse);
            return getResponse(okHttpResponse);
          }
        });
        final Response okHttpResponse = okHttpResponseCapture.get();
        // Ideally we should build newOkHttpResponse only based on parseResponse, however
        // ParseHttpResponse does not have all the info we need to build the newOkHttpResponse, so
        // we rely on the okHttpResponse to generate the builder and change the necessary info
        // inside
        Response.Builder newOkHttpResponseBuilder =  okHttpResponse.newBuilder();
        // Set status
        newOkHttpResponseBuilder
            .code(parseResponse.getStatusCode())
            .message(parseResponse.getReasonPhrase());
        // Set headers
        if (parseResponse.getAllHeaders() != null) {
          for (Map.Entry entry : parseResponse.getAllHeaders().entrySet()) {
            newOkHttpResponseBuilder.header(entry.getKey(), entry.getValue());
          }
        }
        // Set body
        newOkHttpResponseBuilder.body(new ResponseBody() {
          @Override
          public MediaType contentType() {
            if (parseResponse.getContentType() == null) {
              return null;
            }
            return MediaType.parse(parseResponse.getContentType());
          }

          @Override
          public long contentLength() {
            return parseResponse.getTotalSize();
          }

          @Override
          public BufferedSource source() {
            // We need to use the proxy stream from interceptor to replace the origin network
            // stream, so when the stream is read by Parse, the network stream is proxyed in the
            // interceptor.
            if (parseResponse.getContent() == null) {
              return null;
            }
            return Okio.buffer(Okio.source(parseResponse.getContent()));
          }
        });

        return newOkHttpResponseBuilder.build();
      }
    });

    okHttpClient = builder.build();
  }

  private static class ParseOkHttpRequestBody extends RequestBody {

    private ParseHttpBody parseBody;

    public ParseOkHttpRequestBody(ParseHttpBody parseBody) {
      this.parseBody = parseBody;
    }

    @Override
    public long contentLength() throws IOException {
      return parseBody.getContentLength();
    }

    @Override
    public MediaType contentType() {
      String contentType = parseBody.getContentType();
      return contentType == null ? null : MediaType.parse(parseBody.getContentType());
    }

    @Override
    public void writeTo(BufferedSink bufferedSink) throws IOException {
      parseBody.writeTo(bufferedSink.outputStream());
    }

    public ParseHttpBody getParseHttpBody() {
      return parseBody;
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy