encoding 설정 지원
This commit is contained in:
@@ -7,13 +7,31 @@ import io.netty.channel.SimpleChannelInboundHandler;
|
|||||||
import io.netty.handler.codec.http.FullHttpResponse;
|
import io.netty.handler.codec.http.FullHttpResponse;
|
||||||
import io.netty.handler.codec.http.HttpHeaders;
|
import io.netty.handler.codec.http.HttpHeaders;
|
||||||
import io.netty.util.CharsetUtil;
|
import io.netty.util.CharsetUtil;
|
||||||
|
import java.nio.charset.Charset;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
public class HttpHandler extends SimpleChannelInboundHandler<FullHttpResponse> {
|
public class HttpHandler extends SimpleChannelInboundHandler<FullHttpResponse> {
|
||||||
|
|
||||||
|
public static final Pattern CHARSET_PATTERN = Pattern.compile("charset=([^;]+)");
|
||||||
|
private static final Charset DEFAULT_CHARSET = determineDefaultCharset();
|
||||||
|
|
||||||
|
private static Charset determineDefaultCharset() {
|
||||||
|
try {
|
||||||
|
String systemEncoding = System.getProperty("file.encoding");
|
||||||
|
if (StringUtils.isNotBlank(systemEncoding)) {
|
||||||
|
return Charset.forName(systemEncoding);
|
||||||
|
}
|
||||||
|
} catch (IllegalArgumentException | SecurityException e) {
|
||||||
|
// Log warning about fallback to UTF-8
|
||||||
|
}
|
||||||
|
return CharsetUtil.UTF_8;
|
||||||
|
}
|
||||||
private final CompletableFuture<ApiResponse> responseFuture;
|
private final CompletableFuture<ApiResponse> responseFuture;
|
||||||
|
|
||||||
public HttpHandler(CompletableFuture<ApiResponse> responseFuture) {
|
public HttpHandler(CompletableFuture<ApiResponse> responseFuture) {
|
||||||
@@ -26,7 +44,8 @@ public class HttpHandler extends SimpleChannelInboundHandler<FullHttpResponse> {
|
|||||||
ApiResponse apiResponse = new ApiResponse();
|
ApiResponse apiResponse = new ApiResponse();
|
||||||
apiResponse.setStatus(response.status().code());
|
apiResponse.setStatus(response.status().code());
|
||||||
apiResponse.setHeaders(convertHttpHeadersToList(response.headers()));
|
apiResponse.setHeaders(convertHttpHeadersToList(response.headers()));
|
||||||
apiResponse.setBody(response.content().toString(CharsetUtil.UTF_8));
|
Charset charset = extractCharset(response.headers());
|
||||||
|
apiResponse.setBody(response.content().toString(charset));
|
||||||
|
|
||||||
int headerSize = calculateHeaderSize(response.headers());
|
int headerSize = calculateHeaderSize(response.headers());
|
||||||
int contentSize = response.content().readableBytes();
|
int contentSize = response.content().readableBytes();
|
||||||
@@ -36,6 +55,25 @@ public class HttpHandler extends SimpleChannelInboundHandler<FullHttpResponse> {
|
|||||||
responseFuture.complete(apiResponse);
|
responseFuture.complete(apiResponse);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Charset extractCharset(HttpHeaders headers) {
|
||||||
|
String contentType = headers.get("Content-Type");
|
||||||
|
|
||||||
|
if (contentType != null) {
|
||||||
|
Matcher matcher = CHARSET_PATTERN.matcher(contentType.toLowerCase());
|
||||||
|
if (matcher.find()) {
|
||||||
|
try {
|
||||||
|
return Charset.forName(matcher.group(1).trim());
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
// Log warning about unsupported charset
|
||||||
|
return DEFAULT_CHARSET;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return DEFAULT_CHARSET;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private int calculateHeaderSize(HttpHeaders headers) {
|
private int calculateHeaderSize(HttpHeaders headers) {
|
||||||
int headerSize = 0;
|
int headerSize = 0;
|
||||||
for (Map.Entry<String, String> entry : headers.entries()) {
|
for (Map.Entry<String, String> entry : headers.entries()) {
|
||||||
|
|||||||
@@ -29,8 +29,7 @@ import io.netty.handler.codec.http.HttpVersion;
|
|||||||
import io.netty.handler.ssl.SslContext;
|
import io.netty.handler.ssl.SslContext;
|
||||||
import io.netty.handler.ssl.SslContextBuilder;
|
import io.netty.handler.ssl.SslContextBuilder;
|
||||||
import io.netty.handler.ssl.util.InsecureTrustManagerFactory;
|
import io.netty.handler.ssl.util.InsecureTrustManagerFactory;
|
||||||
import io.netty.util.CharsetUtil;
|
import java.nio.charset.Charset;
|
||||||
import java.net.URLEncoder;
|
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
import javax.net.ssl.SSLException;
|
import javax.net.ssl.SSLException;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
@@ -80,7 +79,8 @@ public class NettyHttpApiClient implements ApiClient {
|
|||||||
Channel ch = b.connect(host, port).sync().channel();
|
Channel ch = b.connect(host, port).sync().channel();
|
||||||
ByteBuf content;
|
ByteBuf content;
|
||||||
if (StringUtils.isNotEmpty(apiRequest.getRequestBody())) {
|
if (StringUtils.isNotEmpty(apiRequest.getRequestBody())) {
|
||||||
content = Unpooled.copiedBuffer(apiRequest.getRequestBody(), CharsetUtil.UTF_8);
|
Charset charset = RequestCharsetUtils.extractRequestCharset(apiRequest);
|
||||||
|
content = Unpooled.copiedBuffer(apiRequest.getRequestBody(), charset );
|
||||||
} else {
|
} else {
|
||||||
content = Unpooled.EMPTY_BUFFER;
|
content = Unpooled.EMPTY_BUFFER;
|
||||||
}
|
}
|
||||||
@@ -116,8 +116,6 @@ public class NettyHttpApiClient implements ApiClient {
|
|||||||
request.headers().add(customHeaders);
|
request.headers().add(customHeaders);
|
||||||
}
|
}
|
||||||
|
|
||||||
// apiRequest.setSentBytes(request.toString().length());
|
|
||||||
|
|
||||||
ch.writeAndFlush(request);
|
ch.writeAndFlush(request);
|
||||||
ch.closeFuture().sync();
|
ch.closeFuture().sync();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
|||||||
@@ -0,0 +1,80 @@
|
|||||||
|
package com.eactive.testmaster.client.service;
|
||||||
|
|
||||||
|
import com.eactive.testmaster.client.dto.ApiRequestDTO;
|
||||||
|
import com.eactive.testmaster.client.dto.ApiRequestKeyValueDTO;
|
||||||
|
import io.netty.util.CharsetUtil;
|
||||||
|
import java.nio.charset.Charset;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
|
public class RequestCharsetUtils {
|
||||||
|
private static final Pattern CHARSET_PATTERN = Pattern.compile("charset\\s*=\\s*([^;\\s]+)", Pattern.CASE_INSENSITIVE);
|
||||||
|
private static final String CONTENT_TYPE_HEADER = "Content-Type";
|
||||||
|
private static final Charset DEFAULT_CHARSET = determineDefaultCharset();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines the default charset based on system properties
|
||||||
|
* Falls back to UTF-8 if system encoding is not available or invalid
|
||||||
|
*/
|
||||||
|
private static Charset determineDefaultCharset() {
|
||||||
|
try {
|
||||||
|
String systemEncoding = System.getProperty("file.encoding");
|
||||||
|
if (StringUtils.isNotBlank(systemEncoding)) {
|
||||||
|
return Charset.forName(systemEncoding);
|
||||||
|
}
|
||||||
|
} catch (IllegalArgumentException | SecurityException e) {
|
||||||
|
// Log warning about fallback to UTF-8
|
||||||
|
}
|
||||||
|
return CharsetUtil.UTF_8;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extracts charset from request headers with fallback to system default charset
|
||||||
|
* @param apiRequest The API request containing headers
|
||||||
|
* @return The detected charset or system default charset
|
||||||
|
*/
|
||||||
|
public static Charset extractRequestCharset(ApiRequestDTO apiRequest) {
|
||||||
|
if (apiRequest == null || apiRequest.getHeaders() == null) {
|
||||||
|
return DEFAULT_CHARSET;
|
||||||
|
}
|
||||||
|
|
||||||
|
return apiRequest.getHeaders().stream()
|
||||||
|
.filter(ApiRequestKeyValueDTO::isEnabled)
|
||||||
|
.filter(header -> CONTENT_TYPE_HEADER.equalsIgnoreCase(header.getKey()))
|
||||||
|
.findFirst()
|
||||||
|
.map(RequestCharsetUtils::parseCharsetFromContentType)
|
||||||
|
.orElse(DEFAULT_CHARSET);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses charset from Content-Type header value
|
||||||
|
* @param contentTypeHeader The Content-Type header DTO
|
||||||
|
* @return The detected charset or system default charset
|
||||||
|
*/
|
||||||
|
private static Charset parseCharsetFromContentType(ApiRequestKeyValueDTO contentTypeHeader) {
|
||||||
|
String contentType = contentTypeHeader.getValue();
|
||||||
|
if (StringUtils.isBlank(contentType)) {
|
||||||
|
return DEFAULT_CHARSET;
|
||||||
|
}
|
||||||
|
|
||||||
|
Matcher matcher = CHARSET_PATTERN.matcher(contentType);
|
||||||
|
if (matcher.find()) {
|
||||||
|
String charsetName = matcher.group(1).trim();
|
||||||
|
try {
|
||||||
|
return Charset.forName(charsetName);
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
// Log warning about unsupported charset
|
||||||
|
return DEFAULT_CHARSET;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle common content types without explicit charset
|
||||||
|
if (contentType.contains("application/json")) {
|
||||||
|
return CharsetUtil.UTF_8; // JSON should always use UTF-8 per RFC 8259
|
||||||
|
}
|
||||||
|
|
||||||
|
return DEFAULT_CHARSET;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user