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

com.landawn.abacus.http.OkHttpRequest Maven / Gradle / Ivy

Go to download

A general programming library in Java/Android. It's easy to learn and simple to use with concise and powerful APIs.

There is a newer version: 5.3.16
Show newest version
/*
 * Copyright (C) 2019 HaiYang Li
 *
 * 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 com.landawn.abacus.http;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.nio.charset.Charset;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;

import javax.annotation.Nullable;

import com.landawn.abacus.annotation.Beta;
import com.landawn.abacus.parser.KryoParser;
import com.landawn.abacus.parser.ParserFactory;
import com.landawn.abacus.parser.ParserUtil;
import com.landawn.abacus.parser.ParserUtil.BeanInfo;
import com.landawn.abacus.parser.ParserUtil.PropInfo;
import com.landawn.abacus.util.BufferedReader;
import com.landawn.abacus.util.Charsets;
import com.landawn.abacus.util.ClassUtil;
import com.landawn.abacus.util.ContinuableFuture;
import com.landawn.abacus.util.IOUtil;
import com.landawn.abacus.util.N;
import com.landawn.abacus.util.Objectory;
import com.landawn.abacus.util.Strings;
import com.landawn.abacus.util.URLEncodedUtil;

import okhttp3.CacheControl;
import okhttp3.FormBody;
import okhttp3.Headers;
import okhttp3.HttpUrl;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import okhttp3.internal.Util;

/**
 * Note: This class contains the codes and docs copied from OkHttp: https://square.github.io/okhttp/ under Apache License v2.
 *
 * @see URLEncodedUtil
 * @see HttpHeaders
 * @since 1.3
 */
public final class OkHttpRequest {

    private static final MediaType APPLICATION_JSON_MEDIA_TYPE = MediaType.get(HttpHeaders.Values.APPLICATION_JSON);
    private static final MediaType APPLICATION_XML_MEDIA_TYPE = MediaType.get(HttpHeaders.Values.APPLICATION_XML);

    private static final KryoParser kryoParser = ParserFactory.isKryoAvailable() ? ParserFactory.createKryoParser() : null;

    private static final OkHttpClient defaultClient = new OkHttpClient();

    private final String url;
    private final HttpUrl httpUrl;

    private Object query;

    private OkHttpClient httpClient;
    private final Request.Builder requestBuilder;
    private RequestBody body;

    private boolean closeHttpClientAfterExecution = false;

    OkHttpRequest(final String url, final HttpUrl httpUrl, final OkHttpClient httpClient) {
        N.checkArgument(!(Strings.isEmpty(url) && httpUrl == null), "'url' can't be null or empty");

        this.url = url;
        this.httpUrl = httpUrl;
        this.httpClient = httpClient;
        this.requestBuilder = new Request.Builder();
    }

    /**
     *
     *
     * @param url
     * @param httpClient
     * @return
     */
    public static OkHttpRequest create(final String url, final OkHttpClient httpClient) {
        return new OkHttpRequest(url, null, httpClient);
    }

    /**
     *
     *
     * @param url
     * @param httpClient
     * @return
     */
    public static OkHttpRequest create(final URL url, final OkHttpClient httpClient) {
        return new OkHttpRequest(null, HttpUrl.get(url), httpClient);
    }

    /**
     *
     *
     * @param url
     * @param httpClient
     * @return
     */
    public static OkHttpRequest create(final HttpUrl url, final OkHttpClient httpClient) {
        return new OkHttpRequest(null, url, httpClient);
    }

    /**
     * Sets the URL target of this request.
     *
     * @param url
     * @return
     * @throws IllegalArgumentException if {@code url} is not a valid HTTP or HTTPS URL. Avoid this
     * exception by calling {@link HttpUrl#parse}; it returns null for invalid URLs.
     */
    public static OkHttpRequest url(final String url) {
        return create(url, defaultClient);
    }

