| New file |
| | |
| | | package com.zy.acs.manager.common.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. |
| | | * |
| | | * - 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); |
| | | } |
| | | } |
| | | } |
| | | |
| | | // ===================== 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()); |
| | | } |
| | | |
| | | } |