iT邦幫忙

1

應用系統建置前準備工具 - NetUtils 網路工具

  • 分享至 

  • xImage
  •  

NetUtils 網路工具類別

概述

NetUtils 是一個提供網路相關功能的工具類別。此類別設計為靜態工具類別,提供多種網路操作方法,包括 HTTP/HTTPS 請求、IP 位址處理、Socket 連線檢查等功能,特別適合在 Web 應用程式中使用。

專案相關程式

  • GlobalConstants
  • CommUtils

第三方元件(Dependency)

  • lombok.extern.slf4j.Slf4j;
  • org.apache.commons.lang3

主要功能

1. IP 位址處理

取得客戶端 IP

// 從 HttpServletRequest 取得客戶端 IP
HttpServletRequest request = ...;
String clientIp = NetUtils.getClientIpAddress(request);

取得伺服器資訊

// 取得伺服器主機名稱
String hostName = NetUtils.getHostName();

// 取得伺服器 IP 位址
String serverIp = NetUtils.getHostIpAddr();

2. HTTP/HTTPS 請求

GET 請求

// 發送 HTTP GET 請求
Map<String, String> headers = new HashMap<>();
headers.put("Authorization", "Bearer token123");
HttpResponse<String> response = NetUtils.invokeHttpGet("http://api.example.com", headers);

// 發送 HTTPS GET 請求
HttpResponse<String> secureResponse = NetUtils.invokeHttpsGet("https://api.example.com", headers);

POST 請求

// 準備請求參數
Map<String, String> headers = new HashMap<>();
headers.put("Content-Type", "application/json");

Map<String, String> body = new HashMap<>();
body.put("key", "value");

// 發送 HTTP POST 請求
HttpResponse<String> response = NetUtils.invokeHttpPost(
    "http://api.example.com",
    headers,
    body
);

// 發送 HTTPS POST 請求
HttpResponse<String> secureResponse = NetUtils.invokeHttpsPost(
    "https://api.example.com",
    headers,
    body
);

3. 請求參數處理

取得請求參數

// 取得指定的 URL 參數
List<String> paramNames = Arrays.asList("id", "name");
Map<String, String> params = NetUtils.getRequestValueFromVarNames(request, paramNames);

// 取得所有請求標頭
Map<String, Object> headers = NetUtils.getRequestHeaderAllValues(request);

// 取得請求主體參數
Map<String, Object> bodyParams = NetUtils.getRequestBodyAllValues(request);

4. 網路連線檢查

Socket 連線測試

// 檢查目標主機和連接埠是否可連線
String host = "example.com";
int port = 80;
int timeoutSeconds = 5;
boolean isConnected = NetUtils.checkSocket(host, port, timeoutSeconds);

5. Ajax 請求判斷

檢查 Ajax 請求

// 判斷是否為 Ajax 請求
boolean isAjax = NetUtils.isAjaxRequest(request);

系統需求

相依套件

  • jakarta.servlet-api
  • java.net.http (Java 11+)
  • lombok
  • commons-lang3
  • jackson-databind

運行環境

  • Java 11 或以上
  • Jakarta EE 9 或以上
  • Spring Framework 5.x 或以上(選用)

配置設定

SSL 憑證設定

在開發環境中,可以使用內建的信任管理器略過 SSL 憑證驗證:

// 在 HTTPS 請求時自動套用
NetUtilsWebClientModel model = new NetUtilsWebClientModel();
model.setIsSSL(true);

HTTP 用戶端設定

// 設定連線超時
HttpClient httpClient = HttpClient.newBuilder()
    .connectTimeout(Duration.ofSeconds(10))
    .build();

效能最佳化

1. 連線管理

  • 適當設定連線超時時間
  • 使用連線池處理大量請求
  • 正確關閉 Socket 連線

2. 請求處理

  • 使用串流處理大型回應
  • 實作請求重試機制
  • 使用非同步請求處理大量請求

3. 記憶體管理

  • 適當釋放資源
  • 避免記憶體洩漏
  • 使用適當的緩衝區大小

注意事項

1. 安全性考量

  • 生產環境必須正確驗證 SSL 憑證
  • 避免在程式碼中硬編碼敏感資訊
  • 實作適當的安全標頭
  • 注意跨網域請求的安全性