    /**
     * Sets the URL target of this request.
     *
     * @param url
     * @return
     * @throws IllegalArgumentException if the scheme of {@code url} is not {@code http} or {@code https}.
     */
    public static OkHttpRequest url(URL url) {
        return create(url, defaultClient);
    }

    /**
     *
     *
     * @param url
     * @return
     */
    public static OkHttpRequest url(HttpUrl url) {
        return create(url, defaultClient);
    }

    /**
     *
     *
     * @param url
     * @param connectionTimeoutInMillis
     * @param readTimeoutInMillis
     * @return
     */
    public static OkHttpRequest url(final String url, final long connectionTimeoutInMillis, final long readTimeoutInMillis) {
        return create(url,
                new OkHttpClient.Builder().connectTimeout(connectionTimeoutInMillis, TimeUnit.MILLISECONDS)
                        .readTimeout(readTimeoutInMillis, TimeUnit.MILLISECONDS)
                        .build()).closeHttpClientAfterExecution(true);
    }

    /**
     *
     *
     * @param url
     * @param connectionTimeoutInMillis
     * @param readTimeoutInMillis
     * @return
     */
    public static OkHttpRequest url(final URL url, final long connectionTimeoutInMillis, final long readTimeoutInMillis) {
        return create(url,
                new OkHttpClient.Builder().connectTimeout(connectionTimeoutInMillis, TimeUnit.MILLISECONDS)
                        .readTimeout(readTimeoutInMillis, TimeUnit.MILLISECONDS)
                        .build()).closeHttpClientAfterExecution(true);
    }

    /**
     *
     *
     * @param url
     * @param connectionTimeoutInMillis
     * @param readTimeoutInMillis
     * @return
     */
    public static OkHttpRequest url(final HttpUrl url, final long connectionTimeoutInMillis, final long readTimeoutInMillis) {
        return create(url,
                new OkHttpClient.Builder().connectTimeout(connectionTimeoutInMillis, TimeUnit.MILLISECONDS)
                        .readTimeout(readTimeoutInMillis, TimeUnit.MILLISECONDS)
                        .build()).closeHttpClientAfterExecution(true);
    }

    OkHttpRequest closeHttpClientAfterExecution(boolean b) {
        this.closeHttpClientAfterExecution = b;

        return this;
    }

    /**
     * Sets this request's {@code Cache-Control} header, replacing any cache control headers already
     * present. If {@code cacheControl} doesn't define any directives, this clears this request's
     * cache-control headers.
     *
     * @param cacheControl
     * @return
     */
    public OkHttpRequest cacheControl(CacheControl cacheControl) {
        requestBuilder.cacheControl(cacheControl);
        return this;
    }

    /**
     * Attaches {@code tag} to the request. It can be used later to cancel the request. If the tag
     * is unspecified or null, the request is canceled by using the request itself as the tag.
     *
     * @param tag
     * @return
     */
    public OkHttpRequest tag(@Nullable Object tag) {
        requestBuilder.tag(tag);
        return this;
    }

