This is an automated email from the ASF dual-hosted git repository.
jianbin pushed a commit to branch 2.x
in repository https://gitbox.apache.org/repos/asf/incubator-seata.git
The following commit(s) were added to refs/heads/2.x by this push:
new 7fd5e590ac refactor: Unify HTTP client utility to OkHttp3 (#7904)
7fd5e590ac is described below
commit 7fd5e590ac29a7c94d7ccbcfa8fcb7665ad5d7ca
Author: xiaoyu <[email protected]>
AuthorDate: Mon Dec 29 09:37:09 2025 +0800
refactor: Unify HTTP client utility to OkHttp3 (#7904)
---
changes/en-us/2.x.md | 1 +
changes/zh-cn/2.x.md | 1 +
common/pom.xml | 5 -
.../apache/seata/common/util/HttpClientUtil.java | 272 +++---
.../seata/common/util/HttpClientUtilTest.java | 921 ++++++++++++++++++++-
.../NamingserverRegistryServiceImpl.java | 59 +-
.../NamingserverRegistryServiceImplTest.java | 171 +++-
.../registry/raft/RaftRegistryServiceImpl.java | 49 +-
.../registry/raft/RaftRegistryServiceImplTest.java | 25 +-
.../seata/namingserver/manager/NamingManager.java | 16 +-
.../seata/namingserver/NamingManagerTest.java | 17 +-
.../server/controller/ClusterControllerTest.java | 42 +-
12 files changed, 1266 insertions(+), 313 deletions(-)
diff --git a/changes/en-us/2.x.md b/changes/en-us/2.x.md
index f40ffda695..552bf94c90 100644
--- a/changes/en-us/2.x.md
+++ b/changes/en-us/2.x.md
@@ -169,6 +169,7 @@ Add changes here for all PR submitted to the 2.x branch.
- [[#7719](https://github.com/apache/incubator-seata/pull/7719)] Replace
synchronized with ReentrantLock in AbstractNettyRemotingClient to support
virtual threads
- [[#7789](https://github.com/apache/incubator-seata/pull/7789)] rename
GROUP_UPDATE_TIME to GROUP_UPDATE_TERM in ClusterWatcherManager
- [[#7698](https://github.com/apache/incubator-seata/pull/7698)] refactor test
module
+- [[#7904](https://github.com/apache/incubator-seata/pull/7904)] Unify HTTP
client utility to OkHttp3
### doc:
diff --git a/changes/zh-cn/2.x.md b/changes/zh-cn/2.x.md
index d9986ebc21..1bad712a98 100644
--- a/changes/zh-cn/2.x.md
+++ b/changes/zh-cn/2.x.md
@@ -169,6 +169,7 @@
- [[#7688](https://github.com/seata/seata/pull/7688)] 增加 extensions 模块
- [[#7789](https://github.com/apache/incubator-seata/pull/7789)] 将
`GROUP_UPDATE_TIME` 重命名为 `GROUP_UPDATE_TERM`,更准确地反映其实际用途
- [[#7698](https://github.com/apache/incubator-seata/pull/7698)] 重构 test 模块
+- [[#7904](https://github.com/apache/incubator-seata/pull/7904)]
统一http客户端工具类为okhttp3
### doc:
diff --git a/common/pom.xml b/common/pom.xml
index 8a1cfffea8..5b157d750a 100644
--- a/common/pom.xml
+++ b/common/pom.xml
@@ -43,11 +43,6 @@
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
- <dependency>
- <groupId>org.apache.httpcomponents</groupId>
- <artifactId>httpclient</artifactId>
- <scope>provided</scope>
- </dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
diff --git
a/common/src/main/java/org/apache/seata/common/util/HttpClientUtil.java
b/common/src/main/java/org/apache/seata/common/util/HttpClientUtil.java
index 5fcd499626..f65219e540 100644
--- a/common/src/main/java/org/apache/seata/common/util/HttpClientUtil.java
+++ b/common/src/main/java/org/apache/seata/common/util/HttpClientUtil.java
@@ -28,31 +28,14 @@ import okhttp3.Protocol;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
-import org.apache.http.NameValuePair;
-import org.apache.http.client.ClientProtocolException;
-import org.apache.http.client.config.RequestConfig;
-import org.apache.http.client.methods.CloseableHttpResponse;
-import org.apache.http.client.methods.HttpGet;
-import org.apache.http.client.methods.HttpPost;
-import org.apache.http.client.utils.URIBuilder;
-import org.apache.http.client.utils.URLEncodedUtils;
-import org.apache.http.entity.ContentType;
-import org.apache.http.entity.StringEntity;
-import org.apache.http.impl.client.CloseableHttpClient;
-import org.apache.http.impl.client.HttpClients;
-import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
-import org.apache.http.message.BasicNameValuePair;
import org.apache.seata.common.executor.HttpCallback;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
-import java.net.URI;
-import java.net.URISyntaxException;
+import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
-import java.util.ArrayList;
import java.util.Collections;
-import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
@@ -61,10 +44,7 @@ public class HttpClientUtil {
private static final Logger LOGGER =
LoggerFactory.getLogger(HttpClientUtil.class);
- private static final Map<Integer /*timeout*/, CloseableHttpClient>
HTTP_CLIENT_MAP = new ConcurrentHashMap<>();
-
- private static final PoolingHttpClientConnectionManager
POOLING_HTTP_CLIENT_CONNECTION_MANAGER =
- new PoolingHttpClientConnectionManager();
+ private static final Map<Integer /*timeout*/, OkHttpClient>
HTTP_CLIENT_MAP = new ConcurrentHashMap<>();
private static final Map<Integer /*timeout*/, OkHttpClient>
HTTP2_CLIENT_MAP = new ConcurrentHashMap<>();
@@ -75,15 +55,21 @@ public class HttpClientUtil {
public static final MediaType MEDIA_TYPE_FORM_URLENCODED =
MediaType.parse("application/x-www-form-urlencoded");
static {
- POOLING_HTTP_CLIENT_CONNECTION_MANAGER.setMaxTotal(10);
- POOLING_HTTP_CLIENT_CONNECTION_MANAGER.setDefaultMaxPerRoute(10);
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
HTTP_CLIENT_MAP.values().parallelStream().forEach(client -> {
try {
// delay 3s, make sure unregister http request send
successfully
Thread.sleep(3000);
- client.close();
- } catch (IOException | InterruptedException e) {
+ client.dispatcher().executorService().shutdown();
+ // Wait for up to 3 seconds for in-flight requests to
complete
+ if
(!client.dispatcher().executorService().awaitTermination(3, TimeUnit.SECONDS)) {
+ LOGGER.warn("Timeout waiting for OkHttp executor
service to terminate.");
+ }
+ client.connectionPool().evictAll();
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ LOGGER.error("Interrupted while waiting for OkHttp
executor service to terminate.", e);
+ } catch (Exception e) {
LOGGER.error(e.getMessage(), e);
}
});
@@ -106,136 +92,50 @@ public class HttpClientUtil {
}));
}
- // post request
- public static CloseableHttpResponse doPost(
- String url, Map<String, String> params, Map<String, String>
header, int timeout) throws IOException {
- try {
- URIBuilder builder = new URIBuilder(url);
- URI uri = builder.build();
- HttpPost httpPost = new HttpPost(uri);
- String contentType = "";
- if (header != null) {
- header.forEach(httpPost::addHeader);
- contentType = header.get("Content-Type");
- }
- if (StringUtils.isNotBlank(contentType)) {
- if
(ContentType.APPLICATION_FORM_URLENCODED.getMimeType().equals(contentType)) {
- List<NameValuePair> nameValuePairs = new ArrayList<>();
- params.forEach((k, v) -> {
- nameValuePairs.add(new BasicNameValuePair(k, v));
- });
- String requestBody =
URLEncodedUtils.format(nameValuePairs, StandardCharsets.UTF_8);
- StringEntity stringEntity = new StringEntity(requestBody,
ContentType.APPLICATION_FORM_URLENCODED);
- httpPost.setEntity(stringEntity);
- } else if
(ContentType.APPLICATION_JSON.getMimeType().equals(contentType)) {
- String requestBody =
OBJECT_MAPPER.writeValueAsString(params);
- StringEntity stringEntity = new StringEntity(requestBody,
ContentType.APPLICATION_JSON);
- httpPost.setEntity(stringEntity);
- }
- }
- CloseableHttpClient client =
HTTP_CLIENT_MAP.computeIfAbsent(timeout, k -> HttpClients.custom()
-
.setConnectionManager(POOLING_HTTP_CLIENT_CONNECTION_MANAGER)
- .setDefaultRequestConfig(RequestConfig.custom()
- .setConnectionRequestTimeout(timeout)
- .setSocketTimeout(timeout)
- .setConnectTimeout(timeout)
- .build())
- .build());
- return client.execute(httpPost);
- } catch (URISyntaxException | ClientProtocolException e) {
- LOGGER.error(e.getMessage(), e);
- }
- return null;
- }
-
- // post request
- public static CloseableHttpResponse doPost(String url, String body,
Map<String, String> header, int timeout)
+ public static Response doPost(String url, Map<String, String> params,
Map<String, String> header, int timeout)
throws IOException {
- try {
- URIBuilder builder = new URIBuilder(url);
- URI uri = builder.build();
- HttpPost httpPost = new HttpPost(uri);
- String contentType = "";
- if (header != null) {
- header.forEach(httpPost::addHeader);
- contentType = header.get("Content-Type");
- }
- if (StringUtils.isNotBlank(contentType)) {
- if
(ContentType.APPLICATION_JSON.getMimeType().equals(contentType)) {
- StringEntity stringEntity = new StringEntity(body,
ContentType.APPLICATION_JSON);
- httpPost.setEntity(stringEntity);
- }
- }
- CloseableHttpClient client =
HTTP_CLIENT_MAP.computeIfAbsent(timeout, k -> HttpClients.custom()
-
.setConnectionManager(POOLING_HTTP_CLIENT_CONNECTION_MANAGER)
- .setDefaultRequestConfig(RequestConfig.custom()
- .setConnectionRequestTimeout(timeout)
- .setSocketTimeout(timeout)
- .setConnectTimeout(timeout)
- .build())
- .build());
- return client.execute(httpPost);
- } catch (URISyntaxException | ClientProtocolException e) {
- LOGGER.error(e.getMessage(), e);
- }
- return null;
+ String contentType = header != null ? header.get("Content-Type") : "";
+ RequestBody requestBody = createRequestBody(params, contentType);
+ Request request = buildRequest(url, header, requestBody, "POST");
+ OkHttpClient client = createHttp1ClientWithTimeout(timeout);
+ return client.newCall(request).execute();
}
- // get request
- public static CloseableHttpResponse doGet(
- String url, Map<String, String> param, Map<String, String> header,
int timeout) throws IOException {
- try {
- URIBuilder builder = new URIBuilder(url);
- if (param != null) {
- for (String key : param.keySet()) {
- builder.addParameter(key, param.get(key));
- }
- }
- URI uri = builder.build();
- HttpGet httpGet = new HttpGet(uri);
- if (header != null) {
- header.forEach(httpGet::addHeader);
- }
- CloseableHttpClient client =
HTTP_CLIENT_MAP.computeIfAbsent(timeout, k -> HttpClients.custom()
-
.setConnectionManager(POOLING_HTTP_CLIENT_CONNECTION_MANAGER)
- .setDefaultRequestConfig(RequestConfig.custom()
- .setConnectionRequestTimeout(timeout)
- .setSocketTimeout(timeout)
- .setConnectTimeout(timeout)
- .build())
- .build());
- return client.execute(httpGet);
- } catch (URISyntaxException | ClientProtocolException e) {
- LOGGER.error(e.getMessage(), e);
- }
- return null;
+ public static Response doPost(String url, String body, Map<String, String>
header, int timeout) throws IOException {
+ String contentType = header != null ? header.get("Content-Type") : "";
+ MediaType mediaType = StringUtils.isNotBlank(contentType) ?
MediaType.parse(contentType) : MEDIA_TYPE_JSON;
+ RequestBody requestBody = StringUtils.isNotBlank(body)
+ ? RequestBody.create(body, mediaType)
+ : RequestBody.create(new byte[0], mediaType);
+ Request request = buildRequest(url, header, requestBody, "POST");
+ OkHttpClient client = createHttp1ClientWithTimeout(timeout);
+ return client.newCall(request).execute();
}
- public static CloseableHttpResponse doPostJson(
- String url, String jsonBody, Map<String, String> headers, int
timeout) throws IOException {
- RequestConfig requestConfig = RequestConfig.custom()
- .setSocketTimeout(timeout)
- .setConnectTimeout(timeout)
- .build();
-
- HttpPost post = new HttpPost(url);
- post.setConfig(requestConfig);
-
- if (headers != null) {
- headers.forEach(post::addHeader);
- }
- post.setHeader("Content-Type", "application/json");
-
- StringEntity entity = new StringEntity(jsonBody,
StandardCharsets.UTF_8);
- post.setEntity(entity);
+ public static Response doGet(String url, Map<String, String> param,
Map<String, String> header, int timeout)
+ throws IOException {
+ String urlWithParams = buildUrlWithParams(url, param);
+ Request request = buildRequest(urlWithParams, header, null, "GET");
+ OkHttpClient client = createHttp1ClientWithTimeout(timeout);
+ return client.newCall(request).execute();
+ }
- CloseableHttpClient client = HttpClients.createDefault();
- return client.execute(post);
+ public static Response doPostJson(String url, String jsonBody, Map<String,
String> headers, int timeout)
+ throws IOException {
+ RequestBody requestBody = jsonBody != null
+ ? RequestBody.create(jsonBody, MEDIA_TYPE_JSON)
+ : RequestBody.create(new byte[0], MEDIA_TYPE_JSON);
+ Map<String, String> headersWithContentType =
+ headers != null ? new java.util.HashMap<>(headers) : new
java.util.HashMap<>();
+ headersWithContentType.put("Content-Type", "application/json");
+ Request request = buildRequest(url, headersWithContentType,
requestBody, "POST");
+ OkHttpClient client = createHttp1ClientWithTimeout(timeout);
+ return client.newCall(request).execute();
}
public static void doPostWithHttp2(
String url, Map<String, String> params, Map<String, String>
headers, HttpCallback<Response> callback) {
- doPostWithHttp2(url, params, headers, callback, 10);
+ doPostWithHttp2(url, params, headers, callback, 10000);
}
public static void doPostWithHttp2(
@@ -243,12 +143,12 @@ public class HttpClientUtil {
Map<String, String> params,
Map<String, String> headers,
HttpCallback<Response> callback,
- int timeoutSeconds) {
+ int timeoutMillis) {
try {
String contentType = headers != null ? headers.get("Content-Type")
: "";
RequestBody requestBody = createRequestBody(params, contentType);
- Request request = buildHttp2Request(url, headers, requestBody,
"POST");
- OkHttpClient client = createHttp2ClientWithTimeout(timeoutSeconds);
+ Request request = buildRequest(url, headers, requestBody, "POST");
+ OkHttpClient client = createHttp2ClientWithTimeout(timeoutMillis);
executeAsync(client, request, callback);
} catch (JsonProcessingException e) {
LOGGER.error(e.getMessage(), e);
@@ -258,22 +158,24 @@ public class HttpClientUtil {
public static void doPostWithHttp2(
String url, String body, Map<String, String> headers,
HttpCallback<Response> callback) {
- // default timeout 10 seconds
- doPostWithHttp2(url, body, headers, callback, 10);
+ // default timeout 10000 milliseconds
+ doPostWithHttp2(url, body, headers, callback, 10000);
}
public static void doPostWithHttp2(
- String url, String body, Map<String, String> headers,
HttpCallback<Response> callback, int timeoutSeconds) {
- RequestBody requestBody = RequestBody.create(body, MEDIA_TYPE_JSON);
- Request request = buildHttp2Request(url, headers, requestBody, "POST");
- OkHttpClient client = createHttp2ClientWithTimeout(timeoutSeconds);
+ String url, String body, Map<String, String> headers,
HttpCallback<Response> callback, int timeout) {
+ RequestBody requestBody = StringUtils.isNotBlank(body)
+ ? RequestBody.create(body, MEDIA_TYPE_JSON)
+ : RequestBody.create(new byte[0], MEDIA_TYPE_JSON);
+ Request request = buildRequest(url, headers, requestBody, "POST");
+ OkHttpClient client = createHttp2ClientWithTimeout(timeout);
executeAsync(client, request, callback);
}
public static void doGetWithHttp2(
- String url, Map<String, String> headers, final
HttpCallback<Response> callback, int timeoutSeconds) {
- Request request = buildHttp2Request(url, headers, null, "GET");
- OkHttpClient client = createHttp2ClientWithTimeout(timeoutSeconds);
+ String url, Map<String, String> headers, final
HttpCallback<Response> callback, int timeout) {
+ Request request = buildRequest(url, headers, null, "GET");
+ OkHttpClient client = createHttp2ClientWithTimeout(timeout);
executeAsync(client, request, callback);
}
@@ -295,17 +197,26 @@ public class HttpClientUtil {
}
}
- private static OkHttpClient createHttp2ClientWithTimeout(int
timeoutSeconds) {
- return HTTP2_CLIENT_MAP.computeIfAbsent(timeoutSeconds, k -> new
OkHttpClient.Builder()
+ private static OkHttpClient createHttp1ClientWithTimeout(int
timeoutMillis) {
+ return HTTP_CLIENT_MAP.computeIfAbsent(timeoutMillis, k -> new
OkHttpClient.Builder()
+ // Use HTTP/1.1 (default protocol, no need to specify)
+ .connectTimeout(timeoutMillis, TimeUnit.MILLISECONDS)
+ .readTimeout(timeoutMillis, TimeUnit.MILLISECONDS)
+ .writeTimeout(timeoutMillis, TimeUnit.MILLISECONDS)
+ .build());
+ }
+
+ private static OkHttpClient createHttp2ClientWithTimeout(int
timeoutMillis) {
+ return HTTP2_CLIENT_MAP.computeIfAbsent(timeoutMillis, k -> new
OkHttpClient.Builder()
// Use HTTP/2 prior knowledge to directly use HTTP/2 without
an initial HTTP/1.1 upgrade
.protocols(Collections.singletonList(Protocol.H2_PRIOR_KNOWLEDGE))
- .connectTimeout(timeoutSeconds, TimeUnit.SECONDS)
- .readTimeout(timeoutSeconds, TimeUnit.SECONDS)
- .writeTimeout(timeoutSeconds, TimeUnit.SECONDS)
+ .connectTimeout(timeoutMillis, TimeUnit.MILLISECONDS)
+ .readTimeout(timeoutMillis, TimeUnit.MILLISECONDS)
+ .writeTimeout(timeoutMillis, TimeUnit.MILLISECONDS)
.build());
}
- private static Request buildHttp2Request(
+ private static Request buildRequest(
String url, Map<String, String> headers, RequestBody requestBody,
String method) {
Headers.Builder headerBuilder = new Headers.Builder();
if (headers != null) {
@@ -314,15 +225,46 @@ public class HttpClientUtil {
Request.Builder requestBuilder = new
Request.Builder().url(url).headers(headerBuilder.build());
- if ("POST".equals(method) && requestBody != null) {
+ if ("POST".equals(method)) {
+ if (requestBody == null) {
+ requestBody = RequestBody.create(new byte[0], MEDIA_TYPE_JSON);
+ }
requestBuilder.post(requestBody);
} else if ("GET".equals(method)) {
requestBuilder.get();
+ } else {
+ throw new IllegalArgumentException("Unsupported HTTP method: " +
method);
}
return requestBuilder.build();
}
+ private static String buildUrlWithParams(String url, Map<String, String>
params) {
+ if (params == null || params.isEmpty()) {
+ return url;
+ }
+ StringBuilder urlBuilder = new StringBuilder(url);
+ boolean first = !url.contains("?");
+ for (Map.Entry<String, String> entry : params.entrySet()) {
+ if (first) {
+ urlBuilder.append("?");
+ first = false;
+ } else {
+ urlBuilder.append("&");
+ }
+ try {
+ urlBuilder
+ .append(URLEncoder.encode(entry.getKey(),
StandardCharsets.UTF_8.name()))
+ .append("=")
+ .append(URLEncoder.encode(entry.getValue(),
StandardCharsets.UTF_8.name()));
+ } catch (java.io.UnsupportedEncodingException e) {
+ // UTF-8 is always supported
+ throw new RuntimeException(e);
+ }
+ }
+ return urlBuilder.toString();
+ }
+
private static void executeAsync(OkHttpClient client, Request request,
final HttpCallback<Response> callback) {
client.newCall(request).enqueue(new Callback() {
@Override
diff --git
a/common/src/test/java/org/apache/seata/common/util/HttpClientUtilTest.java
b/common/src/test/java/org/apache/seata/common/util/HttpClientUtilTest.java
index 6df6b2c8c0..631ae7f247 100644
--- a/common/src/test/java/org/apache/seata/common/util/HttpClientUtilTest.java
+++ b/common/src/test/java/org/apache/seata/common/util/HttpClientUtilTest.java
@@ -17,21 +17,28 @@
package org.apache.seata.common.util;
import okhttp3.OkHttpClient;
+import okhttp3.Request;
+import okhttp3.RequestBody;
import okhttp3.Response;
-import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.seata.common.executor.HttpCallback;
+import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Assumptions;
+import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import java.io.IOException;
import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.net.ConnectException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
+import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.fail;
import static org.mockito.Mockito.RETURNS_DEEP_STUBS;
@@ -41,14 +48,459 @@ import static org.mockito.Mockito.verify;
public class HttpClientUtilTest {
+ @BeforeEach
+ public void setUp() {
+ // Reset any static state if needed
+ }
+
+ @AfterEach
+ public void tearDown() {
+ // Clean up if needed
+ }
+
+ @Test
+ public void testDoPost_InvalidUrl() {
+ Assertions.assertThrows(IllegalArgumentException.class, () -> {
+ HttpClientUtil.doPost("http:", new HashMap<>(), new HashMap<>(),
1000);
+ });
+ }
+
+ @Test
+ public void testDoGet_InvalidUrl() {
+ Assertions.assertThrows(IllegalArgumentException.class, () -> {
+ HttpClientUtil.doGet("http", new HashMap<>(), new HashMap<>(),
1000);
+ });
+ }
+
+ @Test
+ public void testDoPost_ConnectionFailure() {
+ // Test connection failure scenario
+ Assertions.assertThrows(ConnectException.class, () -> {
+ HttpClientUtil.doPost("http://localhost:9999/invalid", new
HashMap<>(), new HashMap<>(), 1000);
+ });
+ }
+
+ @Test
+ public void testDoPost_WithBlankParams() throws IOException {
+ // Test with blank params - should create empty RequestBody
+ Assertions.assertThrows(ConnectException.class, () -> {
+ HttpClientUtil.doPost("http://localhost:9999/invalid", "", new
HashMap<>(), 1000);
+ });
+ }
+
+ @Test
+ public void testDoPost_WithEmptyParams() throws IOException {
+ // Test with empty params - should create empty RequestBody
+ Assertions.assertThrows(IOException.class, () -> {
+ HttpClientUtil.doPost("http://localhost:9999/invalid", new
HashMap<>(), new HashMap<>(), 1000);
+ });
+ }
+
+ @Test
+ public void testDoPost_WithFormUrlEncoded() throws IOException {
+ // Test with form-urlencoded content type
+ Map<String, String> params = new HashMap<>();
+ params.put("key1", "value1");
+ params.put("key2", "value2");
+ Map<String, String> headers = new HashMap<>();
+ headers.put("Content-Type", "application/x-www-form-urlencoded");
+
+ Assertions.assertThrows(IOException.class, () -> {
+ HttpClientUtil.doPost("http://localhost:9999/invalid", params,
headers, 1000);
+ });
+ }
+
+ @Test
+ public void testDoPost_WithJsonContentType() throws IOException {
+ // Test with JSON content type
+ Map<String, String> params = new HashMap<>();
+ params.put("key", "value");
+ Map<String, String> headers = new HashMap<>();
+ headers.put("Content-Type", "application/json");
+
+ Assertions.assertThrows(IOException.class, () -> {
+ HttpClientUtil.doPost("http://localhost:9999/invalid", params,
headers, 1000);
+ });
+ }
+
+ @Test
+ public void testDoPost_WithNullHeaders() throws IOException {
+ // Test with null headers
+ Map<String, String> params = new HashMap<>();
+ params.put("key", "value");
+
+ Assertions.assertThrows(IOException.class, () -> {
+ HttpClientUtil.doPost("http://localhost:9999/invalid", params,
null, 1000);
+ });
+ }
+
+ @Test
+ public void testDoPost_StringBody_WithNullBody() throws IOException {
+ // Test doPost with String body - null body should create empty
RequestBody
+ Map<String, String> headers = new HashMap<>();
+ headers.put("Content-Type", "application/json");
+
+ Assertions.assertThrows(IOException.class, () -> {
+ HttpClientUtil.doPost("http://localhost:9999/invalid", (String)
null, headers, 1000);
+ });
+ }
+
+ @Test
+ public void testDoPost_StringBody_WithEmptyBody() throws IOException {
+ // Test doPost with empty String body
+ Map<String, String> headers = new HashMap<>();
+ headers.put("Content-Type", "application/json");
+
+ Assertions.assertThrows(IOException.class, () -> {
+ HttpClientUtil.doPost("http://localhost:9999/invalid", "",
headers, 1000);
+ });
+ }
+
+ @Test
+ public void testDoPost_StringBody_WithCustomContentType() throws
IOException {
+ // Test doPost with custom Content-Type
+ Map<String, String> headers = new HashMap<>();
+ headers.put("Content-Type", "text/plain");
+
+ Assertions.assertThrows(IOException.class, () -> {
+ HttpClientUtil.doPost("http://localhost:9999/invalid", "test
body", headers, 1000);
+ });
+ }
+
+ @Test
+ public void testDoPost_StringBody_WithNullHeaders() throws IOException {
+ // Test doPost with null headers - should default to JSON
+ Assertions.assertThrows(IOException.class, () -> {
+ HttpClientUtil.doPost("http://localhost:9999/invalid", "test
body", null, 1000);
+ });
+ }
+
+ @Test
+ public void testDoGet_WithNullParams() throws IOException {
+ // Test doGet with null params
+ Assertions.assertThrows(IOException.class, () -> {
+ HttpClientUtil.doGet("http://localhost:9999/invalid", null, new
HashMap<>(), 1000);
+ });
+ }
+
+ @Test
+ public void testDoGet_WithEmptyParams() throws IOException {
+ // Test doGet with empty params
+ Assertions.assertThrows(IOException.class, () -> {
+ HttpClientUtil.doGet("http://localhost:9999/invalid", new
HashMap<>(), new HashMap<>(), 1000);
+ });
+ }
+
+ @Test
+ public void testDoGet_WithParams() throws IOException {
+ // Test doGet with query parameters
+ Map<String, String> params = new HashMap<>();
+ params.put("key1", "value1");
+ params.put("key2", "value2");
+
+ Assertions.assertThrows(IOException.class, () -> {
+ HttpClientUtil.doGet("http://localhost:9999/invalid", params, new
HashMap<>(), 1000);
+ });
+ }
+
+ @Test
+ public void testDoGet_WithUrlContainingQuery() throws IOException {
+ // Test doGet with URL already containing query parameters
+ Map<String, String> params = new HashMap<>();
+ params.put("key", "value");
+
+ Assertions.assertThrows(IOException.class, () -> {
+
HttpClientUtil.doGet("http://localhost:9999/invalid?existing=param", params,
new HashMap<>(), 1000);
+ });
+ }
+
+ @Test
+ public void testDoGet_WithNullHeaders() throws IOException {
+ // Test doGet with null headers
+ Assertions.assertThrows(IOException.class, () -> {
+ HttpClientUtil.doGet("http://localhost:9999/invalid", new
HashMap<>(), null, 1000);
+ });
+ }
+
+ @Test
+ public void testDoPostJson_WithNullJsonBody() throws IOException {
+ // Test doPostJson with null jsonBody
+ Map<String, String> headers = new HashMap<>();
+
+ Assertions.assertThrows(IOException.class, () -> {
+ HttpClientUtil.doPostJson("http://localhost:9999/invalid", null,
headers, 1000);
+ });
+ }
+
+ @Test
+ public void testDoPostJson_WithEmptyJsonBody() throws IOException {
+ // Test doPostJson with empty jsonBody
+ Map<String, String> headers = new HashMap<>();
+
+ Assertions.assertThrows(IOException.class, () -> {
+ HttpClientUtil.doPostJson("http://localhost:9999/invalid", "",
headers, 1000);
+ });
+ }
+
+ @Test
+ public void testDoPostJson_WithNullHeaders() throws IOException {
+ // Test doPostJson with null headers - should still set Content-Type
to application/json
+ Assertions.assertThrows(IOException.class, () -> {
+ HttpClientUtil.doPostJson("http://localhost:9999/invalid", "{}",
null, 1000);
+ });
+ }
+
+ @Test
+ public void testDoPostJson_WithExistingContentType() throws IOException {
+ // Test doPostJson with existing Content-Type header - should be
overridden
+ Map<String, String> headers = new HashMap<>();
+ headers.put("Content-Type", "text/plain");
+
+ Assertions.assertThrows(IOException.class, () -> {
+ HttpClientUtil.doPostJson("http://localhost:9999/invalid", "{}",
headers, 1000);
+ });
+ }
+
@Test
- public void testDoPost() throws IOException {
- Assertions.assertNull(HttpClientUtil.doPost("test", new HashMap<>(),
new HashMap<>(), 0));
- Assertions.assertNull(HttpClientUtil.doGet("test", new HashMap<>(),
new HashMap<>(), 0));
+ void testDoPostWithHttp2_param_onFailure() throws Exception {
+ CountDownLatch latch = new CountDownLatch(1);
+
+ HttpCallback<Response> callback = new HttpCallback<Response>() {
+ @Override
+ public void onSuccess(Response response) {
+ fail("Should not succeed");
+ }
+
+ @Override
+ public void onFailure(Throwable t) {
+ assertNotNull(t);
+ latch.countDown();
+ }
+
+ @Override
+ public void onCancelled() {
+ fail("Should not be cancelled");
+ }
+ };
+
+ Map<String, String> params = new HashMap<>();
+ params.put("key", "value");
+
+ Map<String, String> headers = new HashMap<>();
+ headers.put("Content-Type", "application/json");
+
+ HttpClientUtil.doPostWithHttp2("http://localhost:9999/invalid",
params, headers, callback);
+ assertTrue(latch.await(10, TimeUnit.SECONDS));
+ }
+
+ @Test
+ void testDoPostWithHttp2_param_WithNullParams() throws Exception {
+ CountDownLatch latch = new CountDownLatch(1);
+
+ HttpCallback<Response> callback = new HttpCallback<Response>() {
+ @Override
+ public void onSuccess(Response response) {
+ fail("Should not succeed");
+ }
+
+ @Override
+ public void onFailure(Throwable t) {
+ assertNotNull(t);
+ latch.countDown();
+ }
+
+ @Override
+ public void onCancelled() {
+ fail("Should not be cancelled");
+ }
+ };
+
+ Map<String, String> headers = new HashMap<>();
+ headers.put("Content-Type", "application/json");
+
+ HttpClientUtil.doPostWithHttp2("http://localhost:9999/invalid", "",
headers, callback);
+ assertTrue(latch.await(10, TimeUnit.SECONDS));
+ }
+
+ @Test
+ void testDoPostWithHttp2_param_WithNullHeaders() throws Exception {
+ CountDownLatch latch = new CountDownLatch(1);
+
+ HttpCallback<Response> callback = new HttpCallback<Response>() {
+ @Override
+ public void onSuccess(Response response) {
+ fail("Should not succeed");
+ }
+
+ @Override
+ public void onFailure(Throwable t) {
+ assertNotNull(t);
+ latch.countDown();
+ }
+
+ @Override
+ public void onCancelled() {
+ fail("Should not be cancelled");
+ }
+ };
+
+ Map<String, String> params = new HashMap<>();
+ params.put("key", "value");
+
+ HttpClientUtil.doPostWithHttp2("http://localhost:9999/invalid",
params, null, callback);
+ assertTrue(latch.await(10, TimeUnit.SECONDS));
+ }
+
+ @Test
+ void testDoPostWithHttp2_param_WithFormUrlEncoded() throws Exception {
+ CountDownLatch latch = new CountDownLatch(1);
+
+ HttpCallback<Response> callback = new HttpCallback<Response>() {
+ @Override
+ public void onSuccess(Response response) {
+ fail("Should not succeed");
+ }
+
+ @Override
+ public void onFailure(Throwable t) {
+ assertNotNull(t);
+ latch.countDown();
+ }
+
+ @Override
+ public void onCancelled() {
+ fail("Should not be cancelled");
+ }
+ };
+
+ Map<String, String> params = new HashMap<>();
+ params.put("key1", "value1");
+ params.put("key2", "value2");
+
+ Map<String, String> headers = new HashMap<>();
+ headers.put("Content-Type", "application/x-www-form-urlencoded");
+
+ HttpClientUtil.doPostWithHttp2("http://localhost:9999/invalid",
params, headers, callback, 5000);
+ assertTrue(latch.await(10, TimeUnit.SECONDS));
+ }
+
+ @Test
+ void testDoPostWithHttp2_body_onFailure() throws Exception {
+ CountDownLatch latch = new CountDownLatch(1);
+
+ HttpCallback<Response> callback = new HttpCallback<Response>() {
+ @Override
+ public void onSuccess(Response response) {
+ fail("Should not succeed");
+ }
+
+ @Override
+ public void onFailure(Throwable t) {
+ assertNotNull(t);
+ latch.countDown();
+ }
+
+ @Override
+ public void onCancelled() {
+ fail("Should not be cancelled");
+ }
+ };
+
+ String body = "{\"key\":\"value\"}";
+
+ Map<String, String> headers = new HashMap<>();
+ headers.put("Content-Type", "application/json");
+
+ HttpClientUtil.doPostWithHttp2("http://localhost:9999/invalid", body,
headers, callback);
+ assertTrue(latch.await(10, TimeUnit.SECONDS));
+ }
+
+ @Test
+ void testDoPostWithHttp2_body_WithNullBody() throws Exception {
+ CountDownLatch latch = new CountDownLatch(1);
+
+ HttpCallback<Response> callback = new HttpCallback<Response>() {
+ @Override
+ public void onSuccess(Response response) {
+ fail("Should not succeed");
+ }
+
+ @Override
+ public void onFailure(Throwable t) {
+ assertNotNull(t);
+ latch.countDown();
+ }
+
+ @Override
+ public void onCancelled() {
+ fail("Should not be cancelled");
+ }
+ };
+
+ Map<String, String> headers = new HashMap<>();
+ headers.put("Content-Type", "application/json");
+
+ HttpClientUtil.doPostWithHttp2("http://localhost:9999/invalid",
(String) null, headers, callback, 5000);
+ assertTrue(latch.await(10, TimeUnit.SECONDS));
+ }
+
+ @Test
+ void testDoPostWithHttp2_body_WithEmptyBody() throws Exception {
+ CountDownLatch latch = new CountDownLatch(1);
+
+ HttpCallback<Response> callback = new HttpCallback<Response>() {
+ @Override
+ public void onSuccess(Response response) {
+ fail("Should not succeed");
+ }
+
+ @Override
+ public void onFailure(Throwable t) {
+ assertNotNull(t);
+ latch.countDown();
+ }
+
+ @Override
+ public void onCancelled() {
+ fail("Should not be cancelled");
+ }
+ };
+
+ Map<String, String> headers = new HashMap<>();
+ headers.put("Content-Type", "application/json");
+
+ HttpClientUtil.doPostWithHttp2("http://localhost:9999/invalid", "",
headers, callback, 5000);
+ assertTrue(latch.await(10, TimeUnit.SECONDS));
+ }
+
+ @Test
+ void testDoPostWithHttp2_body_WithNullHeaders() throws Exception {
+ CountDownLatch latch = new CountDownLatch(1);
+
+ HttpCallback<Response> callback = new HttpCallback<Response>() {
+ @Override
+ public void onSuccess(Response response) {
+ fail("Should not succeed");
+ }
+
+ @Override
+ public void onFailure(Throwable t) {
+ assertNotNull(t);
+ latch.countDown();
+ }
+
+ @Override
+ public void onCancelled() {
+ fail("Should not be cancelled");
+ }
+ };
+
+ HttpClientUtil.doPostWithHttp2("http://localhost:9999/invalid", "{}",
null, callback, 5000);
+ assertTrue(latch.await(10, TimeUnit.SECONDS));
}
@Test
- void testDoPostWithHttp2_param_onFailure() throws Exception {
+ void testDoPostWithHttp2_body_withCharset_onFailure() throws Exception {
CountDownLatch latch = new CountDownLatch(1);
HttpCallback<Response> callback = new HttpCallback<Response>() {
@@ -73,14 +525,14 @@ public class HttpClientUtilTest {
params.put("key", "value");
Map<String, String> headers = new HashMap<>();
- headers.put("Content-Type", "application/json");
+ headers.put("Content-Type",
"application/x-www-form-urlencoded;charset=UTF-8");
- HttpClientUtil.doPostWithHttp2("http://localhost:9999/invalid",
params, headers, callback);
+ HttpClientUtil.doPostWithHttp2("http://localhost:9999/invalid",
params, headers, callback, 30000);
assertTrue(latch.await(10, TimeUnit.SECONDS));
}
@Test
- void testDoPostWithHttp2_body_onFailure() throws Exception {
+ void testDoPostWithHttp2_withEmptyParam_onFailure() throws Exception {
CountDownLatch latch = new CountDownLatch(1);
HttpCallback<Response> callback = new HttpCallback<Response>() {
@@ -101,17 +553,16 @@ public class HttpClientUtilTest {
}
};
- String body = "{\"key\":\"value\"}";
-
+ Map<String, String> params = new HashMap<>();
Map<String, String> headers = new HashMap<>();
- headers.put("Content-Type", "application/json");
+ headers.put("Content-Type", "application/json;charset=UTF-8");
- HttpClientUtil.doPostWithHttp2("http://localhost:9999/invalid", body,
headers, callback);
+ HttpClientUtil.doPostWithHttp2("http://localhost:9999/invalid",
params, headers, callback, 30000);
assertTrue(latch.await(10, TimeUnit.SECONDS));
}
@Test
- void testDoPostWithHttp2_body_withCharset_onFailure() throws Exception {
+ void testDoGetHttp_param_onFailure() throws Exception {
CountDownLatch latch = new CountDownLatch(1);
HttpCallback<Response> callback = new HttpCallback<Response>() {
@@ -136,14 +587,14 @@ public class HttpClientUtilTest {
params.put("key", "value");
Map<String, String> headers = new HashMap<>();
- headers.put("Content-Type",
"application/x-www-form-urlencoded;charset=UTF-8");
+ headers.put("Content-Type", "application/json");
- HttpClientUtil.doPostWithHttp2("http://localhost:9999/invalid",
params, headers, callback, 30000);
+ HttpClientUtil.doGetWithHttp2("http://localhost:9999/invalid",
headers, callback, 30000);
assertTrue(latch.await(10, TimeUnit.SECONDS));
}
@Test
- void testDoPostWithHttp2_withEmptyParam_onFailure() throws Exception {
+ void testDoGetWithHttp2_WithNullHeaders() throws Exception {
CountDownLatch latch = new CountDownLatch(1);
HttpCallback<Response> callback = new HttpCallback<Response>() {
@@ -164,16 +615,146 @@ public class HttpClientUtilTest {
}
};
+ HttpClientUtil.doGetWithHttp2("http://localhost:9999/invalid", null,
callback, 5000);
+ assertTrue(latch.await(10, TimeUnit.SECONDS));
+ }
+
+ @Test
+ void testDoPostWithHttp2_JsonProcessingException() throws Exception {
+ CountDownLatch latch = new CountDownLatch(1);
+
+ HttpCallback<Response> callback = new HttpCallback<Response>() {
+ @Override
+ public void onSuccess(Response response) {
+ fail("Should not succeed");
+ }
+
+ @Override
+ public void onFailure(Throwable t) {
+ assertNotNull(t);
+ latch.countDown();
+ }
+
+ @Override
+ public void onCancelled() {
+ fail("Should not be cancelled");
+ }
+ };
+
+ // Create params that might cause JSON processing issues
+ // Note: In practice, Map<String, String> should always serialize
correctly
+ // This test mainly covers the exception handling path
Map<String, String> params = new HashMap<>();
+ params.put("key", "value");
+
Map<String, String> headers = new HashMap<>();
- headers.put("Content-Type", "application/json;charset=UTF-8");
+ headers.put("Content-Type", "application/json");
- HttpClientUtil.doPostWithHttp2("http://localhost:9999/invalid",
params, headers, callback, 30000);
+ // This will fail due to connection error, not JSON processing
+ HttpClientUtil.doPostWithHttp2("http://localhost:9999/invalid",
params, headers, callback, 5000);
assertTrue(latch.await(10, TimeUnit.SECONDS));
}
@Test
- void testDoGetHttp_param_onFailure() throws Exception {
+ void testDoPost_JsonProcessingException() {
+ // Test that JsonProcessingException is wrapped in IOException
+ Map<String, String> params = new HashMap<>();
+ params.put("key", "value");
+ Map<String, String> headers = new HashMap<>();
+ headers.put("Content-Type", "application/json");
+
+ // This will fail due to connection error, not JSON processing
+ // But the exception handling path is covered
+ assertThrows(IOException.class, () -> {
+ HttpClientUtil.doPost("http://localhost:9999/invalid", params,
headers, 1000);
+ });
+ }
+
+ @Test
+ void testBuildUrlWithParams_WithSpecialCharacters() throws IOException {
+ // Test URL encoding with special characters
+ Map<String, String> params = new HashMap<>();
+ params.put("key with spaces", "value&with=special");
+ params.put("中文", "测试");
+
+ // This will fail due to connection error, but URL building is tested
+ assertThrows(IOException.class, () -> {
+ HttpClientUtil.doGet("http://localhost:9999/invalid", params, new
HashMap<>(), 1000);
+ });
+ }
+
+ @Test
+ void testBuildUrlWithParams_WithEmptyKeyOrValue() throws IOException {
+ // Test URL building with empty key or value
+ Map<String, String> params = new HashMap<>();
+ params.put("", "value");
+ params.put("key", "");
+
+ assertThrows(IOException.class, () -> {
+ HttpClientUtil.doGet("http://localhost:9999/invalid", params, new
HashMap<>(), 1000);
+ });
+ }
+
+ @Test
+ void testDoPost_WithTimeout() {
+ // Test with different timeout values
+ Map<String, String> params = new HashMap<>();
+ params.put("key", "value");
+
+ assertThrows(IOException.class, () -> {
+ HttpClientUtil.doPost("http://localhost:9999/invalid", params, new
HashMap<>(), 100);
+ });
+ }
+
+ @Test
+ void testDoGet_WithTimeout() {
+ // Test with different timeout values
+ assertThrows(IOException.class, () -> {
+ HttpClientUtil.doGet("http://localhost:9999/invalid", new
HashMap<>(), new HashMap<>(), 100);
+ });
+ }
+
+ @Test
+ void testDoPostJson_WithTimeout() {
+ // Test with different timeout values
+ assertThrows(IOException.class, () -> {
+ HttpClientUtil.doPostJson("http://localhost:9999/invalid", "{}",
new HashMap<>(), 100);
+ });
+ }
+
+ @Test
+ void testDoPostWithHttp2_DefaultTimeout() throws Exception {
+ // Test default timeout (10000ms) when not specified
+ CountDownLatch latch = new CountDownLatch(1);
+
+ HttpCallback<Response> callback = new HttpCallback<Response>() {
+ @Override
+ public void onSuccess(Response response) {
+ fail("Should not succeed");
+ }
+
+ @Override
+ public void onFailure(Throwable t) {
+ assertNotNull(t);
+ latch.countDown();
+ }
+
+ @Override
+ public void onCancelled() {
+ fail("Should not be cancelled");
+ }
+ };
+
+ Map<String, String> params = new HashMap<>();
+ params.put("key", "value");
+
+ HttpClientUtil.doPostWithHttp2("http://localhost:9999/invalid",
params, new HashMap<>(), callback);
+ assertTrue(latch.await(15, TimeUnit.SECONDS));
+ }
+
+ @Test
+ void testDoPostWithHttp2_StringBody_DefaultTimeout() throws Exception {
+ // Test default timeout for String body overload
CountDownLatch latch = new CountDownLatch(1);
HttpCallback<Response> callback = new HttpCallback<Response>() {
@@ -194,14 +775,227 @@ public class HttpClientUtilTest {
}
};
+ HttpClientUtil.doPostWithHttp2("http://localhost:9999/invalid", "{}",
new HashMap<>(), callback);
+ assertTrue(latch.await(15, TimeUnit.SECONDS));
+ }
+
+ @Test
+ void testDoPost_WithVeryLongUrl() {
+ // Test with very long URL (edge case)
+ StringBuilder longUrl = new StringBuilder("http://localhost:9999/");
+ for (int i = 0; i < 1000; i++) {
+ longUrl.append("path/");
+ }
+
+ assertThrows(IOException.class, () -> {
+ HttpClientUtil.doPost(longUrl.toString(), new HashMap<>(), new
HashMap<>(), 1000);
+ });
+ }
+
+ @Test
+ void testDoGet_WithVeryLongUrl() {
+ // Test with very long URL (edge case)
+ StringBuilder longUrl = new StringBuilder("http://localhost:9999/");
+ for (int i = 0; i < 1000; i++) {
+ longUrl.append("path/");
+ }
+
+ assertThrows(IOException.class, () -> {
+ HttpClientUtil.doGet(longUrl.toString(), new HashMap<>(), new
HashMap<>(), 1000);
+ });
+ }
+
+ @Test
+ void testDoPost_WithLargeParams() {
+ // Test with large number of parameters
+ Map<String, String> params = new HashMap<>();
+ for (int i = 0; i < 100; i++) {
+ params.put("key" + i, "value" + i);
+ }
+
+ assertThrows(IOException.class, () -> {
+ HttpClientUtil.doPost("http://localhost:9999/invalid", params, new
HashMap<>(), 1000);
+ });
+ }
+
+ @Test
+ void testDoGet_WithLargeParams() {
+ // Test with large number of query parameters
+ Map<String, String> params = new HashMap<>();
+ for (int i = 0; i < 100; i++) {
+ params.put("key" + i, "value" + i);
+ }
+
+ assertThrows(IOException.class, () -> {
+ HttpClientUtil.doGet("http://localhost:9999/invalid", params, new
HashMap<>(), 1000);
+ });
+ }
+
+ @Test
+ void testDoPost_WithLargeBody() {
+ // Test with large request body
+ StringBuilder largeBody = new StringBuilder("{");
+ for (int i = 0; i < 1000; i++) {
+ if (i > 0) {
+ largeBody.append(",");
+ }
+
largeBody.append("\"key").append(i).append("\":\"value").append(i).append("\"");
+ }
+ largeBody.append("}");
+
+ Map<String, String> headers = new HashMap<>();
+ headers.put("Content-Type", "application/json");
+
+ assertThrows(IOException.class, () -> {
+ HttpClientUtil.doPost("http://localhost:9999/invalid",
largeBody.toString(), headers, 1000);
+ });
+ }
+
+ @Test
+ void testDoPostJson_WithLargeBody() {
+ // Test with large JSON body
+ StringBuilder largeBody = new StringBuilder("{");
+ for (int i = 0; i < 1000; i++) {
+ if (i > 0) {
+ largeBody.append(",");
+ }
+
largeBody.append("\"key").append(i).append("\":\"value").append(i).append("\"");
+ }
+ largeBody.append("}");
+
+ assertThrows(IOException.class, () -> {
+ HttpClientUtil.doPostJson("http://localhost:9999/invalid",
largeBody.toString(), new HashMap<>(), 1000);
+ });
+ }
+
+ @Test
+ void testDoPost_WithMultipleHeaders() {
+ // Test with multiple headers
+ Map<String, String> headers = new HashMap<>();
+ headers.put("Content-Type", "application/json");
+ headers.put("Authorization", "Bearer token123");
+ headers.put("X-Custom-Header", "custom-value");
+ headers.put("User-Agent", "Seata-Test");
+
Map<String, String> params = new HashMap<>();
params.put("key", "value");
+ assertThrows(IOException.class, () -> {
+ HttpClientUtil.doPost("http://localhost:9999/invalid", params,
headers, 1000);
+ });
+ }
+
+ @Test
+ void testDoGet_WithMultipleHeaders() {
+ // Test with multiple headers
+ Map<String, String> headers = new HashMap<>();
+ headers.put("Authorization", "Bearer token123");
+ headers.put("X-Custom-Header", "custom-value");
+ headers.put("User-Agent", "Seata-Test");
+
+ assertThrows(IOException.class, () -> {
+ HttpClientUtil.doGet("http://localhost:9999/invalid", new
HashMap<>(), headers, 1000);
+ });
+ }
+
+ @Test
+ void testDoPost_WithDuplicateHeaderKeys() {
+ // Test that duplicate header keys are handled (OkHttp allows this)
Map<String, String> headers = new HashMap<>();
headers.put("Content-Type", "application/json");
+ headers.put("X-Header", "value1");
+ // Note: HashMap doesn't allow duplicate keys, but we can test the
behavior
- HttpClientUtil.doGetWithHttp2("http://localhost:9999/invalid",
headers, callback, 30000);
- assertTrue(latch.await(10, TimeUnit.SECONDS));
+ Map<String, String> params = new HashMap<>();
+ params.put("key", "value");
+
+ assertThrows(IOException.class, () -> {
+ HttpClientUtil.doPost("http://localhost:9999/invalid", params,
headers, 1000);
+ });
+ }
+
+ @Test
+ void testDoPost_WithZeroTimeout() {
+ // Test with zero timeout (should still attempt connection)
+ Map<String, String> params = new HashMap<>();
+ params.put("key", "value");
+
+ assertThrows(IOException.class, () -> {
+ HttpClientUtil.doPost("http://localhost:9999/invalid", params, new
HashMap<>(), 0);
+ });
+ }
+
+ @Test
+ void testDoGet_WithZeroTimeout() {
+ // Test with zero timeout
+ assertThrows(IOException.class, () -> {
+ HttpClientUtil.doGet("http://localhost:9999/invalid", new
HashMap<>(), new HashMap<>(), 0);
+ });
+ }
+
+ @Test
+ void testDoPostJson_WithZeroTimeout() {
+ // Test with zero timeout
+ assertThrows(IOException.class, () -> {
+ HttpClientUtil.doPostJson("http://localhost:9999/invalid", "{}",
new HashMap<>(), 0);
+ });
+ }
+
+ @Test
+ void testDoPostWithHttp2_WithZeroTimeout() throws Exception {
+ // Test with zero timeout
+ CountDownLatch latch = new CountDownLatch(1);
+
+ HttpCallback<Response> callback = new HttpCallback<Response>() {
+ @Override
+ public void onSuccess(Response response) {
+ fail("Should not succeed");
+ }
+
+ @Override
+ public void onFailure(Throwable t) {
+ assertNotNull(t);
+ latch.countDown();
+ }
+
+ @Override
+ public void onCancelled() {
+ fail("Should not be cancelled");
+ }
+ };
+
+ Map<String, String> params = new HashMap<>();
+ params.put("key", "value");
+
+ HttpClientUtil.doPostWithHttp2("http://localhost:9999/invalid",
params, new HashMap<>(), callback, 0);
+ assertTrue(latch.await(5, TimeUnit.SECONDS));
+ }
+
+ @Test
+ void testDoGetWithHttp2_WithZeroTimeout() throws Exception {
+ // Test with zero timeout
+ CountDownLatch latch = new CountDownLatch(1);
+
+ HttpCallback<Response> callback = new HttpCallback<Response>() {
+ @Override
+ public void onSuccess(Response response) {
+ fail("Should not succeed");
+ }
+
+ @Override
+ public void onFailure(Throwable t) {
+ assertNotNull(t);
+ latch.countDown();
+ }
+
+ @Override
+ public void onCancelled() {
+ fail("Should not be cancelled");
+ }
+ };
+
+ HttpClientUtil.doGetWithHttp2("http://localhost:9999/invalid", new
HashMap<>(), callback, 0);
+ assertTrue(latch.await(5, TimeUnit.SECONDS));
}
@Test
@@ -237,16 +1031,95 @@ public class HttpClientUtilTest {
http2ClientMapField.setAccessible(true);
Map<Integer, OkHttpClient> http2ClientMap = (Map<Integer,
OkHttpClient>) http2ClientMapField.get(null);
- CloseableHttpClient mockCloseableClient =
mock(CloseableHttpClient.class);
OkHttpClient mockHttp2Client = mock(OkHttpClient.class,
RETURNS_DEEP_STUBS);
+ OkHttpClient mockHttp1Client = mock(OkHttpClient.class,
RETURNS_DEEP_STUBS);
- httpClientMap.put(1, mockCloseableClient);
+ httpClientMap.put(1, mockHttp1Client);
http2ClientMap.put(2, mockHttp2Client);
targetHook.run();
- verify(mockCloseableClient, atLeastOnce()).close();
verify(mockHttp2Client.dispatcher().executorService(),
atLeastOnce()).shutdown();
verify(mockHttp2Client.connectionPool(), atLeastOnce()).evictAll();
+
+ verify(mockHttp1Client.dispatcher().executorService(),
atLeastOnce()).shutdown();
+ verify(mockHttp1Client.connectionPool(), atLeastOnce()).evictAll();
+ }
+
+ @Test
+ void testBuildRequest_PostWithNullRequestBody() throws Exception {
+ // Test that null requestBody for POST is replaced with empty
RequestBody
+ Method buildRequestMethod = HttpClientUtil.class.getDeclaredMethod(
+ "buildRequest", String.class, Map.class, RequestBody.class,
String.class);
+ buildRequestMethod.setAccessible(true);
+
+ String url = "http://localhost:8080/test";
+ Map<String, String> headers = new HashMap<>();
+ headers.put("Content-Type", "application/json");
+
+ Request request = (Request) buildRequestMethod.invoke(null, url,
headers, null, "POST");
+
+ assertNotNull(request);
+ assertEquals("POST", request.method());
+ assertNotNull(request.body());
+ assertEquals(0, request.body().contentLength());
+ }
+
+ @Test
+ void testBuildRequest_PostWithRequestBody() throws Exception {
+ Method buildRequestMethod = HttpClientUtil.class.getDeclaredMethod(
+ "buildRequest", String.class, Map.class, RequestBody.class,
String.class);
+ buildRequestMethod.setAccessible(true);
+
+ String url = "http://localhost:8080/test";
+ Map<String, String> headers = new HashMap<>();
+ RequestBody requestBody = RequestBody.create("test body",
okhttp3.MediaType.parse("application/json"));
+
+ Request request = (Request) buildRequestMethod.invoke(null, url,
headers, requestBody, "POST");
+
+ assertNotNull(request);
+ assertEquals("POST", request.method());
+ assertNotNull(request.body());
+ assertTrue(request.body().contentLength() > 0);
+ }
+
+ @Test
+ void testBuildRequest_UnsupportedMethod() throws Exception {
+ Method buildRequestMethod = HttpClientUtil.class.getDeclaredMethod(
+ "buildRequest", String.class, Map.class, RequestBody.class,
String.class);
+ buildRequestMethod.setAccessible(true);
+
+ String url = "http://localhost:8080/test";
+ Map<String, String> headers = new HashMap<>();
+
+ Exception exception = assertThrows(Exception.class, () -> {
+ buildRequestMethod.invoke(null, url, headers, null, "PUT");
+ });
+ assertTrue(exception.getCause() instanceof IllegalArgumentException);
+ assertTrue(exception.getCause().getMessage().contains("Unsupported
HTTP method: PUT"));
+
+ exception = assertThrows(Exception.class, () -> {
+ buildRequestMethod.invoke(null, url, headers, null, "DELETE");
+ });
+ assertTrue(exception.getCause() instanceof IllegalArgumentException);
+ assertTrue(exception.getCause().getMessage().contains("Unsupported
HTTP method: DELETE"));
+
+ exception = assertThrows(Exception.class, () -> {
+ buildRequestMethod.invoke(null, url, headers, null, "PATCH");
+ });
+ assertTrue(exception.getCause() instanceof IllegalArgumentException);
+ assertTrue(exception.getCause().getMessage().contains("Unsupported
HTTP method: PATCH"));
+ }
+
+ @Test
+ void testHttpSendRes() throws IOException {
+ Response response = HttpClientUtil.doGet("http:www.baidu.com", null,
null, 3000);
+ Response postResponse = HttpClientUtil.doPost("http:www.baidu.com",
new HashMap<>(), new HashMap<>(), 3000);
+ Response postResponse2 = HttpClientUtil.doPost("http:www.baidu.com",
"", new HashMap<>(), 3000);
+ Response nonjsonResponse =
HttpClientUtil.doPostJson("http:www.baidu.com", "nonjson", new HashMap<>(),
3000);
+ assertNotNull(response);
+ assertNotNull(postResponse);
+ assertNotNull(postResponse2);
+ assertNotNull(nonjsonResponse);
}
}
diff --git
a/discovery/seata-discovery-namingserver/src/main/java/org/apache/seata/discovery/registry/namingserver/NamingserverRegistryServiceImpl.java
b/discovery/seata-discovery-namingserver/src/main/java/org/apache/seata/discovery/registry/namingserver/NamingserverRegistryServiceImpl.java
index 8eb28c439f..a889fbdf0a 100644
---
a/discovery/seata-discovery-namingserver/src/main/java/org/apache/seata/discovery/registry/namingserver/NamingserverRegistryServiceImpl.java
+++
b/discovery/seata-discovery-namingserver/src/main/java/org/apache/seata/discovery/registry/namingserver/NamingserverRegistryServiceImpl.java
@@ -20,12 +20,10 @@ import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
+import okhttp3.Response;
import org.apache.http.HttpStatus;
-import org.apache.http.StatusLine;
-import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.entity.ContentType;
import org.apache.http.protocol.HTTP;
-import org.apache.http.util.EntityUtils;
import org.apache.seata.common.ConfigurationKeys;
import org.apache.seata.common.exception.AuthenticationFailedException;
import org.apache.seata.common.exception.RetryableException;
@@ -49,7 +47,6 @@ import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.net.InetSocketAddress;
-import java.nio.charset.StandardCharsets;
import java.rmi.RemoteException;
import java.util.ArrayList;
import java.util.Arrays;
@@ -230,8 +227,8 @@ public class NamingserverRegistryServiceImpl implements
RegistryService<NamingLi
}
header.put(HTTP.CONTENT_TYPE,
ContentType.APPLICATION_JSON.getMimeType());
- try (CloseableHttpResponse response = HttpClientUtil.doPost(url,
jsonBody, header, 3000)) {
- int statusCode = response.getStatusLine().getStatusCode();
+ try (Response response = HttpClientUtil.doPost(url, jsonBody,
header, 3000)) {
+ int statusCode = response.code();
if (statusCode == 200) {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("instance has been registered
successfully:{}", statusCode);
@@ -249,8 +246,8 @@ public class NamingserverRegistryServiceImpl implements
RegistryService<NamingLi
url = HTTP_PREFIX + url + "/naming/v1/health";
Map<String, String> header = new HashMap<>();
header.put(HTTP.CONTENT_TYPE,
ContentType.APPLICATION_JSON.getMimeType());
- try (CloseableHttpResponse response = HttpClientUtil.doGet(url, null,
header, 3000)) {
- int statusCode = response.getStatusLine().getStatusCode();
+ try (Response response = HttpClientUtil.doGet(url, null, header,
3000)) {
+ int statusCode = response.code();
return statusCode == 200;
} catch (Exception e) {
return false;
@@ -274,8 +271,8 @@ public class NamingserverRegistryServiceImpl implements
RegistryService<NamingLi
url += params;
Map<String, String> header = new HashMap<>();
header.put(HTTP.CONTENT_TYPE,
ContentType.APPLICATION_JSON.getMimeType());
- try (CloseableHttpResponse response = HttpClientUtil.doPost(url,
jsonBody, header, 3000)) {
- int statusCode = response.getStatusLine().getStatusCode();
+ try (Response response = HttpClientUtil.doPost(url, jsonBody,
header, 3000)) {
+ int statusCode = response.code();
if (statusCode == 200) {
LOGGER.info("instance has been unregistered
successfully:{}", statusCode);
} else {
@@ -357,10 +354,9 @@ public class NamingserverRegistryServiceImpl implements
RegistryService<NamingLi
if (StringUtils.isNotBlank(jwtToken)) {
header.put(AUTHORIZATION_HEADER, jwtToken);
}
- try (CloseableHttpResponse response = HttpClientUtil.doPost(watchAddr,
(String) null, header, 30000)) {
+ try (Response response = HttpClientUtil.doPost(watchAddr, (String)
null, header, 30000)) {
if (response != null) {
- StatusLine statusLine = response.getStatusLine();
- return statusLine != null && statusLine.getStatusCode() ==
HttpStatus.SC_OK;
+ return response.code() == HttpStatus.SC_OK;
}
} catch (Exception e) {
LOGGER.error("watch failed: {}", e.getMessage());
@@ -438,12 +434,15 @@ public class NamingserverRegistryServiceImpl implements
RegistryService<NamingLi
if (StringUtils.isNotBlank(jwtToken)) {
header.put(AUTHORIZATION_HEADER, jwtToken);
}
- try (CloseableHttpResponse response = HttpClientUtil.doGet(url,
paraMap, header, 3000)) {
- if (response == null || response.getStatusLine().getStatusCode()
!= HttpStatus.SC_OK) {
+ try (Response response = HttpClientUtil.doGet(url, paraMap, header,
3000)) {
+ if (response == null || response.code() != HttpStatus.SC_OK) {
throw new NamingRegistryException("cannot lookup server list
in vgroup: " + vGroup + ", http code: "
- + response.getStatusLine().getStatusCode());
+ + (response != null ? response.code() : -1));
}
- String jsonResponse = EntityUtils.toString(response.getEntity(),
StandardCharsets.UTF_8);
+ if (response.body() == null) {
+ throw new NamingRegistryException("Response body is null for
vgroup: " + vGroup);
+ }
+ String jsonResponse = response.body().string();
// jsonResponse -> MetaResponse
MetaResponse metaResponse = OBJECT_MAPPER.readValue(jsonResponse,
new TypeReference<MetaResponse>() {});
return handleMetadata(metaResponse, vGroup);
@@ -576,20 +575,24 @@ public class NamingserverRegistryServiceImpl implements
RegistryService<NamingLi
Map<String, String> header = new HashMap<>();
header.put(HTTP.CONTENT_TYPE,
ContentType.APPLICATION_JSON.getMimeType());
String response = null;
- try (CloseableHttpResponse httpResponse =
+ try (Response httpResponse =
HttpClientUtil.doPost("http://" + namingServerAddress +
"/api/v1/auth/login", param, header, 1000)) {
if (httpResponse != null) {
- if (httpResponse.getStatusLine().getStatusCode() ==
HttpStatus.SC_OK) {
- response = EntityUtils.toString(httpResponse.getEntity(),
StandardCharsets.UTF_8);
- JsonNode jsonNode = OBJECT_MAPPER.readTree(response);
- String codeStatus = jsonNode.get("code").asText();
- if (!StringUtils.equals(codeStatus, "200")) {
- // authorized failed,throw exception to kill process
- throw new AuthenticationFailedException(
- "Authentication failed! you should configure
the correct username and password.");
+ if (httpResponse.code() == HttpStatus.SC_OK) {
+ if (httpResponse.body() != null) {
+ response = httpResponse.body().string();
+ JsonNode jsonNode = OBJECT_MAPPER.readTree(response);
+ String codeStatus = jsonNode.get("code").asText();
+ if (!StringUtils.equals(codeStatus, "200")) {
+ // authorized failed,throw exception to kill
process
+ throw new AuthenticationFailedException(
+ "Authentication failed! you should
configure the correct username and password.");
+ }
+ jwtToken = jsonNode.get("data").asText();
+ tokenTimeStamp = System.currentTimeMillis();
+ } else {
+ throw new
AuthenticationFailedException("Authentication failed! Response body is null.");
}
- jwtToken = jsonNode.get("data").asText();
- tokenTimeStamp = System.currentTimeMillis();
} else {
// authorized failed,throw exception to kill process
throw new AuthenticationFailedException(
diff --git
a/discovery/seata-discovery-namingserver/src/test/java/org/apache/seata/discovery/registry/namingserver/NamingserverRegistryServiceImplTest.java
b/discovery/seata-discovery-namingserver/src/test/java/org/apache/seata/discovery/registry/namingserver/NamingserverRegistryServiceImplTest.java
index 4f72e1f5b1..9980676da6 100644
---
a/discovery/seata-discovery-namingserver/src/test/java/org/apache/seata/discovery/registry/namingserver/NamingserverRegistryServiceImplTest.java
+++
b/discovery/seata-discovery-namingserver/src/test/java/org/apache/seata/discovery/registry/namingserver/NamingserverRegistryServiceImplTest.java
@@ -16,13 +16,18 @@
*/
package org.apache.seata.discovery.registry.namingserver;
-import org.apache.http.StatusLine;
-import org.apache.http.client.methods.CloseableHttpResponse;
+import okhttp3.MediaType;
+import okhttp3.Protocol;
+import okhttp3.Request;
+import okhttp3.Response;
+import okhttp3.ResponseBody;
import org.apache.http.entity.ContentType;
import org.apache.http.protocol.HTTP;
+import org.apache.seata.common.exception.RetryableException;
import org.apache.seata.common.holder.ObjectHolder;
import org.apache.seata.common.metadata.Cluster;
import org.apache.seata.common.metadata.ClusterRole;
+import org.apache.seata.common.metadata.Instance;
import org.apache.seata.common.metadata.Node;
import org.apache.seata.common.metadata.namingserver.MetaResponse;
import org.apache.seata.common.metadata.namingserver.NamingServerNode;
@@ -32,15 +37,19 @@ import org.apache.seata.config.Configuration;
import org.apache.seata.config.ConfigurationFactory;
import org.apache.seata.discovery.registry.RegistryService;
import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
+import org.mockito.Answers;
+import org.mockito.MockedStatic;
import org.mockito.Mockito;
import
org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.MutablePropertySources;
import org.springframework.core.env.PropertiesPropertySource;
+import java.io.IOException;
import java.lang.reflect.Field;
import java.net.InetSocketAddress;
import java.rmi.RemoteException;
@@ -55,14 +64,15 @@ import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import static
org.apache.seata.common.Constants.OBJECT_KEY_SPRING_CONFIGURABLE_ENVIRONMENT;
+import static org.junit.Assert.assertTrue;
import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyMap;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.mockStatic;
-import static org.mockito.Mockito.when;
class NamingserverRegistryServiceImplTest {
@@ -110,14 +120,24 @@ class NamingserverRegistryServiceImplTest {
NamingserverRegistryServiceImpl spyService =
Mockito.spy(NamingserverRegistryServiceImpl.getInstance());
doReturn("127.0.0.1:8081").when(spyService).getNamingAddr();
- CloseableHttpResponse mockResponse = mock(CloseableHttpResponse.class);
- StatusLine mockStatusLine = mock(StatusLine.class);
- when(mockStatusLine.getStatusCode()).thenReturn(200);
- when(mockResponse.getStatusLine()).thenReturn(mockStatusLine);
- mockStatic(HttpClientUtil.class);
- when(HttpClientUtil.doPost(anyString(), anyString(), anyMap(),
anyInt()))
- .thenReturn(mockResponse);
- spyService.watch("testGroup");
+ ResponseBody body = ResponseBody.create("",
MediaType.get("application/json"));
+
+ Response mockResponse = new Response.Builder()
+ .request(new Request.Builder().url("http://localhost").build())
+ .protocol(Protocol.HTTP_1_1)
+ .code(200)
+ .message("OK")
+ .body(body)
+ .build();
+
+ try (MockedStatic<HttpClientUtil> mockedStatic =
mockStatic(HttpClientUtil.class)) {
+
+ mockedStatic
+ .when(() -> HttpClientUtil.doPost(anyString(),
anyString(), anyMap(), anyInt()))
+ .thenReturn(mockResponse);
+
+ spyService.watch("testGroup");
+ }
}
@Test
@@ -389,12 +409,138 @@ class NamingserverRegistryServiceImplTest {
Map<String, String> header = new HashMap<>();
header.put(HTTP.CONTENT_TYPE,
ContentType.APPLICATION_FORM_URLENCODED.getMimeType());
try {
- CloseableHttpResponse response = HttpClientUtil.doGet(url,
paraMap, header, 30000);
+ Response response = HttpClientUtil.doGet(url, paraMap, header,
30000);
} catch (Exception e) {
throw new RemoteException();
}
}
+ @Test
+ void testDoHealthCheck_success() {
+ ResponseBody body = ResponseBody.create("",
MediaType.get("application/json"));
+
+ Response mockResponse = new Response.Builder()
+ .request(new Request.Builder().url("http://localhost").build())
+ .protocol(Protocol.HTTP_1_1)
+ .code(200)
+ .message("OK")
+ .body(body)
+ .build();
+
+ try (MockedStatic<HttpClientUtil> mockedStatic =
mockStatic(HttpClientUtil.class)) {
+
+ mockedStatic
+ .when(() -> HttpClientUtil.doGet(any(), any(), any(),
anyInt()))
+ .thenReturn(mockResponse);
+
+ NamingserverRegistryServiceImpl service =
+ mock(NamingserverRegistryServiceImpl.class,
Answers.CALLS_REAL_METHODS);
+
+ boolean result = service.doHealthCheck("127.0.0.1:8080");
+
+ assertTrue(result);
+ }
+ }
+
+ @Test
+ void testUnregister_success() {
+ ResponseBody body = ResponseBody.create("",
MediaType.get("application/json"));
+
+ Response mockResponse = new Response.Builder()
+ .request(new Request.Builder().url("http://localhost").build())
+ .protocol(Protocol.HTTP_1_1)
+ .code(200)
+ .message("OK")
+ .body(body)
+ .build();
+
+ try (MockedStatic<HttpClientUtil> mockedStatic =
mockStatic(HttpClientUtil.class)) {
+
+ mockedStatic
+ .when(() -> HttpClientUtil.doPost(any(), anyString(),
any(), anyInt()))
+ .thenReturn(mockResponse);
+
+ NamingserverRegistryServiceImpl service =
+ mock(NamingserverRegistryServiceImpl.class,
Answers.CALLS_REAL_METHODS);
+
+ service.unregister(Instance.getInstance());
+ }
+ }
+
+ @Test
+ void testRefreshGroup_withResponse() throws RetryableException,
IOException {
+ NamingserverRegistryServiceImpl spyService =
Mockito.spy(NamingserverRegistryServiceImpl.getInstance());
+ doReturn("127.0.0.1:8081").when(spyService).getNamingAddr();
+
+ ResponseBody body =
ResponseBody.create("{\"clusterList\":[],\"term\":1}",
MediaType.get("application/json"));
+
+ Response mockResponse = new Response.Builder()
+ .request(new Request.Builder().url("http://localhost").build())
+ .protocol(Protocol.HTTP_1_1)
+ .code(200)
+ .message("OK")
+ .body(body)
+ .build();
+
+ try (MockedStatic<HttpClientUtil> mockedStatic =
mockStatic(HttpClientUtil.class)) {
+
+ mockedStatic
+ .when(() -> HttpClientUtil.doGet(any(), any(), any(),
anyInt()))
+ .thenReturn(mockResponse);
+
+ spyService.refreshGroup("testGroup");
+ }
+ }
+
+ @Test
+ void testRefreshGroup_withNoBody() throws RetryableException, IOException {
+ NamingserverRegistryServiceImpl spyService =
Mockito.spy(NamingserverRegistryServiceImpl.getInstance());
+ doReturn("127.0.0.1:8081").when(spyService).getNamingAddr();
+
+ ResponseBody body = ResponseBody.create("",
MediaType.get("application/json"));
+
+ Response mockResponse = new Response.Builder()
+ .request(new Request.Builder().url("http://localhost").build())
+ .protocol(Protocol.HTTP_1_1)
+ .code(200)
+ .message("OK")
+ .body(body)
+ .build();
+
+ try (MockedStatic<HttpClientUtil> mockedStatic =
mockStatic(HttpClientUtil.class)) {
+
+ mockedStatic
+ .when(() -> HttpClientUtil.doGet(any(), any(), any(),
anyInt()))
+ .thenReturn(mockResponse);
+
+ Assertions.assertThrows(IOException.class, () ->
spyService.refreshGroup("testGroup"));
+ }
+ }
+
+ @Test
+ void testRefreshGroup_withNoResponse() throws RetryableException,
IOException {
+ NamingserverRegistryServiceImpl spyService =
Mockito.spy(NamingserverRegistryServiceImpl.getInstance());
+ doReturn("127.0.0.1:8081").when(spyService).getNamingAddr();
+
+ ResponseBody body = ResponseBody.create("",
MediaType.get("application/json"));
+
+ Response mockResponse = new Response.Builder()
+ .request(new Request.Builder().url("http://localhost").build())
+ .protocol(Protocol.HTTP_1_1)
+ .code(200)
+ .message("OK")
+ .body(body)
+ .build();
+
+ try (MockedStatic<HttpClientUtil> mockedStatic =
mockStatic(HttpClientUtil.class)) {
+ mockedStatic
+ .when(() -> HttpClientUtil.doGet(any(), any(), any(),
anyInt()))
+ .thenReturn(null);
+
+ Assertions.assertThrows(NamingRegistryException.class, () ->
spyService.refreshGroup("testGroup"));
+ }
+ }
+
private class NamingListenerimpl implements NamingListener {
public boolean isNotified = false;
@@ -413,4 +559,3 @@ class NamingserverRegistryServiceImplTest {
}
}
}
-;
diff --git
a/discovery/seata-discovery-raft/src/main/java/org/apache/seata/discovery/registry/raft/RaftRegistryServiceImpl.java
b/discovery/seata-discovery-raft/src/main/java/org/apache/seata/discovery/registry/raft/RaftRegistryServiceImpl.java
index ded156e6c6..aa154aed4d 100644
---
a/discovery/seata-discovery-raft/src/main/java/org/apache/seata/discovery/registry/raft/RaftRegistryServiceImpl.java
+++
b/discovery/seata-discovery-raft/src/main/java/org/apache/seata/discovery/registry/raft/RaftRegistryServiceImpl.java
@@ -19,12 +19,10 @@ package org.apache.seata.discovery.registry.raft;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
+import okhttp3.Response;
import org.apache.http.HttpStatus;
-import org.apache.http.StatusLine;
-import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.entity.ContentType;
import org.apache.http.protocol.HTTP;
-import org.apache.http.util.EntityUtils;
import org.apache.seata.common.ConfigurationKeys;
import org.apache.seata.common.exception.AuthenticationFailedException;
import org.apache.seata.common.exception.NotSupportYetException;
@@ -47,7 +45,6 @@ import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.net.InetSocketAddress;
-import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@@ -425,11 +422,11 @@ public class RaftRegistryServiceImpl implements
RegistryService<ConfigChangeList
if (StringUtils.isNotBlank(jwtToken)) {
header.put(AUTHORIZATION_HEADER, jwtToken);
}
- try (CloseableHttpResponse response =
+ try (Response response =
HttpClientUtil.doPost("http://" + tcAddress +
"/metadata/v1/watch", param, header, 30000)) {
if (response != null) {
- StatusLine statusLine = response.getStatusLine();
- if (statusLine != null && statusLine.getStatusCode() ==
HttpStatus.SC_UNAUTHORIZED) {
+ int statusCode = response.code();
+ if (statusCode == HttpStatus.SC_UNAUTHORIZED) {
if (StringUtils.isNotBlank(USERNAME) &&
StringUtils.isNotBlank(PASSWORD)) {
throw new RetryableException("Authentication
failed!");
} else {
@@ -437,7 +434,7 @@ public class RaftRegistryServiceImpl implements
RegistryService<ConfigChangeList
"Authentication failed! you should
configure the correct username and password.");
}
}
- return statusLine != null && statusLine.getStatusCode() ==
HttpStatus.SC_OK;
+ return statusCode == HttpStatus.SC_OK;
}
} catch (IOException e) {
LOGGER.error("watch cluster node: {}, fail: {}", tcAddress,
e.getMessage());
@@ -498,12 +495,16 @@ public class RaftRegistryServiceImpl implements
RegistryService<ConfigChangeList
Map<String, String> param = new HashMap<>();
param.put("group", group);
String response = null;
- try (CloseableHttpResponse httpResponse =
+ try (Response httpResponse =
HttpClientUtil.doGet("http://" + tcAddress +
"/metadata/v1/cluster", param, header, 1000)) {
if (httpResponse != null) {
- int statusCode =
httpResponse.getStatusLine().getStatusCode();
+ int statusCode = httpResponse.code();
if (statusCode == HttpStatus.SC_OK) {
- response =
EntityUtils.toString(httpResponse.getEntity(), StandardCharsets.UTF_8);
+ if (httpResponse.body() != null) {
+ response = httpResponse.body().string();
+ } else {
+ throw new RetryableException("Response body is
null");
+ }
} else if (statusCode == HttpStatus.SC_UNAUTHORIZED) {
if (StringUtils.isNotBlank(USERNAME) &&
StringUtils.isNotBlank(PASSWORD)) {
refreshToken(tcAddress);
@@ -544,20 +545,24 @@ public class RaftRegistryServiceImpl implements
RegistryService<ConfigChangeList
Map<String, String> header = new HashMap<>();
header.put(HTTP.CONTENT_TYPE,
ContentType.APPLICATION_JSON.getMimeType());
String response = null;
- try (CloseableHttpResponse httpResponse =
+ try (Response httpResponse =
HttpClientUtil.doPost("http://" + tcAddress +
"/api/v1/auth/login", param, header, 1000)) {
if (httpResponse != null) {
- if (httpResponse.getStatusLine().getStatusCode() ==
HttpStatus.SC_OK) {
- response = EntityUtils.toString(httpResponse.getEntity(),
StandardCharsets.UTF_8);
- JsonNode jsonNode = OBJECT_MAPPER.readTree(response);
- String codeStatus = jsonNode.get("code").asText();
- if (!StringUtils.equals(codeStatus, "200")) {
- // authorized failed,throw exception to kill process
- throw new AuthenticationFailedException(
- "Authentication failed! you should configure
the correct username and password.");
+ if (httpResponse.code() == HttpStatus.SC_OK) {
+ if (httpResponse.body() != null) {
+ response = httpResponse.body().string();
+ JsonNode jsonNode = OBJECT_MAPPER.readTree(response);
+ String codeStatus = jsonNode.get("code").asText();
+ if (!StringUtils.equals(codeStatus, "200")) {
+ // authorized failed,throw exception to kill
process
+ throw new AuthenticationFailedException(
+ "Authentication failed! you should
configure the correct username and password.");
+ }
+ jwtToken = jsonNode.get("data").asText();
+ tokenTimeStamp = System.currentTimeMillis();
+ } else {
+ throw new
AuthenticationFailedException("Authentication failed! Response body is null.");
}
- jwtToken = jsonNode.get("data").asText();
- tokenTimeStamp = System.currentTimeMillis();
} else {
// authorized failed,throw exception to kill process
throw new AuthenticationFailedException(
diff --git
a/discovery/seata-discovery-raft/src/test/java/org/apache/seata/discovery/registry/raft/RaftRegistryServiceImplTest.java
b/discovery/seata-discovery-raft/src/test/java/org/apache/seata/discovery/registry/raft/RaftRegistryServiceImplTest.java
index 90b9f76a0b..027f2cad87 100644
---
a/discovery/seata-discovery-raft/src/test/java/org/apache/seata/discovery/registry/raft/RaftRegistryServiceImplTest.java
+++
b/discovery/seata-discovery-raft/src/test/java/org/apache/seata/discovery/registry/raft/RaftRegistryServiceImplTest.java
@@ -18,10 +18,9 @@ package org.apache.seata.discovery.registry.raft;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
+import okhttp3.Response;
+import okhttp3.ResponseBody;
import org.apache.http.HttpStatus;
-import org.apache.http.StatusLine;
-import org.apache.http.client.methods.CloseableHttpResponse;
-import org.apache.http.entity.StringEntity;
import org.apache.seata.common.metadata.MetadataResponse;
import org.apache.seata.common.metadata.Node;
import org.apache.seata.common.util.*;
@@ -74,12 +73,12 @@ class RaftRegistryServiceImplTest {
try (MockedStatic<HttpClientUtil> mockedStatic =
Mockito.mockStatic(HttpClientUtil.class)) {
- CloseableHttpResponse mockResponse =
mock(CloseableHttpResponse.class);
- StatusLine mockStatusLine = mock(StatusLine.class);
+ ResponseBody mockResponseBody = mock(ResponseBody.class);
+ Response mockResponse = mock(Response.class);
- when(mockResponse.getEntity()).thenReturn(new
StringEntity(responseBody));
- when(mockResponse.getStatusLine()).thenReturn(mockStatusLine);
- when(mockStatusLine.getStatusCode()).thenReturn(HttpStatus.SC_OK);
+ when(mockResponseBody.string()).thenReturn(responseBody);
+ when(mockResponse.code()).thenReturn(HttpStatus.SC_OK);
+ when(mockResponse.body()).thenReturn(mockResponseBody);
when(HttpClientUtil.doPost(any(String.class), any(Map.class),
any(Map.class), any(int.class)))
.thenReturn(mockResponse);
@@ -106,12 +105,12 @@ class RaftRegistryServiceImplTest {
try (MockedStatic<HttpClientUtil> mockedStatic =
Mockito.mockStatic(HttpClientUtil.class)) {
- CloseableHttpResponse mockResponse =
mock(CloseableHttpResponse.class);
- StatusLine mockStatusLine = mock(StatusLine.class);
+ ResponseBody mockResponseBody = mock(ResponseBody.class);
+ Response mockResponse = mock(Response.class);
- when(mockResponse.getEntity()).thenReturn(new
StringEntity(responseBody));
- when(mockResponse.getStatusLine()).thenReturn(mockStatusLine);
- when(mockStatusLine.getStatusCode()).thenReturn(HttpStatus.SC_OK);
+ when(mockResponseBody.string()).thenReturn(responseBody);
+ when(mockResponse.code()).thenReturn(HttpStatus.SC_OK);
+ when(mockResponse.body()).thenReturn(mockResponseBody);
when(HttpClientUtil.doPost(any(String.class), any(Map.class),
any(Map.class), any(int.class)))
.thenReturn(mockResponse);
diff --git
a/namingserver/src/main/java/org/apache/seata/namingserver/manager/NamingManager.java
b/namingserver/src/main/java/org/apache/seata/namingserver/manager/NamingManager.java
index cfa50d0688..13a6e42cdf 100644
---
a/namingserver/src/main/java/org/apache/seata/namingserver/manager/NamingManager.java
+++
b/namingserver/src/main/java/org/apache/seata/namingserver/manager/NamingManager.java
@@ -21,7 +21,7 @@ import com.github.benmanes.caffeine.cache.LoadingCache;
import com.github.benmanes.caffeine.cache.RemovalCause;
import com.github.benmanes.caffeine.cache.RemovalListener;
import jakarta.annotation.PostConstruct;
-import org.apache.http.client.methods.CloseableHttpResponse;
+import okhttp3.Response;
import org.apache.http.entity.ContentType;
import org.apache.http.protocol.HTTP;
import org.apache.seata.common.NamingServerConstants;
@@ -219,11 +219,10 @@ public class NamingManager {
Map<String, String> header = new HashMap<>();
header.put(HTTP.CONTENT_TYPE,
ContentType.APPLICATION_FORM_URLENCODED.getMimeType());
- try (CloseableHttpResponse closeableHttpResponse =
HttpClientUtil.doGet(httpUrl, params, header, 3000)) {
- if (closeableHttpResponse == null
- ||
closeableHttpResponse.getStatusLine().getStatusCode() != 200) {
+ try (Response httpResponse = HttpClientUtil.doGet(httpUrl, params,
header, 3000)) {
+ if (httpResponse == null || httpResponse.code() != 200) {
return new Result<>(
-
String.valueOf(closeableHttpResponse.getStatusLine().getStatusCode()),
+ String.valueOf(httpResponse != null ?
httpResponse.code() : 500),
"add vGroup in new cluster failed");
}
LOGGER.info(
@@ -252,12 +251,11 @@ public class NamingManager {
params.put(NamingServerConstants.CONSTANT_UNIT, unitName);
Map<String, String> header = new HashMap<>();
header.put(HTTP.CONTENT_TYPE,
ContentType.APPLICATION_FORM_URLENCODED.getMimeType());
- try (CloseableHttpResponse closeableHttpResponse =
HttpClientUtil.doGet(httpUrl, params, header, 3000)) {
- if (closeableHttpResponse == null
- ||
closeableHttpResponse.getStatusLine().getStatusCode() != 200) {
+ try (Response httpResponse = HttpClientUtil.doGet(httpUrl, params,
header, 3000)) {
+ if (httpResponse == null || httpResponse.code() != 200) {
LOGGER.warn("remove vGroup in old cluster failed");
return new Result<>(
-
String.valueOf(closeableHttpResponse.getStatusLine().getStatusCode()),
+ String.valueOf(httpResponse != null ?
httpResponse.code() : 500),
"removing vGroup " + vGroup + " in old cluster " +
clusterName + " failed");
}
LOGGER.info(
diff --git
a/namingserver/src/test/java/org/apache/seata/namingserver/NamingManagerTest.java
b/namingserver/src/test/java/org/apache/seata/namingserver/NamingManagerTest.java
index a44fbac321..10169707b3 100644
---
a/namingserver/src/test/java/org/apache/seata/namingserver/NamingManagerTest.java
+++
b/namingserver/src/test/java/org/apache/seata/namingserver/NamingManagerTest.java
@@ -16,8 +16,8 @@
*/
package org.apache.seata.namingserver;
-import org.apache.http.StatusLine;
-import org.apache.http.client.methods.CloseableHttpResponse;
+import okhttp3.Response;
+import okhttp3.ResponseBody;
import org.apache.seata.common.metadata.Cluster;
import org.apache.seata.common.metadata.ClusterRole;
import org.apache.seata.common.metadata.Node;
@@ -64,10 +64,10 @@ class NamingManagerTest {
private ApplicationContext applicationContext;
@Mock
- private CloseableHttpResponse httpResponse;
+ private Response httpResponse;
@Mock
- private StatusLine statusLine;
+ private ResponseBody responseBody;
private MockedStatic<HttpClientUtil> mockedHttpClientUtil;
@@ -78,7 +78,8 @@ class NamingManagerTest {
ReflectionTestUtils.setField(namingManager, "heartbeatTimeThreshold",
500000);
ReflectionTestUtils.setField(namingManager,
"heartbeatCheckTimePeriod", 10000000);
- Mockito.when(httpResponse.getStatusLine()).thenReturn(statusLine);
+ Mockito.when(httpResponse.code()).thenReturn(200);
+ Mockito.when(httpResponse.body()).thenReturn(responseBody);
mockedHttpClientUtil = Mockito.mockStatic(HttpClientUtil.class);
mockedHttpClientUtil
.when(() -> HttpClientUtil.doGet(anyString(), anyMap(),
anyMap(), anyInt()))
@@ -228,7 +229,7 @@ class NamingManagerTest {
node.getMetadata().put(CONSTANT_GROUP, vGroups);
namingManager.registerInstance(node, namespace, clusterName, unitName);
- Mockito.when(statusLine.getStatusCode()).thenReturn(200);
+ Mockito.when(httpResponse.code()).thenReturn(200);
Result<String> result = namingManager.createGroup(namespace, vGroup,
clusterName, unitName);
assertFalse(result.isSuccess());
vGroup = "test-vGroup2";
@@ -292,8 +293,8 @@ class NamingManagerTest {
nodeList.add(node);
unit.setNamingInstanceList(nodeList);
- Mockito.when(httpResponse.getStatusLine()).thenReturn(statusLine);
- Mockito.when(statusLine.getStatusCode()).thenReturn(200);
+ Mockito.when(httpResponse.code()).thenReturn(200);
+ Mockito.when(httpResponse.body()).thenReturn(responseBody);
mockedHttpClientUtil
.when(() -> HttpClientUtil.doGet(anyString(), anyMap(),
anyMap(), anyInt()))
diff --git
a/server/src/test/java/org/apache/seata/server/controller/ClusterControllerTest.java
b/server/src/test/java/org/apache/seata/server/controller/ClusterControllerTest.java
index 825e423f63..efcf24bdd6 100644
---
a/server/src/test/java/org/apache/seata/server/controller/ClusterControllerTest.java
+++
b/server/src/test/java/org/apache/seata/server/controller/ClusterControllerTest.java
@@ -19,8 +19,6 @@ package org.apache.seata.server.controller;
import okhttp3.Protocol;
import okhttp3.Response;
import org.apache.http.HttpStatus;
-import org.apache.http.StatusLine;
-import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.entity.ContentType;
import org.apache.http.protocol.HTTP;
import org.apache.seata.common.executor.HttpCallback;
@@ -71,11 +69,10 @@ class ClusterControllerTest extends BaseSpringBootTest {
header.put(HTTP.CONN_KEEP_ALIVE, "close");
Map<String, String> param = new HashMap<>();
param.put("default-test", "1");
- try (CloseableHttpResponse response = HttpClientUtil.doPost(
+ try (Response response = HttpClientUtil.doPost(
"http://127.0.0.1:" + port +
"/metadata/v1/watch?timeout=3000", param, header, 5000)) {
if (response != null) {
- StatusLine statusLine = response.getStatusLine();
- Assertions.assertEquals(HttpStatus.SC_NOT_MODIFIED,
statusLine.getStatusCode());
+ Assertions.assertEquals(HttpStatus.SC_NOT_MODIFIED,
response.code());
return;
}
}
@@ -138,11 +135,10 @@ class ClusterControllerTest extends BaseSpringBootTest {
}
});
thread.start();
- try (CloseableHttpResponse response =
+ try (Response response =
HttpClientUtil.doPost("http://127.0.0.1:" + port +
"/metadata/v1/watch", param, header, 30000)) {
if (response != null) {
- StatusLine statusLine = response.getStatusLine();
- Assertions.assertEquals(HttpStatus.SC_OK,
statusLine.getStatusCode());
+ Assertions.assertEquals(HttpStatus.SC_OK, response.code());
return;
}
}
@@ -202,14 +198,13 @@ class ClusterControllerTest extends BaseSpringBootTest {
String malicious = "<script>alert('xss')</script>";
Map<String, String> header = new HashMap<>();
header.put(HTTP.CONTENT_TYPE,
ContentType.APPLICATION_FORM_URLENCODED.getMimeType());
- try (CloseableHttpResponse response = HttpClientUtil.doGet(
+ try (Response response = HttpClientUtil.doGet(
"http://127.0.0.1:" + port +
"/metadata/v1/watch?timeout=3000&testParam="
+ URLEncoder.encode(malicious,
String.valueOf(StandardCharsets.UTF_8)),
new HashMap<>(),
header,
5000)) {
- Assertions.assertEquals(
- HttpStatus.SC_BAD_REQUEST,
response.getStatusLine().getStatusCode());
+ Assertions.assertEquals(HttpStatus.SC_BAD_REQUEST,
response.code());
}
}
@@ -333,10 +328,9 @@ class ClusterControllerTest extends BaseSpringBootTest {
Map<String, String> params = new HashMap<>();
params.put("testParam", "<script>alert('xss')</script>");
- try (CloseableHttpResponse response = HttpClientUtil.doPost(
+ try (Response response = HttpClientUtil.doPost(
"http://127.0.0.1:" + port +
"/metadata/v1/watch?timeout=3000", params, headers, 5000)) {
- Assertions.assertEquals(
- HttpStatus.SC_BAD_REQUEST,
response.getStatusLine().getStatusCode());
+ Assertions.assertEquals(HttpStatus.SC_BAD_REQUEST,
response.code());
}
}
@@ -348,10 +342,9 @@ class ClusterControllerTest extends BaseSpringBootTest {
String jsonBody = "{\"testParam\":\"<script>alert('xss')</script>\"}";
- try (CloseableHttpResponse response = HttpClientUtil.doPostJson(
+ try (Response response = HttpClientUtil.doPostJson(
"http://127.0.0.1:" + port +
"/metadata/v1/watch?timeout=3000", jsonBody, headers, 5000)) {
- Assertions.assertEquals(
- HttpStatus.SC_BAD_REQUEST,
response.getStatusLine().getStatusCode());
+ Assertions.assertEquals(HttpStatus.SC_BAD_REQUEST,
response.code());
}
}
@@ -365,10 +358,9 @@ class ClusterControllerTest extends BaseSpringBootTest {
Map<String, String> params = new HashMap<>();
params.put("safeParam", "123");
- try (CloseableHttpResponse response = HttpClientUtil.doPost(
+ try (Response response = HttpClientUtil.doPost(
"http://127.0.0.1:" + port +
"/metadata/v1/watch?timeout=3000", params, headers, 5000)) {
- Assertions.assertEquals(
- HttpStatus.SC_BAD_REQUEST,
response.getStatusLine().getStatusCode());
+ Assertions.assertEquals(HttpStatus.SC_BAD_REQUEST,
response.code());
}
}
@@ -381,14 +373,13 @@ class ClusterControllerTest extends BaseSpringBootTest {
String jsonBody = "{\"testParam\":\"<script>alert('xss')</script>\"}";
- try (CloseableHttpResponse response = HttpClientUtil.doPostJson(
+ try (Response response = HttpClientUtil.doPostJson(
"http://127.0.0.1:" + port +
"/metadata/v1/watch?timeout=3000&urlParam="
+ URLEncoder.encode("<script>alert('xss')</script>",
String.valueOf(StandardCharsets.UTF_8)),
jsonBody,
headers,
5000)) {
- Assertions.assertEquals(
- HttpStatus.SC_BAD_REQUEST,
response.getStatusLine().getStatusCode());
+ Assertions.assertEquals(HttpStatus.SC_BAD_REQUEST,
response.code());
}
}
@@ -401,10 +392,9 @@ class ClusterControllerTest extends BaseSpringBootTest {
Map<String, String> params = new HashMap<>();
params.put("testParam", "custom1");
- try (CloseableHttpResponse response = HttpClientUtil.doPost(
+ try (Response response = HttpClientUtil.doPost(
"http://127.0.0.1:" + port +
"/metadata/v1/watch?timeout=3000", params, headers, 5000)) {
- Assertions.assertEquals(
- HttpStatus.SC_BAD_REQUEST,
response.getStatusLine().getStatusCode());
+ Assertions.assertEquals(HttpStatus.SC_BAD_REQUEST,
response.code());
}
}
}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]