2. 錯誤處理

  • 妥善處理網路連線異常
  • 實作超時機制
  • 記錄詳細的錯誤日誌
  • 提供適當的錯誤回應

3. 效能考量

  • 避免過度頻繁的網路請求
  • 實作快取機制
  • 使用適當的連線池設定
  • 監控網路效能

4. 相容性

  • 注意不同 Java 版本的 API 差異
  • 處理不同字元編碼
  • 相容不同的 HTTP 協議版本
  • 處理舊版瀏覽器的請求

進階用法

NetUtilsWebClientModel 資料模型

用於封裝完整的網路請求參數:

NetUtilsWebClientModel model = new NetUtilsWebClientModel();
model.setUrl("https://api.example.com");
model.setIsSSL(true);
model.setIsGetMethod(false);

// 設定標頭
Map<String, String> headers = new HashMap<>();
headers.put("Content-Type", "application/json");
model.setMapHeader(headers);

// 設定表單資料
Map<String, String> formData = new HashMap<>();
formData.put("key", "value");
model.setFormData(formData);

// 執行請求
HttpResponse<String> response = NetUtils.invokeWebService(model);

自訂 SSL 處理

// 使用自訂信任管理器
TrustManager[] trustManagers = NetUtils.getTrustManagers();
SSLContext sslContext = SSLContext.getInstance("SSL");
sslContext.init(null, trustManagers, new SecureRandom());

// 設定 HTTPS 連線
HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.getSocketFactory());

單元測試範例

以下測試範例示範 NetUtils 的常見測試策略:

1. 取得 Client IP

@Test
void testGetClientIpAddress() {
    MockHttpServletRequest req = new MockHttpServletRequest();
    req.addHeader("X-Forwarded-For", "203.0.113.1");
    String ip = NetUtils.getClientIpAddress(req);
    assertEquals("203.0.113.1", ip);
}

2. Socket 連線檢查

@Test
void testCheckSocket() {
    // 使用常見的開放埠或模擬環境進行測試
    boolean reachable = NetUtils.checkSocket("example.com", 80, 5);
    // 無法保證對外網路的穩定性,建議在 CI 使用 Mock 或內部測試伺服器
    assertNotNull(reachable);
}

3. HTTP 請求(建議使用 Mock)

@Test
void testInvokeHttpGetWithMock() throws Exception {
    // 建議使用 WireMock 或 Mockito 模擬外部 API
    // 此處示意:驗證當 HttpClient 回傳時,方法能正確回傳 HttpResponse
    // 測試時請在隔離環境模擬 HTTP 回應
}

測試說明

  • 建議使用 Mock 或測試伺服器避免對外依賴
  • 測試 IP 解析、Header 處理與 Ajax 判斷邏輯
  • 對可能有網路不穩的測試使用容錯或跳過標記

程式碼 NetUtils.java

package tw.lewishome.webapp.base.utility.common;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.Serializable;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketAddress;
import java.net.URI;
import java.net.URLEncoder;
import java.net.UnknownHostException;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpRequest.Builder;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;

import org.apache.commons.lang3.StringUtils;

import com.fasterxml.jackson.databind.ObjectMapper;

import jakarta.servlet.http.HttpServletRequest;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;

/**
 * NetUtils 是一個實用程式類,提供各種與網路相關的輔助方法。
 *
 * 功能包括:
 * <ul>
 * <li>從 HttpServletRequest 中擷取客戶端 IP 位址,並考慮代理程式標頭。</li>
 * <li>偵測 Ajax 請求。</li>
 * <li>檢查與主機和連接埠的連線。</li>
 * <li>從 HttpServletRequest 中擷取參數值。</li>
 * <li>取得伺服器主機名稱和 IP 位址。</li>
 * <li>傳送帶有自訂標頭和表單資料的 HTTP/HTTPS GET 和 POST 請求。</li>
 * <li>繞過 HTTPS 請求的 SSL 憑證驗證(用於開發/測試目的)。</li>
 * </ul>
 *
 * 包含一個內部靜態類別 {@code NetUtilsWebClientModel},用於封裝 Web 用戶端請求資料。
 *
 * <b>注意:</b>停用 SSL 憑證驗證不安全,不應在生產環境中使用。
 *
 * @author lewis
 * @version 1.0
 */
@Slf4j
public class NetUtils {

