From 9289f105c8c5b8e4f3407c4146d5ea0ff8336ea1 Mon Sep 17 00:00:00 2001 From: 13009 Date: Wed, 10 Apr 2024 11:23:55 +0800 Subject: [PATCH] =?UTF-8?q?ref:=20=E6=97=A5=E5=BF=97=E6=A0=BC=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../api/core/cipher/DefaultValidator.java | 7 +- .../scfs/api/core/http/AbstractApiClient.java | 5 +- .../api/core/http/DefaultHttpProfile.java | 14 +- .../czcb/scfs/api/core/http/HttpLogger.java | 101 ++- .../czcb/scfs/api/core/http/HttpProfile.java | 2 +- .../czcb/scfs/api/core/http/HttpStatus.java | 665 ++++++++++++++++++ .../com/czcb/scfs/api/core/http/LogLevel.java | 11 + .../api/core/http/ApiClientBuilderTest.java | 4 +- .../api/core/http/DefaultHttpProfileTest.java | 4 +- .../client/ApacheHttpclientProxyTest.java | 2 +- .../http/client/ApacheHttpclientTest.java | 2 +- .../client/ApacheHttpclientTestProxyTest.java | 2 +- .../http/client/ApacheHttpclientV2Test.java | 2 +- .../http/client/ApacheHttpclientV3Test.java | 2 +- .../czcb/scfs/api/service/MockResponse.java | 3 +- .../starter/AbstractAutoConfiguration.java | 2 +- .../starter/ScfsApiGatewayProperties.java | 13 +- .../starter/ScfsApiGatewayPropertiesTest.java | 7 +- .../boot/starter/SmConfigurationTest.java | 3 +- 19 files changed, 781 insertions(+), 70 deletions(-) create mode 100644 scfs-api-core/src/main/java/com/czcb/scfs/api/core/http/HttpStatus.java create mode 100644 scfs-api-core/src/main/java/com/czcb/scfs/api/core/http/LogLevel.java diff --git a/scfs-api-core/src/main/java/com/czcb/scfs/api/core/cipher/DefaultValidator.java b/scfs-api-core/src/main/java/com/czcb/scfs/api/core/cipher/DefaultValidator.java index f39251c..9b1c853 100644 --- a/scfs-api-core/src/main/java/com/czcb/scfs/api/core/cipher/DefaultValidator.java +++ b/scfs-api-core/src/main/java/com/czcb/scfs/api/core/cipher/DefaultValidator.java @@ -6,6 +6,7 @@ import com.czcb.scfs.api.core.exception.TimestampException; import com.czcb.scfs.api.core.exception.ValidationException; import com.czcb.scfs.api.core.http.HttpLogger; import com.czcb.scfs.api.core.http.HttpRequest; +import com.czcb.scfs.api.core.http.LogLevel; import com.czcb.scfs.api.core.http.OriginalResponse; import com.czcb.scfs.api.core.util.Strings; @@ -30,7 +31,7 @@ public final class DefaultValidator implements Validator { public DefaultValidator(Profile profile) { this.profile = profile; this.verifier = profile.getSignature().getVerifier(); - this.httpLogger = new HttpLogger(profile.getHttpProfile() == null || profile.getHttpProfile().logEnabled()); + this.httpLogger = new HttpLogger(profile.getHttpProfile() == null ? LogLevel.basic : profile.getHttpProfile().logLevel()); } private boolean isInvalidHttpCode(int httpCode) { @@ -59,10 +60,6 @@ public final class DefaultValidator implements Validator { @Override public void validate(HttpRequest request, OriginalResponse response, Channel channel) { - if (isInvalidHttpCode(response.getStatusCode())) { - httpLogger.logHttpStatus(response); - } - // 校验时间戳 validateTimestamp(request, response); diff --git a/scfs-api-core/src/main/java/com/czcb/scfs/api/core/http/AbstractApiClient.java b/scfs-api-core/src/main/java/com/czcb/scfs/api/core/http/AbstractApiClient.java index ac7dacf..9ff3288 100644 --- a/scfs-api-core/src/main/java/com/czcb/scfs/api/core/http/AbstractApiClient.java +++ b/scfs-api-core/src/main/java/com/czcb/scfs/api/core/http/AbstractApiClient.java @@ -35,7 +35,7 @@ public abstract class AbstractApiClient implements ApiClient { this.privacy = profile.getPrivacy(); this.signature = profile.getSignature(); this.validator = new DefaultValidator(profile); - this.httpLogger = new HttpLogger(profile.getHttpProfile() == null || profile.getHttpProfile().logEnabled()); + this.httpLogger = new HttpLogger(profile.getHttpProfile() == null ? LogLevel.basic : profile.getHttpProfile().logLevel()); } @Override @@ -140,9 +140,8 @@ public abstract class AbstractApiClient implements ApiClient { byte[] secretKey = cipher.getSecretKey(); // 密钥进行非对称加密 PrivacyEncryptor encryptor = privacy.getEncryptor(); - String encryptSecretKey = encryptor.encrypt(Strings.toStr(secretKey)); // 加密后的密钥放入到请求头 - httpRequest.getHttpHeaders().addHeader(SECRET_KEY, encryptSecretKey); + httpRequest.getHttpHeaders().addHeader(SECRET_KEY, encryptor.encrypt(Strings.toStr(secretKey))); // 加密证书序列号 httpRequest.getHttpHeaders().addHeader(BANK_CERTIFICATE_SERIAL, encryptor.getCertificateSerial()); diff --git a/scfs-api-core/src/main/java/com/czcb/scfs/api/core/http/DefaultHttpProfile.java b/scfs-api-core/src/main/java/com/czcb/scfs/api/core/http/DefaultHttpProfile.java index aea2f3a..41f4bf8 100644 --- a/scfs-api-core/src/main/java/com/czcb/scfs/api/core/http/DefaultHttpProfile.java +++ b/scfs-api-core/src/main/java/com/czcb/scfs/api/core/http/DefaultHttpProfile.java @@ -21,7 +21,7 @@ public class DefaultHttpProfile implements HttpProfile { // 请求代理 private final Proxy proxy; // 是否启用日志打印 默认true - private final boolean logEnabled; + private final LogLevel logLevel; // 是否启用压缩 private final boolean compressionEnabled; @@ -31,13 +31,13 @@ public class DefaultHttpProfile implements HttpProfile { this.connPool = builder.connPool; this.headers = builder.headers; this.proxy = builder.proxy; - this.logEnabled = builder.logEnabled; + this.logLevel = builder.logLevel; this.compressionEnabled = builder.compressionEnabled; } @Override - public boolean logEnabled() { - return logEnabled; + public LogLevel logLevel() { + return logLevel; } @Override @@ -82,7 +82,7 @@ public class DefaultHttpProfile implements HttpProfile { // 请求代理 private Proxy proxy; // 是否启用日志打印 - private boolean logEnabled = true; + private LogLevel logLevel = LogLevel.basic; // 是否启用压缩 private boolean compressionEnabled = false; @@ -111,8 +111,8 @@ public class DefaultHttpProfile implements HttpProfile { return this; } - public Builder logEnabled(boolean logEnabled) { - this.logEnabled = logEnabled; + public Builder logLevel(LogLevel logLevel) { + this.logLevel = logLevel; return this; } diff --git a/scfs-api-core/src/main/java/com/czcb/scfs/api/core/http/HttpLogger.java b/scfs-api-core/src/main/java/com/czcb/scfs/api/core/http/HttpLogger.java index 3c3e438..ae9c84e 100644 --- a/scfs-api-core/src/main/java/com/czcb/scfs/api/core/http/HttpLogger.java +++ b/scfs-api-core/src/main/java/com/czcb/scfs/api/core/http/HttpLogger.java @@ -5,16 +5,22 @@ import com.czcb.scfs.api.core.util.Strings; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.util.Objects; + +import static com.czcb.scfs.api.core.Constants.REQUEST_ID; +import static com.czcb.scfs.api.core.http.LogLevel.isFull; + /** * @author wangwei * @since 2.0.0 */ public class HttpLogger { private final Logger logger = LoggerFactory.getLogger(getClass()); - private final boolean logEnabled; + private final LogLevel logLevel; + private final boolean logPrefix = false; - public HttpLogger(boolean logEnabled) { - this.logEnabled = logEnabled; + public HttpLogger(LogLevel logLevel) { + this.logLevel = logLevel; } /** @@ -24,23 +30,27 @@ public class HttpLogger { * @param httpVersion http 版本 */ protected void logRequest(HttpRequest request, String httpVersion) { - if (!logEnabled) { - return; + String logPrefixText = logPrefix(request); + logger.info("{}请求:{} {} {}", logPrefixText, request.getHttpMethod(), request.getUrl(), httpVersion); + + if (isFull(logLevel)) { + request.getHttpHeaders().getHeaders().forEach((k, v) -> logger.info("{}请求头:{}:{}", logPrefixText, k, v)); } - logger.info("[{}] 请求行:{} {} {}", request.getId(), request.getHttpMethod(), request.getUrl(), httpVersion); - - StringBuilder sb = new StringBuilder(); - request.getHttpHeaders().getHeaders().forEach((k, v) -> sb.append(k).append("=").append(v).append(";")); - logger.info("[{}] 请求头:{}", request.getId(), Strings.removeSuffix(sb.toString(), ";")); - if (request.getRequestBody() instanceof EncryptRequestBody) { - EncryptRequestBody body = (EncryptRequestBody) request.getRequestBody(); - logger.info("[{}] 请求原始报文:{}", request.getId(), Strings.toStr(body.getOriginalBody())); - logger.info("[{}] 请求加密报文:{}", request.getId(), Strings.toStr(body.getBody())); + EncryptRequestBody requestBody = (EncryptRequestBody) request.getRequestBody(); + + String originalBodyText = Strings.toStr(requestBody.getOriginalBody()); + logger.info("{}请求报文:{}", logPrefixText, originalBodyText); + + if (isFull(logLevel)) { + String bodyText = Strings.toStr(requestBody.getBody()); + logger.info("{}请求加密报文:{}", logPrefixText, bodyText); + } } else if (request.getRequestBody() instanceof JsonRequestBody) { - JsonRequestBody body = (JsonRequestBody) request.getRequestBody(); - logger.info("[{}] 请求原始报文:{}", request.getId(), Strings.toStr(body.getBody())); + JsonRequestBody requestBody = (JsonRequestBody) request.getRequestBody(); + String bodyText = Strings.toStr(requestBody.getBody()); + logger.info("{}请求报文:{}", logPrefixText, bodyText); } } @@ -53,18 +63,26 @@ public class HttpLogger { * @param 返回类型 */ protected void logResponse(HttpRequest request, OriginalResponse originalResponse, HttpResponse response, long duration) { - if (!logEnabled) { - return; + String logPrefixText = logPrefix(request); + if (isFull(logLevel)) { + String reasonPhrase = httpReasonPhrase(originalResponse); + logger.info("{}应答:{} {} {}", logPrefixText, originalResponse.getVersion(), originalResponse.getStatusCode(), reasonPhrase); + + response.getHttpHeaders().getHeaders().forEach((k, v) -> logger.info("{}应答头:{}:{}", logPrefixText, k, v)); + + String originalBodyText = Strings.toStr(response.getOriginalBody()); + logger.info("{}应答原始报文:{}", logPrefixText, originalBodyText); } - logger.info("[{}] 响应行:{} {}", request.getId(), originalResponse.getVersion(), originalResponse.getStatusCode()); - StringBuilder sb = new StringBuilder(); - response.getHttpHeaders().getHeaders().forEach((k, v) -> sb.append(k).append("=").append(v).append(";")); - logger.info("[{}] 响应头:{}", request.getId(), Strings.removeSuffix(sb.toString(), ";")); - logger.info("[{}] 响应原始报文:{}", request.getId(), Strings.toStr(response.getOriginalBody())); - logger.info("[{}] 响应解密报文:{}", request.getId(), Json.toJson(response.getServiceResponse())); + String requestId = originalResponse.getHttpHeaders().getHeader(REQUEST_ID); + logger.info("{}应答-服务端Request-Id:{}", logPrefixText, requestId); - logger.info("[{}] 请求-响应耗时(毫秒):{}", request.getId(), duration); + String text = Json.toJson(response.getServiceResponse()); + logger.info("{}应答报文:{}", logPrefixText, text); + + if (isFull(logLevel)) { + logger.info("{}请求-应答耗时(毫秒):{}", logPrefixText, duration); + } } /** @@ -73,18 +91,35 @@ public class HttpLogger { * @param originalResponse 响应数据 */ public void logResponseError(HttpRequest request, OriginalResponse originalResponse) { - logger.error("[{}] 响应:{} {}", request.getId(), originalResponse.getVersion(), originalResponse.getStatusCode()); - StringBuilder sb = new StringBuilder(); - originalResponse.getHttpHeaders().getHeaders().forEach((k, v) -> sb.append(k).append("=").append(v).append(";")); - logger.error("[{}] 响应头:{}", request.getId(), Strings.removeSuffix(sb.toString(), ";")); - logger.error("[{}] 响应原始报文:{}", request.getId(), Strings.toStr((originalResponse.getBody()))); + String logPrefixText = logPrefix(request); + logger.error("{}响应:{} {}", request.getId(), originalResponse.getVersion(), originalResponse.getStatusCode()); + if (isFull(logLevel)) { + originalResponse.getHttpHeaders().getHeaders().forEach((k, v) -> logger.info("{}请求头:{}:{}", logPrefixText, k, v)); + + String body = Strings.toStr(originalResponse.getBody()); + logger.error("{}响应原始报文:{}", logPrefixText, body); + } } - public void logHttpStatus(OriginalResponse response) { - // TODO 打印状态码说明 + public String httpReasonPhrase(OriginalResponse response) { + HttpStatus status = HttpStatus.resolve(response.getStatusCode()); + if (Objects.isNull(status)) { + return ""; + } + + return status.getReasonPhrase(); } public void logRemoteError(HttpRequest newRequest, String message) { - logger.error("[{}] 远程调用异常:{}", newRequest.getId(), message); + String logPrefixText = logPrefix(newRequest); + logger.error("{}远程调用异常:{}", logPrefixText, message); + } + + private String logPrefix(HttpRequest request) { + if (logPrefix) { + return String.format("[%s] ", request.getId()); + } + + return ""; } } diff --git a/scfs-api-core/src/main/java/com/czcb/scfs/api/core/http/HttpProfile.java b/scfs-api-core/src/main/java/com/czcb/scfs/api/core/http/HttpProfile.java index 894f44f..9b83690 100644 --- a/scfs-api-core/src/main/java/com/czcb/scfs/api/core/http/HttpProfile.java +++ b/scfs-api-core/src/main/java/com/czcb/scfs/api/core/http/HttpProfile.java @@ -19,7 +19,7 @@ public interface HttpProfile { * * @return Boolean */ - boolean logEnabled(); + LogLevel logLevel(); /** * 是否开启压缩 diff --git a/scfs-api-core/src/main/java/com/czcb/scfs/api/core/http/HttpStatus.java b/scfs-api-core/src/main/java/com/czcb/scfs/api/core/http/HttpStatus.java new file mode 100644 index 0000000..4f13427 --- /dev/null +++ b/scfs-api-core/src/main/java/com/czcb/scfs/api/core/http/HttpStatus.java @@ -0,0 +1,665 @@ +package com.czcb.scfs.api.core.http; + +public enum HttpStatus { + CONTINUE(100, Series.INFORMATIONAL, "Continue"), + /** + * {@code 101 Switching Protocols}. + * + * @see HTTP/1.1: Semantics and Content, section 6.2.2 + */ + SWITCHING_PROTOCOLS(101, Series.INFORMATIONAL, "Switching Protocols"), + /** + * {@code 102 Processing}. + * + * @see WebDAV + */ + PROCESSING(102, Series.INFORMATIONAL, "Processing"), + /** + * {@code 103 Checkpoint}. + * + * @see A proposal for supporting + * resumable POST/PUT HTTP requests in HTTP/1.0 + */ + CHECKPOINT(103, Series.INFORMATIONAL, "Checkpoint"), + + // 2xx Success + + /** + * {@code 200 OK}. + * + * @see HTTP/1.1: Semantics and Content, section 6.3.1 + */ + OK(200, Series.SUCCESSFUL, "OK"), + /** + * {@code 201 Created}. + * + * @see HTTP/1.1: Semantics and Content, section 6.3.2 + */ + CREATED(201, Series.SUCCESSFUL, "Created"), + /** + * {@code 202 Accepted}. + * + * @see HTTP/1.1: Semantics and Content, section 6.3.3 + */ + ACCEPTED(202, Series.SUCCESSFUL, "Accepted"), + /** + * {@code 203 Non-Authoritative Information}. + * + * @see HTTP/1.1: Semantics and Content, section 6.3.4 + */ + NON_AUTHORITATIVE_INFORMATION(203, Series.SUCCESSFUL, "Non-Authoritative Information"), + /** + * {@code 204 No Content}. + * + * @see HTTP/1.1: Semantics and Content, section 6.3.5 + */ + NO_CONTENT(204, Series.SUCCESSFUL, "No Content"), + /** + * {@code 205 Reset Content}. + * + * @see HTTP/1.1: Semantics and Content, section 6.3.6 + */ + RESET_CONTENT(205, Series.SUCCESSFUL, "Reset Content"), + /** + * {@code 206 Partial Content}. + * + * @see HTTP/1.1: Range Requests, section 4.1 + */ + PARTIAL_CONTENT(206, Series.SUCCESSFUL, "Partial Content"), + /** + * {@code 207 Multi-Status}. + * + * @see WebDAV + */ + MULTI_STATUS(207, Series.SUCCESSFUL, "Multi-Status"), + /** + * {@code 208 Already Reported}. + * + * @see WebDAV Binding Extensions + */ + ALREADY_REPORTED(208, Series.SUCCESSFUL, "Already Reported"), + /** + * {@code 226 IM Used}. + * + * @see Delta encoding in HTTP + */ + IM_USED(226, Series.SUCCESSFUL, "IM Used"), + + // 3xx Redirection + + /** + * {@code 300 Multiple Choices}. + * + * @see HTTP/1.1: Semantics and Content, section 6.4.1 + */ + MULTIPLE_CHOICES(300, Series.REDIRECTION, "Multiple Choices"), + /** + * {@code 301 Moved Permanently}. + * + * @see HTTP/1.1: Semantics and Content, section 6.4.2 + */ + MOVED_PERMANENTLY(301, Series.REDIRECTION, "Moved Permanently"), + /** + * {@code 302 Found}. + * + * @see HTTP/1.1: Semantics and Content, section 6.4.3 + */ + FOUND(302, Series.REDIRECTION, "Found"), + /** + * {@code 302 Moved Temporarily}. + * + * @see HTTP/1.0, section 9.3 + * @deprecated in favor of {@link #FOUND} which will be returned from {@code HttpStatus.valueOf(302)} + */ + @Deprecated + MOVED_TEMPORARILY(302, Series.REDIRECTION, "Moved Temporarily"), + /** + * {@code 303 See Other}. + * + * @see HTTP/1.1: Semantics and Content, section 6.4.4 + */ + SEE_OTHER(303, Series.REDIRECTION, "See Other"), + /** + * {@code 304 Not Modified}. + * + * @see HTTP/1.1: Conditional Requests, section 4.1 + */ + NOT_MODIFIED(304, Series.REDIRECTION, "Not Modified"), + /** + * {@code 305 Use Proxy}. + * + * @see HTTP/1.1: Semantics and Content, section 6.4.5 + * @deprecated due to security concerns regarding in-band configuration of a proxy + */ + @Deprecated + USE_PROXY(305, Series.REDIRECTION, "Use Proxy"), + /** + * {@code 307 Temporary Redirect}. + * + * @see HTTP/1.1: Semantics and Content, section 6.4.7 + */ + TEMPORARY_REDIRECT(307, Series.REDIRECTION, "Temporary Redirect"), + /** + * {@code 308 Permanent Redirect}. + * + * @see RFC 7238 + */ + PERMANENT_REDIRECT(308, Series.REDIRECTION, "Permanent Redirect"), + + // --- 4xx Client Error --- + + /** + * {@code 400 Bad Request}. + * + * @see HTTP/1.1: Semantics and Content, section 6.5.1 + */ + BAD_REQUEST(400, Series.CLIENT_ERROR, "Bad Request"), + /** + * {@code 401 Unauthorized}. + * + * @see HTTP/1.1: Authentication, section 3.1 + */ + UNAUTHORIZED(401, Series.CLIENT_ERROR, "Unauthorized"), + /** + * {@code 402 Payment Required}. + * + * @see HTTP/1.1: Semantics and Content, section 6.5.2 + */ + PAYMENT_REQUIRED(402, Series.CLIENT_ERROR, "Payment Required"), + /** + * {@code 403 Forbidden}. + * + * @see HTTP/1.1: Semantics and Content, section 6.5.3 + */ + FORBIDDEN(403, Series.CLIENT_ERROR, "Forbidden"), + /** + * {@code 404 Not Found}. + * + * @see HTTP/1.1: Semantics and Content, section 6.5.4 + */ + NOT_FOUND(404, Series.CLIENT_ERROR, "Not Found"), + /** + * {@code 405 Method Not Allowed}. + * + * @see HTTP/1.1: Semantics and Content, section 6.5.5 + */ + METHOD_NOT_ALLOWED(405, Series.CLIENT_ERROR, "Method Not Allowed"), + /** + * {@code 406 Not Acceptable}. + * + * @see HTTP/1.1: Semantics and Content, section 6.5.6 + */ + NOT_ACCEPTABLE(406, Series.CLIENT_ERROR, "Not Acceptable"), + /** + * {@code 407 Proxy Authentication Required}. + * + * @see HTTP/1.1: Authentication, section 3.2 + */ + PROXY_AUTHENTICATION_REQUIRED(407, Series.CLIENT_ERROR, "Proxy Authentication Required"), + /** + * {@code 408 Request Timeout}. + * + * @see HTTP/1.1: Semantics and Content, section 6.5.7 + */ + REQUEST_TIMEOUT(408, Series.CLIENT_ERROR, "Request Timeout"), + /** + * {@code 409 Conflict}. + * + * @see HTTP/1.1: Semantics and Content, section 6.5.8 + */ + CONFLICT(409, Series.CLIENT_ERROR, "Conflict"), + /** + * {@code 410 Gone}. + * + * @see + * HTTP/1.1: Semantics and Content, section 6.5.9 + */ + GONE(410, Series.CLIENT_ERROR, "Gone"), + /** + * {@code 411 Length Required}. + * + * @see + * HTTP/1.1: Semantics and Content, section 6.5.10 + */ + LENGTH_REQUIRED(411, Series.CLIENT_ERROR, "Length Required"), + /** + * {@code 412 Precondition failed}. + * + * @see + * HTTP/1.1: Conditional Requests, section 4.2 + */ + PRECONDITION_FAILED(412, Series.CLIENT_ERROR, "Precondition Failed"), + /** + * {@code 413 Payload Too Large}. + * + * @see + * HTTP/1.1: Semantics and Content, section 6.5.11 + * @since 4.1 + */ + PAYLOAD_TOO_LARGE(413, Series.CLIENT_ERROR, "Payload Too Large"), + /** + * {@code 413 Request Entity Too Large}. + * + * @see HTTP/1.1, section 10.4.14 + * @deprecated in favor of {@link #PAYLOAD_TOO_LARGE} which will be + * returned from {@code HttpStatus.valueOf(413)} + */ + @Deprecated + REQUEST_ENTITY_TOO_LARGE(413, Series.CLIENT_ERROR, "Request Entity Too Large"), + /** + * {@code 414 URI Too Long}. + * + * @see + * HTTP/1.1: Semantics and Content, section 6.5.12 + * @since 4.1 + */ + URI_TOO_LONG(414, Series.CLIENT_ERROR, "URI Too Long"), + /** + * {@code 414 Request-URI Too Long}. + * + * @see HTTP/1.1, section 10.4.15 + * @deprecated in favor of {@link #URI_TOO_LONG} which will be returned from {@code HttpStatus.valueOf(414)} + */ + @Deprecated + REQUEST_URI_TOO_LONG(414, Series.CLIENT_ERROR, "Request-URI Too Long"), + /** + * {@code 415 Unsupported Media Type}. + * + * @see + * HTTP/1.1: Semantics and Content, section 6.5.13 + */ + UNSUPPORTED_MEDIA_TYPE(415, Series.CLIENT_ERROR, "Unsupported Media Type"), + /** + * {@code 416 Requested Range Not Satisfiable}. + * + * @see HTTP/1.1: Range Requests, section 4.4 + */ + REQUESTED_RANGE_NOT_SATISFIABLE(416, Series.CLIENT_ERROR, "Requested range not satisfiable"), + /** + * {@code 417 Expectation Failed}. + * + * @see + * HTTP/1.1: Semantics and Content, section 6.5.14 + */ + EXPECTATION_FAILED(417, Series.CLIENT_ERROR, "Expectation Failed"), + /** + * {@code 418 I'm a teapot}. + * + * @see HTCPCP/1.0 + */ + I_AM_A_TEAPOT(418, Series.CLIENT_ERROR, "I'm a teapot"), + /** + * @deprecated See + * + * WebDAV Draft Changes + */ + @Deprecated + INSUFFICIENT_SPACE_ON_RESOURCE(419, Series.CLIENT_ERROR, "Insufficient Space On Resource"), + /** + * @deprecated See + * + * WebDAV Draft Changes + */ + @Deprecated + METHOD_FAILURE(420, Series.CLIENT_ERROR, "Method Failure"), + /** + * @deprecated See + * WebDAV Draft Changes + */ + @Deprecated + DESTINATION_LOCKED(421, Series.CLIENT_ERROR, "Destination Locked"), + /** + * {@code 422 Unprocessable Entity}. + * + * @see WebDAV + */ + UNPROCESSABLE_ENTITY(422, Series.CLIENT_ERROR, "Unprocessable Entity"), + /** + * {@code 423 Locked}. + * + * @see WebDAV + */ + LOCKED(423, Series.CLIENT_ERROR, "Locked"), + /** + * {@code 424 Failed Dependency}. + * + * @see WebDAV + */ + FAILED_DEPENDENCY(424, Series.CLIENT_ERROR, "Failed Dependency"), + /** + * {@code 425 Too Early}. + * + * @see RFC 8470 + * @since 5.2 + */ + TOO_EARLY(425, Series.CLIENT_ERROR, "Too Early"), + /** + * {@code 426 Upgrade Required}. + * + * @see Upgrading to TLS Within HTTP/1.1 + */ + UPGRADE_REQUIRED(426, Series.CLIENT_ERROR, "Upgrade Required"), + /** + * {@code 428 Precondition Required}. + * + * @see Additional HTTP Status Codes + */ + PRECONDITION_REQUIRED(428, Series.CLIENT_ERROR, "Precondition Required"), + /** + * {@code 429 Too Many Requests}. + * + * @see Additional HTTP Status Codes + */ + TOO_MANY_REQUESTS(429, Series.CLIENT_ERROR, "Too Many Requests"), + /** + * {@code 431 Request Header Fields Too Large}. + * + * @see Additional HTTP Status Codes + */ + REQUEST_HEADER_FIELDS_TOO_LARGE(431, Series.CLIENT_ERROR, "Request Header Fields Too Large"), + /** + * {@code 451 Unavailable For Legal Reasons}. + * + * @see + * An HTTP Status Code to Report Legal Obstacles + * @since 4.3 + */ + UNAVAILABLE_FOR_LEGAL_REASONS(451, Series.CLIENT_ERROR, "Unavailable For Legal Reasons"), + + // --- 5xx Server Error --- + + /** + * {@code 500 Internal Server Error}. + * + * @see HTTP/1.1: Semantics and Content, section 6.6.1 + */ + INTERNAL_SERVER_ERROR(500, Series.SERVER_ERROR, "Internal Server Error"), + /** + * {@code 501 Not Implemented}. + * + * @see HTTP/1.1: Semantics and Content, section 6.6.2 + */ + NOT_IMPLEMENTED(501, Series.SERVER_ERROR, "Not Implemented"), + /** + * {@code 502 Bad Gateway}. + * + * @see HTTP/1.1: Semantics and Content, section 6.6.3 + */ + BAD_GATEWAY(502, Series.SERVER_ERROR, "Bad Gateway"), + /** + * {@code 503 Service Unavailable}. + * + * @see HTTP/1.1: Semantics and Content, section 6.6.4 + */ + SERVICE_UNAVAILABLE(503, Series.SERVER_ERROR, "Service Unavailable"), + /** + * {@code 504 Gateway Timeout}. + * + * @see HTTP/1.1: Semantics and Content, section 6.6.5 + */ + GATEWAY_TIMEOUT(504, Series.SERVER_ERROR, "Gateway Timeout"), + /** + * {@code 505 HTTP Version Not Supported}. + * + * @see HTTP/1.1: Semantics and Content, section 6.6.6 + */ + HTTP_VERSION_NOT_SUPPORTED(505, Series.SERVER_ERROR, "HTTP Version not supported"), + /** + * {@code 506 Variant Also Negotiates} + * + * @see Transparent Content Negotiation + */ + VARIANT_ALSO_NEGOTIATES(506, Series.SERVER_ERROR, "Variant Also Negotiates"), + /** + * {@code 507 Insufficient Storage} + * + * @see WebDAV + */ + INSUFFICIENT_STORAGE(507, Series.SERVER_ERROR, "Insufficient Storage"), + /** + * {@code 508 Loop Detected} + * + * @see WebDAV Binding Extensions + */ + LOOP_DETECTED(508, Series.SERVER_ERROR, "Loop Detected"), + /** + * {@code 509 Bandwidth Limit Exceeded} + */ + BANDWIDTH_LIMIT_EXCEEDED(509, Series.SERVER_ERROR, "Bandwidth Limit Exceeded"), + /** + * {@code 510 Not Extended} + * + * @see HTTP Extension Framework + */ + NOT_EXTENDED(510, Series.SERVER_ERROR, "Not Extended"), + /** + * {@code 511 Network Authentication Required}. + * + * @see Additional HTTP Status Codes + */ + NETWORK_AUTHENTICATION_REQUIRED(511, Series.SERVER_ERROR, "Network Authentication Required"); + + + private static final HttpStatus[] VALUES; + + static { + VALUES = values(); + } + + + private final int value; + + private final Series series; + + private final String reasonPhrase; + + HttpStatus(int value, Series series, String reasonPhrase) { + this.value = value; + this.series = series; + this.reasonPhrase = reasonPhrase; + } + + + /** + * Return the integer value of this status code. + */ + public int value() { + return this.value; + } + + /** + * Return the HTTP status series of this status code. + * + * @see HttpStatus.Series + */ + public Series series() { + return this.series; + } + + /** + * Return the reason phrase of this status code. + */ + public String getReasonPhrase() { + return this.reasonPhrase; + } + + /** + * Whether this status code is in the HTTP series + *

This is a shortcut for checking the value of {@link #series()}. + * + * @see #series() + * @since 4.0 + */ + public boolean is1xxInformational() { + return (series() == Series.INFORMATIONAL); + } + + /** + * Whether this status code is in the HTTP series + *

This is a shortcut for checking the value of {@link #series()}. + * + * @see #series() + * @since 4.0 + */ + public boolean is2xxSuccessful() { + return (series() == Series.SUCCESSFUL); + } + + /** + * Whether this status code is in the HTTP series + *

This is a shortcut for checking the value of {@link #series()}. + * + * @see #series() + * @since 4.0 + */ + public boolean is3xxRedirection() { + return (series() == Series.REDIRECTION); + } + + /** + * Whether this status code is in the HTTP series + *

This is a shortcut for checking the value of {@link #series()}. + * + * @see #series() + * @since 4.0 + */ + public boolean is4xxClientError() { + return (series() == Series.CLIENT_ERROR); + } + + /** + * Whether this status code is in the HTTP series + *

This is a shortcut for checking the value of {@link #series()}. + * + * @see #series() + * @since 4.0 + */ + public boolean is5xxServerError() { + return (series() == Series.SERVER_ERROR); + } + + /** + * Whether this status code is in the HTTP series + *

This is a shortcut for checking the value of {@link #series()}. + * + * @see #is4xxClientError() + * @see #is5xxServerError() + * @since 5.0 + */ + public boolean isError() { + return (is4xxClientError() || is5xxServerError()); + } + + /** + * Return a string representation of this status code. + */ + @Override + public String toString() { + return this.value + " " + name(); + } + + + /** + * Return the {@code HttpStatus} enum constant with the specified numeric value. + * + * @param statusCode the numeric value of the enum to be returned + * @return the enum constant with the specified numeric value + * @throws IllegalArgumentException if this enum has no constant for the specified numeric value + */ + public static HttpStatus valueOf(int statusCode) { + HttpStatus status = resolve(statusCode); + if (status == null) { + throw new IllegalArgumentException("No matching constant for [" + statusCode + "]"); + } + return status; + } + + /** + * Resolve the given status code to an {@code HttpStatus}, if possible. + * + * @param statusCode the HTTP status code (potentially non-standard) + * @return the corresponding {@code HttpStatus}, or {@code null} if not found + * @since 5.0 + */ + public static HttpStatus resolve(int statusCode) { + // Use cached VALUES instead of values() to prevent array allocation. + for (HttpStatus status : VALUES) { + if (status.value == statusCode) { + return status; + } + } + return null; + } + + + /** + * Enumeration of HTTP status series. + *

Retrievable via {@link HttpStatus#series()}. + */ + public enum Series { + + INFORMATIONAL(1), + SUCCESSFUL(2), + REDIRECTION(3), + CLIENT_ERROR(4), + SERVER_ERROR(5); + + private final int value; + + Series(int value) { + this.value = value; + } + + /** + * Return the integer value of this status series. Ranges from 1 to 5. + */ + public int value() { + return this.value; + } + + /** + * Return the {@code Series} enum constant for the supplied {@code HttpStatus}. + * + * @param status a standard HTTP status enum constant + * @return the {@code Series} enum constant for the supplied {@code HttpStatus} + * @deprecated as of 5.3, in favor of invoking {@link HttpStatus#series()} directly + */ + @Deprecated + public static Series valueOf(HttpStatus status) { + return status.series; + } + + /** + * Return the {@code Series} enum constant for the supplied status code. + * + * @param statusCode the HTTP status code (potentially non-standard) + * @return the {@code Series} enum constant for the supplied status code + * @throws IllegalArgumentException if this enum has no corresponding constant + */ + public static Series valueOf(int statusCode) { + Series series = resolve(statusCode); + if (series == null) { + throw new IllegalArgumentException("No matching constant for [" + statusCode + "]"); + } + return series; + } + + /** + * Resolve the given status code to an {@code HttpStatus.Series}, if possible. + * + * @param statusCode the HTTP status code (potentially non-standard) + * @return the corresponding {@code Series}, or {@code null} if not found + * @since 5.1.3 + */ + public static Series resolve(int statusCode) { + int seriesCode = statusCode / 100; + for (Series series : values()) { + if (series.value == seriesCode) { + return series; + } + } + return null; + } + } + +} diff --git a/scfs-api-core/src/main/java/com/czcb/scfs/api/core/http/LogLevel.java b/scfs-api-core/src/main/java/com/czcb/scfs/api/core/http/LogLevel.java new file mode 100644 index 0000000..da1e494 --- /dev/null +++ b/scfs-api-core/src/main/java/com/czcb/scfs/api/core/http/LogLevel.java @@ -0,0 +1,11 @@ +package com.czcb.scfs.api.core.http; + +import java.util.Objects; + +public enum LogLevel { + basic, full; + + public static boolean isFull(LogLevel logLevel) { + return Objects.equals(full, logLevel); + } +} diff --git a/scfs-api-core/src/test/java/com/czcb/scfs/api/core/http/ApiClientBuilderTest.java b/scfs-api-core/src/test/java/com/czcb/scfs/api/core/http/ApiClientBuilderTest.java index 88743e2..ae55f9c 100644 --- a/scfs-api-core/src/test/java/com/czcb/scfs/api/core/http/ApiClientBuilderTest.java +++ b/scfs-api-core/src/test/java/com/czcb/scfs/api/core/http/ApiClientBuilderTest.java @@ -40,7 +40,7 @@ class ApiClientBuilderTest { .build(), new DefaultHttpProfile.Builder() .online(false) - .logEnabled(true) + .logLevel(LogLevel.basic) .compressionEnabled(false) .host("http://127.0.0.1:8888") .build() @@ -142,7 +142,7 @@ class ApiClientBuilderTest { .port("1000") .build()) .online(false) - .logEnabled(true) + .logLevel(LogLevel.basic) .compressionEnabled(false) .host("http://127.0.0.1:8888") .build() diff --git a/scfs-api-core/src/test/java/com/czcb/scfs/api/core/http/DefaultHttpProfileTest.java b/scfs-api-core/src/test/java/com/czcb/scfs/api/core/http/DefaultHttpProfileTest.java index 67ebca2..e51a473 100644 --- a/scfs-api-core/src/test/java/com/czcb/scfs/api/core/http/DefaultHttpProfileTest.java +++ b/scfs-api-core/src/test/java/com/czcb/scfs/api/core/http/DefaultHttpProfileTest.java @@ -16,7 +16,7 @@ class DefaultHttpProfileTest { .headers(data) .compressionEnabled(true) .online(true) - .logEnabled(true) + .logLevel(LogLevel.basic) .host("http://10.133.129.74:8761/") .build(); @@ -26,7 +26,7 @@ class DefaultHttpProfileTest { Assertions.assertTrue(httpProfile.compressionEnabled()); Assertions.assertTrue(httpProfile.online()); - Assertions.assertTrue(httpProfile.logEnabled()); + Assertions.assertEquals(LogLevel.basic, httpProfile.logLevel()); } @Test diff --git a/scfs-api-core/src/test/java/com/czcb/scfs/api/core/http/client/ApacheHttpclientProxyTest.java b/scfs-api-core/src/test/java/com/czcb/scfs/api/core/http/client/ApacheHttpclientProxyTest.java index ce6206b..00f42bb 100644 --- a/scfs-api-core/src/test/java/com/czcb/scfs/api/core/http/client/ApacheHttpclientProxyTest.java +++ b/scfs-api-core/src/test/java/com/czcb/scfs/api/core/http/client/ApacheHttpclientProxyTest.java @@ -51,7 +51,7 @@ class ApacheHttpclientProxyTest { .appNo("100000") .build(), new DefaultHttpProfile.Builder() .online(false) - .logEnabled(true) + .logLevel(LogLevel.basic) .compressionEnabled(false) .host("http://127.0.0.1:8888") .build() diff --git a/scfs-api-core/src/test/java/com/czcb/scfs/api/core/http/client/ApacheHttpclientTest.java b/scfs-api-core/src/test/java/com/czcb/scfs/api/core/http/client/ApacheHttpclientTest.java index 195cb4f..ced90da 100644 --- a/scfs-api-core/src/test/java/com/czcb/scfs/api/core/http/client/ApacheHttpclientTest.java +++ b/scfs-api-core/src/test/java/com/czcb/scfs/api/core/http/client/ApacheHttpclientTest.java @@ -53,7 +53,7 @@ class ApacheHttpclientTest { .appNo("100000") .build(), new DefaultHttpProfile.Builder() .online(false) - .logEnabled(true) + .logLevel(LogLevel.basic) .compressionEnabled(compressionEnabled) .host("http://127.0.0.1:8888") .build() diff --git a/scfs-api-core/src/test/java/com/czcb/scfs/api/core/http/client/ApacheHttpclientTestProxyTest.java b/scfs-api-core/src/test/java/com/czcb/scfs/api/core/http/client/ApacheHttpclientTestProxyTest.java index 270526c..d1e48cf 100644 --- a/scfs-api-core/src/test/java/com/czcb/scfs/api/core/http/client/ApacheHttpclientTestProxyTest.java +++ b/scfs-api-core/src/test/java/com/czcb/scfs/api/core/http/client/ApacheHttpclientTestProxyTest.java @@ -51,7 +51,7 @@ class ApacheHttpclientTestProxyTest { .appNo("100000") .build(), new DefaultHttpProfile.Builder() .online(false) - .logEnabled(true) + .logLevel(LogLevel.basic) .compressionEnabled(false) .host("http://127.0.0.1:8888") .build() diff --git a/scfs-api-core/src/test/java/com/czcb/scfs/api/core/http/client/ApacheHttpclientV2Test.java b/scfs-api-core/src/test/java/com/czcb/scfs/api/core/http/client/ApacheHttpclientV2Test.java index 994a878..1f4b67a 100644 --- a/scfs-api-core/src/test/java/com/czcb/scfs/api/core/http/client/ApacheHttpclientV2Test.java +++ b/scfs-api-core/src/test/java/com/czcb/scfs/api/core/http/client/ApacheHttpclientV2Test.java @@ -52,7 +52,7 @@ class ApacheHttpclientV2Test { .appNo("100000") .build(), new DefaultHttpProfile.Builder() .online(false) - .logEnabled(true) + .logLevel(LogLevel.basic) .compressionEnabled(false) .host("http://127.0.0.1:8888") .build() diff --git a/scfs-api-core/src/test/java/com/czcb/scfs/api/core/http/client/ApacheHttpclientV3Test.java b/scfs-api-core/src/test/java/com/czcb/scfs/api/core/http/client/ApacheHttpclientV3Test.java index dcf8202..bfac8b9 100644 --- a/scfs-api-core/src/test/java/com/czcb/scfs/api/core/http/client/ApacheHttpclientV3Test.java +++ b/scfs-api-core/src/test/java/com/czcb/scfs/api/core/http/client/ApacheHttpclientV3Test.java @@ -52,7 +52,7 @@ class ApacheHttpclientV3Test { .appNo("100000") .build(), new DefaultHttpProfile.Builder() .online(false) - .logEnabled(true) + .logLevel(LogLevel.basic) .compressionEnabled(false) .host("http://127.0.0.1:8888") .build() diff --git a/scfs-api-service/src/test/java/com/czcb/scfs/api/service/MockResponse.java b/scfs-api-service/src/test/java/com/czcb/scfs/api/service/MockResponse.java index 7e88d59..b1cf577 100644 --- a/scfs-api-service/src/test/java/com/czcb/scfs/api/service/MockResponse.java +++ b/scfs-api-service/src/test/java/com/czcb/scfs/api/service/MockResponse.java @@ -6,6 +6,7 @@ import com.czcb.scfs.api.core.Profile; import com.czcb.scfs.api.core.cipher.*; import com.czcb.scfs.api.core.http.ApiClientBuilder; import com.czcb.scfs.api.core.http.DefaultHttpProfile; +import com.czcb.scfs.api.core.http.LogLevel; import com.czcb.scfs.api.core.util.DateTimes; import com.czcb.scfs.api.core.util.Nonce; import com.czcb.scfs.api.core.util.Strings; @@ -52,7 +53,7 @@ public class MockResponse { .appNo("100000") .build(), new DefaultHttpProfile.Builder() .online(false) - .logEnabled(true) + .logLevel(LogLevel.basic) .compressionEnabled(false) .host("http://127.0.0.1:8888") .build() diff --git a/scfs-api-spring-boot-starter/src/main/java/com/czcb/scfs/spring/boot/starter/AbstractAutoConfiguration.java b/scfs-api-spring-boot-starter/src/main/java/com/czcb/scfs/spring/boot/starter/AbstractAutoConfiguration.java index ae76b2e..b5a3cb8 100644 --- a/scfs-api-spring-boot-starter/src/main/java/com/czcb/scfs/spring/boot/starter/AbstractAutoConfiguration.java +++ b/scfs-api-spring-boot-starter/src/main/java/com/czcb/scfs/spring/boot/starter/AbstractAutoConfiguration.java @@ -37,7 +37,7 @@ public abstract class AbstractAutoConfiguration { .online(getProperties().getOnline()) .host(getProperties().getHost()) .headers(getProperties().getHttpclient().getHeaders()) - .logEnabled(getProperties().getHttpclient().getLogEnabled()) + .logLevel(getProperties().getHttpclient().getLogLevel()) .compressionEnabled(getProperties().getHttpclient().getCompressionEnabled()) .proxy(proxy.build()) .connPool(connPool) diff --git a/scfs-api-spring-boot-starter/src/main/java/com/czcb/scfs/spring/boot/starter/ScfsApiGatewayProperties.java b/scfs-api-spring-boot-starter/src/main/java/com/czcb/scfs/spring/boot/starter/ScfsApiGatewayProperties.java index db45991..f817487 100644 --- a/scfs-api-spring-boot-starter/src/main/java/com/czcb/scfs/spring/boot/starter/ScfsApiGatewayProperties.java +++ b/scfs-api-spring-boot-starter/src/main/java/com/czcb/scfs/spring/boot/starter/ScfsApiGatewayProperties.java @@ -1,6 +1,7 @@ package com.czcb.scfs.spring.boot.starter; import com.czcb.scfs.api.core.cipher.StoreType; +import com.czcb.scfs.api.core.http.LogLevel; import org.springframework.boot.context.properties.ConfigurationProperties; import java.util.HashMap; @@ -160,9 +161,9 @@ public class ScfsApiGatewayProperties { public static class Httpclient { /** - * 打印请求日志 默认 true + * 请求日志打印级别 默认 basic */ - private Boolean logEnabled = true; + private LogLevel logLevel = LogLevel.basic; /** * 请求压缩 默认 false */ @@ -183,12 +184,12 @@ public class ScfsApiGatewayProperties { */ private Map headers = new HashMap<>(); - public Boolean getLogEnabled() { - return logEnabled; + public LogLevel getLogLevel() { + return logLevel; } - public void setLogEnabled(Boolean logEnabled) { - this.logEnabled = logEnabled; + public void setLogLevel(LogLevel logLevel) { + this.logLevel = logLevel; } public Boolean getCompressionEnabled() { diff --git a/scfs-api-spring-boot-starter/src/test/java/com/czcb/scfs/spring/boot/starter/ScfsApiGatewayPropertiesTest.java b/scfs-api-spring-boot-starter/src/test/java/com/czcb/scfs/spring/boot/starter/ScfsApiGatewayPropertiesTest.java index 0bdf480..f18e8b1 100644 --- a/scfs-api-spring-boot-starter/src/test/java/com/czcb/scfs/spring/boot/starter/ScfsApiGatewayPropertiesTest.java +++ b/scfs-api-spring-boot-starter/src/test/java/com/czcb/scfs/spring/boot/starter/ScfsApiGatewayPropertiesTest.java @@ -1,6 +1,7 @@ package com.czcb.scfs.spring.boot.starter; import com.czcb.scfs.api.core.cipher.StoreType; +import com.czcb.scfs.api.core.http.LogLevel; import com.google.common.collect.Lists; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; @@ -65,15 +66,15 @@ class ScfsApiGatewayPropertiesTest { httpclient.setConnPool(connPool); httpclient.setCompressionEnabled(false); - httpclient.setLogEnabled(false); + httpclient.setLogLevel(LogLevel.full); ScfsApiGatewayProperties properties = new ScfsApiGatewayProperties(); properties.setHttpclient(httpclient); Assertions.assertNotNull(properties.getHttpclient()); Assertions.assertFalse(properties.getHttpclient().getCompressionEnabled()); - Assertions.assertFalse(properties.getHttpclient().getLogEnabled()); + Assertions.assertEquals(LogLevel.full, properties.getHttpclient().getLogLevel()); - assertThatJson("{\"logEnabled\":false,\"compressionEnabled\":false," + + assertThatJson("{\"logLevel\":\"full\",\"compressionEnabled\":false," + "\"connPool\":{\"maxRequests\":100,\"maxRequestPerHost\":100,\"connectTimeout\":100,\"socketTimeout\":100}," + "\"proxy\":{\"enabled\":true,\"host\":\"128.0.0.1\",\"port\":\"1000\",\"username\":\"name\",\"password\":\"123456\",\"scheme\":\"https\"}," + "\"headers\":{\"aa\":\"123456\"}}") diff --git a/scfs-api-spring-boot-starter/src/test/java/com/czcb/scfs/spring/boot/starter/SmConfigurationTest.java b/scfs-api-spring-boot-starter/src/test/java/com/czcb/scfs/spring/boot/starter/SmConfigurationTest.java index 4b61c7e..6894339 100644 --- a/scfs-api-spring-boot-starter/src/test/java/com/czcb/scfs/spring/boot/starter/SmConfigurationTest.java +++ b/scfs-api-spring-boot-starter/src/test/java/com/czcb/scfs/spring/boot/starter/SmConfigurationTest.java @@ -1,6 +1,7 @@ package com.czcb.scfs.spring.boot.starter; import com.czcb.scfs.api.core.cipher.StoreType; +import com.czcb.scfs.api.core.http.LogLevel; import org.assertj.core.util.Lists; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; @@ -42,7 +43,7 @@ class SmConfigurationTest { connPool.setSocketTimeout(100); httpclient.setConnPool(connPool); httpclient.setCompressionEnabled(false); - httpclient.setLogEnabled(false); + httpclient.setLogLevel(LogLevel.basic); properties.setHttpclient(httpclient); ScfsApiGatewayProperties.Channel channel = new ScfsApiGatewayProperties.Channel();