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());
|
}
|
|
}
|