    /** Private constructor to prevent instantiation */
    private NetUtils() {
        throw new IllegalStateException("This is a utility class and cannot be instantiated");
    }

    /**
     * HTTP HEADERS
     **/
    private static final String[] HEADERS_TO_TRY = { "X-Forwarded-For", "Proxy-Client-IP", "WL-Proxy-Client-IP",
            "HTTP_X_FORWARDED_FOR", "HTTP_X_FORWARDED", "HTTP_X_CLUSTER_CLIENT_IP", "HTTP_CLIENT_IP",
            "HTTP_FORWARDED_FOR", "HTTP_FORWARDED", "HTTP_VIA", "REMOTE_ADDR" };

    /**
     * 取得 Http Request 的 IP Address
     *
     * @param request HttpServletRequest
     * @return String IP 位址
     */
    public static String getClientIpAddress(HttpServletRequest request) {

        String ip = request.getHeader("X-Forwarded-For");
        String unknownString = "unknown";
        if (StringUtils.isBlank(ip) || unknownString.equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (StringUtils.isBlank(ip) || unknownString.equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (StringUtils.isBlank(ip) || unknownString.equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_CLIENT_IP");
        }
        if (StringUtils.isBlank(ip) || unknownString.equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_X_FORWARDED_FOR");
        }
        if (StringUtils.isBlank(ip) || unknownString.equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }
        if (StringUtils.isBlank(ip) || unknownString.equalsIgnoreCase(ip)) {
            for (String header : HEADERS_TO_TRY) {
                ip = request.getHeader(header);
                if (StringUtils.isNotBlank(ip) && !unknownString.equalsIgnoreCase(ip)) {
                    return ip;
                }
            }
            ip = request.getRemoteAddr();
        }
        return ip;
    }

    /**
     * 判斷是否為 Ajax 請求
     *
     * @param request HttpServletRequest
     * @return boolean 是否為 Ajax
     */
    public static boolean isAjaxRequest(HttpServletRequest request) {
        return "XMLHttpRequest".equals(request.getHeader("X-Requested-With"))
                || "XMLHttpRequest".equals(request.getAttribute("X-Requested-With"));
    }

    /**
     * 檢查 Socket 連線 (host, port)
     *
     * @param node    IP 位址
     * @param port    連接埠
     * @param timeout 嘗試時間(秒)
     * @return boolean 是否連線成功
     */
    public static boolean checkSocket(String node, int port, int timeout) {
        try (Socket socket = new Socket()) {
            SocketAddress socketAddress = new InetSocketAddress(node, port);
            socket.connect(socketAddress, timeout * 1000);
            if (socket.isConnected()) {
                return true;
            }
        } catch (IOException e) {
            return false;
        }
        return false;
    }

    /**
     * 從 Http Request 中取得指定 URL 參數(變數)的內容
     *
     * @param request    HttpServletRequest
     * @param parameters 變數名稱清單 (URL 參數)
     * @return Map 變數名稱與內容(找不到變數則為 null)
     */
    public static Map<String, String> getRequestValueFromVarNames(HttpServletRequest request, List<String> parameters) {

        Map<String, String> rtnValidMap = new HashMap<>();
        for (String parameter : parameters) {
            rtnValidMap.put(parameter.trim(), request.getParameter(parameter.trim()));
        }
        return rtnValidMap;
    }

    /**
     * 從 Http Request 中取得指定 body 參數(變數)的內容
     *
     * @param request HttpServletRequest
     * @return Map 變數名稱與內容(找不到變數則為 null)
     * @throws IOException IOException 當讀取請求主體時發生錯誤
     */
    @SuppressWarnings("unchecked")
    public static Map<String, Object> getRequestBodyAllValues(HttpServletRequest request) throws IOException {
        Map<String, Object> mapBodyValues = new HashMap<>();
        StringBuilder requestBody = new StringBuilder();
        try (BufferedReader reader = request.getReader()) {
            String line;
            while ((line = reader.readLine()) != null) {
                requestBody.append(line);
            }
        }
        String bodyString = requestBody.toString();
        ObjectMapper objectMapper = new ObjectMapper();
        try {
            mapBodyValues = objectMapper.readValue(bodyString, Map.class);
        } catch (IOException ex) {
            System.err.println("Could not parse request body as JSON");
        }
        return mapBodyValues;
    }

    /**
     * 從 Http Request 中取得所有參數(變數)的內容
     *
     * @param request HttpServletRequest
     * @return Map 變數名稱與內容(找不到變數則為 null)
     */
    public static Map<String, Object> getRequestHeaderAllValues(HttpServletRequest request) {
        Map<String, Object> mapHeaderValues = new HashMap<>();
        Enumeration<String> headerNames = request.getHeaderNames();
        while (headerNames.hasMoreElements()) {
            String headerName = headerNames.nextElement();
            mapHeaderValues.put(headerName, request.getHeader(headerName));
        }
        return mapHeaderValues;
    }

    /**
     * 取得主機名稱 (Server Name)
     *
     * @return String 主機名稱
     */
    public static String getHostName() {
        String hostname = "Unknown";
        try {
            InetAddress hostAddr = InetAddress.getLocalHost();
            hostname = hostAddr.getHostName();
        } catch (UnknownHostException ex) {
            log.error("無法取得主機名稱");
        }
        return hostname;
    }

    /**
     * 取得主機 IP (Server IP)
     *
     * @return String 主機 IP
     */
    public static String getHostIpAddr() {
        String hostIpAddr = "Unknown";
        try {
            InetAddress hostAddr = InetAddress.getLocalHost();
            hostIpAddr = hostAddr.getHostName();
        } catch (UnknownHostException ex) {
            log.error("無法取得主機名稱");
        }
        return hostIpAddr;
    }

    /**
     * 發送 HTTP GET 請求
     *
     * @param url       請求網址
     * @param mapHeader 標頭參數
     * @return HttpResponse 回應物件
     */
    public static HttpResponse<String> invokeHttpGet(String url, Map<String, String> mapHeader) {
        NetUtilsWebClientModel webClientModel = new NetUtilsWebClientModel();
        webClientModel.setUrl(url);
        webClientModel.setIsSSL(false);
        webClientModel.setIsGetMethod(true);
        webClientModel.setMapHeader(mapHeader);
        return invokeWebService(webClientModel);
    }

    /**
     * 發送 HTTPS GET 請求
     *
     * @param url       請求網址
     * @param mapHeader 標頭參數
     * @return HttpResponse 回應物件
     */
    public static HttpResponse<String> invokeHttpsGet(String url, Map<String, String> mapHeader) {
        NetUtilsWebClientModel webClientModel = new NetUtilsWebClientModel();
        webClientModel.setUrl(url);
        webClientModel.setIsSSL(true);
        webClientModel.setIsGetMethod(true);
        webClientModel.setMapHeader(mapHeader);
        return invokeWebService(webClientModel);
    }

    /**
     * invoke Http Post request
     *
     * @param url       URL
     * @param mapHeader Header Param
     * @param mapBody   mapBody
     * @return HttpResponse HttpResponse
     */
    public static HttpResponse<String> invokeHttpPost(String url, Map<String, String> mapHeader,
            Map<String, String> mapBody) {
        NetUtilsWebClientModel webClientModel = new NetUtilsWebClientModel();
        webClientModel.setUrl(url);
        webClientModel.setIsSSL(false);
        webClientModel.setIsGetMethod(false);
        webClientModel.setMapHeader(mapHeader);
        webClientModel.setFormData(mapBody);
        return invokeWebService(webClientModel);
    }

    /**
     * invoke Https Post request
     *
     * @param url       URL
     * @param mapHeader Header Param
     * @param mapBody   mapBody
     * @return HttpResponse HttpResponse
     */
    public static HttpResponse<String> invokeHttpsPost(String url, Map<String, String> mapHeader,
            Map<String, String> mapBody) {
        NetUtilsWebClientModel webClientModel = new NetUtilsWebClientModel();
        webClientModel.setUrl(url);
        webClientModel.setIsSSL(true);
        webClientModel.setIsGetMethod(false);
        webClientModel.setMapHeader(mapHeader);
        webClientModel.setFormData(mapBody);
        return invokeWebService(webClientModel);

    }

    /**
     * invoke WebService (Http/https) request
     *
     * @param webClientModel webClientModel
     * @return HttpResponse HttpResponse
     */
    public static HttpResponse<String> invokeWebService(NetUtilsWebClientModel webClientModel) {
        String url = webClientModel.getUrl();
        Boolean isSSL = webClientModel.getIsSSL();
        Boolean isGetMethod = webClientModel.getIsGetMethod();
        Map<String, String> formData = webClientModel.getFormData();
        Map<String, String> mapHeader = webClientModel.getMapHeader();

        // Create a trust manager that does not validate certificate chains
        String formDataString = "";
        if (formData != null && formData.size() > 0) {
            formDataString = getFormDataAsString(formData);
        }
        TrustManager[] trustAllCerts = getTrustManagers();
        try {
            // Install the all-trusting trust manager
            SSLContext sslContext = SSLContext.getInstance("SSL");
            sslContext.init(null, trustAllCerts, new java.security.SecureRandom());
            HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.getSocketFactory());
            // Now you can access an https URL without having the certificate in the
            // trust store URL
            Builder builder = null;
            if (Boolean.TRUE.equals(isGetMethod)) {
                builder = HttpRequest.newBuilder().uri(new URI(url)).GET();
            } else {
                if (StringUtils.isBlank(formDataString)) {
                    builder = HttpRequest.newBuilder().uri(new URI(url)).POST(HttpRequest.BodyPublishers.noBody());
                } else {
                    builder = HttpRequest.newBuilder().uri(new URI(url))
                            .POST(HttpRequest.BodyPublishers.ofString(formDataString));
                }
            }

            for (Map.Entry<String, String> entry : mapHeader.entrySet()) {
                builder.setHeader(entry.getKey(), entry.getValue());
            }
            HttpClient httpClient = null;
            if (Boolean.TRUE.equals(isSSL)) {
                httpClient = HttpClient.newBuilder()
                        // .connectTimeout(Duration.ofMillis(<timeoutInSeconds> * 1000))
                        .connectTimeout(Duration.ofMillis(10 * 1000))
                        // SSL context 'sslContext' initialized as earlier
                        .sslContext(sslContext)
                        // .sslParameters(parameters) // ssl parameters if overridden
                        .build();
            } else {
                httpClient = HttpClient.newHttpClient();
            }
            builder.setHeader("Content-Type", "application/application/json");
            HttpRequest requestBuilder = builder.build();
            return httpClient.send(requestBuilder, HttpResponse.BodyHandlers.ofString());
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return null;

    }

    /**
     * Create a trust manager that does not validate certificate chains
     * 
     * @return
     */
    private static TrustManager[] getTrustManagers() {

        // Create a trust manager that does not validate certificate chains
        TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() {
            public java.security.cert.X509Certificate[] getAcceptedIssuers() {
                return null;
            }

            public void checkClientTrusted(java.security.cert.X509Certificate[] certs, String authType) {
            }

            public void checkServerTrusted(java.security.cert.X509Certificate[] certs, String authType) {
            }
        } };
        return trustAllCerts;
    }

    /**
     * @param formData Http Form Data
     * @return String String
     */
    private static String getFormDataAsString(Map<String, String> formData) {
        StringBuilder formBodyBuilder = new StringBuilder();
        for (Map.Entry<String, String> singleEntry : formData.entrySet()) {
            if (formBodyBuilder.length() > 0) {
                formBodyBuilder.append("&");
            }
            formBodyBuilder.append(URLEncoder.encode(singleEntry.getKey(), StandardCharsets.UTF_8));
            formBodyBuilder.append("=");
            formBodyBuilder.append(URLEncoder.encode(singleEntry.getValue(), StandardCharsets.UTF_8));
        }
        return formBodyBuilder.toString();
    }

    /**
     * <pre>
     * 執行 WebClient 時的資料 Model
     * </pre>
     *
     * @author Lewis

     */
    /**
     * <pre>
     * NetUtilsWebClientModel 是用於處理網路請求的資料模型類別。
     * 包含以下主要屬性:
     * - URL:網路服務的網址
     * - SSL 設定:是否使用 HTTPS
     * - 請求方法:GET 或 POST
     * - 標頭資訊:請求標頭參數
     * - 表單資料:請求主體參數
     * - JSON 資料:JSON 格式的請求資料
     * - 上傳檔案:位元組陣列格式的上傳檔案
     * </pre>
     */
    @Data
    public static class NetUtilsWebClientModel implements Serializable {

        /**
         * Fix for javadoc warning :
         * use of default constructor, which does not provide a comment
         * Constructs a new NetUtilsWebClientModel instance.
         * This is the default constructor, implicitly provided by the compiler
         * if no other constructors are defined.
         */
        public NetUtilsWebClientModel() {
            // Constructor body (can be empty)
        }

        /** serialVersionUID */
        private static final long serialVersionUID = 1L;
        /** web service URL */
        private String url = "";

        /** web service is SSL (https) */
        private Boolean isSSL = false;

        /** web service method is Get (else Post) */
        private Boolean isGetMethod = true;

        /** Header info Map */
        private Map<String, String> mapHeader = new HashMap<>();

        /** Body info Map */
        private Map<String, String> formData = new HashMap<>();

        /** Body info Map */
        private String requestJsonData = "";

        /** Upload File Data (Byte Array) */
        private Byte[] uploadData = null;

    }

}

單元測試程式碼 NetUtilsTest.java

package tw.lewishome.webapp.base.utility.common;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.DisplayName;
import jakarta.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.net.http.HttpResponse;
import java.util.*;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.*;

public class NetUtilsTest {