    /**
     * Attaches {@code tag} to the request using {@code type} as a key. Tags can be read from a
     * request using {@link Request#tag}. Use null to remove any existing tag assigned for {@code
     * type}.
     *
     * 

Use this API to attach timing, debugging, or other application data to a request so that * you may read it in interceptors, event listeners, or callbacks. * * @param * @param type * @param tag * @return */ public OkHttpRequest tag(Class type, @Nullable T tag) { requestBuilder.tag(type, tag); return this; } /** * * @param user * @param password * @return */ public OkHttpRequest basicAuth(String user, Object password) { requestBuilder.header(HttpHeaders.Names.AUTHORIZATION, "Basic " + Strings.base64Encode((user + ":" + password).getBytes(Charsets.UTF_8))); return this; } /** * Sets the header named {@code name} to {@code value}. * If this request already has any headers with that name, they are all replaced. * * @param name * @param value * @return * @see Request.Builder#header(String, String) * @see HttpHeaders */ public OkHttpRequest header(String name, String value) { requestBuilder.header(name, value); return this; } /** * Set http headers specified by {@code name1/value1}, {@code name2/value2}. * If this request already has any headers with that name, they are all replaced. * * @param name1 * @param value1 * @param name2 * @param value2 * @return * @see Request.Builder#header(String, String) * @see HttpHeaders */ public OkHttpRequest headers(String name1, String value1, String name2, String value2) { requestBuilder.header(name1, value1); requestBuilder.header(name2, value2); return this; } /** * Set http headers specified by {@code name1/value1}, {@code name2/value2}, {@code name3/value3}. * If this request already has any headers with that name, they are all replaced. * * @param name1 * @param value1 * @param name2 * @param value2 * @param name3 * @param value3 * @return * @see Request.Builder#header(String, String) * @see HttpHeaders */ public OkHttpRequest headers(String name1, String value1, String name2, String value2, String name3, String value3) { requestBuilder.header(name1, value1); requestBuilder.header(name2, value2); requestBuilder.header(name3, value3); return this; } /** * Set http headers specified by the key/value entities from {@code Map}. * If this request already has any headers with that name, they are all replaced. * * @param headers * @return * @see Request.Builder#header(String, String) * @see HttpHeaders */ public OkHttpRequest headers(final Map headers) { if (N.notEmpty(headers)) { for (Map.Entry entry : headers.entrySet()) { requestBuilder.header(entry.getKey(), HttpHeaders.valueOf(entry.getValue())); } } return this; } /** * Removes all headers on this builder and adds {@code headers}. * * @param headers * @return * @see Request.Builder#headers(Headers) * @see HttpHeaders */ public OkHttpRequest headers(Headers headers) { requestBuilder.headers(headers); return this; } /** * Removes all headers on this builder and adds {@code headers}. * * @param headers * @return * @see Request.Builder#headers(Headers) * @see HttpHeaders */ public OkHttpRequest headers(HttpHeaders headers) { if (headers != null && !headers.isEmpty()) { for (String headerName : headers.headerNameSet()) { requestBuilder.header(headerName, HttpHeaders.valueOf(headers.get(headerName))); } } return this; } /** * Adds a header with {@code name} and {@code value}. Prefer this method for multiply-valued * headers like "Cookie". * *

Note that for some headers including {@code Content-Length} and {@code Content-Encoding}, * OkHttp may replace {@code value} with a header derived from the request body. * * @param name * @param value * @return * @deprecated no use case? */ @Deprecated public OkHttpRequest addHeader(String name, String value) { requestBuilder.addHeader(name, value); return this; } /** * * @param name * @return * @deprecated no use case? */ @Deprecated public OkHttpRequest removeHeader(String name) { requestBuilder.removeHeader(name); return this; } /** * Set query parameters for {@code GET} or {@code DELETE} request. * * @param query * @return */ public OkHttpRequest query(final String query) { this.query = query; return this; } /** * Set query parameters for {@code GET} or {@code DELETE} request. * * @param queryParams * @return */ public OkHttpRequest query(final Map queryParams) { this.query = queryParams; return this; } /** * * @param json * @return */ public OkHttpRequest jsonBody(final String json) { return body(json, APPLICATION_JSON_MEDIA_TYPE); } /** * * @param obj * @return */ public OkHttpRequest jsonBody(final Object obj) { return body(N.toJSON(obj), APPLICATION_JSON_MEDIA_TYPE); } /** * * @param xml * @return */ public OkHttpRequest xmlBody(final String xml) { return body(xml, APPLICATION_XML_MEDIA_TYPE); } /** * * @param obj * @return */ public OkHttpRequest xmlBody(final Object obj) { return body(N.toXML(obj), APPLICATION_XML_MEDIA_TYPE); } /** * * @param formBodyByMap * @return * @see {@code FormBody.Builder} */ public OkHttpRequest formBody(final Map formBodyByMap) { if (N.isEmpty(formBodyByMap)) { this.body = Util.EMPTY_REQUEST; return this; } final FormBody.Builder formBodyBuilder = new FormBody.Builder(); for (Map.Entry entry : formBodyByMap.entrySet()) { formBodyBuilder.add(N.stringOf(entry.getKey()), N.stringOf(entry.getValue())); } this.body = formBodyBuilder.build(); return this; } /** * * @param formBodyByBean * @return * @see {@code FormBody.Builder} */ public OkHttpRequest formBody(final Object formBodyByBean) { if (formBodyByBean == null) { this.body = Util.EMPTY_REQUEST; return this; } final Class cls = formBodyByBean.getClass(); N.checkArgument(ClassUtil.isBeanClass(cls), "{} is not a bean class with getter/setter methods", cls); final BeanInfo beanInfo = ParserUtil.getBeanInfo(cls); final FormBody.Builder formBodyBuilder = new FormBody.Builder(); for (PropInfo propInfo : beanInfo.propInfoList) { formBodyBuilder.add(propInfo.name, N.stringOf(propInfo.getPropValue(formBodyByBean))); } this.body = formBodyBuilder.build(); return this; } /** * * @param formBodyByMap * @return * @see {@code FormBody.Builder} * @deprecated replaced by {@link #formBody(Map)}. */ @Deprecated public OkHttpRequest body(final Map formBodyByMap) { return formBody(formBodyByMap); } /** * * @param formBodyByBean * @return * @see {@code FormBody.Builder} * @deprecated replaced by {@link #formBody(Object)}. */ @Deprecated public OkHttpRequest body(final Object formBodyByBean) { return formBody(formBodyByBean); } /** * * @param body * @return * @see {@code RequestBody} */ public OkHttpRequest body(RequestBody body) { this.body = body; return this; } /** * * @param content * @param contentType * @return * @see RequestBody#create(MediaType, String) */ public OkHttpRequest body(String content, @Nullable MediaType contentType) { this.body = RequestBody.create(contentType, content); return this; } /** * * @param content * @param contentType * @return * @see RequestBody#create(MediaType, byte[]) */ public OkHttpRequest body(final byte[] content, @Nullable MediaType contentType) { this.body = RequestBody.create(contentType, content); return this; } /** * * * @param content * @param offset * @param byteCount * @param contentType * @return * @see RequestBody#create(MediaType, byte[], int, int) */ public OkHttpRequest body(final byte[] content, final int offset, final int byteCount, @Nullable MediaType contentType) { this.body = RequestBody.create(contentType, content, offset, byteCount); return this; } /** * * @param content * @param contentType * @return */ public OkHttpRequest body(final File content, @Nullable MediaType contentType) { this.body = RequestBody.create(contentType, content); return this; } /** * * * @return * @throws IOException */ public Response get() throws IOException { return execute(HttpMethod.GET); } /** * * * @param * @param resultClass * @return * @throws IOException */ public T get(Class resultClass) throws IOException { return execute(HttpMethod.GET, resultClass); } /** * * * @return * @throws IOException */ public Response post() throws IOException { return execute(HttpMethod.POST); } /** * * * @param * @param resultClass * @return * @throws IOException */ public T post(Class resultClass) throws IOException { return execute(HttpMethod.POST, resultClass); } /** * * * @return * @throws IOException */ public Response put() throws IOException { return execute(HttpMethod.PUT); } /** * * * @param * @param resultClass * @return * @throws IOException */ public T put(Class resultClass) throws IOException { return execute(HttpMethod.PUT, resultClass); } /** * * * @return * @throws IOException */ public Response patch() throws IOException { return execute(HttpMethod.PATCH); } /** * * * @param * @param resultClass * @return * @throws IOException */ public T patch(Class resultClass) throws IOException { return execute(HttpMethod.PATCH, resultClass); } /** * * * @return * @throws IOException */ public Response delete() throws IOException { return execute(HttpMethod.DELETE); } /** * * * @param * @param resultClass * @return * @throws IOException */ public T delete(Class resultClass) throws IOException { return execute(HttpMethod.DELETE, resultClass); } /** * * * @return * @throws IOException */ public Response head() throws IOException { return execute(HttpMethod.HEAD); } /** * * * @param httpMethod * @return * @throws IOException */ @Beta public Response execute(final HttpMethod httpMethod) throws IOException { // body = (body == null && HttpMethod.DELETE.equals(httpMethod)) ? Util.EMPTY_REQUEST : body; final Request request = createRequest(httpMethod); return execute(request); } /** * * * @param * @param httpMethod * @param resultClass * @return * @throws IOException */ @Beta public T execute(final HttpMethod httpMethod, final Class resultClass) throws IOException { N.checkArgNotNull(resultClass, "resultClass"); N.checkArgument(!HttpResponse.class.equals(resultClass), "Return type can't be HttpResponse"); final Request request = createRequest(httpMethod); try (Response resp = execute(request)) { if (Response.class.equals(resultClass)) { return (T) resp; } else if (resp.isSuccessful()) { final String contentType = request.header(HttpHeaders.Names.CONTENT_TYPE); final String contentEncoding = request.header(HttpHeaders.Names.CONTENT_ENCODING); final ContentFormat requestContentFormat = HttpUtil.getContentFormat(contentType, contentEncoding); final Charset requestCharset = HttpUtil.getCharset(contentType); final Map> respHeaders = resp.headers().toMultimap(); final Charset respCharset = HttpUtil.getResponseCharset(respHeaders, requestCharset); final ContentFormat respContentFormat = HttpUtil.getResponseContentFormat(respHeaders, requestContentFormat); final InputStream is = HttpUtil.wrapInputStream(resp.body().byteStream(), respContentFormat); if (resultClass == null || resultClass.equals(Void.class)) { return null; } else if (resultClass.equals(String.class)) { return (T) IOUtil.readAllToString(is, respCharset); } else if (byte[].class.equals(resultClass)) { return (T) IOUtil.readAllBytes(is); } else { if (respContentFormat == ContentFormat.KRYO && kryoParser != null) { return kryoParser.deserialize(resultClass, is); } else if (respContentFormat == ContentFormat.FormUrlEncoded) { return URLEncodedUtil.decode(IOUtil.readAllToString(is, respCharset), resultClass); } else { final BufferedReader br = Objectory.createBufferedReader(IOUtil.newInputStreamReader(is, respCharset)); try { return HttpUtil.getParser(respContentFormat).deserialize(resultClass, br); } finally { Objectory.recycle(br); } } } } else { throw new IOException(resp.code() + ": " + resp.message()); } } finally { doAfterExecution(); } } private Response execute(final Request request) throws IOException { try { return httpClient.newCall(request).execute(); } finally { doAfterExecution(); } } void doAfterExecution() { if (closeHttpClientAfterExecution) { // Shutdown isn't necessary? } } private Request createRequest(final HttpMethod httpMethod) { if (query == null || (query instanceof String && Strings.isEmpty((String) query))) { if (httpUrl == null) { requestBuilder.url(HttpUrl.get(url)); } else { requestBuilder.url(httpUrl); } } else { if (httpUrl == null) { requestBuilder.url(HttpUrl.get(URLEncodedUtil.encode(url, query))); } else { requestBuilder.url(HttpUrl.get(URLEncodedUtil.encode(httpUrl.toString(), query))); } } return requestBuilder.method(httpMethod.name(), body).build(); } /** * * * @return */ public ContinuableFuture asyncGet() { return asyncGet(HttpUtil.DEFAULT_EXECUTOR); } /** * * * @param executor * @return */ public ContinuableFuture asyncGet(final Executor executor) { return ContinuableFuture.call(this::get, executor); } /** * * * @param * @param resultClass * @return */ public ContinuableFuture asyncGet(final Class resultClass) { return asyncGet(resultClass, HttpUtil.DEFAULT_EXECUTOR); } /** * * * @param * @param resultClass * @param executor * @return */ public ContinuableFuture asyncGet(final Class resultClass, final Executor executor) { return ContinuableFuture.call(() -> get(resultClass), executor); } /** * * * @return */ public ContinuableFuture asyncPost() { return asyncPost(HttpUtil.DEFAULT_EXECUTOR); } /** * * * @param executor * @return */ public ContinuableFuture asyncPost(final Executor executor) { return ContinuableFuture.call(this::post, executor); } /** * * * @param * @param resultClass * @return */ public ContinuableFuture asyncPost(final Class resultClass) { return asyncPost(resultClass, HttpUtil.DEFAULT_EXECUTOR); } /** * * * @param * @param resultClass * @param executor * @return */ public ContinuableFuture asyncPost(final Class resultClass, final Executor executor) { return ContinuableFuture.call(() -> post(resultClass), executor); } /** * * * @return */ public ContinuableFuture asyncPut() { return asyncPut(HttpUtil.DEFAULT_EXECUTOR); } /** * * * @param executor * @return */ public ContinuableFuture asyncPut(final Executor executor) { return ContinuableFuture.call(this::put, executor); } /** * * * @param * @param resultClass * @return */ public ContinuableFuture asyncPut(final Class resultClass) { return asyncPut(resultClass, HttpUtil.DEFAULT_EXECUTOR); } /** * * * @param * @param resultClass * @param executor * @return */ public ContinuableFuture asyncPut(final Class resultClass, final Executor executor) { return ContinuableFuture.call(() -> put(resultClass), executor); } /** * * * @return */ public ContinuableFuture asyncPatch() { return asyncPatch(HttpUtil.DEFAULT_EXECUTOR); } /** * * * @param executor * @return */ public ContinuableFuture asyncPatch(final Executor executor) { return ContinuableFuture.call(this::patch, executor); } /** * * * @param * @param resultClass * @return */ public ContinuableFuture asyncPatch(final Class resultClass) { return asyncPatch(resultClass, HttpUtil.DEFAULT_EXECUTOR); } /** * * * @param * @param resultClass * @param executor * @return */ public ContinuableFuture asyncPatch(final Class resultClass, final Executor executor) { return ContinuableFuture.call(() -> patch(resultClass), executor); } /** * * * @return */ public ContinuableFuture asyncDelete() { return asyncDelete(HttpUtil.DEFAULT_EXECUTOR); } /** * * * @param executor * @return */ public ContinuableFuture asyncDelete(final Executor executor) { return ContinuableFuture.call(this::delete, executor); } /** * * * @param * @param resultClass * @return */ public ContinuableFuture asyncDelete(final Class resultClass) { return asyncDelete(resultClass, HttpUtil.DEFAULT_EXECUTOR); } /** * * * @param * @param resultClass * @param executor * @return */ public ContinuableFuture asyncDelete(final Class resultClass, final Executor executor) { return ContinuableFuture.call(() -> delete(resultClass), executor); } /** * * * @return */ public ContinuableFuture asyncHead() { return asyncHead(HttpUtil.DEFAULT_EXECUTOR); } /** * * * @param executor * @return */ public ContinuableFuture asyncHead(final Executor executor) { return ContinuableFuture.call(this::head, executor); } /** * * * @param httpMethod * @return */ @Beta public ContinuableFuture asyncExecute(final HttpMethod httpMethod) { return asyncExecute(httpMethod, HttpUtil.DEFAULT_EXECUTOR); } /** * * * @param httpMethod * @param executor * @return */ @Beta public ContinuableFuture asyncExecute(final HttpMethod httpMethod, final Executor executor) { return ContinuableFuture.call(() -> execute(httpMethod), executor); } /** * * * @param * @param httpMethod * @param resultClass * @return */ @Beta public ContinuableFuture asyncExecute(final HttpMethod httpMethod, final Class resultClass) { return asyncExecute(httpMethod, resultClass, HttpUtil.DEFAULT_EXECUTOR); } /** * * * @param * @param httpMethod * @param resultClass * @param executor * @return */ @Beta public ContinuableFuture asyncExecute(final HttpMethod httpMethod, final Class resultClass, final Executor executor) { return ContinuableFuture.call(() -> execute(httpMethod, resultClass), executor); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy