修复自查发现的门禁与体验问题:格式、超时态语义、压缩 CPU 边界
Project CI / AI game creator shell Rust crates (pull_request) Successful in 4m48s
Project CI / AI game creator shell Rust lane 1/2 (pull_request) Successful in 7m3s
Project CI / AI game creator shell Rust lane 2/2 (pull_request) Successful in 6m33s
Project CI / Frontend tests (pull_request) Successful in 3m24s
Project CI / Backend tests (pull_request) Successful in 7m29s
Project CI / AI game creator shell web tests (pull_request) Successful in 2m55s
Project CI / Repository checks (pull_request) Successful in 6m33s
Project CI / Native shell tests (pull_request) Successful in 9m0s
Project CI / AI game creator shell Rust crates (pull_request) Successful in 4m48s
Project CI / AI game creator shell Rust lane 1/2 (pull_request) Successful in 7m3s
Project CI / AI game creator shell Rust lane 2/2 (pull_request) Successful in 6m33s
Project CI / Frontend tests (pull_request) Successful in 3m24s
Project CI / Backend tests (pull_request) Successful in 7m29s
Project CI / AI game creator shell web tests (pull_request) Successful in 2m55s
Project CI / Repository checks (pull_request) Successful in 6m33s
Project CI / Native shell tests (pull_request) Successful in 9m0s
- 新增 Rust 测试补齐 cargo fmt;ESLint 按仓库约定收口:常量与解析函数拆到 platformGameLoadingSurfaceModel.ts,删除 GAME_PLAY_STARTUP_TIMEOUT_MS 常量别名(测试直接用共享常量) - 共享加载面新增 stalled:宿主判定等待不再推进时停掉进度动画并把 aria-busy 置回 false(去掉与 role=status 重复的 aria-live) - 后台试玩超时时传入 stalled,避免「一边转圈一边说超时」 - Accept-Encoding 权重参数名改为大小写不敏感(RFC 9110 §12.5.3),GZIP;Q=0 视为拒绝并补用例 - 发行资源压缩分级:≥2 MiB 改用 level 1(zlib 端实测约 1/3 耗时、压缩比差约 3%),小文件仍用 level 6 - 文档:主规范补压缩与条件请求契约(并写明 60 秒窗口与撤销窗口不放宽)、nginx README 修正 Pingora 压缩关闭时的口径、pitfalls 补 CPU 边界与措辞修正
This commit is contained in:
@@ -927,11 +927,16 @@ fn accepts_gzip_encoding(header: Option<&HeaderValue>) -> bool {
|
||||
return false;
|
||||
}
|
||||
!parts.any(|parameter| {
|
||||
parameter
|
||||
.trim()
|
||||
.strip_prefix("q=")
|
||||
.and_then(|value| value.trim().parse::<f32>().ok())
|
||||
.is_some_and(|quality| quality <= 0.0)
|
||||
// 权重参数名大小写不敏感(RFC 9110 §12.5.3):`Q=0` 与 `q=0` 一样是明确拒绝。
|
||||
let parameter = parameter.trim();
|
||||
let Some((name, value)) = parameter.split_once('=') else {
|
||||
return false;
|
||||
};
|
||||
name.eq_ignore_ascii_case("q")
|
||||
&& value
|
||||
.trim()
|
||||
.parse::<f32>()
|
||||
.is_ok_and(|quality| quality <= 0.0)
|
||||
})
|
||||
})
|
||||
}
|
||||
@@ -945,8 +950,21 @@ fn is_compressible_release_content_type(content_type: &str) -> bool {
|
||||
|| content_type.contains("application/wasm")
|
||||
}
|
||||
|
||||
/// 超过这个体积改用更快的压缩级别。
|
||||
///
|
||||
/// 单文件上限是 64 MiB,而发行网关是公开无鉴权端点:release 构建下 level 6 实测
|
||||
/// 1.3 MiB→10 ms、8 MiB→59 ms、64 MiB→522 ms 纯 CPU,每请求重算会占满 worker 线程。
|
||||
/// 大文件改用 level 1(zlib 端实测约为 level 6 的 1/3 耗时、压缩比只差约 3%),
|
||||
/// 小文件仍用 level 6 拿更好的比例。
|
||||
const RELEASE_COMPRESSION_FAST_ABOVE_BYTES: usize = 2 * 1024 * 1024;
|
||||
|
||||
fn gzip_release_asset(content: &[u8]) -> Option<Vec<u8>> {
|
||||
let mut encoder = GzEncoder::new(Vec::new(), GzipCompression::new(6));
|
||||
let level = if content.len() >= RELEASE_COMPRESSION_FAST_ABOVE_BYTES {
|
||||
GzipCompression::fast()
|
||||
} else {
|
||||
GzipCompression::new(6)
|
||||
};
|
||||
let mut encoder = GzEncoder::new(Vec::new(), level);
|
||||
encoder.write_all(content).ok()?;
|
||||
encoder.finish().ok()
|
||||
}
|
||||
@@ -4933,8 +4951,14 @@ mod tests {
|
||||
#[test]
|
||||
fn release_asset_etag_is_scoped_to_version_and_path() {
|
||||
let etag = release_asset_etag("version_1", "assets/phaser.min.js");
|
||||
assert_eq!(etag, release_asset_etag("version_1", "assets/phaser.min.js"));
|
||||
assert_ne!(etag, release_asset_etag("version_2", "assets/phaser.min.js"));
|
||||
assert_eq!(
|
||||
etag,
|
||||
release_asset_etag("version_1", "assets/phaser.min.js")
|
||||
);
|
||||
assert_ne!(
|
||||
etag,
|
||||
release_asset_etag("version_2", "assets/phaser.min.js")
|
||||
);
|
||||
assert_ne!(etag, release_asset_etag("version_1", "assets/game.js"));
|
||||
assert!(etag.starts_with('"'), "ETag 必须是带引号的实体标记:{etag}");
|
||||
}
|
||||
@@ -4942,7 +4966,13 @@ mod tests {
|
||||
#[test]
|
||||
fn release_asset_conditional_request_matches_lists_and_weak_validators() {
|
||||
let etag = "\"abc\"";
|
||||
for accepted in ["*", "\"abc\"", "W/\"abc\"", "\"zzz\", \"abc\"", " W/\"abc\" "] {
|
||||
for accepted in [
|
||||
"*",
|
||||
"\"abc\"",
|
||||
"W/\"abc\"",
|
||||
"\"zzz\", \"abc\"",
|
||||
" W/\"abc\" ",
|
||||
] {
|
||||
assert!(
|
||||
if_none_match_matches(
|
||||
Some(&HeaderValue::from_str(accepted).expect("header")),
|
||||
@@ -4967,17 +4997,21 @@ mod tests {
|
||||
fn release_asset_gzip_acceptance_honours_quality_zero() {
|
||||
for accepted in ["gzip", "GZIP", "*", "br, gzip;q=0.8", "deflate, gzip"] {
|
||||
assert!(
|
||||
accepts_gzip_encoding(Some(
|
||||
&HeaderValue::from_str(accepted).expect("header")
|
||||
)),
|
||||
accepts_gzip_encoding(Some(&HeaderValue::from_str(accepted).expect("header"))),
|
||||
"应接受 gzip:{accepted}"
|
||||
);
|
||||
}
|
||||
for rejected in ["", "br", "gzip;q=0", "*;q=0.0", "identity"] {
|
||||
for rejected in [
|
||||
"",
|
||||
"br",
|
||||
"gzip;q=0",
|
||||
"*;q=0.0",
|
||||
"identity",
|
||||
"gzip;Q=0",
|
||||
"GZIP;Q=0.000",
|
||||
] {
|
||||
assert!(
|
||||
!accepts_gzip_encoding(Some(
|
||||
&HeaderValue::from_str(rejected).expect("header")
|
||||
)),
|
||||
!accepts_gzip_encoding(Some(&HeaderValue::from_str(rejected).expect("header"))),
|
||||
"不应接受 gzip:{rejected}"
|
||||
);
|
||||
}
|
||||
@@ -5125,9 +5159,15 @@ mod tests {
|
||||
},
|
||||
)
|
||||
.expect("首次响应");
|
||||
assert_eq!(served.headers().get(header::ETAG).expect("ETag"), etag.as_str());
|
||||
assert_eq!(
|
||||
served.headers().get(header::CACHE_CONTROL).expect("缓存策略"),
|
||||
served.headers().get(header::ETAG).expect("ETag"),
|
||||
etag.as_str()
|
||||
);
|
||||
assert_eq!(
|
||||
served
|
||||
.headers()
|
||||
.get(header::CACHE_CONTROL)
|
||||
.expect("缓存策略"),
|
||||
cache_control
|
||||
);
|
||||
|
||||
@@ -5145,7 +5185,10 @@ mod tests {
|
||||
.expect("条件请求");
|
||||
assert_eq!(not_modified.status(), StatusCode::NOT_MODIFIED);
|
||||
assert_eq!(
|
||||
not_modified.headers().get(header::CACHE_CONTROL).expect("缓存策略"),
|
||||
not_modified
|
||||
.headers()
|
||||
.get(header::CACHE_CONTROL)
|
||||
.expect("缓存策略"),
|
||||
cache_control
|
||||
);
|
||||
assert_eq!(
|
||||
|
||||
Reference in New Issue
Block a user