    @Test
    @DisplayName("getClientIpAddress should return X-Forwarded-For if present")
    void testGetClientIpAddress_XForwardedFor() {
        HttpServletRequest request = mock(HttpServletRequest.class);
        when(request.getHeader("X-Forwarded-For")).thenReturn("1.2.3.4");
        assertEquals("1.2.3.4", NetUtils.getClientIpAddress(request));
    }

    @Test
    @DisplayName("getClientIpAddress should fallback to Proxy-Client-IP")
    void testGetClientIpAddress_ProxyClientIp() {
        HttpServletRequest request = mock(HttpServletRequest.class);
        when(request.getHeader("X-Forwarded-For")).thenReturn(null);
        when(request.getHeader("Proxy-Client-IP")).thenReturn("5.6.7.8");
        assertEquals("5.6.7.8", NetUtils.getClientIpAddress(request));
    }

    @Test
    @DisplayName("getClientIpAddress should fallback to WL-Proxy-Client-IP")
    void testGetClientIpAddress_WLProxyClientIp() {
        HttpServletRequest request = mock(HttpServletRequest.class);
        when(request.getHeader("X-Forwarded-For")).thenReturn(null);
        when(request.getHeader("Proxy-Client-IP")).thenReturn(null);
        when(request.getHeader("WL-Proxy-Client-IP")).thenReturn("9.10.11.12");
        assertEquals("9.10.11.12", NetUtils.getClientIpAddress(request));
    }

