zhang
2 天以前 14f2c4fce50c871d84f89d2dca2298e0892b4672
zy-acs-hex/src/main/java/com/zy/acs/hex/utils/HttpGo.java
New file
@@ -0,0 +1,354 @@
package com.zy.acs.hex.utils;
import lombok.extern.slf4j.Slf4j;
import okhttp3.*;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.security.SecureRandom;
import java.security.cert.X509Certificate;
import java.time.Duration;
import java.util.*;
import java.util.concurrent.TimeUnit;
/**
 * Minimal OkHttp wrapper: GET / POST only.
 * <p>
 * - fluent API (get / postJson / postForm)
 * - default singleton instance (thread-safe)
 * - per-request headers + default headers
 * - simple response wrapper with tookMs
 * - optional trust-all SSL (ONLY for internal test)
 */
@Slf4j
public final class HttpGo {
    private static final MediaType JSON = MediaType.get("application/json; charset=utf-8");
    private static final Charset DEFAULT_CHARSET = StandardCharsets.UTF_8;
    private final OkHttpClient client;
    private final Map<String, String> defaultHeaders;
    private HttpGo(OkHttpClient client, Map<String, String> defaultHeaders) {
        this.client = Objects.requireNonNull(client, "client");
        this.defaultHeaders = defaultHeaders == null
                ? Collections.emptyMap()
                : Collections.unmodifiableMap(new LinkedHashMap<>(defaultHeaders));
    }
    /**
     * Shared default instance (safe SSL by default).
     */
    public static HttpGo defaults() {
        return Holder.DEFAULT;
    }
    public static Builder builder() {
        return new Builder();
    }
    // ===================== GET =====================
    public HttpResponse get(String url) throws IOException {
        return get(url, null, null);
    }
    public HttpResponse get(String url, Map<String, String> queryParams) throws IOException {
        return get(url, null, queryParams);
    }
    public HttpResponse get(String url, Map<String, String> headers, Map<String, String> queryParams) throws IOException {
        HttpUrl parsed = HttpUrl.parse(url);
        if (parsed == null) throw new IllegalArgumentException("Invalid url: " + url);
        HttpUrl.Builder ub = parsed.newBuilder();
        if (queryParams != null) {
            queryParams.forEach((k, v) -> {
                if (k != null && v != null) ub.addQueryParameter(k, v);
            });
        }
        Request.Builder rb = new Request.Builder().url(ub.build()).get();
        applyHeaders(rb, headers);
        return execute(rb.build());
    }
    // ===================== POST =====================
    /**
     * POST JSON string payload (null/blank -> "{}").
     */
    public HttpResponse postJson(String url, String json) throws IOException {
        return postJson(url, null, json);
    }
    /**
     * POST JSON string payload (null/blank -> "{}").
     */
    public HttpResponse postJson(String url, Map<String, String> headers, String json) throws IOException {
        String payload = (json == null || json.trim().isEmpty()) ? "{}" : json;
        RequestBody body = RequestBody.create(payload, JSON);
        Request.Builder rb = new Request.Builder().url(url).post(body);
        applyHeaders(rb, headers);
        // ensure content-type unless caller overrides
        if (rb.build().header("Content-Type") == null) {
            rb.header("Content-Type", "application/json; charset=utf-8");
        }
        return execute(rb.build());
    }
    /**
     * POST x-www-form-urlencoded fields.
     */
    public HttpResponse postForm(String url, Map<String, String> formFields) throws IOException {
        return postForm(url, null, formFields);
    }
    /**
     * POST x-www-form-urlencoded fields.
     */
    public HttpResponse postForm(String url, Map<String, String> headers, Map<String, String> formFields) throws IOException {
        FormBody.Builder fb = new FormBody.Builder(DEFAULT_CHARSET);
        if (formFields != null) {
            formFields.forEach((k, v) -> {
                if (k != null && v != null) fb.add(k, v);
            });
        }
        Request.Builder rb = new Request.Builder().url(url).post(fb.build());
        applyHeaders(rb, headers);
        return execute(rb.build());
    }
    // ===================== Internals =====================
    private HttpResponse execute(Request request) throws IOException {
        long start = System.nanoTime();
        try (Response resp = client.newCall(request).execute()) {
            long tookMs = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start);
            String body = null;
            ResponseBody rb = resp.body();
            if (rb != null) body = rb.string(); // one-shot
            return new HttpResponse(resp.code(), resp.headers(), body, tookMs);
        } catch (IOException e) {
            log.error("HttpGo request failed: {} {}", request.method(), request.url(), e);
            throw e;
        }
    }
    private void applyHeaders(Request.Builder rb, Map<String, String> headers) {
        // defaults first, then per-request overrides
        if (defaultHeaders != null) {
            defaultHeaders.forEach((k, v) -> {
                if (k != null && v != null) rb.header(k, v);
            });
        }
        if (headers != null) {
            headers.forEach((k, v) -> {
                if (k != null && v != null) rb.header(k, v);
            });
        }
    }
    private static final class Holder {
        private static final HttpGo DEFAULT = HttpGo.builder().build();
    }
    // ===================== Response =====================
    public static final class HttpResponse {
        private final int statusCode;
        private final Headers headers;
        private final String body;
        private final long tookMs;
        public HttpResponse(int statusCode, Headers headers, String body, long tookMs) {
            this.statusCode = statusCode;
            this.headers = headers == null ? new Headers.Builder().build() : headers;
            this.body = body;
            this.tookMs = tookMs;
        }
        public int statusCode() {
            return statusCode;
        }
        public Headers headers() {
            return headers;
        }
        public String body() {
            return body;
        }
        public long tookMs() {
            return tookMs;
        }
        public boolean is2xx() {
            return statusCode >= 200 && statusCode < 300;
        }
        public String header(String name) {
            return headers.get(name);
        }
        @Override
        public String toString() {
            return "HttpResponse{status=" + statusCode + ", tookMs=" + tookMs
                    + ", bodyLen=" + (body == null ? 0 : body.length()) + "}";
        }
    }
    // ===================== Builder =====================
    public static final class Builder {
        private Duration connectTimeout = Duration.ofSeconds(10);
        private Duration readTimeout = Duration.ofSeconds(20);
        private Duration writeTimeout = Duration.ofSeconds(20);
        private boolean trustAllSsl = false;
        private final Map<String, String> defaultHeaders = new LinkedHashMap<>();
        public Builder defaultHeader(String name, String value) {
            if (name != null && value != null) defaultHeaders.put(name, value);
            return this;
        }
        public Builder connectTimeout(Duration d) {
            if (d != null) connectTimeout = d;
            return this;
        }
        public Builder readTimeout(Duration d) {
            if (d != null) readTimeout = d;
            return this;
        }
        public Builder writeTimeout(Duration d) {
            if (d != null) writeTimeout = d;
            return this;
        }
        /**
         * Trust ALL certificates. ONLY for internal testing/self-signed endpoints.
         */
        public Builder trustAllSsl(boolean enable) {
            this.trustAllSsl = enable;
            return this;
        }
        public HttpGo build() {
            OkHttpClient.Builder cb = new OkHttpClient.Builder()
                    .connectTimeout(connectTimeout.toMillis(), TimeUnit.MILLISECONDS)
                    .readTimeout(readTimeout.toMillis(), TimeUnit.MILLISECONDS)
                    .writeTimeout(writeTimeout.toMillis(), TimeUnit.MILLISECONDS);
            if (trustAllSsl) {
                TrustAll trustAll = new TrustAll();
                cb.sslSocketFactory(trustAll.sslSocketFactory, trustAll.trustManager)
                        .hostnameVerifier((hostname, session) -> true);
            }
            return new HttpGo(cb.build(), defaultHeaders);
        }
    }
    // ===================== Trust-all SSL helper =====================
    private static final class TrustAll {
        final X509TrustManager trustManager;
        final SSLSocketFactory sslSocketFactory;
        TrustAll() {
            try {
                this.trustManager = new X509TrustManager() {
                    @Override
                    public void checkClientTrusted(X509Certificate[] chain, String authType) {
                    }
                    @Override
                    public void checkServerTrusted(X509Certificate[] chain, String authType) {
                    }
                    @Override
                    public X509Certificate[] getAcceptedIssuers() {
                        return new X509Certificate[0];
                    }
                };
                SSLContext sslContext = SSLContext.getInstance("TLS");
                sslContext.init(null, new TrustManager[]{trustManager}, new SecureRandom());
                this.sslSocketFactory = sslContext.getSocketFactory();
            } catch (Exception e) {
                throw new IllegalStateException("Failed to init trust-all SSL", e);
            }
        }
    }
    // ======================== tools ========================
    public String buildUrl(String host, Integer port, String path) {
        return buildUrl(host, port, path, false);
    }
    public String buildUrl(String host, Integer port, String path, boolean ssl) {
        String p = (path == null) ? "" : (path.startsWith("/") ? path : ("/" + path));
        return (ssl ? "https" : "http") + "://" + host + ":" + port + p;
    }
    // ===================== Demo (main) =====================
    public static void main(String[] args) throws Exception {
        HttpGo http = HttpGo.builder()
                .connectTimeout(Duration.ofSeconds(8))
                .readTimeout(Duration.ofSeconds(15))
                .defaultHeader("User-Agent", "HttpGo/1.0")
                // .trustAllSsl(true) // ONLY if you really need it
                .build();
        // 1) GET with query params + per-request headers
        String getUrl = "https://httpbin.org/get";
        Map<String, String> query = new HashMap<>();
        query.put("q", "vincent");
        query.put("page", "1");
        Map<String, String> headers = new HashMap<>();
        headers.put("X-Trace-Id", "trace-001");
        HttpResponse r1 = http.get(getUrl, headers, query);
        System.out.println("GET status=" + r1.statusCode() + ", tookMs=" + r1.tookMs());
        System.out.println(r1.body());
        // 2) POST JSON
        String postUrl = "https://httpbin.org/post";
        String json = "{\"name\":\"Vincent\",\"role\":\"engineer\"}";
        Map<String, String> postHeaders = new HashMap<>();
        postHeaders.put("X-Trace-Id", "trace-002");
        HttpResponse r2 = http.postJson(postUrl, postHeaders, json);
        System.out.println("POST(JSON) status=" + r2.statusCode() + ", tookMs=" + r2.tookMs());
        System.out.println(r2.body());
        // 3) POST Form
        Map<String, String> form = new HashMap<>();
        form.put("username", "vincent");
        form.put("password", "123456");
        HttpResponse r3 = http.postForm(postUrl, null, form);
        System.out.println("POST(Form) status=" + r3.statusCode() + ", tookMs=" + r3.tookMs());
        System.out.println(r3.body());
    }
}