| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340 |
- package com.cyksj.common.util;
- import org.apache.commons.lang3.StringUtils;
- import java.io.IOException;
- import java.net.URI;
- import java.net.http.HttpClient;
- import java.net.http.HttpRequest;
- import java.net.http.HttpResponse;
- import java.time.Duration;
- import java.util.concurrent.CompletableFuture;
- /**
- * debug jvm params:
- * 1. httpClient:
- * -Djdk.internal.httpclient.debug=true
- * 2. webSocket:
- * -Djdk.internal.httpclient.websocket.debug=true
- * 3. hpack
- * -Djdk.internal.httpclient.hpack.debug=true
- *
- * <p>
- * jdk11 Http Client is Thread-safe
- * Build connection pool and selector thread pool internally
- * </p>
- *
- * @author valor.
- */
- public class J11HttpC {
- private J11HttpC() { }
- private static final J11HttpC instance;
- static {
- instance = new J11HttpC();
- }
- /* ---------- register http client (Editable) ---------- */
- /**
- * 连接超时时间
- */
- private static final Duration CONNECT_TIMEOUT = Duration.ofSeconds(4);
- /**
- * 读取超时时间
- */
- private static final Duration SOCKET_TIMEOUT = Duration.ofSeconds(6);
- /**
- * Thread-safe
- */
- private static final HttpClient defaultClient;
- static {
- defaultClient = HttpClient.newBuilder()
- .connectTimeout(CONNECT_TIMEOUT)
- .build();
- }
- /**
- * 微信退款 专用 ssl http client
- */
- private static final HttpClient wx_refund;
- static {
- wx_refund = HttpClient.newBuilder()
- .connectTimeout(CONNECT_TIMEOUT)
- .sslContext(NSslKit.KeyCert.wechat_refund.getSslContext())
- .build();
- }
- /**
- * http clients
- */
- public enum Client {
- BASE(defaultClient),
- WX_REFUND(wx_refund),
- ;
- private HttpClient symbol;
- Client(HttpClient symbol) {
- this.symbol = symbol;
- }
- }
- /* ---------- http request builder ---------- */
- public static Builder custom() {
- return new Builder();
- }
- public static class Builder {
- private String url;
- private Method method;
- private KVAdapter[] headers;
- private HttpRequest.BodyPublisher bodyPublisher;
- /**
- * cache URI
- */
- private boolean cacheURI;
- private Builder() {
- this.cacheURI = false;
- }
- public Builder url(String url) {
- this.url = url;
- return this;
- }
- public Builder cacheURI() {
- this.cacheURI = true;
- return this;
- }
- public Builder method(Method method) {
- this.method = method;
- return this;
- }
- public Builder ofGet() {
- this.method = Method.GET;
- return this;
- }
- public Builder ofPost() {
- this.method = Method.POST;
- return this;
- }
- public Builder headers(ReqType reqType) {
- this.headers = new KVAdapter[] {ofHeader("content-type", reqType.mime)};
- return this;
- }
- public Builder headers(KVAdapter... headers) {
- this.headers = headers;
- return this;
- }
- public Builder headers(ReqType reqType, KVAdapter... headers) {
- final int oldLength = headers.length;
- final int newLength = oldLength+ 1;
- KVAdapter[] newHeaders = new KVAdapter[newLength];
- System.arraycopy(headers, 0, newHeaders, 1, oldLength);
- newHeaders[0] = ofHeader("content-type", reqType.mime);
- this.headers = newHeaders;
- return this;
- }
- public Builder body(HttpRequest.BodyPublisher bodyPublisher) {
- this.bodyPublisher = bodyPublisher;
- return this;
- }
- /* --- 同步send --- */
- public <T> HttpResponse<T> send(HttpResponse.BodyHandler<T> responseBodyHandler) throws IOException, InterruptedException {
- return this.send(Client.BASE, responseBodyHandler);
- }
- public <T> HttpResponse<T> send(Client httpClient, HttpResponse.BodyHandler<T> responseBodyHandler) throws IOException, InterruptedException {
- return instance.send(httpClient.symbol, this.getRequest(), responseBodyHandler);
- }
- /* --- 异步send --- */
- public <T> CompletableFuture<HttpResponse<T>> sendAsync(HttpResponse.BodyHandler<T> responseBodyHandler) {
- return this.sendAsync(Client.BASE, responseBodyHandler);
- }
- public <T> CompletableFuture<HttpResponse<T>> sendAsync(Client httpClient, HttpResponse.BodyHandler<T> responseBodyHandler) {
- return instance.sendAsync(httpClient.symbol, this.getRequest(), responseBodyHandler);
- }
- /* --- ------- --- */
- private HttpRequest getRequest() {
- // require http URL
- if (StringUtils.isBlank(this.url)) {
- throw new RuntimeException("Empty Http URL.");
- }
- // default http method: GET
- if (this.method == null) {
- this.method = Method.GET;
- }
- // default http request body: noBody
- if (this.bodyPublisher == null) {
- this.bodyPublisher = HttpRequest.BodyPublishers.noBody();
- }
- URI uri = getURI(this.url, this.cacheURI);
- HttpRequest request;
- if (this.headers == null || this.headers.length <= 0) {
- request = instance.buildRequest(uri, this.method, this.bodyPublisher);
- } else {
- request = instance.buildRequest(uri, this.method, this.bodyPublisher, this.headers);
- }
- return request;
- }
- }
- /* ---------- cache http request uri ---------- */
- /**
- * 缓存URI的数量
- */
- // private static final int URI_CACHE_SIZE = 25;
- /**
- * 减少URI类的解析 LRU cache
- */
- // private static volatile LruCache<String, URI> uriCache = new LruCache<>(URI_CACHE_SIZE);
- /**
- * 获取URI
- */
- private static URI getURI(String url, boolean cache) {
- // 非常频繁的接口调用 会导致cache维护开销增加
- // if (cache) {
- // URI uri = uriCache.get(url);
- // if (null == uri) {
- // uri = URI.create(url);
- // uriCache.put(url, uri);
- // }
- // return uri;
- // }
- return URI.create(url);
- }
- /* ---------- pack http client ---------- */
- /**
- * 构建request
- *
- * @param uri {@link URI}
- * @param headers {@link J11HttpC.KVAdapter}
- * @param httpMethod {@link J11HttpC.Method}
- * @param bodyPublisher {@link HttpRequest.BodyPublisher}
- * @return {@link HttpRequest}
- */
- private HttpRequest buildRequest(URI uri,
- Method httpMethod,
- HttpRequest.BodyPublisher bodyPublisher,
- KVAdapter... headers) {
- HttpRequest.Builder builder = HttpRequest.newBuilder();
- builder.timeout(SOCKET_TIMEOUT);
- builder.uri(uri);
- // 设置请求头
- if (headers.length > 0) {
- for (KVAdapter e : headers) {
- builder.setHeader(e.name, e.value);
- }
- }
- // 设置请求方式和body参数
- builder.method(httpMethod.symbol, bodyPublisher);
- return builder.build();
- }
- /**
- * 同步send
- *
- * @see HttpClient#send(HttpRequest, HttpResponse.BodyHandler)
- */
- public <T> HttpResponse<T>
- send(HttpClient httpClient, HttpRequest request, HttpResponse.BodyHandler<T> responseBodyHandler)
- throws IOException, InterruptedException {
- return httpClient.send(request, responseBodyHandler);
- }
- /**
- * 同步send
- *
- * @see HttpClient#sendAsync(HttpRequest, HttpResponse.BodyHandler)
- */
- public <T> CompletableFuture<HttpResponse<T>>
- sendAsync(HttpClient httpClient, HttpRequest request, HttpResponse.BodyHandler<T> responseBodyHandler) {
- return httpClient.sendAsync(request, responseBodyHandler);
- }
- /* ---------- http extensions ---------- */
- public static KVAdapter ofHeader(String name, String value) {
- return new KVAdapter(name, value);
- }
- /**
- * Http Request Header
- */
- public static final class KVAdapter {
- private String name;
- private String value;
- private KVAdapter(String name, String value) {
- this.name = name;
- this.value = value;
- }
- }
- public enum Method {
- GET("GET"),
- POST("POST"),
- HEAD("HEAD"),
- OPTIONS("OPTIONS"),
- PUT("PUT"),
- DELETE("DELETE"),
- TRACE("TRACE");
- private String symbol;
- Method(String symbol) {
- this.symbol = symbol;
- }
- }
- public enum ReqType {
- // form_data("multipart/form-data"),
- form_urlencoded("application/x-www-form-urlencoded; charset=utf-8"),
- raw_text("text/plain; charset=utf-8"),
- raw_js("application/javascript; charset=utf-8"),
- raw_json("application/json; charset=utf-8"),
- raw_html("text/html; charset=utf-8"),
- raw_xml("application/xml; charset=utf-8");
- // binary("application/octet-stream");
- private String mime;
- ReqType(String mime) {
- this.mime = mime;
- }
- }
- }
|