fix(protocol): fence gateway work recovery
This commit is contained in:
@@ -69,6 +69,21 @@ let audio = try AudioProfile(codec: "opus", sampleRateHz: 48000, channels: 2, ch
|
||||
let display = try DisplayMode(resolutionWidth: 2560, resolutionHeight: 1440, fps: 120)
|
||||
let adjustment = try SessionAdjustment(displayReason: "none", bitrateReason: "none")
|
||||
let descriptor = try SelectedSessionDescriptor(videoProfile: video, audioProfile: audio, displayMode: display, bitrateTargetKbps: 40000, bitrateMaximumKbps: 50000, adjustment: adjustment, mediaTimestampBasis: "gateway-send-wall-clock-ms")
|
||||
let operationId = "12345678-1234-1234-1234-123456789abc"
|
||||
_ = try GatewayQualityWorkRequest(version: "1", sessionId: "session", gatewayId: "gateway", reconnectSequence: 2, acquisition: "poll", operationId: nil, revision: nil, leaseGeneration: nil, currentAppliedRevision: nil)
|
||||
_ = try GatewayQualityWorkRequest(version: "1", sessionId: "session", gatewayId: "gateway", reconnectSequence: 2, acquisition: "prompt", operationId: operationId, revision: 7, leaseGeneration: nil, currentAppliedRevision: nil)
|
||||
_ = try GatewayQualityWorkRequest(version: "1", sessionId: "session", gatewayId: "gateway", reconnectSequence: 2, acquisition: "observation", operationId: operationId, revision: 7, leaseGeneration: 3, currentAppliedRevision: 6)
|
||||
do {
|
||||
_ = try GatewayQualityWorkRequest(version: "1", sessionId: "session", gatewayId: "gateway", reconnectSequence: 2, acquisition: "poll", operationId: operationId, revision: 7, leaseGeneration: nil, currentAppliedRevision: nil)
|
||||
fatalError("poll accepted unknown operation coordinates")
|
||||
} catch { }
|
||||
_ = try GatewayQualityAck(version: "1", sessionId: "session", gatewayId: "gateway", reconnectSequence: 2, operationId: operationId, revision: 7, leaseGeneration: 3, outcome: "applied", currentAppliedRevision: 7, failureCode: nil)
|
||||
_ = try GatewayQualityAck(version: "1", sessionId: "session", gatewayId: "gateway", reconnectSequence: 2, operationId: operationId, revision: 7, leaseGeneration: 3, outcome: "proven_prior", currentAppliedRevision: 6, failureCode: nil)
|
||||
_ = try GatewayQualityAck(version: "1", sessionId: "session", gatewayId: "gateway", reconnectSequence: 2, operationId: operationId, revision: 7, leaseGeneration: 3, outcome: "unknown", currentAppliedRevision: nil, failureCode: nil)
|
||||
do {
|
||||
_ = try GatewayQualityAck(version: "1", sessionId: "session", gatewayId: "gateway", reconnectSequence: 2, operationId: operationId, revision: 7, leaseGeneration: 3, outcome: "applied", currentAppliedRevision: 6, failureCode: nil)
|
||||
fatalError("applied ack accepted a contradictory revision")
|
||||
} catch { }
|
||||
let capability = try CapabilityProfile(
|
||||
transport: "quic-tls13", framing: "datagram-v1", media: "encoded",
|
||||
sourceRateControl: "server", videoProfiles: [video], audioProfiles: [audio]
|
||||
@@ -191,6 +206,19 @@ do {
|
||||
_ = try CapabilityProfile.decodeJSON(duplicateCapability)
|
||||
fatalError("capability accepted duplicate JSON keys")
|
||||
} catch { }
|
||||
do {
|
||||
_ = try CapabilityProfile.decodeJSON(Data(repeating: 32, count: 1_048_577))
|
||||
fatalError("capability accepted oversized JSON")
|
||||
} catch let error as ContractValidationError {
|
||||
guard error.code == "payload_too_large" else { fatalError("oversized JSON was not rejected before parsing") }
|
||||
}
|
||||
let deeplyNested = Data((String(repeating: "[", count: 65) + "null" + String(repeating: "]", count: 65)).utf8)
|
||||
do {
|
||||
_ = try CapabilityProfile.decodeJSON(deeplyNested)
|
||||
fatalError("capability accepted over-deep JSON")
|
||||
} catch let error as ContractValidationError {
|
||||
guard error.code == "nesting_too_deep" else { fatalError("over-deep JSON was not rejected before decoding") }
|
||||
}
|
||||
for field in ["version", "session_id", "gateway_id", "audience", "reconnect_sequence", "expires_at", "capabilities", "selected_descriptor"] {
|
||||
var missing = clientAuthorityObject
|
||||
missing.removeValue(forKey: field)
|
||||
@@ -385,6 +413,15 @@ fn main() {
|
||||
let display = DisplayMode::new(2560, 1440, 120).unwrap();
|
||||
let adjustment = SessionAdjustment::new("none".into(), "none".into()).unwrap();
|
||||
let descriptor = SelectedSessionDescriptor::new(video.clone(), audio.clone(), display.clone(), 40000, 50000, adjustment, "gateway-send-wall-clock-ms".into()).unwrap();
|
||||
let operation_id = "12345678-1234-1234-1234-123456789abc".to_string();
|
||||
assert!(GatewayQualityWorkRequest::new("1".into(), "session".into(), "gateway".into(), 2, "poll".into(), None, None, None, None).is_ok());
|
||||
assert!(GatewayQualityWorkRequest::new("1".into(), "session".into(), "gateway".into(), 2, "prompt".into(), Some(operation_id.clone()), Some(7), None, None).is_ok());
|
||||
assert!(GatewayQualityWorkRequest::new("1".into(), "session".into(), "gateway".into(), 2, "observation".into(), Some(operation_id.clone()), Some(7), Some(3), Some(6)).is_ok());
|
||||
assert!(GatewayQualityWorkRequest::new("1".into(), "session".into(), "gateway".into(), 2, "poll".into(), Some(operation_id.clone()), Some(7), None, None).is_err());
|
||||
assert!(GatewayQualityAck::new("1".into(), "session".into(), "gateway".into(), 2, operation_id.clone(), 7, 3, "applied".into(), Some(7), None).is_ok());
|
||||
assert!(GatewayQualityAck::new("1".into(), "session".into(), "gateway".into(), 2, operation_id.clone(), 7, 3, "proven_prior".into(), Some(6), None).is_ok());
|
||||
assert!(GatewayQualityAck::new("1".into(), "session".into(), "gateway".into(), 2, operation_id.clone(), 7, 3, "unknown".into(), None, None).is_ok());
|
||||
assert!(GatewayQualityAck::new("1".into(), "session".into(), "gateway".into(), 2, operation_id, 7, 3, "applied".into(), Some(6), None).is_err());
|
||||
let capabilities = CapabilityProfile::new(
|
||||
"quic-tls13".into(), "datagram-v1".into(), "encoded".into(),
|
||||
"server".into(), vec![video.clone()], vec![audio.clone()],
|
||||
@@ -544,22 +581,68 @@ fn main() {
|
||||
)
|
||||
run(["rustc", str(rust), "-o", str(workspace / "rust-contracts")], ROOT)
|
||||
run([str(workspace / "rust-contracts")], ROOT)
|
||||
expected_protobuf_fields = [
|
||||
("version", 1),
|
||||
("session_id", 2),
|
||||
("gateway_id", 3),
|
||||
("audience", 4),
|
||||
("reconnect_sequence", 5),
|
||||
("expires_at", 6),
|
||||
("capabilities", 7),
|
||||
("selected_descriptor", 8),
|
||||
]
|
||||
actual_protobuf_fields = protobuf_message_fields("ClientSessionAuthority")
|
||||
if actual_protobuf_fields != expected_protobuf_fields:
|
||||
raise RuntimeError(
|
||||
f"ClientSessionAuthority protobuf fields = {actual_protobuf_fields}; "
|
||||
f"want {expected_protobuf_fields}"
|
||||
)
|
||||
expected_protobuf_messages = {
|
||||
"ClientSessionAuthority": [
|
||||
("version", 1),
|
||||
("session_id", 2),
|
||||
("gateway_id", 3),
|
||||
("audience", 4),
|
||||
("reconnect_sequence", 5),
|
||||
("expires_at", 6),
|
||||
("capabilities", 7),
|
||||
("selected_descriptor", 8),
|
||||
],
|
||||
"GatewayQualityWorkRequest": [
|
||||
("version", 1),
|
||||
("session_id", 2),
|
||||
("gateway_id", 3),
|
||||
("reconnect_sequence", 4),
|
||||
("operation_id", 5),
|
||||
("revision", 6),
|
||||
("current_applied_revision", 7),
|
||||
("acquisition", 8),
|
||||
("lease_generation", 9),
|
||||
],
|
||||
"GatewayQualityWork": [
|
||||
("version", 1),
|
||||
("session_id", 2),
|
||||
("gateway_id", 3),
|
||||
("reconnect_sequence", 4),
|
||||
("operation_id", 5),
|
||||
("revision", 6),
|
||||
("lease_expires_at", 7),
|
||||
("selected_descriptor", 8),
|
||||
("current_applied_revision", 9),
|
||||
("lease_generation", 10),
|
||||
],
|
||||
"GatewayQualityAck": [
|
||||
("version", 1),
|
||||
("session_id", 2),
|
||||
("gateway_id", 3),
|
||||
("reconnect_sequence", 4),
|
||||
("operation_id", 5),
|
||||
("revision", 6),
|
||||
("outcome", 7),
|
||||
("current_applied_revision", 8),
|
||||
("failure_code", 9),
|
||||
("lease_generation", 10),
|
||||
],
|
||||
"GatewayStopWorkRequest": [
|
||||
("version", 1),
|
||||
("session_id", 2),
|
||||
("gateway_id", 3),
|
||||
("reconnect_sequence", 4),
|
||||
("operation_id", 5),
|
||||
("acquisition", 6),
|
||||
],
|
||||
}
|
||||
for message, expected_protobuf_fields in expected_protobuf_messages.items():
|
||||
actual_protobuf_fields = protobuf_message_fields(message)
|
||||
if actual_protobuf_fields != expected_protobuf_fields:
|
||||
raise RuntimeError(
|
||||
f"{message} protobuf fields = {actual_protobuf_fields}; "
|
||||
f"want {expected_protobuf_fields}"
|
||||
)
|
||||
rust_unknown = workspace / "unknown.rs"
|
||||
shutil.copyfile(ROOT / "gen/rust/protocol.rs", rust_unknown)
|
||||
with rust_unknown.open("a", encoding="utf-8") as output:
|
||||
|
||||
Reference in New Issue
Block a user