    @Test
    @DisplayName("getClientIpAddress should fallback to HTTP_CLIENT_IP")
    void testGetClientIpAddress_HTTP_CLIENT_IP() {
        HttpServletRequest request = mock(HttpServletRequest.class);
        when(request.getHeader("X-Forwarded-For")).thenReturn(null);
        when(request.getHeader("Proxy-Client-IP")).thenReturn(null);
        when(request.getHeader("WL-Proxy-Client-IP")).thenReturn(null);
        when(request.getHeader("HTTP_CLIENT_IP")).thenReturn("13.14.15.16");
        assertEquals("13.14.15.16", NetUtils.getClientIpAddress(request));
    }

    @Test
    @DisplayName("getClientIpAddress should fallback to HTTP_X_FORWARDED_FOR")
    void testGetClientIpAddress_HTTP_X_FORWARDED_FOR() {
        HttpServletRequest request = mock(HttpServletRequest.class);
        when(request.getHeader("X-Forwarded-For")).thenReturn(null);
        when(request.getHeader("Proxy-Client-IP")).thenReturn(null);
        when(request.getHeader("WL-Proxy-Client-IP")).thenReturn(null);
        when(request.getHeader("HTTP_CLIENT_IP")).thenReturn(null);
        when(request.getHeader("HTTP_X_FORWARDED_FOR")).thenReturn("17.18.19.20");
        assertEquals("17.18.19.20", NetUtils.getClientIpAddress(request));
    }

