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

org.aoju.bus.http.Httpx Maven / Gradle / Ivy

The newest version!
/*********************************************************************************
 *                                                                               *
 * The MIT License (MIT)                                                         *
 *                                                                               *
 * Copyright (c) 2015-2022 aoju.org and other contributors.                      *
 *                                                                               *
 * Permission is hereby granted, free of charge, to any person obtaining a copy  *
 * of this software and associated documentation files (the "Software"), to deal *
 * in the Software without restriction, including without limitation the rights  *
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell     *
 * copies of the Software, and to permit persons to whom the Software is         *
 * furnished to do so, subject to the following conditions:                      *
 *                                                                               *
 * The above copyright notice and this permission notice shall be included in    *
 * all copies or substantial portions of the Software.                           *
 *                                                                               *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR    *
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,      *
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE   *
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER        *
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, *
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN     *
 * THE SOFTWARE.                                                                 *
 *                                                                               *
 ********************************************************************************/
package org.aoju.bus.http;

import org.aoju.bus.core.exception.InternalException;
import org.aoju.bus.core.lang.*;
import org.aoju.bus.core.net.tls.SSLContextBuilder;
import org.aoju.bus.core.toolkit.ArrayKit;
import org.aoju.bus.core.toolkit.MapKit;
import org.aoju.bus.core.toolkit.ObjectKit;
import org.aoju.bus.core.toolkit.StringKit;
import org.aoju.bus.http.accord.ConnectionPool;
import org.aoju.bus.http.bodys.FormBody;
import org.aoju.bus.http.bodys.MultipartBody;
import org.aoju.bus.http.bodys.RequestBody;
import org.aoju.bus.http.metric.Dispatcher;
import org.aoju.bus.http.plugin.httpx.HttpProxy;
import org.aoju.bus.logger.Logger;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.X509TrustManager;
import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * 发送HTTP请求辅助类
 *
 * @author Kimi Liu
 * @since Java 17+
 */
public class Httpx {

    /**
     * 懒汉安全加同步
     * 私有的静态成员变量 只声明不创建
     * 私有的构造方法
     * 提供返回实例的静态方法
     */
    private static Httpd httpd;

    static {
        new Httpx(SSLContextBuilder.newTrustManager());
    }

    /**
     * 提供返回实例的静态方法
     */
    public Httpx() {
        this(30, 30, 30);
    }

    /**
     * 提供返回实例的静态方法
     *
     * @param x509TrustManager 信任管理器
     */
    public Httpx(X509TrustManager x509TrustManager) {
        this(null, null, 30, 30, 30, 64, 5, 5, 5, SSLContextBuilder.newSslSocketFactory(x509TrustManager), x509TrustManager, (hostname, session) -> true);
    }

    /**
     * 构建一个自定义配置的 HTTP Client 类
     *
     * @param connTimeout  连接
     * @param readTimeout  读取
     * @param writeTimeout 输出
     */
    public Httpx(int connTimeout,
                 int readTimeout,
                 int writeTimeout) {
        this(null, null, connTimeout, readTimeout, writeTimeout, Normal._64, 5, 5, 5);
    }

    /**
     * 构建一个自定义配置的 HTTP Client 类
     *
     * @param connTimeout        连接
     * @param readTimeout        读取
     * @param writeTimeout       输出
     * @param maxRequests        最大请求
     * @param maxRequestsPerHost 主机最大请求
     * @param maxIdleConnections 最大连接
     * @param keepAliveDuration  链接时长
     */
    public Httpx(int connTimeout,
                 int readTimeout,
                 int writeTimeout,
                 int maxRequests,
                 int maxRequestsPerHost,
                 int maxIdleConnections,
                 int keepAliveDuration) {
        this(null, null, connTimeout, readTimeout, writeTimeout, maxRequests, maxRequestsPerHost, maxIdleConnections, keepAliveDuration);
    }

