refactor: 移除 soulsoft_serialization / soulsoft_net_http 依赖——全部 HTTP 改 stdx.net.http

- 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 全链路实测通过(上传/预签名/删除)
This commit is contained in:
2026-08-18 01:56:32 +08:00
parent 5c30f201ad
commit aa2af7aee1
4 changed files with 124 additions and 84 deletions
+12 -13
View File
@@ -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"}
-2
View File
@@ -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" }
+51 -24
View File
@@ -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}&timestamp=${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<SimApiBaseResponse>(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<Byte>(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()
}
// ===== 群组相关 =====
/**
+61 -45
View File
@@ -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<Byte>(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<UInt8>(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 {