    @Test
    @DisplayName("getClientIpAddress should fallback to getRemoteAddr")
    void testGetClientIpAddress_RemoteAddr() {
        HttpServletRequest request = mock(HttpServletRequest.class);
        when(request.getHeader(anyString())).thenReturn(null);
        when(request.getRemoteAddr()).thenReturn("21.22.23.24");
        assertEquals("21.22.23.24", NetUtils.getClientIpAddress(request));
    }

    @Test
    @DisplayName("isAjaxRequest should return true for XMLHttpRequest header")
    void testIsAjaxRequest_Header() {
        HttpServletRequest request = mock(HttpServletRequest.class);
        when(request.getHeader("X-Requested-With")).thenReturn("XMLHttpRequest");
        assertTrue(NetUtils.isAjaxRequest(request));
    }

    @Test
    @DisplayName("isAjaxRequest should return true for XMLHttpRequest attribute")
    void testIsAjaxRequest_Attribute() {
        HttpServletRequest request = mock(HttpServletRequest.class);
        when(request.getHeader("X-Requested-With")).thenReturn(null);
        when(request.getAttribute("X-Requested-With")).thenReturn("XMLHttpRequest");
        assertTrue(NetUtils.isAjaxRequest(request));
    }

    @Test
    @DisplayName("isAjaxRequest should return false if not Ajax")
    void testIsAjaxRequest_False() {
        HttpServletRequest request = mock(HttpServletRequest.class);
        when(request.getHeader("X-Requested-With")).thenReturn(null);
        when(request.getAttribute("X-Requested-With")).thenReturn(null);
        assertFalse(NetUtils.isAjaxRequest(request));
    }

    @Test
    @DisplayName("getRequestValueFromVarNames should return correct map")
    void testGetRequestValueFromVarNames() {
        HttpServletRequest request = mock(HttpServletRequest.class);
        when(request.getParameter("foo")).thenReturn("bar");
        when(request.getParameter("baz")).thenReturn("qux");
        List<String> params = Arrays.asList("foo", "baz");
        Map<String, String> result = NetUtils.getRequestValueFromVarNames(request, params);
        assertEquals(2, result.size());
        assertEquals("bar", result.get("foo"));
        assertEquals("qux", result.get("baz"));
    }

    @Test
    @DisplayName("getHostName should return a non-null string")
    void testGetHostName() {
        String hostName = NetUtils.getHostName();
        assertNotNull(hostName);
        assertFalse(hostName.isEmpty());
    }

    @Test
    @DisplayName("getHostIpAddr should return a non-null string")
    void testGetHostIpAddr() {
        String hostIp = NetUtils.getHostIpAddr();
        assertNotNull(hostIp);
        assertFalse(hostIp.isEmpty());
    }

    @Test
    @DisplayName("checkSocket should return false for invalid host")
    void testCheckSocket_InvalidHost() {
        assertFalse(NetUtils.checkSocket("256.256.256.256", 80, 1));
    }

    /**
     * @throws IOException IOException
     */
    @Test
    @DisplayName("checkSocket should return false for closed port")
    void testCheckSocket_ClosedPort() throws IOException {
        // 127.0.0.1:9 is usually closed (discard protocol)
        assertFalse(NetUtils.checkSocket("127.0.0.1", 9, 1));
    }

    // The following tests for HTTP methods are smoke tests (not real HTTP calls)
    // because actual HTTP calls require network and a running server.
    // These tests just check that the methods do not throw and return null (since
    // no server).

    @Test
    @DisplayName("invokeHttpGet should return null for invalid URL")
    void testInvokeHttpGet_InvalidUrl() {
        HttpResponse<String> response = NetUtils.invokeHttpGet("http://localhost:9999/notfound",
                Collections.emptyMap());
        assertNull(response);
    }

    @Test
    @DisplayName("invokeHttpsGet should return null for invalid URL")
    void testInvokeHttpsGet_InvalidUrl() {
        HttpResponse<String> response = NetUtils.invokeHttpsGet("https://localhost:9999/notfound",
                Collections.emptyMap());
        assertNull(response);
    }

    @Test
    @DisplayName("invokeHttpPost should return null for invalid URL")
    void testInvokeHttpPost_InvalidUrl() {
        HttpResponse<String> response = NetUtils.invokeHttpPost("http://localhost:9999/notfound",
                Collections.emptyMap(), Collections.emptyMap());
        assertNull(response);
    }

    @Test
    @DisplayName("invokeHttpsPost should return null for invalid URL")
    void testInvokeHttpsPost_InvalidUrl() {
        HttpResponse<String> response = NetUtils.invokeHttpsPost("https://localhost:9999/notfound",
                Collections.emptyMap(), Collections.emptyMap());
        assertNull(response);
    }
}

圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言