    /**
     * 构建一个自定义配置的 HTTP Client 类
     *
     * @param dns                DNS 信息
     * @param httpProxy          代理信息
     * @param connTimeout        连接
     * @param readTimeout        读取
     * @param writeTimeout       输出
     * @param maxRequests        最大请求
     * @param maxRequestsPerHost 主机最大请求
     * @param maxIdleConnections 最大连接
     * @param keepAliveDuration  链接时长
     */
    public Httpx(DnsX dns,
                 HttpProxy httpProxy,
                 int connTimeout,
                 int readTimeout,
                 int writeTimeout,
                 int maxRequests,
                 int maxRequestsPerHost,
                 int maxIdleConnections,
                 int keepAliveDuration
    ) {
        this(dns, httpProxy, connTimeout, readTimeout, writeTimeout, maxRequests, maxRequestsPerHost, maxIdleConnections, keepAliveDuration, null, null, null);
    }

    /**
     * 构建一个自定义配置的 HTTP Client 类
     *
     * @param dns                DNS 信息
     * @param httpProxy          代理信息
     * @param connTimeout        连接
     * @param readTimeout        读取
     * @param writeTimeout       输出
     * @param maxRequests        最大请求
     * @param maxRequestsPerHost 主机最大请求
     * @param maxIdleConnections 最大连接
     * @param keepAliveDuration  链接时长
     * @param sslSocketFactory   抽象类,扩展自SocketFactory, SSLSocket的工厂
     * @param x509TrustManager   证书信任管理器
     * @param hostnameVerifier   主机名校验信息
     */
    public Httpx(final DnsX dns,
                 final HttpProxy httpProxy,
                 int connTimeout,
                 int readTimeout,
                 int writeTimeout,
                 int maxRequests,
                 int maxRequestsPerHost,
                 int maxIdleConnections,
                 int keepAliveDuration,
                 SSLSocketFactory sslSocketFactory,
                 javax.net.ssl.X509TrustManager x509TrustManager,
                 HostnameVerifier hostnameVerifier
    ) {
        synchronized (Httpx.class) {
            Dispatcher dispatcher = new Dispatcher();
            dispatcher.setMaxRequests(maxRequests);
            dispatcher.setMaxRequestsPerHost(maxRequestsPerHost);
            ConnectionPool connectPool = new ConnectionPool(maxIdleConnections,
                    keepAliveDuration, TimeUnit.MINUTES);
            Httpd.Builder builder = new Httpd.Builder();

            builder.dispatcher(dispatcher);
            builder.connectionPool(connectPool);
            builder.addNetworkInterceptor(chain -> {
                Request request = chain.request();
                return chain.proceed(request);
            });
            if (ObjectKit.isNotEmpty(dns)) {
                builder.dns(hostname -> {
                    try {
                        return dns.lookup(hostname);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    return DnsX.SYSTEM.lookup(hostname);
                });
            }
            if (ObjectKit.isNotEmpty(httpProxy)) {
                builder.proxy(httpProxy.proxy());
                if (null != httpProxy.user && null != httpProxy.password) {
                    builder.proxyAuthenticator(httpProxy.authenticator());
                }
            }
            builder.connectTimeout(connTimeout, TimeUnit.SECONDS);
            builder.readTimeout(readTimeout, TimeUnit.SECONDS);
            builder.writeTimeout(writeTimeout, TimeUnit.SECONDS);
            if (ObjectKit.isNotEmpty(sslSocketFactory)) {
                builder.sslSocketFactory(sslSocketFactory, x509TrustManager);
            }
            if (ObjectKit.isNotEmpty(hostnameVerifier)) {
                builder.hostnameVerifier(hostnameVerifier);
            }
            httpd = builder.build();
        }
    }

    /**
     * 简单的 GET 请求 使用默认编码 UTF-8
     *
     * @param url URL地址 String
     * @return String
     */
    public static String get(final String url) {
        return get(url, Charset.DEFAULT_UTF_8);
    }

    /**
     * 简单的 GET 请求 使用自定义编码
     *
     * @param url     URL地址 String
     * @param charset 自定义编码 String
     * @return String
     */
    public static String get(final String url, final String charset) {
        return execute(Builder.builder().url(url).requestCharset(charset).responseCharset(charset).build());
    }

    /**
     * 异步get请求,回调
     *
     * @param url     URL地址
     * @param isAsync 是否异步
     * @return String
     */
    public static String get(final String url, final boolean isAsync) {
        if (isAsync) {
            return enqueue(Builder.builder().url(url).method(Http.GET).build());
        }
        return get(url);
    }

    /**
     * 带查询参数 GET 请求 使用默认编码 UTF-8
     *
     * @param url      URL地址 String
     * @param queryMap 查询参数 Map
     * @return String
     */
    public static String get(final String url, final Map queryMap) {
        return get(url, queryMap, null, Charset.DEFAULT_UTF_8);
    }

    /**
     * 带查询参数 GET 请求 使用默认编码 UTF-8
     *
     * @param url       URL地址 String
     * @param queryMap  查询参数 Map
     * @param headerMap Header参数 Map
     * @return String
     */
    public static String get(final String url, final Map queryMap, Map headerMap) {
        return get(url, queryMap, headerMap, Charset.DEFAULT_UTF_8);
    }

    /**
     * 带查询参数 GET 请求 使用自定义编码
     *
     * @param url       URL地址 String
     * @param queryMap  查询参数 Map
     * @param headerMap Header参数 Map
     * @param charset   自定义编码 String
     * @return String
     */
    public static String get(final String url, final Map queryMap, Map headerMap,
                             final String charset) {
        return execute(Builder.builder().url(url).headerMap(headerMap).queryMap(queryMap)
                .requestCharset(charset).responseCharset(charset).build());
    }

    /**
     * 异步处理的GET请求,自定义请求类型
     *
     * @param url      URL地址 String
     * @param callback 回调信息 callback
     */
    public static void get(String url, Callback callback) {
        Request request = new Request.Builder().url(url).get().build();
        NewCall call = httpd.newCall(request);
        call.enqueue(callback);
    }

    /**
     * 异步处理的POST请求,自定义请求类型
     *
     * @param url      URL地址 String
     * @param queryMap 查询参数 Map
     * @param callback 回调信息 callback
     */
    public static void post(String url, Map queryMap, Callback callback) {
        StringBuilder data = new StringBuilder();
        if (ObjectKit.isNotEmpty(queryMap)) {
            Set keys = queryMap.keySet();
            for (String key : keys) {
                data.append(key).append(Symbol.EQUAL).append(queryMap.get(key)).append(Symbol.AND);
            }
        }
        RequestBody requestBody = RequestBody.create(MediaType.TEXT_HTML_TYPE, data.toString());
        Request request = new Request.Builder().url(url).post(requestBody).build();
        NewCall call = httpd.newCall(request);
        call.enqueue(callback);
    }

    /**
     * form 方式 POST 请求
     *
     * @param url URL地址 String
     * @return String
     */
    public static String post(final String url) {
        return post(url, null);
    }

    /**
     * form 方式 POST 请求
     * application/x-www-form-urlencoded
     *
     * @param url     URL地址 String
     * @param formMap 查询参数 Map
     * @return String
     */
    public static String post(final String url, final Map formMap) {
        String data = Normal.EMPTY;
        if (MapKit.isNotEmpty(formMap)) {
            data = formMap.entrySet().stream()
                    .map(entry -> String.format("%s=%s", entry.getKey(), entry.getValue()))
                    .collect(Collectors.joining(Symbol.AND));
        }
        return post(url, data, MediaType.APPLICATION_FORM_URLENCODED);
    }

    /**
     * 带查询参数 POST 请求 使用默认编码 UTF-8
     *
     * @param url       URL地址 String
     * @param data      请求数据 String
     * @param mediaType 类型 String
     * @return String
     */
    public static String post(final String url, final String data,
                              final String mediaType) {
        return post(url, data, mediaType, Charset.DEFAULT_UTF_8);
    }

    /**
     * 带查询参数 POST 请求 使用自定义编码
     *
     * @param url       URL地址 String
     * @param data      请求数据 String
     * @param mediaType 类型 String
     * @param charset   自定义编码 String
     * @return String
     */
    public static String post(final String url, final String data, final String mediaType,
                              final String charset) {
        return execute(Builder.builder().url(url).method(Http.POST).data(data).mediaType(mediaType)
                .requestCharset(charset).responseCharset(charset).build());
    }

    /**
     * 带查询参数 POST 请求 使用默认编码 UTF-8
     *
     * @param url       URL地址 String
     * @param queryMap  请求数据 Map
     * @param mediaType 类型 String
     * @return String
     */
    public static String post(final String url, final Map queryMap,
                              final String mediaType) {
        return post(url, queryMap, mediaType, Charset.DEFAULT_UTF_8);
    }

    /**
     * 带查询参数 POST 请求 使用自定义编码
     *
     * @param url       URL地址 String
     * @param headerMap 头部数据 Map
     * @param queryMap  请求数据 Map
     * @return String
     */
    public static String post(final String url, final Map queryMap,
                              final Map headerMap) {
        return post(url, queryMap, headerMap, MediaType.APPLICATION_FORM_URLENCODED);
    }

    /**
     * 带查询参数 POST 请求 使用自定义编码
     *
     * @param url       URL地址 String
     * @param queryMap  请求数据 Map
     * @param mediaType 类型 String
     * @param charset   自定义编码 String
     * @return String
     */
    public static String post(final String url, final Map queryMap,
                              final String mediaType, final String charset) {
        return execute(Builder.builder().url(url).method(Http.POST).queryMap(queryMap).mediaType(mediaType)
                .requestCharset(charset).responseCharset(charset).build());
    }

    /**
     * 带查询参数 POST 请求 使用自定义编码
     *
     * @param url       URL地址 String
     * @param headerMap 头部数据 Map
     * @param queryMap  请求数据 Map
     * @param mediaType 类型 String
     * @return String
     */
    public static String post(final String url, final Map queryMap,
                              final Map headerMap, final String mediaType) {
        return post(url, queryMap, headerMap, mediaType, Charset.DEFAULT_UTF_8);
    }

    /**
     * 带查询参数 POST 请求 使用自定义编码
     *
     * @param url       URL地址 String
     * @param headerMap 头部数据 Map
     * @param queryMap  请求数据 Map
     * @param mediaType 类型 String
     * @param charset   自定义编码 String
     * @return String
     */
    public static String post(final String url, final Map queryMap,
                              final Map headerMap, final String mediaType,
                              final String charset) {
        return execute(Builder.builder().url(url).method(Http.POST).headerMap(headerMap).queryMap(queryMap)
                .mediaType(mediaType).requestCharset(charset).responseCharset(charset).build());
    }

    /**
     * 表单提交带文件上传
     *
     * @param url      请求地址 String
     * @param params   请求参数 Map
     * @param pathList 上传文件 List
     * @return String
     */
    public static String post(final String url, final Map params,
                              final List pathList) {
        MediaType mediaType = MediaType.valueOf(MediaType.APPLICATION_FORM_URLENCODED + Symbol.SEMICOLON + Charset.DEFAULT_UTF_8);
        RequestBody bodyParams = RequestBody.create(mediaType, params.toString());
        MultipartBody.Builder requestBodyBuilder = new MultipartBody.Builder().setType(MediaType.MULTIPART_FORM_DATA_TYPE)
                .addFormDataPart("params", Normal.EMPTY, bodyParams);

        File file;
        for (String path : pathList) {
            file = new File(path);
            requestBodyBuilder.addFormDataPart("file", file.getName(), RequestBody.create(mediaType, new File(path)));
        }
        RequestBody requestBody = requestBodyBuilder.build();
        Request request = new Request.Builder().url(url).post(requestBody).build();
        String result = Normal.EMPTY;
        try {
            Response response = httpd.newCall(request).execute();
            if (null != response.body()) {
                byte[] bytes = response.body().bytes();
                result = new String(bytes, Charset.DEFAULT_UTF_8);
            }
        } catch (Exception e) {
            Logger.error(">>>>>>>>Requesting HTTP Error [%s]<<<<<<<<", e);
        }
        return result;
    }

    /**
     * 通用同步执行方法
     *
     * @param builder Builder
     * @return Request 信息
     */
    private static Request.Builder builder(final Builder builder) {
        if (StringKit.isBlank(builder.requestCharset)) {
            builder.requestCharset = Charset.DEFAULT_UTF_8;
        }
        if (StringKit.isBlank(builder.responseCharset)) {
            builder.responseCharset = Charset.DEFAULT_UTF_8;
        }
        if (StringKit.isBlank(builder.method)) {
            builder.method = Http.GET;
        }
        if (StringKit.isBlank(builder.mediaType)) {
            builder.mediaType = MediaType.APPLICATION_FORM_URLENCODED;
        }
        if (builder.tracer) {
            Logger.info(">>>>>>>>Builder[{}]<<<<<<<<", builder.toString());
        }

        Request.Builder request = new Request.Builder();

        if (MapKit.isNotEmpty(builder.headerMap)) {
            builder.headerMap.forEach(request::addHeader);
        }
        String method = builder.method.toUpperCase();
        String mediaType = String.format("%s;charset=%s", builder.mediaType, builder.requestCharset);
        if (StringKit.equals(method, Http.GET)) {
            if (MapKit.isNotEmpty(builder.queryMap)) {
                String queryParams = builder.queryMap.entrySet().stream()
                        .map(entry -> String.format("%s=%s", entry.getKey(), entry.getValue()))
                        .collect(Collectors.joining(Symbol.AND));
                builder.url = String.format("%s%s%s", builder.url, builder.url.contains(Symbol.QUESTION_MARK) ? Symbol.AND : Symbol.QUESTION_MARK, queryParams);
            }
            request.get();
        } else if (ArrayKit.contains(new String[]{Http.POST, Http.PUT, Http.DELETE, Http.PATCH}, method)) {
            if (StringKit.isNotEmpty(builder.data)) {
                RequestBody requestBody = RequestBody.create(MediaType.valueOf(mediaType), builder.data);
                request.method(method, requestBody);
            }
            if (MapKit.isNotEmpty(builder.queryMap)) {
                FormBody.Builder form = new FormBody.Builder(Charset.UTF_8);
                builder.queryMap.forEach((key, value) -> form.add(key, StringKit.toString(value)));
                request.method(method, form.build());
            }
        } else {
            throw new InternalException(String.format(">>>>>>>>Request Method Not found[%s]<<<<<<<<", method));
        }
        return request;
    }

    /**
     * 通用同步执行方法
     *
     * @param builder Builder
     * @return String 执行结果
     */
    private static String execute(final Builder builder) {
        Request.Builder request = builder(builder);
        String result = Normal.EMPTY;
        try {
            Response response = httpd.newCall(request.url(builder.url).build()).execute();
            if (null != response.body()) {
                byte[] bytes = response.body().bytes();
                result = new String(bytes, builder.responseCharset);
            }
            if (builder.tracer) {
                Logger.info(">>>>>>>>Url[{}],Response[{}]<<<<<<<<", builder.url, result);
            }
        } catch (Exception e) {
            Logger.error(e, ">>>>>>>>Builder[{}] Error<<<<<<<<", builder.toString());
        }
        return result;
    }

    /**
     * 通用异步执行方法
     *
     * @param builder Builder
     * @return String 执行结果
     */
    private static String enqueue(final Builder builder) {
        Request.Builder request = builder(builder);
        NewCall call = httpd.newCall(request.url(builder.url).build());
        String[] result = {Normal.EMPTY};
        call.enqueue(new Callback() {
            @Override
            public void onFailure(NewCall call, IOException e) {
                Logger.info(String.format(">>>>>>>>Url[%s] Failure<<<<<<<<", builder.url));
            }

            @Override
            public void onResponse(NewCall call, Response response) throws IOException {
                if (null != response.body()) {
                    byte[] bytes = response.body().bytes();
                    result[0] = new String(bytes, builder.responseCharset);
                    if (builder.tracer) {
                        Logger.info(">>>>>>>>Url[{}],Response[{}]<<<<<<<<", builder.url, result[0]);
                    }
                }
            }
        });
        return result[0];
    }

    @lombok.Builder
    @lombok.ToString
    private static class Builder {
        /**
         * 请求 url
         */
        private String url;
        /**
         * 方法类型
         */
        private String method;
        /**
         * 请求参数
         */
        private String data;
        /**
         * 数据格式类型
         */
        private String mediaType;
        /**
         * 请求参数
         */
        private Map queryMap;
        /**
         * 头部参数
         */
        private Map headerMap;

        /**
         * 请求编码
         */
        private String requestCharset;
        /**
         * 响应编码
         */
        private String responseCharset;
        /**
         * 日志追踪
         */
        private boolean tracer;
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy