Compare commits

...
Author SHA1 Message Date
Brandon Thomas 3b7614a8cd more tests 2026-06-12 21:46:25 -04:00
Brandon Thomas 9209ba12e8 also bump 2026-06-12 21:37:34 -04:00
Brandon Thomas aefac2fa7b upgrade actix 2026-06-12 21:18:02 -04:00
19 changed files with 204 additions and 883 deletions
Generated
+127 -157
View File
@@ -2,31 +2,6 @@
# It is not intended for manual editing.
version = 4
[[package]]
name = "actix"
version = "0.13.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "de7fa236829ba0841304542f7614c42b80fca007455315c45c785ccfa873a85b"
dependencies = [
"actix-macros",
"actix-rt",
"actix_derive",
"bitflags 2.11.0",
"bytes",
"crossbeam-channel",
"futures-core",
"futures-sink",
"futures-task",
"futures-util",
"log",
"once_cell",
"parking_lot 0.12.5",
"pin-project-lite",
"smallvec",
"tokio",
"tokio-util",
]
[[package]]
name = "actix-codec"
version = "0.5.2"
@@ -46,13 +21,13 @@ dependencies = [
[[package]]
name = "actix-cors"
version = "0.6.4"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b340e9cfa5b08690aae90fb61beb44e9b06f44fe3d0f93781aaa58cfba86245e"
checksum = "daa239b93927be1ff123eebada5a3ff23e89f0124ccb8609234e5103d5a5ae6d"
dependencies = [
"actix-utils",
"actix-web",
"derive_more 0.99.17",
"derive_more 2.0.1",
"futures-util",
"log",
"once_cell",
@@ -61,25 +36,25 @@ dependencies = [
[[package]]
name = "actix-http"
version = "3.7.0"
version = "3.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4eb9843d84c775696c37d9a418bbb01b932629d01870722c0f13eb3f95e2536d"
checksum = "93acb4a42f64936f9b8cae4a433b237599dd6eb6ed06124eb67132ef8cc90662"
dependencies = [
"actix-codec",
"actix-rt",
"actix-service",
"actix-utils",
"ahash 0.8.12",
"base64 0.22.1",
"bitflags 2.11.0",
"brotli 6.0.0",
"brotli",
"bytes",
"bytestring",
"derive_more 0.99.17",
"derive_more 2.0.1",
"encoding_rs",
"flate2",
"foldhash 0.1.5",
"futures-core",
"h2 0.3.26",
"h2 0.3.27",
"http 0.2.11",
"httparse",
"httpdate",
@@ -89,8 +64,8 @@ dependencies = [
"mime",
"percent-encoding",
"pin-project-lite",
"rand 0.8.5",
"sha1 0.10.6",
"rand 0.10.1",
"sha1 0.11.0",
"smallvec",
"tokio",
"tokio-util",
@@ -110,14 +85,13 @@ dependencies = [
[[package]]
name = "actix-multipart"
version = "0.6.2"
version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d974dd6c4f78d102d057c672dcf6faa618fafa9df91d44f9c466688fc1275a3a"
checksum = "d5118a26dee7e34e894f7e85aa0ee5080ae4c18bf03c0e30d49a80e418f00a53"
dependencies = [
"actix-multipart-derive",
"actix-utils",
"actix-web",
"bytes",
"derive_more 0.99.17",
"futures-core",
"futures-util",
@@ -136,9 +110,9 @@ dependencies = [
[[package]]
name = "actix-multipart-derive"
version = "0.6.1"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0a0a77f836d869f700e5b47ac7c3c8b9c8bc82e4aec861954c6198abee3ebd4d"
checksum = "e11eb847f49a700678ea2fa73daeb3208061afa2b9d1a8527c03390f4c4a1c6b"
dependencies = [
"darling 0.20.10",
"parse-size",
@@ -149,9 +123,9 @@ dependencies = [
[[package]]
name = "actix-router"
version = "0.5.3"
version = "0.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13d324164c51f63867b57e73ba5936ea151b8a41a1d23d1031eeb9f70d0236f8"
checksum = "14f8c75c51892f18d9c46150c5ac7beb81c95f78c8b83a634d49f4ca32551fe7"
dependencies = [
"bytestring",
"cfg-if",
@@ -164,9 +138,9 @@ dependencies = [
[[package]]
name = "actix-rt"
version = "2.10.0"
version = "2.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24eda4e2a6e042aa4e55ac438a2ae052d3b5da0ecf83d7411e1a368946925208"
checksum = "92589714878ca59a7626ea19734f0e07a6a875197eec751bb5d3f99e64998c63"
dependencies = [
"actix-macros",
"futures-core",
@@ -175,16 +149,16 @@ dependencies = [
[[package]]
name = "actix-server"
version = "2.4.0"
version = "2.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b02303ce8d4e8be5b855af6cf3c3a08f3eff26880faad82bab679c22d3650cb5"
checksum = "a65064ea4a457eaf07f2fba30b4c695bf43b721790e9530d26cb6f9019ff7502"
dependencies = [
"actix-rt",
"actix-service",
"actix-utils",
"futures-core",
"futures-util",
"mio 0.8.11",
"mio",
"socket2 0.5.10",
"tokio",
"tracing",
@@ -212,9 +186,9 @@ dependencies = [
[[package]]
name = "actix-web"
version = "4.7.0"
version = "4.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5d6316df3fa569627c98b12557a8b6ff0674e5be4bb9b5e4ae2550ddb4964ed6"
checksum = "ff87453bc3b56e9b2b23c1cc0b1be8797184accf51d2abe0f8a33ec275d316bf"
dependencies = [
"actix-codec",
"actix-http",
@@ -225,15 +199,16 @@ dependencies = [
"actix-service",
"actix-utils",
"actix-web-codegen",
"ahash 0.8.12",
"bytes",
"bytestring",
"cfg-if",
"cookie 0.16.2",
"derive_more 0.99.17",
"derive_more 2.0.1",
"encoding_rs",
"foldhash 0.1.5",
"futures-core",
"futures-util",
"impl-more 0.1.6",
"itoa 1.0.11",
"language-tags",
"log",
@@ -246,29 +221,12 @@ dependencies = [
"serde_json",
"serde_urlencoded",
"smallvec",
"socket2 0.5.10",
"socket2 0.6.2",
"time",
"tracing",
"url",
]
[[package]]
name = "actix-web-actors"
version = "4.3.1+deprecated"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f98c5300b38fd004fe7d2a964f9a90813fdbe8a81fed500587e78b1b71c6f980"
dependencies = [
"actix",
"actix-codec",
"actix-http",
"actix-web",
"bytes",
"bytestring",
"futures-core",
"pin-project-lite",
"tokio",
"tokio-util",
]
[[package]]
name = "actix-web-codegen"
version = "4.3.0"
@@ -283,9 +241,9 @@ dependencies = [
[[package]]
name = "actix-web-lab"
version = "0.20.2"
version = "0.26.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7675c1a84eec1b179c844cdea8488e3e409d8e4984026e92fa96c87dd86f33c6"
checksum = "1a89606b86fd2f8c0ddd72ef0be171e58c919ee0b6914aa7b52354063e5ee361"
dependencies = [
"actix-http",
"actix-router",
@@ -295,37 +253,36 @@ dependencies = [
"actix-web-lab-derive",
"ahash 0.8.12",
"arc-swap",
"async-trait",
"bytes",
"bytestring",
"csv",
"derive_more 0.99.17",
"derive_more 2.0.1",
"form_urlencoded",
"futures-core",
"futures-util",
"http 0.2.11",
"impl-more",
"itertools 0.12.1",
"impl-more 0.3.1",
"itertools 0.14.0",
"local-channel",
"mediatype",
"mime",
"once_cell",
"pin-project-lite",
"regex",
"serde",
"serde_html_form",
"serde_json",
"serde_path_to_error",
"tokio",
"tokio-stream",
"tracing",
"url",
]
[[package]]
name = "actix-web-lab-derive"
version = "0.20.0"
version = "0.26.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9aa0b287c8de4a76b691f29dbb5451e8dd5b79d777eaf87350c9b0cbfdb5e968"
checksum = "2ac4d6e04e97fe707286509b4f338e99c5fb7249c770e1da074af5e27faa96b3"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.117",
]
@@ -363,17 +320,6 @@ dependencies = [
"workspace-build-acceleration",
]
[[package]]
name = "actix_derive"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7c7db3d5a9718568e4cf4a537cfd7070e6e6ff7481510d0237fb529ac850f6d3"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.117",
]
[[package]]
name = "actix_helpers"
version = "0.0.1"
@@ -405,7 +351,7 @@ checksum = "9e8b47f52ea9bae42228d07ec09eb676433d7c4ed1ebdf0f1d1c29ed446f1ab8"
dependencies = [
"cfg-if",
"cipher 0.3.0",
"cpufeatures",
"cpufeatures 0.2.12",
"opaque-debug",
]
@@ -417,7 +363,7 @@ checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0"
dependencies = [
"cfg-if",
"cipher 0.4.4",
"cpufeatures",
"cpufeatures 0.2.12",
]
[[package]]
@@ -893,7 +839,7 @@ version = "0.4.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ddb939d66e4ae03cee6091612804ba446b12878410cfa17f785f4dd67d4014e8"
dependencies = [
"brotli 8.0.2",
"brotli",
"flate2",
"futures-core",
"memchr",
@@ -1777,17 +1723,6 @@ dependencies = [
"syn 2.0.117",
]
[[package]]
name = "brotli"
version = "6.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "74f7971dbd9326d58187408ab83117d8ac1bb9c17b085fdacd1cf2f598719b6b"
dependencies = [
"alloc-no-stdlib",
"alloc-stdlib",
"brotli-decompressor 4.0.1",
]
[[package]]
name = "brotli"
version = "8.0.2"
@@ -1796,17 +1731,7 @@ checksum = "4bd8b9603c7aa97359dbd97ecf258968c95f3adddd6db2f7e7a5bef101c84560"
dependencies = [
"alloc-no-stdlib",
"alloc-stdlib",
"brotli-decompressor 5.0.0",
]
[[package]]
name = "brotli-decompressor"
version = "4.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a45bd2e4095a8b518033b128020dd4a55aab1c0a381ba4404a472630f4bc362"
dependencies = [
"alloc-no-stdlib",
"alloc-stdlib",
"brotli-decompressor",
]
[[package]]
@@ -2155,6 +2080,17 @@ version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
[[package]]
name = "chacha20"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6f8d983286843e49675a4b7a2d174efe136dc93a18d69130dd18198a6c167601"
dependencies = [
"cfg-if",
"cpufeatures 0.3.0",
"rand_core 0.10.1",
]
[[package]]
name = "chrono"
version = "0.4.44"
@@ -2733,6 +2669,15 @@ dependencies = [
"libc",
]
[[package]]
name = "cpufeatures"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b2a41393f66f16b0823bb79094d54ac5fbd34ab292ddafb9a0456ac9f87d201"
dependencies = [
"libc",
]
[[package]]
name = "crc"
version = "3.0.1"
@@ -4686,6 +4631,7 @@ dependencies = [
"js-sys",
"libc",
"r-efi",
"rand_core 0.10.1",
"wasip2",
"wasip3",
"wasm-bindgen",
@@ -5062,9 +5008,9 @@ dependencies = [
[[package]]
name = "h2"
version = "0.3.26"
version = "0.3.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8"
checksum = "0beca50380b1fc32983fc1cb4587bfa4bb9e78fc259aad4a0032d2080309222d"
dependencies = [
"bytes",
"fnv",
@@ -5503,7 +5449,7 @@ dependencies = [
"futures-channel",
"futures-core",
"futures-util",
"h2 0.3.26",
"h2 0.3.27",
"http 0.2.11",
"http-body 0.4.6",
"httparse",
@@ -5888,6 +5834,12 @@ version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "206ca75c9c03ba3d4ace2460e57b189f39f43de612c2f85836e65c929701bb2d"
[[package]]
name = "impl-more"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "35a84fd5aa25fae5c0f4a33d9cac2ca017fc622cbd089be2229993514990f870"
[[package]]
name = "indexmap"
version = "1.9.3"
@@ -6102,6 +6054,15 @@ dependencies = [
"either",
]
[[package]]
name = "itertools"
version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285"
dependencies = [
"either",
]
[[package]]
name = "itoa"
version = "0.4.8"
@@ -6349,7 +6310,7 @@ version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cb26cec98cce3a3d96cbb7bced3c4b16e3d13f27ec56dbd62cbc8f39cfb9d653"
dependencies = [
"cpufeatures",
"cpufeatures 0.2.12",
]
[[package]]
@@ -6983,12 +6944,6 @@ dependencies = [
"workspace-build-acceleration",
]
[[package]]
name = "mediatype"
version = "0.19.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bf0bc9784973713e4a90d515a4302991ca125a7c4516951cb607f2298cb757e5"
[[package]]
name = "memchr"
version = "2.7.4"
@@ -7189,18 +7144,6 @@ dependencies = [
"simd-adler32",
]
[[package]]
name = "mio"
version = "0.8.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c"
dependencies = [
"libc",
"log",
"wasi 0.11.0+wasi-snapshot-preview1",
"windows-sys 0.48.0",
]
[[package]]
name = "mio"
version = "1.0.2"
@@ -7209,6 +7152,7 @@ checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec"
dependencies = [
"hermit-abi 0.3.9",
"libc",
"log",
"wasi 0.11.0+wasi-snapshot-preview1",
"windows-sys 0.52.0",
]
@@ -9268,6 +9212,17 @@ dependencies = [
"rand_core 0.9.3",
]
[[package]]
name = "rand"
version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2e8e8bcc7961af1fdac401278c6a831614941f6164ee3bf4ce61b7edb162207"
dependencies = [
"chacha20",
"getrandom 0.4.1",
"rand_core 0.10.1",
]
[[package]]
name = "rand_chacha"
version = "0.2.2"
@@ -9340,6 +9295,12 @@ dependencies = [
"getrandom 0.3.1",
]
[[package]]
name = "rand_core"
version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "63b8176103e19a2643978565ca18b50549f6101881c443590420e4dc998a3c69"
[[package]]
name = "rand_hc"
version = "0.2.0"
@@ -9661,7 +9622,7 @@ dependencies = [
"encoding_rs",
"futures-core",
"futures-util",
"h2 0.3.26",
"h2 0.3.27",
"http 0.2.11",
"http-body 0.4.6",
"hyper 0.14.28",
@@ -10685,15 +10646,15 @@ dependencies = [
[[package]]
name = "serde_html_form"
version = "0.2.3"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "224e6a14f315852940f3ec103125aa6482f0e224732ed91ed3330ed633077c34"
checksum = "0946d52b4b7e28823148aebbeceb901012c595ad737920d504fa8634bb099e6f"
dependencies = [
"form_urlencoded",
"indexmap 2.13.0",
"itoa 1.0.11",
"ryu",
"serde",
"serde_core",
]
[[package]]
@@ -10957,10 +10918,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba"
dependencies = [
"cfg-if",
"cpufeatures",
"cpufeatures 0.2.12",
"digest 0.10.7",
]
[[package]]
name = "sha1"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aacc4cc499359472b4abe1bf11d0b12e688af9a805fa5e3016f9a386dc2d0214"
dependencies = [
"cfg-if",
"cpufeatures 0.3.0",
"digest 0.11.2",
]
[[package]]
name = "sha1_smol"
version = "1.0.0"
@@ -10975,7 +10947,7 @@ checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800"
dependencies = [
"block-buffer 0.9.0",
"cfg-if",
"cpufeatures",
"cpufeatures 0.2.12",
"digest 0.9.0",
"opaque-debug",
]
@@ -10987,7 +10959,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8"
dependencies = [
"cfg-if",
"cpufeatures",
"cpufeatures 0.2.12",
"digest 0.10.7",
]
@@ -10998,7 +10970,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7c5f3b1e2dc8aad28310d8410bd4d7e180eca65fca176c52ab00d364475d0024"
dependencies = [
"cfg-if",
"cpufeatures",
"cpufeatures 0.2.12",
"digest 0.11.2",
]
@@ -11519,12 +11491,10 @@ checksum = "ef5430c8e36b713e13b48a9f709cc21e046723fe44ce34587b73a830203b533e"
name = "storyteller-web"
version = "0.0.1"
dependencies = [
"actix",
"actix-http",
"actix-multipart",
"actix-service",
"actix-web",
"actix-web-actors",
"actix-web-lab",
"actix_artcraft",
"actix_cors_configs",
@@ -12370,7 +12340,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ab3a62cf2e6253936a8b267c2e95839674e7439f104fa96ad0025e149d54d8a"
dependencies = [
"base64 0.22.1",
"brotli 8.0.2",
"brotli",
"ico",
"json-patch",
"plist",
@@ -12606,7 +12576,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41a3852fdf9a4f8fbeaa63dc3e9a85284dd6ef7200751f0bd66ceee30c93f212"
dependencies = [
"anyhow",
"brotli 8.0.2",
"brotli",
"cargo_metadata",
"ctor",
"dunce",
@@ -12894,7 +12864,7 @@ checksum = "27ad5e34374e03cfffefc301becb44e9dc3c17584f414349ebe29ed26661822d"
dependencies = [
"bytes",
"libc",
"mio 1.0.2",
"mio",
"parking_lot 0.12.5",
"pin-project-lite",
"signal-hook-registry",
@@ -12968,9 +12938,9 @@ dependencies = [
[[package]]
name = "tokio-stream"
version = "0.1.16"
version = "0.1.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4f4e6ce100d0eb49a2734f8c0812bcd324cf357d21810932c5df6b96ef2b86f1"
checksum = "32da49809aab5c3bc678af03902d4ccddea2a87d028d86392a4b1560c6906c70"
dependencies = [
"futures-core",
"pin-project-lite",
@@ -13164,7 +13134,7 @@ dependencies = [
"axum",
"base64 0.21.7",
"bytes",
"h2 0.3.26",
"h2 0.3.27",
"http 0.2.11",
"http-body 0.4.6",
"hyper 0.14.28",
@@ -15269,7 +15239,7 @@ checksum = "f79937f6c4df65b3f6f78715b9de2977afe9ee3b3436483c7949a24511e25935"
dependencies = [
"ahash 0.8.12",
"boring2",
"brotli 8.0.2",
"brotli",
"bytes",
"cookie 0.18.1",
"flate2",
+6 -8
View File
@@ -295,16 +295,14 @@ worldlabs_consumer_client = { path = "crates/api_clients/worldlabs_consumer_clie
# External libs
# Centrally pinned versions so that we can uniformly upgrade/downgrade versions across the workspace
# If a crate needs to use an older/newer version, the dependency should be managed in that crate's own Cargo.toml
actix = "0.13.5"
actix-cors = "0.6.4" # TODO(bt): Carefully upgrade this. CORS broke last time.
actix-http = "3.7.0"
actix-multipart = "0.6.2"
actix-rt = "2.10.0"
actix-cors = "0.7.1"
actix-http = "3.12.1"
actix-multipart = "0.7.2"
actix-rt = "2.11.0"
actix-service = "2.0.3"
actix-utils = "3.0.1"
actix-web = "4.7.0" # TODO(bt): We're behind and need to bump versions.
actix-web-actors = "4.3.1+deprecated"
actix-web-lab = "0.20.2"
actix-web = "4.13.0"
actix-web-lab = "0.26.0"
anyhow = "1.0.102"
assertor = "0.0.3"
base64 = "0.22.1"
+44 -1
View File
@@ -65,7 +65,13 @@ fn do_build_cors_config(is_production: bool) -> Cors {
}
// Remaining setup
cors.allowed_methods(vec!["GET", "POST", "PUT", "OPTIONS", "DELETE"])
//
// NB: actix-cors 0.7 flipped the `block_on_origin_mismatch` default to
// false (mismatched origins get a 200 without CORS headers and the browser
// enforces the block). We keep the long-standing 0.6 behavior of rejecting
// mismatched origins server-side with a 400.
cors.block_on_origin_mismatch(true)
.allowed_methods(vec!["GET", "POST", "PUT", "OPTIONS", "DELETE"])
.supports_credentials()
.allowed_headers(vec![
actix_http::header::ACCEPT,
@@ -82,6 +88,7 @@ fn do_build_cors_config(is_production: bool) -> Cors {
mod tests {
use server_environment::ServerEnvironment;
use crate::testing::assert_no_origin_header_ok;
use crate::testing::assert_origin_invalid;
use crate::testing::assert_origin_ok;
use crate::testing::assert_preflight_method_invalid;
@@ -89,6 +96,42 @@ mod tests {
use super::build_cors_config;
/// Every production origin that hosts a product (or IS a backend that
/// serves same-origin pages like swagger) must be allowed. This is the
/// guard against `block_on_origin_mismatch(true)` locking out a real
/// frontend after a CORS-crate upgrade.
#[actix_rt::test]
async fn test_production_product_and_backend_origins_are_allowed() {
let production_cors = build_cors_config(ServerEnvironment::Production);
let origins = [
// Backends (same-origin browser requests still carry an Origin header).
"https://api.fakeyou.com",
"https://api.storyteller.ai",
// Products
"https://fakeyou.com",
"https://storyteller.ai",
"https://app.getartcraft.com",
"https://getartcraft.com",
"https://www.getartcraft.com",
"https://artcraft.ai",
"https://www.artcraft.ai",
"https://artcraft-dashboard.netlify.app",
];
for origin in origins {
assert_origin_ok(&production_cors, origin).await;
}
}
/// Non-browser clients (curl, python API clients, server-to-server) send
/// no Origin header and must never be blocked, even with
/// `block_on_origin_mismatch(true)`.
#[actix_rt::test]
async fn test_requests_without_origin_header_are_not_blocked() {
let production_cors = build_cors_config(ServerEnvironment::Production);
assert_no_origin_header_ok(&production_cors).await;
}
#[actix_rt::test]
async fn test_preflight_allows_every_supported_method() {
let production_cors = build_cors_config(ServerEnvironment::Production);
@@ -38,6 +38,22 @@ pub (crate) async fn assert_preflight_method_invalid(cors: &Cors, hostname: &str
.is_equal_to(StatusCode::BAD_REQUEST);
}
/// Assert that a request WITHOUT an Origin header (curl, server-to-server,
/// native API clients) is never blocked by CORS, regardless of
/// `block_on_origin_mismatch`.
pub (crate) async fn assert_no_origin_header_ok(cors: &Cors) {
let cors = cors.new_transform(test::ok_service())
.await
.unwrap();
let request = TestRequest::default().to_srv_request();
let response = test::call_service(&cors, request).await;
asserting("Requests without an Origin header are not blocked")
.that(&response.status())
.is_equal_to(StatusCode::OK);
}
async fn make_test_request(cors: &Cors, hostname: &str) -> ServiceResponse<EitherBody<BoxBody>> {
let cors= cors.new_transform(test::ok_service())
.await
@@ -162,12 +162,10 @@ tempfile.workspace = true
# https://github.com/durch/rust-s3/pull/254
rust-s3.workspace = true
actix.workspace = true
actix-http.workspace = true
actix-multipart.workspace = true
actix-service.workspace = true
actix-web.workspace = true
actix-web-actors.workspace = true
actix-web-lab.workspace = true
# Crypto
@@ -30,7 +30,7 @@ pub async fn drain_multipart_request(mut multipart_payload: Multipart) -> Anyhow
let mut field_name = None;
let mut field_filename = None;
if let content_disposition = field.content_disposition() {
if let Some(content_disposition) = field.content_disposition() {
field_name = content_disposition.get_name()
.map(|s| s.to_string());
field_filename = content_disposition.get_filename() // NB: Only used for the file bytes.
@@ -36,7 +36,7 @@ pub async fn drain_multipart_request(mut multipart_payload: Multipart) -> Anyhow
let mut field_name = None;
let mut field_filename = None;
if let content_disposition = field.content_disposition() {
if let Some(content_disposition) = field.content_disposition() {
field_name = content_disposition.get_name()
.map(|s| s.to_string());
field_filename = content_disposition.get_filename() // NB: Only used for the file bytes.
@@ -1,272 +0,0 @@
use std::fmt;
use std::sync::Arc;
use std::time::Instant;
use actix::*;
use actix_web::error::ResponseError;
use actix_web::http::StatusCode;
use actix_web::web::{Json, Path};
use actix_web::{
web, App, Error, HttpRequest, HttpResponse, HttpServer, Responder,
};
use actix_web_actors::ws;
use chrono::{DateTime, Utc};
use log::error;
use redis::{Commands, RedisResult};
use utoipa::ToSchema;
use web::Data;
use enums::by_table::generic_inference_jobs::frontend_failure_category::FrontendFailureCategory;
use enums::common::job_status_plus::JobStatusPlus;
use mysql_queries::queries::generic_inference::web::get_inference_job_status::get_inference_job_status;
use mysql_queries::queries::generic_inference::web::job_status::GenericInferenceJobStatus;
use redis_schema::keys::inference_job::style_transfer_progress_key::StyleTransferProgressKey;
use redis_schema::payloads::inference_job::style_transfer_progress_state::InferenceProgressDetailsResponse;
use tokens::tokens::generic_inference_jobs::InferenceJobToken;
use crate::http_server::deprecated_endpoints::workflows::enqueue::{progress_tracker_server, progress_tracker_session};
use crate::http_server::endpoints::inference_job::utils::estimates::estimate_job_progress::estimate_job_progress;
use crate::http_server::endpoints::inference_job::utils::extractors::extract_polymorphic_inference_args::extract_polymorphic_inference_args;
use crate::http_server::web_utils::filter_model_name::maybe_filter_model_name;
use crate::http_server::web_utils::response_error_helpers::to_simple_json_error;
use crate::state::server_state::ServerState;
/// For certain jobs or job classes (eg. non-premium), we kill the jobs if the user hasn't
/// maintained a keepalive. This prevents wasted work when users who are unlikely to return
/// navigate away. Premium users have accounts and can always return to the site, so they
/// typically do not require keepalive.
const JOB_KEEPALIVE_TTL_SECONDS : usize = 60 * 3;
/// For the URL PathInfo
#[derive(Deserialize, ToSchema)]
pub struct GetInferenceJobStatusPathInfo {
token: InferenceJobToken,
}
#[derive(Serialize, ToSchema)]
pub struct GetInferenceJobStatusSuccessResponse {
pub success: bool,
pub state: InferenceJobStatusResponsePayload,
}
#[derive(Serialize, ToSchema)]
pub struct InferenceJobStatusResponsePayload {
pub job_token: InferenceJobToken,
pub request: RequestDetailsResponse,
pub status: StatusDetailsResponse,
pub maybe_result: Option<InferenceProgressDetailsResponse>,
pub created_at: DateTime<Utc>,
pub updated_at: DateTime<Utc>,
}
/// Details about what the user requested for generation
#[derive(Serialize, ToSchema)]
pub struct RequestDetailsResponse {
}
/// Details about the ongoing job status
#[derive(Serialize, ToSchema)]
pub struct StatusDetailsResponse {
/// Primary status from the database (a state machine).
pub status: JobStatusPlus,
/// Extra, temporary status from Redis.
/// This can denote inference progress, and the Python code can write to it.
pub maybe_extra_status_description: Option<String>,
pub maybe_assigned_worker: Option<String>,
pub maybe_assigned_cluster: Option<String>,
pub maybe_first_started_at: Option<DateTime<Utc>>,
pub attempt_count: u8,
/// Whether the frontend needs to maintain a keepalive check.
/// This is typically only for non-premium users.
pub requires_keepalive: bool,
/// An enum the frontend can use to display localized/I18N error
/// messages. These pertain to both transient and permanent failures.
pub maybe_failure_category: Option<FrontendFailureCategory>,
/// This is an integer number between 0 and 100 (both inclusive) that
/// reports the completeness.
pub progress_percentage: u8,
}
// #[derive(Serialize, ToSchema)]
// pub struct InferenceStageDetails {
// pub stage_progress: u8,
// pub expected_frame_count: u8,
// pub stage_complete: bool,
// pub frames: Vec<Url>,
// }
/// Details about the completed result (if any)
// #[derive(Serialize, ToSchema)]
// pub struct InferenceProgressDetailsResponse {
// pub expected_stages: u8,
// pub currently_active_stage: u8,
// pub per_stage_frame_count: u8,
// pub stages: Vec<InferenceStageDetails>,
// }
#[derive(Debug, ToSchema)]
pub enum GetInferenceJobStatusError {
ServerError,
NotFound,
}
impl ResponseError for GetInferenceJobStatusError {
fn status_code(&self) -> StatusCode {
match *self {
GetInferenceJobStatusError::ServerError => StatusCode::INTERNAL_SERVER_ERROR,
GetInferenceJobStatusError::NotFound => StatusCode::NOT_FOUND,
}
}
fn error_response(&self) -> HttpResponse {
let error_reason = match self {
Self::ServerError => "server error".to_string(),
Self::NotFound => "not found".to_string(),
};
to_simple_json_error(&error_reason, self.status_code())
}
}
// NB: Not using derive_more::Display since Clion doesn't understand it.
impl fmt::Display for GetInferenceJobStatusError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{:?}", self)
}
}
pub async fn get_inference_preview_status_handler_ws(
http_request: HttpRequest,
path: Path<GetInferenceJobStatusPathInfo>,
server_state: Data<Arc<ServerState>>) -> Result<Json<GetInferenceJobStatusSuccessResponse>, GetInferenceJobStatusError>
{
if path.token.as_str().trim() == "None" {
// NB: A bunch of Python clients use our API and can fail in this manner.
// This was a large traffic driver during the 2023-03-08 outage.
// Presumably it's this client: https://github.com/shards-7/fakeyou.py
return Err(GetInferenceJobStatusError::NotFound);
}
// NB: Since this is publicly exposed, we don't query sensitive data.
let maybe_status = get_inference_job_status(
&path.token,
&server_state.mysql_pool
).await;
let record = match maybe_status {
Ok(Some(record)) => record,
Ok(None) => return Err(GetInferenceJobStatusError::NotFound),
Err(err) => {
error!("tts job query error: {:?}", err);
return Err(GetInferenceJobStatusError::ServerError);
}
};
let mut redis = server_state.redis_pool
.get()
.map_err(|e| {
error!("redis error: {:?}", e);
GetInferenceJobStatusError::ServerError
})?;
let progress_key = StyleTransferProgressKey::new_for_job_id(record.job_token.clone());
let progress_value: RedisResult<Option<String>> = redis.get(progress_key.to_string());
let maybe_result = match progress_value {
Ok(Some(value)) => {
let progress_details: InferenceProgressDetailsResponse = serde_json::from_str(&value)
.map_err(|e| {
error!("redis error: {:?}", e);
GetInferenceJobStatusError::ServerError
})?;
Some(progress_details)
},
Ok(None) => None,
Err(e) => {
error!("redis error: {:?}", e);
return Err(GetInferenceJobStatusError::ServerError);
}
};
Ok(Json(GetInferenceJobStatusSuccessResponse {
success: true,
state: record_to_payload(record, maybe_result, None),
}))
}
fn record_to_payload(
record: GenericInferenceJobStatus,
progress_details: Option<InferenceProgressDetailsResponse>,
maybe_extra_status_description: Option<String>,
) -> InferenceJobStatusResponsePayload {
let inference_category = record.request_details.inference_category;
// NB: Fail open. We don't want to fail the request if we can't extract the args.
let maybe_polymorphic_args = extract_polymorphic_inference_args(&record)
.ok()
.flatten();
let progress_percentage = estimate_job_progress(&record, maybe_polymorphic_args.as_ref());
InferenceJobStatusResponsePayload {
job_token: record.job_token,
request: RequestDetailsResponse {
// inference_category: record.request_details.inference_category,
// maybe_model_type: maybe_filter_model_name(record.request_details.maybe_model_type.as_deref()),
// maybe_model_token: record.request_details.maybe_model_token,
// maybe_model_title: record.request_details.maybe_model_title,
// maybe_raw_inference_text: record.request_details.maybe_raw_inference_text,
// maybe_style_name: record.request_details.maybe_style_name,
},
status: StatusDetailsResponse {
status: record.status,
maybe_extra_status_description,
maybe_assigned_worker: maybe_filter_model_name(record.maybe_assigned_worker.as_deref()),
maybe_assigned_cluster: record.maybe_assigned_cluster,
maybe_first_started_at: record.maybe_first_started_at,
attempt_count: record.attempt_count as u8,
requires_keepalive: record.is_keepalive_required,
maybe_failure_category: record.maybe_frontend_failure_category,
progress_percentage,
},
maybe_result: progress_details,
created_at: record.created_at,
updated_at: record.updated_at,
}
}
pub struct ProgressTrackerState {
}
pub async fn ws_progress_route(
req: HttpRequest,
stream: web::Payload,
srv: web::Data<Addr<progress_tracker_server::ChatServer>>,
) -> Result<HttpResponse, Error> {
ws::start(
progress_tracker_session::WsChatSession {
id: 0,
hb: Instant::now(),
room: "main".to_owned(),
name: None,
addr: srv.get_ref().clone(),
},
&req,
stream,
)
}
@@ -3,7 +3,4 @@ pub mod enqueue_live_portrait_workflow_handler;
pub mod enqueue_studio_workflow_handler;
pub mod enqueue_video_style_transfer_workflow_handler;
pub mod get_inference_preview_status;
pub mod get_inference_preview_status_ws;
pub mod progress_tracker_server;
pub mod progress_tracker_session;
pub mod vst_common;
@@ -1,216 +0,0 @@
//! `ChatServer` is an actor. It maintains a list of connection client sessions.
//! It also manages available rooms. Peers send messages to other peers in same
//! room through `ChatServer`.
use std::{
collections::{HashMap, HashSet},
sync::{
atomic::{AtomicUsize, Ordering},
Arc,
},
};
use actix::prelude::*;
use log::info;
use rand::{rngs::ThreadRng, Rng};
/// Chat server sends this messages to session
#[derive(Message)]
#[rtype(result = "()")]
pub struct Message(pub String);
/// Message for chat server communications
/// New chat session is created
#[derive(Message)]
#[rtype(usize)]
pub struct Connect {
pub addr: Recipient<Message>,
}
/// Session is disconnected
#[derive(Message)]
#[rtype(result = "()")]
pub struct Disconnect {
pub id: usize,
}
/// Send message to specific room
#[derive(Message)]
#[rtype(result = "()")]
pub struct ClientMessage {
/// Id of the client session
pub id: usize,
/// Peer message
pub msg: String,
/// Room name
pub room: String,
}
/// List of available rooms
pub struct ListRooms;
impl actix::Message for ListRooms {
type Result = Vec<String>;
}
/// Join room, if room does not exists create new one.
#[derive(Message)]
#[rtype(result = "()")]
pub struct Join {
/// Client ID
pub id: usize,
/// Room name
pub name: String,
}
/// `ChatServer` manages chat rooms and responsible for coordinating chat session.
///
/// Implementation is very naïve.
#[derive(Debug)]
pub struct ChatServer {
sessions: HashMap<usize, Recipient<Message>>,
rooms: HashMap<String, HashSet<usize>>,
rng: ThreadRng,
visitor_count: Arc<AtomicUsize>,
}
impl ChatServer {
pub fn new(visitor_count: Arc<AtomicUsize>) -> ChatServer {
// default room
let mut rooms = HashMap::new();
rooms.insert("main".to_owned(), HashSet::new());
ChatServer {
sessions: HashMap::new(),
rooms,
rng: rand::thread_rng(),
visitor_count,
}
}
}
impl ChatServer {
/// Send message to all users in the room
fn send_message(&self, room: &str, message: &str, skip_id: usize) {
if let Some(sessions) = self.rooms.get(room) {
for id in sessions {
if *id != skip_id {
if let Some(addr) = self.sessions.get(id) {
addr.do_send(Message(message.to_owned()));
}
}
}
}
}
}
/// Make actor from `ChatServer`
impl Actor for ChatServer {
/// We are going to use simple Context, we just need ability to communicate
/// with other actors.
type Context = Context<Self>;
}
/// Handler for Connect message.
///
/// Register new session and assign unique id to this session
impl Handler<Connect> for ChatServer {
type Result = usize;
fn handle(&mut self, msg: Connect, _: &mut Context<Self>) -> Self::Result {
info!("Someone joined");
// notify all users in same room
self.send_message("main", "Someone joined", 0);
// register session with random id
let id = self.rng.random::<u64>() as usize;
self.sessions.insert(id, msg.addr);
// auto join session to main room
self.rooms.entry("main".to_owned()).or_default().insert(id);
let count = self.visitor_count.fetch_add(1, Ordering::SeqCst);
self.send_message("main", &format!("Total visitors {count}"), 0);
// send id back
id
}
}
/// Handler for Disconnect message.
impl Handler<Disconnect> for ChatServer {
type Result = ();
fn handle(&mut self, msg: Disconnect, _: &mut Context<Self>) {
info!("Someone disconnected");
let mut rooms: Vec<String> = Vec::new();
// remove address
if self.sessions.remove(&msg.id).is_some() {
// remove session from all rooms
for (name, sessions) in &mut self.rooms {
if sessions.remove(&msg.id) {
rooms.push(name.to_owned());
}
}
}
// send message to other users
for room in rooms {
self.send_message(&room, "Someone disconnected", 0);
}
}
}
/// Handler for Message message.
impl Handler<ClientMessage> for ChatServer {
type Result = ();
fn handle(&mut self, msg: ClientMessage, _: &mut Context<Self>) {
self.send_message(&msg.room, msg.msg.as_str(), msg.id);
}
}
/// Handler for `ListRooms` message.
impl Handler<ListRooms> for ChatServer {
type Result = MessageResult<ListRooms>;
fn handle(&mut self, _: ListRooms, _: &mut Context<Self>) -> Self::Result {
let mut rooms = Vec::new();
for key in self.rooms.keys() {
rooms.push(key.to_owned())
}
MessageResult(rooms)
}
}
/// Join room, send disconnect message to old room
/// send join message to new room
impl Handler<Join> for ChatServer {
type Result = ();
fn handle(&mut self, msg: Join, _: &mut Context<Self>) {
let Join { id, name } = msg;
let mut rooms = Vec::new();
// remove session from all rooms
for (n, sessions) in &mut self.rooms {
if sessions.remove(&id) {
rooms.push(n.to_owned());
}
}
// send message to other users
for room in rooms {
self.send_message(&room, "Someone disconnected", 0);
}
self.rooms.entry(name.clone()).or_default().insert(id);
self.send_message(&name, "Someone connected", id);
}
}
@@ -1,205 +0,0 @@
use std::time::{Duration, Instant};
use actix::prelude::*;
use actix_web_actors::ws;
use log::{debug, info, warn};
use crate::http_server::deprecated_endpoints::workflows::enqueue::progress_tracker_server;
use crate::http_server::deprecated_endpoints::workflows::enqueue::progress_tracker_server::ChatServer;
/// How often heartbeat pings are sent
const HEARTBEAT_INTERVAL: Duration = Duration::from_secs(5);
/// How long before lack of client response causes a timeout
const CLIENT_TIMEOUT: Duration = Duration::from_secs(10);
#[derive(Debug)]
pub struct WsChatSession {
/// unique session id
pub id: usize,
/// Client must send ping at least once per 10 seconds (CLIENT_TIMEOUT),
/// otherwise we drop connection.
pub hb: Instant,
/// joined room
pub room: String,
/// peer name
pub name: Option<String>,
/// Chat server
pub addr: Addr<ChatServer>,
}
impl WsChatSession {
/// helper method that sends ping to client every 5 seconds (HEARTBEAT_INTERVAL).
///
/// also this method checks heartbeats from client
fn hb(&self, ctx: &mut ws::WebsocketContext<Self>) {
ctx.run_interval(HEARTBEAT_INTERVAL, |act, ctx| {
// check client heartbeats
if Instant::now().duration_since(act.hb) > CLIENT_TIMEOUT {
// heartbeat timed out
warn!("Websocket Client heartbeat failed, disconnecting!");
// notify chat server
act.addr.do_send(progress_tracker_server::Disconnect { id: act.id });
// stop actor
ctx.stop();
// don't try to send a ping
return;
}
ctx.ping(b"");
});
}
}
impl Actor for WsChatSession {
type Context = ws::WebsocketContext<Self>;
/// Method is called on actor start.
/// We register ws session with ChatServer
fn started(&mut self, ctx: &mut Self::Context) {
// we'll start heartbeat process on session start.
self.hb(ctx);
// register self in chat server. `AsyncContext::wait` register
// future within context, but context waits until this future resolves
// before processing any other events.
// HttpContext::state() is instance of WsChatSessionState, state is shared
// across all routes within application
let addr = ctx.address();
self.addr
.send(progress_tracker_server::Connect {
addr: addr.recipient(),
})
.into_actor(self)
.then(|res, act, ctx| {
match res {
Ok(res) => act.id = res,
// something is wrong with chat server
_ => ctx.stop(),
}
fut::ready(())
})
.wait(ctx);
}
fn stopping(&mut self, _: &mut Self::Context) -> Running {
// notify chat server
self.addr.do_send(progress_tracker_server::Disconnect { id: self.id });
Running::Stop
}
}
/// Handle messages from chat server, we simply send it to peer websocket
impl Handler<progress_tracker_server::Message> for WsChatSession {
type Result = ();
fn handle(&mut self, msg: progress_tracker_server::Message, ctx: &mut Self::Context) {
ctx.text(msg.0);
}
}
/// WebSocket message handler
impl StreamHandler<Result<ws::Message, ws::ProtocolError>> for WsChatSession {
fn handle(&mut self, msg: Result<ws::Message, ws::ProtocolError>, ctx: &mut Self::Context) {
log::debug!("WEBSOCKET MESSAGE: {msg:?}");
let msg = match msg {
Err(_) => {
ctx.stop();
return;
}
Ok(msg) => msg,
};
log::debug!("WEBSOCKET MESSAGE: {msg:?}");
match msg {
ws::Message::Ping(msg) => {
self.hb = Instant::now();
ctx.pong(&msg);
}
ws::Message::Pong(_) => {
self.hb = Instant::now();
}
ws::Message::Text(text) => {
let m = text.trim();
// we check for /sss type of messages
if m.starts_with('/') {
let v: Vec<&str> = m.splitn(2, ' ').collect();
match v[0] {
"/list" => {
// Send ListRooms message to chat server and wait for
// response
debug!("List rooms");
self.addr
.send(progress_tracker_server::ListRooms)
.into_actor(self)
.then(|res, _, ctx| {
match res {
Ok(rooms) => {
for room in rooms {
ctx.text(room);
}
}
_ => warn!("Something is wrong"),
}
fut::ready(())
})
.wait(ctx)
// .wait(ctx) pauses all events in context,
// so actor wont receive any new messages until it get list
// of rooms back
}
"/join" => {
if v.len() == 2 {
v[1].clone_into(&mut self.room);
self.addr.do_send(progress_tracker_server::Join {
id: self.id,
name: self.room.clone(),
});
ctx.text("joined");
} else {
ctx.text("!!! room name is required");
}
}
"/name" => {
if v.len() == 2 {
self.name = Some(v[1].to_owned());
} else {
ctx.text("!!! name is required");
}
}
_ => ctx.text(format!("!!! unknown command: {m:?}")),
}
} else {
let msg = if let Some(ref name) = self.name {
format!("{name}: {m}")
} else {
m.to_owned()
};
// send message to chat server
self.addr.do_send(progress_tracker_server::ClientMessage {
id: self.id,
msg,
room: self.room.clone(),
})
}
}
ws::Message::Binary(_) => warn!("Unexpected binary"),
ws::Message::Close(reason) => {
ctx.close(reason);
ctx.stop();
}
ws::Message::Continuation(_) => {
ctx.stop();
}
ws::Message::Nop => (),
}
}
}
@@ -47,7 +47,7 @@ pub async fn drain_multipart_request(mut multipart_payload: Multipart) -> Anyhow
let mut field_name = None;
let mut field_filename = None;
if let content_disposition = field.content_disposition() {
if let Some(content_disposition) = field.content_disposition() {
field_name = content_disposition.get_name()
.map(|s| s.to_string());
field_filename = content_disposition.get_filename() // NB: Only used for the file bytes.
@@ -47,7 +47,7 @@ pub async fn drain_multipart_request(mut multipart_payload: Multipart) -> Anyhow
let mut field_name = None;
let mut field_filename = None;
if let content_disposition = field.content_disposition() {
if let Some(content_disposition) = field.content_disposition() {
field_name = content_disposition.get_name()
.map(|s| s.to_string());
field_filename = content_disposition.get_filename() // NB: Only used for the file bytes.
@@ -43,7 +43,7 @@ pub async fn drain_multipart_request(mut multipart_payload: Multipart) -> Anyhow
let mut field_name = None;
let mut field_filename = None;
if let content_disposition = field.content_disposition() {
if let Some(content_disposition) = field.content_disposition() {
field_name = content_disposition.get_name()
.map(|s| s.to_string());
field_filename = content_disposition.get_filename() // NB: Only used for the file bytes.
@@ -45,7 +45,7 @@ pub async fn drain_multipart_request(mut multipart_payload: Multipart) -> Result
let mut field_name = None;
let mut field_filename = None;
if let content_disposition = field.content_disposition() {
if let Some(content_disposition) = field.content_disposition() {
field_name = content_disposition.get_name()
.map(|s| s.to_string());
field_filename = content_disposition.get_filename() // NB: Only used for the file bytes.
@@ -34,7 +34,7 @@ pub async fn drain_multipart_request(mut multipart_payload: Multipart) -> Result
let mut field_name = None;
let mut field_filename = None;
if let content_disposition = field.content_disposition() {
if let Some(content_disposition) = field.content_disposition() {
field_name = content_disposition.get_name()
.map(|s| s.to_string());
field_filename = content_disposition.get_filename() // NB: Only used for the file bytes.
@@ -47,14 +47,7 @@ where
.route(web::get().to(get_inference_preview_status_handler))
.route(web::head().to(|| HttpResponse::Ok()))
)
)
// .service(web::resource("/ws")
// .route(web::get().to(ws_progress_route))
// .route(web::head().to(|| HttpResponse::Ok()))
// )
//
;
);
add_legacy_workflow_routes(app)
}
@@ -19,7 +19,6 @@
use std::sync::Arc;
use actix::Actor;
use actix_artcraft::sessions::user_sessions::http_user_session_manager::HttpUserSessionManager;
use actix_cors_configs::cors::build_cors_config;
use actix_cors_configs::shared_array_buffer_cors::shared_array_buffer_cors;
@@ -15,8 +15,8 @@ publish = false
### BEGIN HAKARI SECTION
[dependencies]
actix-router = { version = "0.5.3" }
actix-rt = { version = "2.10.0" }
actix-router = { version = "0.5.4" }
actix-rt = { version = "2.11.0" }
ahash = { version = "0.8.11" }
anstream = { version = "0.6.14" }
arrayvec = { version = "0.7.6" }
@@ -104,7 +104,7 @@ void = { version = "1.0.2" }
zeroize = { version = "1.8.1", features = ["zeroize_derive"] }
[build-dependencies]
actix-router = { version = "0.5.3" }
actix-router = { version = "0.5.4" }
ahash = { version = "0.8.11" }
base64-3c51e837cfc5589a = { package = "base64", version = "0.22.1" }
base64-647d43efb71741da = { package = "base64", version = "0.21.7" }