From aa2af7aee11b29a64a105085d95741985e79b188 Mon Sep 17 00:00:00 2001 From: xRain Date: Tue, 18 Aug 2026 01:56:32 +0800 Subject: [PATCH] =?UTF-8?q?refactor:=20=E7=A7=BB=E9=99=A4=20soulsoft=5Fser?= =?UTF-8?q?ialization=20/=20soulsoft=5Fnet=5Fhttp=20=E4=BE=9D=E8=B5=96?= =?UTF-8?q?=E2=80=94=E2=80=94=E5=85=A8=E9=83=A8=20HTTP=20=E6=94=B9=20stdx.?= =?UTF-8?q?net.http?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - SimApiStorage 4 处 S3 请求(PUT 上传/HEAD 检桶/PUT 建桶/POST 删除)改 stdx ClientBuilder/HttpRequestBuilder - SimApiAuthCenter.verifySign 改 stdx(post + body + 读响应) - cjpm.toml 删除 soulsoft_serialization、soulsoft_net_http(soulsoft web 栈传递依赖提供序列化) - S3 全链路实测通过(上传/预签名/删除) --- cjpm.lock | 25 ++++---- cjpm.toml | 2 - src/authsdk/SimApiAuthCenter.cj | 75 ++++++++++++++-------- src/helpers/SimApiStorage.cj | 106 ++++++++++++++++++-------------- 4 files changed, 124 insertions(+), 84 deletions(-) diff --git a/cjpm.lock b/cjpm.lock index 9fab0c3..ffec643 100644 --- a/cjpm.lock +++ b/cjpm.lock @@ -1,20 +1,19 @@ version = 0 [requires] - soulsoft_extensions_hosting = {version = "1.0.20260528"} - soulsoft_web_http = {version = "1.0.20260528"} soulsoft_extensions_options_configuration = {version = "1.0.20260528"} - soulsoft_web_routing = {version = "1.0.20260528"} - soulsoft_web_hosting = {version = "1.0.20260528"} - soulsoft_extensions_logging = {version = "1.0.20260528"} - soulsoft_web_mvc = {version = "1.0.20260528"} - soulsoft_serialization = {version = "1.0.20260528"} - soulsoft_net_http = {version = "1.0.20260528"} - soulsoft_extensions_logging_console = {version = "1.0.20260528"} - soulsoft_identity_claims = {version = "1.0.20260528"} - redis = {version = "1.0.20260627"} - soulsoft_extensions_logging_configuration = {version = "1.0.20260528"} - soulsoft_extensions_configuration = {version = "1.0.20260528"} soulsoft_extensions_options = {version = "1.0.20260528"} + redis = {version = "1.0.20260627"} + soulsoft_serialization = {version = "1.0.20260528"} + soulsoft_web_http = {version = "1.0.20260528"} + soulsoft_identity_claims = {version = "1.0.20260528"} + soulsoft_web_routing = {version = "1.0.20260528"} + soulsoft_extensions_hosting = {version = "1.0.20260528"} + soulsoft_extensions_logging = {version = "1.0.20260528"} soulsoft_web_cors = {version = "1.0.20260528"} + soulsoft_extensions_logging_console = {version = "1.0.20260528"} + soulsoft_extensions_configuration = {version = "1.0.20260528"} soulsoft_extensions_injection = {version = "1.0.20260528"} + soulsoft_extensions_logging_configuration = {version = "1.0.20260528"} + soulsoft_web_hosting = {version = "1.0.20260528"} + soulsoft_web_mvc = {version = "1.0.20260528"} diff --git a/cjpm.toml b/cjpm.toml index 9b32497..16d4ef9 100644 --- a/cjpm.toml +++ b/cjpm.toml @@ -21,8 +21,6 @@ soulsoft_extensions_configuration = "1.0.20260528" soulsoft_extensions_injection = "1.0.20260528" soulsoft_extensions_options = "1.0.20260528" - soulsoft_serialization = "1.0.20260528" - soulsoft_net_http = "1.0.20260528" redis = "1.0.20260627" simapi_serialization = { path = "../simapi-serialization" } diff --git a/src/authsdk/SimApiAuthCenter.cj b/src/authsdk/SimApiAuthCenter.cj index e9cf833..033e3c0 100644 --- a/src/authsdk/SimApiAuthCenter.cj +++ b/src/authsdk/SimApiAuthCenter.cj @@ -7,10 +7,10 @@ package simapi.authsdk import std.collection.* +import std.io.* +import stdx.net.http.* import stdx.net.tls.* import stdx.net.tls.common.* -import soulsoft_net_http.{HttpClient, HttpRequestMessage, JsonContent} -import soulsoft_net_http.{HttpMethod as NetHttpMethod} import simapi_serialization.* import simapi.communications.* import simapi.helpers.* @@ -39,31 +39,22 @@ public class SimApiAuthCenter { */ public func verifySign(appId: String, timestamp: String, nonce: String, sign: String): Unit { let url = "${_client.server}/api/auth/sign/verify?appId=${appId}×tamp=${timestamp}&nonce=${nonce}&sign=${sign}" - let http = HttpClient.create { builder => - builder.noProxy() - var tls = TlsClientConfig() - tls.verifyMode = CertificateVerifyMode.TrustAll - match (_client.server.indexOf("://")) { - case Some(i) => - let rest = _client.server[i + 3..] - let slash = rest.indexOf("/") ?? rest.size - let q = rest.indexOf("?") ?? rest.size - let end = if (slash < q) { slash } else { q } - let host = rest[0..end] - if (!host.isEmpty()) { - tls.serverName = Some(host) - } - case None => () - } - builder.tlsConfig(tls) - } + let http = ClientBuilder(). + noProxy(). + tlsConfig(buildTlsConfig(_client.server)). + build() try { - let request = HttpRequestMessage(NetHttpMethod.Post, url) - request.content = JsonContent.create("{}") + let request = HttpRequestBuilder(). + post(). + url(url). + header("Content-Type", "application/json"). + body("{}"). + build() let response = http.send(request) try { - response.ensureSuccessStatusCode() - let json = response.content.readAsString() + SimApiError.errorWhenFalse(isSuccess(response.status), code: Int64(response.status), + message: "HTTP ERROR: ${response.status}") + let json = readBodyText(response.body) let resp = JsonSerializer.Deserialize(json) SimApiError.errorWhen(resp.code != 200, code: 400, message: "签名验证失败") } finally { @@ -74,6 +65,42 @@ public class SimApiAuthCenter { } } + /// 构建 TLS 配置:信任所有证书 + SNI 域名 + private static func buildTlsConfig(server: String): TlsClientConfig { + var tls = TlsClientConfig() + tls.verifyMode = CertificateVerifyMode.TrustAll + match (server.indexOf("://")) { + case Some(i) => + let rest = server[i + 3..] + let slash = rest.indexOf("/") ?? rest.size + let q = rest.indexOf("?") ?? rest.size + let end = if (slash < q) { slash } else { q } + let host = rest[0..end] + if (!host.isEmpty()) { + tls.serverName = Some(host) + } + case None => () + } + tls + } + + /// 2xx 视为成功 + private static func isSuccess(status: UInt16): Bool { + status >= 200 && status < 300 + } + + /// 读取响应体 InputStream 为字符串 + private static func readBodyText(body: InputStream): String { + var buffer = Array(4096, repeat: 0) + var sb = StringBuilder() + var read = body.read(buffer) + while (read > 0) { + sb.appendFromUtf8(buffer.slice(0, read)) + read = body.read(buffer) + } + sb.toString() + } + // ===== 群组相关 ===== /** diff --git a/src/helpers/SimApiStorage.cj b/src/helpers/SimApiStorage.cj index 83d472c..6d7bc52 100644 --- a/src/helpers/SimApiStorage.cj +++ b/src/helpers/SimApiStorage.cj @@ -14,14 +14,14 @@ package simapi.helpers import std.collection.* +import std.io.* import std.time.* import stdx.crypto.digest.* import stdx.encoding.base64.* import stdx.encoding.hex.* +import stdx.net.http.* import stdx.net.tls.* import stdx.net.tls.common.* -import soulsoft_net_http.{ByteArrayContent, HttpClient, HttpRequestMessage} -import soulsoft_net_http.{HttpMethod as NetHttpMethod} import soulsoft_web_http.* import simapi.configurations.* @@ -238,17 +238,19 @@ public class SimApiStorage { toHexString(hmacSha256(buildSigningKey(dateStamp), stringToSign.toArray()))) let client = createClient() try { - let request = HttpRequestMessage(NetHttpMethod.Put, "${_endpoint}${canonicalUri}") - request.headers.add("x-amz-content-sha256", payloadHash) - request.headers.add("x-amz-date", amzDate) - request.headers.add("Authorization", authorization) - let content = ByteArrayContent(data) - content.headers.add("Content-Type", contentType) - request.content = content + let request = HttpRequestBuilder(). + put(). + url("${_endpoint}${canonicalUri}"). + header("x-amz-content-sha256", payloadHash). + header("x-amz-date", amzDate). + header("Authorization", authorization). + header("Content-Type", contentType). + body(data). + build() let response = client.send(request) try { - SimApiError.errorWhenFalse(response.isSuccessStatusCode, code: response.statusCode, - message: "SimApiStorage 上传失败: HTTP ${response.statusCode}") + SimApiError.errorWhenFalse(isSuccess(response.status), code: Int64(response.status), + message: "SimApiStorage 上传失败: HTTP ${response.status}") } finally { response.close() } @@ -273,13 +275,16 @@ public class SimApiStorage { toHexString(hmacSha256(buildSigningKey(dateStamp), stringToSign.toArray()))) let client = createClient() try { - let request = HttpRequestMessage(NetHttpMethod.Head, "${_endpoint}${canonicalUri}") - request.headers.add("x-amz-content-sha256", payloadHash) - request.headers.add("x-amz-date", amzDate) - request.headers.add("Authorization", authorization) + let request = HttpRequestBuilder(). + head(). + url("${_endpoint}${canonicalUri}"). + header("x-amz-content-sha256", payloadHash). + header("x-amz-date", amzDate). + header("Authorization", authorization). + build() let response = client.send(request) try { - response.statusCode == 200 + response.status == 200 } finally { response.close() } @@ -304,15 +309,18 @@ public class SimApiStorage { toHexString(hmacSha256(buildSigningKey(dateStamp), stringToSign.toArray()))) let client = createClient() try { - let request = HttpRequestMessage(NetHttpMethod.Put, "${_endpoint}${canonicalUri}") - request.headers.add("x-amz-content-sha256", payloadHash) - request.headers.add("x-amz-date", amzDate) - request.headers.add("Authorization", authorization) - request.content = ByteArrayContent(Array(0, repeat: 0)) + let request = HttpRequestBuilder(). + put(). + url("${_endpoint}${canonicalUri}"). + header("x-amz-content-sha256", payloadHash). + header("x-amz-date", amzDate). + header("Authorization", authorization). + body(Array(0, repeat: 0u8)). + build() let response = client.send(request) try { - SimApiError.errorWhenFalse(response.isSuccessStatusCode, code: response.statusCode, - message: "SimApiStorage 创建桶失败: HTTP ${response.statusCode}") + SimApiError.errorWhenFalse(isSuccess(response.status), code: Int64(response.status), + message: "SimApiStorage 创建桶失败: HTTP ${response.status}") } finally { response.close() } @@ -347,20 +355,22 @@ public class SimApiStorage { toHexString(hmacSha256(buildSigningKey(dateStamp), stringToSign.toArray()))) let client = createClient() try { - let request = HttpRequestMessage(NetHttpMethod.Post, "${_endpoint}${canonicalUri}?${canonicalQuery}") - request.headers.add("x-amz-content-sha256", payloadHash) - request.headers.add("x-amz-date", amzDate) - request.headers.add("Authorization", authorization) let contentMd5 = md5Base64(xml.toArray()) - let content = ByteArrayContent(xml.toArray()) - content.headers.add("Content-Type", "application/xml") - // MinIO 的 DeleteObjects 强制要求 Content-Md5(缺失返回 MissingContentMD5) - content.headers.add("Content-Md5", contentMd5) - request.content = content + let request = HttpRequestBuilder(). + post(). + url("${_endpoint}${canonicalUri}?${canonicalQuery}"). + header("x-amz-content-sha256", payloadHash). + header("x-amz-date", amzDate). + header("Authorization", authorization). + header("Content-Type", "application/xml"). + // MinIO 的 DeleteObjects 强制要求 Content-Md5(缺失返回 MissingContentMD5) + header("Content-Md5", contentMd5). + body(xml). + build() let response = client.send(request) try { - SimApiError.errorWhenFalse(response.isSuccessStatusCode, code: response.statusCode, - message: "SimApiStorage 批量删除失败: HTTP ${response.statusCode}") + SimApiError.errorWhenFalse(isSuccess(response.status), code: Int64(response.status), + message: "SimApiStorage 批量删除失败: HTTP ${response.status}") } finally { response.close() } @@ -381,19 +391,25 @@ public class SimApiStorage { hmacSha256(kService, "aws4_request".toArray()) } - private func createClient(): HttpClient { - HttpClient.create { builder => - builder.noProxy() - if (_useSsl) { - var tls = TlsClientConfig() - tls.verifyMode = CertificateVerifyMode.TrustAll - let host = extractHost(_endpoint) - if (!host.isEmpty()) { - tls.serverName = Some(host) - } - builder.tlsConfig(tls) + private func createClient(): Client { + let builder = ClientBuilder(). + noProxy(). + readTimeout(Duration.second * 60) + if (_useSsl) { + var tls = TlsClientConfig() + tls.verifyMode = CertificateVerifyMode.TrustAll + let host = extractHost(_endpoint) + if (!host.isEmpty()) { + tls.serverName = Some(host) } + return builder.tlsConfig(tls).build() } + builder.build() + } + + /// 2xx 视为成功 + private static func isSuccess(status: UInt16): Bool { + status >= 200 && status < 300 } private func checkPath(path: String): Unit {