diff --git a/Cargo.lock b/Cargo.lock index 700c0814..77e70251 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -172,6 +172,15 @@ dependencies = [ "stable_deref_trait", ] +[[package]] +name = "as-slice" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "516b6b4f0e40d50dcda9365d53964ec74560ad4284da2e7fc97122cd83174516" +dependencies = [ + "stable_deref_trait", +] + [[package]] name = "atomic-polyfill" version = "1.0.3" @@ -181,6 +190,18 @@ dependencies = [ "critical-section", ] +[[package]] +name = "atomic-pool" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58c5fc22e05ec2884db458bf307dc7b278c9428888d2b6e6fad9c0ae7804f5f6" +dependencies = [ + "as-slice 0.1.5", + "as-slice 0.2.1", + "atomic-polyfill", + "stable_deref_trait", +] + [[package]] name = "autocfg" version = "1.4.0" @@ -230,6 +251,12 @@ dependencies = [ "smallvec", ] +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + [[package]] name = "bitvec" version = "1.0.1" @@ -289,7 +316,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.89", ] [[package]] @@ -427,7 +454,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.89", ] [[package]] @@ -450,9 +477,9 @@ checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" [[package]] name = "cpufeatures" -version = "0.2.15" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ca741a962e1b0bff6d724a1a0958b686406e853bb14061f218562e1896f95e6" +checksum = "16b80225097f2e5ae4e7179dd2266824648f3e2f49d9134d584b76389d31c4c3" dependencies = [ "libc", ] @@ -541,7 +568,7 @@ checksum = "30542c1ad912e0e3d22a1935c290e12e8a29d704a420177a31faad4a601a0800" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.89", ] [[package]] @@ -551,16 +578,39 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer", + "const-oid", "crypto-common 0.1.6", "subtle", ] +[[package]] +name = "document-features" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb6969eaabd2421f8a2775cfd2471a2b634372b4a25d41e3bd647b79912850a0" +dependencies = [ + "litrs", +] + [[package]] name = "dotenv" version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77c90badedccf4105eca100756a0b1289e191f6fcbdadd3cee1d2f614f97da8f" +[[package]] +name = "ecdsa" +version = "0.16.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" +dependencies = [ + "der", + "digest", + "elliptic-curve", + "rfc6979", + "signature", +] + [[package]] name = "elliptic-curve" version = "0.13.8" @@ -580,6 +630,105 @@ dependencies = [ "zeroize", ] +[[package]] +name = "embassy-net" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55cf91dd36dfd623de32242af711fd294d41159f02130052fc93c5c5ba93febe" +dependencies = [ + "as-slice 0.2.1", + "atomic-pool", + "document-features", + "embassy-net-driver", + "embassy-sync", + "embassy-time", + "embedded-io-async", + "embedded-nal-async", + "futures", + "generic-array 0.14.7", + "heapless 0.8.0", + "managed", + "smoltcp", + "stable_deref_trait", +] + +[[package]] +name = "embassy-net-driver" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "524eb3c489760508f71360112bca70f6e53173e6fe48fc5f0efd0f5ab217751d" + +[[package]] +name = "embassy-sync" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd938f25c0798db4280fcd8026bf4c2f48789aebf8f77b6e5cf8a7693ba114ec" +dependencies = [ + "cfg-if", + "critical-section", + "embedded-io-async", + "futures-util", + "heapless 0.8.0", +] + +[[package]] +name = "embassy-time" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "158080d48f824fad101d7b2fae2d83ac39e3f7a6fa01811034f7ab8ffc6e7309" +dependencies = [ + "cfg-if", + "critical-section", + "document-features", + "embassy-time-driver", + "embassy-time-queue-driver", + "embedded-hal 0.2.7", + "embedded-hal 1.0.0", + "embedded-hal-async", + "futures-util", + "heapless 0.8.0", +] + +[[package]] +name = "embassy-time-driver" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e0c214077aaa9206958b16411c157961fb7990d4ea628120a78d1a5a28aed24" +dependencies = [ + "document-features", +] + +[[package]] +name = "embassy-time-queue-driver" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1177859559ebf42cd24ae7ba8fe6ee707489b01d0bf471f8827b7b12dcb0bc0" + +[[package]] +name = "embedded-hal" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35949884794ad573cf46071e41c9b60efb0cb311e3ca01f7af807af1debc66ff" +dependencies = [ + "nb 0.1.3", + "void", +] + +[[package]] +name = "embedded-hal" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "361a90feb7004eca4019fb28352a9465666b24f840f5c3cddf0ff13920590b89" + +[[package]] +name = "embedded-hal-async" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c4c685bbef7fe13c3c6dd4da26841ed3980ef33e841cddfa15ce8a8fb3f1884" +dependencies = [ + "embedded-hal 1.0.0", +] + [[package]] name = "embedded-io" version = "0.6.1" @@ -596,14 +745,34 @@ dependencies = [ ] [[package]] -name = "embedded-tls" -version = "0.17.0" +name = "embedded-nal" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6efb76fdd004a4ef787640177237b83449e6c5847765ea50bf15900061fd601" +checksum = "b8a943fad5ed3d3f8a00f1e80f6bba371f1e7f0df28ec38477535eb318dc19cc" +dependencies = [ + "nb 1.1.0", + "no-std-net", +] + +[[package]] +name = "embedded-nal-async" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72229137a4fc12d239b0b7f50f04b30790678da6d782a0f3f1909bf57ec4b759" +dependencies = [ + "embedded-io-async", + "embedded-nal", + "no-std-net", +] + +[[package]] +name = "embedded-tls" +version = "0.17.1" +source = "git+https://github.com/drogue-iot/embedded-tls#7936bc11c123260065b5b9776b584308ad2e8f8a" dependencies = [ "aes-gcm 0.10.3", - "atomic-polyfill", "digest", + "ecdsa", "embedded-io", "embedded-io-async", "generic-array 0.14.7", @@ -612,8 +781,10 @@ dependencies = [ "hkdf", "hmac", "p256", + "portable-atomic", "rand_core", "sha2", + "signature", "typenum", ] @@ -662,6 +833,79 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" +[[package]] +name = "futures" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" + +[[package]] +name = "futures-io" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" + +[[package]] +name = "futures-macro" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.89", +] + +[[package]] +name = "futures-sink" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" + +[[package]] +name = "futures-task" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" + +[[package]] +name = "futures-util" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" +dependencies = [ + "futures-core", + "futures-macro", + "futures-sink", + "futures-task", + "pin-project-lite", + "pin-utils", +] + [[package]] name = "generic-array" version = "0.12.4" @@ -788,7 +1032,7 @@ version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "634bd4d29cbf24424d0a4bfcbf80c6960129dc24424752a7d1d1390607023422" dependencies = [ - "as-slice", + "as-slice 0.1.5", "generic-array 0.14.7", "hash32 0.1.1", "stable_deref_trait", @@ -909,9 +1153,9 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.11" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" +checksum = "540654e97a3f4470a492cd30ff187bc95d89557a903a2bbf112e2fae98104ef2" [[package]] name = "jobserver" @@ -939,9 +1183,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" -version = "0.2.162" +version = "0.2.164" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18d287de67fe55fd7e1581fe933d965a5a9477b38e949cfa9f8574ef01506398" +checksum = "433bfe06b8c75da9b2e3fbea6e5329ff87748f0b144ef75306e674c3f6f7c13f" [[package]] name = "libfuzzer-sys" @@ -962,12 +1206,24 @@ dependencies = [ "zlib-rs", ] +[[package]] +name = "litrs" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4ce301924b7887e9d637144fdade93f9dfff9b60981d4ac161db09720d39aa5" + [[package]] name = "log" version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" +[[package]] +name = "managed" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ca88d725a0a943b096803bd34e73a4437208b6077654cc4ecb2947a5f91618d" + [[package]] name = "matchers" version = "0.1.0" @@ -1016,6 +1272,27 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "nb" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "801d31da0513b6ec5214e9bf433a77966320625a37860f910be265be6e18d06f" +dependencies = [ + "nb 1.1.0", +] + +[[package]] +name = "nb" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d5439c4ad607c3c23abf66de8c8bf57ba8adcd1f129e699851a6e43935d339d" + +[[package]] +name = "no-std-net" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43794a0ace135be66a25d3ae77d41b91615fb68ae937f904090203e81f755b65" + [[package]] name = "nom" version = "7.1.3" @@ -1071,8 +1348,10 @@ version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b" dependencies = [ + "ecdsa", "elliptic-curve", "primeorder", + "sha2", ] [[package]] @@ -1112,6 +1391,12 @@ version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "915a1e146535de9163f3987b8944ed8cf49a18bb0056bcebcdcece385cece4ff" +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + [[package]] name = "polyval" version = "0.6.2" @@ -1138,15 +1423,15 @@ dependencies = [ [[package]] name = "portable-atomic" -version = "1.9.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc9c68a3f6da06753e9335d63e27f6b9754dd1920d941135b7ea8224f141adb2" +checksum = "280dc24453071f1b63954171985a0b0d30058d287960968b9b2aca264c8d4ee6" [[package]] name = "portable-atomic-util" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90a7d5beecc52a491b54d6dd05c7a45ba1801666a5baad9fdbfc6fef8d2d206c" +checksum = "d8a2f0d8d040d7848a709caf78912debcc3f33ee4b3cac47d73d1e1069e83507" dependencies = [ "portable-atomic", ] @@ -1180,9 +1465,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.89" +version = "1.0.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e" +checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" dependencies = [ "unicode-ident", ] @@ -1314,6 +1599,16 @@ dependencies = [ "bytecheck", ] +[[package]] +name = "rfc6979" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" +dependencies = [ + "hmac", + "subtle", +] + [[package]] name = "ring" version = "0.17.8" @@ -1382,9 +1677,9 @@ checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" [[package]] name = "rustls" -version = "0.23.16" +version = "0.23.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eee87ff5d9b36712a58574e12e9f0ea80f915a5b0ac518d322b24a465617925e" +checksum = "9c9cc1d47e243d655ace55ed38201c19ae02c148ae56412ab8750e8f0166ab7f" dependencies = [ "once_cell", "ring", @@ -1462,14 +1757,14 @@ checksum = "ad1e866f866923f252f05c889987993144fb74e722403468a4ebd70c3cd756c0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.89", ] [[package]] name = "serde_json" -version = "1.0.132" +version = "1.0.133" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d726bfaff4b320266d395898905d0eba0345aae23b54aee3a737e260fd46db03" +checksum = "c7fceb2473b9166b2294ef05efcb65a3db80803f0b03ef86a5fc88a2b85ee377" dependencies = [ "itoa", "memchr", @@ -1529,6 +1824,7 @@ version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" dependencies = [ + "digest", "rand_core", ] @@ -1544,6 +1840,19 @@ version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" +[[package]] +name = "smoltcp" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a1a996951e50b5971a2c8c0fa05a381480d70a933064245c4a223ddc87ccc97" +dependencies = [ + "bitflags", + "byteorder", + "cfg-if", + "heapless 0.8.0", + "managed", +] + [[package]] name = "socket2" version = "0.5.7" @@ -1595,9 +1904,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.87" +version = "2.0.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25aa4ce346d03a6dcd68dd8b4010bcb74e54e62c90c573f394c46eae99aba32d" +checksum = "44d46482f1c1c87acd84dea20c1bf5ebff4c757009ed6bf19cfd36fb10e92c4e" dependencies = [ "proc-macro2", "quote", @@ -1642,7 +1951,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.89", ] [[package]] @@ -1694,7 +2003,7 @@ checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.89", ] [[package]] @@ -1761,7 +2070,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.89", ] [[package]] @@ -1823,9 +2132,9 @@ checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" [[package]] name = "unicode-ident" -version = "1.0.13" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" +checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" [[package]] name = "universal-hash" @@ -1871,6 +2180,12 @@ version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" +[[package]] +name = "void" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" + [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" @@ -1899,7 +2214,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.89", "wasm-bindgen-shared", ] @@ -1921,7 +2236,7 @@ checksum = "26c6ab57572f7a24a4985830b120de1594465e5d500f24afe89e16b4e833ef68" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.89", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -1934,9 +2249,9 @@ checksum = "65fc09f10666a9f147042251e0dda9c18f166ff7de300607007e96bdebc1068d" [[package]] name = "webpki-roots" -version = "0.26.6" +version = "0.26.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "841c67bff177718f1d4dfefde8d8f0e78f9b6589319ba88312f567fc5841a958" +checksum = "5d642ff16b7e79272ae451b7322067cdc17cadf68c23264be9d94a32319efe7e" dependencies = [ "rustls-pki-types", ] @@ -2063,6 +2378,7 @@ dependencies = [ "cl-aux", "crypto-common 0.1.6", "digest", + "embassy-net", "embedded-io-async", "embedded-tls", "fastrand", @@ -2189,7 +2505,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.89", ] [[package]] @@ -2209,7 +2525,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.89", ] [[package]] diff --git a/wtx-docs/src/web-socket/README.md b/wtx-docs/src/web-socket/README.md index 3386cf75..524a569a 100644 --- a/wtx-docs/src/web-socket/README.md +++ b/wtx-docs/src/web-socket/README.md @@ -2,6 +2,8 @@ Implementation of [RFC6455](https://datatracker.ietf.org/doc/html/rfc6455) and [RFC7692](https://datatracker.ietf.org/doc/html/rfc7692). WebSocket is a communication protocol that enables full-duplex communication between a client (typically a web browser) and a server over a single TCP connection. Unlike traditional HTTP, which is request-response based, WebSocket allows real-time data exchange without the need for polling. +In-house benchmarks are available at . If you are aware of other benchmark tools, please open an discussion in the GitHub project. + To use this functionality, it is necessary to activate the `web-socket` feature. ![WebSocket Benchmark](https://i.imgur.com/Iv2WzJV.jpg) @@ -21,7 +23,7 @@ To get the most performance possible, try compiling your program with `RUSTFLAGS Although not officially endorsed, the `no-masking` parameter described at is supported to increase performance. If such a thing is not desirable, please make sure to check the handshake parameters to avoid accidental scenarios. -To make everything work as intended both partys, client and server, need to implement this feature. For example, web browser won't stop masking frames. +To make everything work as intended both parties, client and server, need to implement this feature. For example, web browser won't stop masking frames. ## Client Example diff --git a/wtx-instances/Cargo.toml b/wtx-instances/Cargo.toml index e83329a1..868c4d90 100644 --- a/wtx-instances/Cargo.toml +++ b/wtx-instances/Cargo.toml @@ -99,7 +99,7 @@ required-features = ["wtx/http-server-framework"] [[example]] name = "http-server-framework-session" path = "http-server-framework-examples/http-server-framework-session.rs" -required-features = ["argon2", "rand_chacha", "rand_core", "serde", "serde_json", "wtx/argon2", "wtx/http-server-framework", "wtx/http-session", "wtx/pool", "wtx/postgres", "wtx/rand_chacha"] +required-features = ["argon2", "rand_chacha", "rand_core", "serde", "serde_json", "wtx/argon2", "wtx/http-server-framework", "wtx/http-session", "wtx/pool", "wtx/postgres", "wtx/rand_chacha", "wtx-macros"] # HTTP/2 Examples diff --git a/wtx-instances/http-server-framework-examples/http-server-framework-session.rs b/wtx-instances/http-server-framework-examples/http-server-framework-session.rs index a91b715f..1a348432 100644 --- a/wtx-instances/http-server-framework-examples/http-server-framework-session.rs +++ b/wtx-instances/http-server-framework-examples/http-server-framework-session.rs @@ -27,33 +27,39 @@ use wtx::{ database::{Executor, Record}, http::{ server_framework::{get, post, Router, ServerFrameworkBuilder, State, StateClean}, - ReqResBuffer, ReqResData, SessionDecoder, SessionEnforcer, SessionTokio, StatusCode, + ReqResBuffer, ReqResData, SessionDecoder, SessionEnforcer, SessionManagerTokio, SessionState, + StatusCode, }, misc::{argon2_pwd, Vector}, pool::{PostgresRM, SimplePoolTokio}, }; -type ConnAux = (Session, ChaCha20Rng); type Pool = SimplePoolTokio>; -type Session = SessionTokio; +type SessionManager = SessionManagerTokio; #[tokio::main] async fn main() -> wtx::Result<()> { - let pool = Pool::new(4, PostgresRM::tokio("postgres://USER:PASSWORD@localhost/DB_NAME".into())); + let uri = "postgres://USER:PASSWORD@localhost/DB_NAME"; + let mut pool = Pool::new(4, PostgresRM::tokio(uri.into())); let mut rng = ChaCha20Rng::from_entropy(); - let (expired_sessions, session) = Session::builder(pool).build_generating_key(&mut rng); + let (expired, sm) = SessionManager::builder().build_generating_key(&mut rng, &mut pool); let router = Router::new( wtx::paths!(("/login", post(login)), ("/logout", get(logout)),), - (SessionDecoder::new(session.clone()), SessionEnforcer::new(["/admin"], session.clone())), + (SessionDecoder::new(sm.clone(), pool.clone()), SessionEnforcer::new(["/admin"])), )?; tokio::spawn(async move { - if let Err(err) = expired_sessions.await { + if let Err(err) = expired.await { eprintln!("{err}"); } }); let rng_clone = rng.clone(); ServerFrameworkBuilder::new(router) - .with_conn_aux(move || (session.clone(), rng_clone.clone())) + .with_conn_aux(move || ConnAux { + pool: pool.clone(), + rng: rng_clone.clone(), + session_manager: sm.clone(), + session_state: None, + }) .tokio("0.0.0.0:9000", rng, |err| eprintln!("{err:?}"), |_| Ok(())) .await?; Ok(()) @@ -61,14 +67,14 @@ async fn main() -> wtx::Result<()> { #[inline] async fn login(state: State<'_, ConnAux, (), ReqResBuffer>) -> wtx::Result { - let (Session { manager, store }, rng) = state.conn_aux; - if manager.inner.lock().await.state().is_some() { - manager.delete_session_cookie(&mut state.req.rrd, store).await?; + let ConnAux { pool, rng, session_manager, session_state } = state.conn_aux; + if session_state.is_some() { + session_manager.delete_session_cookie(&mut state.req.rrd, session_state, pool).await?; return Ok(StatusCode::Forbidden); } let user: UserLoginReq<'_> = serde_json::from_slice(state.req.rrd.body())?; - let mut guard = store.get().await?; - let record = guard + let mut pool_guard = pool.get().await?; + let record = pool_guard .fetch_with_stmt("SELECT id,first_name,password,salt FROM user WHERE email = $1", (user.email,)) .await?; let id = record.decode::<_, u32>(0)?; @@ -81,18 +87,26 @@ async fn login(state: State<'_, ConnAux, (), ReqResBuffer>) -> wtx::Result) -> wtx::Result { - let (Session { manager, store }, _) = state.conn_aux; - manager.delete_session_cookie(&mut state.req.rrd, store).await?; + let ConnAux { pool, rng: _, session_manager, session_state } = state.conn_aux; + session_manager.delete_session_cookie(&mut state.req.rrd, session_state, pool).await?; Ok(StatusCode::Ok) } +#[derive(Clone, Debug, wtx_macros::ConnAux)] +struct ConnAux { + pool: Pool, + rng: ChaCha20Rng, + session_manager: SessionManager, + session_state: Option>, +} + #[derive(Debug, serde::Deserialize)] struct UserLoginReq<'req> { email: &'req str, diff --git a/wtx-instances/src/lib.rs b/wtx-instances/src/lib.rs index 8151c769..e3f8faa6 100644 --- a/wtx-instances/src/lib.rs +++ b/wtx-instances/src/lib.rs @@ -39,8 +39,6 @@ pub static ROOT_CA: &[u8] = include_bytes!("../../.certs/root-ca.crt"); pub async fn executor_postgres( uri_str: &str, ) -> wtx::Result> { - use std::usize; - let uri = Uri::new(uri_str); let mut rng = Xorshift64::from(simple_seed()); Executor::connect( diff --git a/wtx-macros/Cargo.toml b/wtx-macros/Cargo.toml index 8619906d..3fd7b073 100644 --- a/wtx-macros/Cargo.toml +++ b/wtx-macros/Cargo.toml @@ -1,7 +1,7 @@ [dependencies] proc-macro2 = { default-features = false, version = "1.0" } quote = { default-features = false, features = ["proc-macro"], version = "1.0" } -syn = { default-features = false, features = ["extra-traits", "full", "parsing", "printing", "proc-macro"], version = "1.0" } +syn = { default-features = false, features = ["derive", "extra-traits", "full", "parsing", "printing", "proc-macro"], version = "1.0" } [dev-dependencies] trybuild = { default-features = false, version = "1.0" } diff --git a/wtx-macros/src/client_api_framework.rs b/wtx-macros/src/client_api_framework.rs new file mode 100644 index 00000000..ea239822 --- /dev/null +++ b/wtx-macros/src/client_api_framework.rs @@ -0,0 +1,6 @@ +pub(crate) mod api_params; +pub(crate) mod contained_attrs; +pub(crate) mod item_with_attr_span; +pub(crate) mod owned_or_ref; +pub(crate) mod pkg; +pub(crate) mod transport_group; diff --git a/wtx-macros/src/api_params.rs b/wtx-macros/src/client_api_framework/api_params.rs similarity index 97% rename from wtx-macros/src/api_params.rs rename to wtx-macros/src/client_api_framework/api_params.rs index a9879372..4703c0ed 100644 --- a/wtx-macros/src/api_params.rs +++ b/wtx-macros/src/client_api_framework/api_params.rs @@ -1,6 +1,9 @@ mod attrs; -use crate::{misc::create_ident, owned_or_ref::OwnedOrRef, transport_group::TransportGroup}; +use crate::{ + client_api_framework::{owned_or_ref::OwnedOrRef, transport_group::TransportGroup}, + misc::create_ident, +}; use proc_macro2::{Ident, Span}; use quote::ToTokens; use syn::{ diff --git a/wtx-macros/src/api_params/attrs.rs b/wtx-macros/src/client_api_framework/api_params/attrs.rs similarity index 95% rename from wtx-macros/src/api_params/attrs.rs rename to wtx-macros/src/client_api_framework/api_params/attrs.rs index cbc4c976..5829762e 100644 --- a/wtx-macros/src/api_params/attrs.rs +++ b/wtx-macros/src/client_api_framework/api_params/attrs.rs @@ -1,4 +1,4 @@ -use crate::transport_group::TransportGroup; +use crate::client_api_framework::transport_group::TransportGroup; use syn::{Meta, MetaList, NestedMeta, Path}; #[derive(Debug)] diff --git a/wtx-macros/src/contained_attrs.rs b/wtx-macros/src/client_api_framework/contained_attrs.rs similarity index 100% rename from wtx-macros/src/contained_attrs.rs rename to wtx-macros/src/client_api_framework/contained_attrs.rs diff --git a/wtx-macros/src/item_with_attr_span.rs b/wtx-macros/src/client_api_framework/item_with_attr_span.rs similarity index 100% rename from wtx-macros/src/item_with_attr_span.rs rename to wtx-macros/src/client_api_framework/item_with_attr_span.rs diff --git a/wtx-macros/src/owned_or_ref.rs b/wtx-macros/src/client_api_framework/owned_or_ref.rs similarity index 100% rename from wtx-macros/src/owned_or_ref.rs rename to wtx-macros/src/client_api_framework/owned_or_ref.rs diff --git a/wtx-macros/src/pkg.rs b/wtx-macros/src/client_api_framework/pkg.rs similarity index 95% rename from wtx-macros/src/pkg.rs rename to wtx-macros/src/client_api_framework/pkg.rs index 08f13a3b..1397563e 100644 --- a/wtx-macros/src/pkg.rs +++ b/wtx-macros/src/client_api_framework/pkg.rs @@ -23,9 +23,11 @@ use syn::{ }; use crate::{ - item_with_attr_span::ItemWithAttrSpan, + client_api_framework::{ + item_with_attr_span::ItemWithAttrSpan, + pkg::{fir::fir_after_sending_item_values::FirAfterSendingItemValues, misc::unit_type}, + }, misc::push_doc, - pkg::{fir::fir_after_sending_item_values::FirAfterSendingItemValues, misc::unit_type}, }; pub(crate) fn pkg( diff --git a/wtx-macros/src/pkg/data_format.rs b/wtx-macros/src/client_api_framework/pkg/data_format.rs similarity index 97% rename from wtx-macros/src/pkg/data_format.rs rename to wtx-macros/src/client_api_framework/pkg/data_format.rs index dff4f649..c9b883ee 100644 --- a/wtx-macros/src/pkg/data_format.rs +++ b/wtx-macros/src/client_api_framework/pkg/data_format.rs @@ -1,4 +1,6 @@ -use crate::{pkg::data_format_elems::DataFormatElems, transport_group::TransportGroup}; +use crate::client_api_framework::{ + pkg::data_format_elems::DataFormatElems, transport_group::TransportGroup, +}; use proc_macro2::{Ident, Span, TokenStream}; use syn::{Lit, Meta, NestedMeta}; diff --git a/wtx-macros/src/pkg/data_format_elems.rs b/wtx-macros/src/client_api_framework/pkg/data_format_elems.rs similarity index 100% rename from wtx-macros/src/pkg/data_format_elems.rs rename to wtx-macros/src/client_api_framework/pkg/data_format_elems.rs diff --git a/wtx-macros/src/pkg/enum_struct_or_type.rs b/wtx-macros/src/client_api_framework/pkg/enum_struct_or_type.rs similarity index 100% rename from wtx-macros/src/pkg/enum_struct_or_type.rs rename to wtx-macros/src/client_api_framework/pkg/enum_struct_or_type.rs diff --git a/wtx-macros/src/pkg/fir.rs b/wtx-macros/src/client_api_framework/pkg/fir.rs similarity index 100% rename from wtx-macros/src/pkg/fir.rs rename to wtx-macros/src/client_api_framework/pkg/fir.rs diff --git a/wtx-macros/src/pkg/fir/fir_after_sending_item_values.rs b/wtx-macros/src/client_api_framework/pkg/fir/fir_after_sending_item_values.rs similarity index 100% rename from wtx-macros/src/pkg/fir/fir_after_sending_item_values.rs rename to wtx-macros/src/client_api_framework/pkg/fir/fir_after_sending_item_values.rs diff --git a/wtx-macros/src/pkg/fir/fir_aux_field_attr.rs b/wtx-macros/src/client_api_framework/pkg/fir/fir_aux_field_attr.rs similarity index 94% rename from wtx-macros/src/pkg/fir/fir_aux_field_attr.rs rename to wtx-macros/src/client_api_framework/pkg/fir/fir_aux_field_attr.rs index 3f3feb44..567f2c1b 100644 --- a/wtx-macros/src/pkg/fir/fir_aux_field_attr.rs +++ b/wtx-macros/src/client_api_framework/pkg/fir/fir_aux_field_attr.rs @@ -1,4 +1,4 @@ -use crate::pkg::keywords; +use crate::client_api_framework::pkg::keywords; use syn::{ parse::{Parse, ParseStream}, spanned::Spanned, diff --git a/wtx-macros/src/pkg/fir/fir_aux_item_values.rs b/wtx-macros/src/client_api_framework/pkg/fir/fir_aux_item_values.rs similarity index 98% rename from wtx-macros/src/pkg/fir/fir_aux_item_values.rs rename to wtx-macros/src/client_api_framework/pkg/fir/fir_aux_item_values.rs index a4df623c..753f69ac 100644 --- a/wtx-macros/src/pkg/fir/fir_aux_item_values.rs +++ b/wtx-macros/src/client_api_framework/pkg/fir/fir_aux_item_values.rs @@ -1,4 +1,4 @@ -use crate::{ +use crate::client_api_framework::{ item_with_attr_span::ItemWithAttrSpan, pkg::{ fir::fir_aux_field_attr::FirAuxFieldAttr, diff --git a/wtx-macros/src/pkg/fir/fir_before_sending_item_values.rs b/wtx-macros/src/client_api_framework/pkg/fir/fir_before_sending_item_values.rs similarity index 100% rename from wtx-macros/src/pkg/fir/fir_before_sending_item_values.rs rename to wtx-macros/src/client_api_framework/pkg/fir/fir_before_sending_item_values.rs diff --git a/wtx-macros/src/pkg/fir/fir_custom_field_attr.rs b/wtx-macros/src/client_api_framework/pkg/fir/fir_custom_field_attr.rs similarity index 87% rename from wtx-macros/src/pkg/fir/fir_custom_field_attr.rs rename to wtx-macros/src/client_api_framework/pkg/fir/fir_custom_field_attr.rs index 52122eac..9bff4d81 100644 --- a/wtx-macros/src/pkg/fir/fir_custom_field_attr.rs +++ b/wtx-macros/src/client_api_framework/pkg/fir/fir_custom_field_attr.rs @@ -1,4 +1,6 @@ -use crate::pkg::{fir::fir_custom_field_field_attr::FirCustomFieldFieldAttr, keywords}; +use crate::client_api_framework::pkg::{ + fir::fir_custom_field_field_attr::FirCustomFieldFieldAttr, keywords, +}; use syn::{ parse::{Parse, ParseStream}, spanned::Spanned, diff --git a/wtx-macros/src/pkg/fir/fir_custom_field_field_attr.rs b/wtx-macros/src/client_api_framework/pkg/fir/fir_custom_field_field_attr.rs similarity index 94% rename from wtx-macros/src/pkg/fir/fir_custom_field_field_attr.rs rename to wtx-macros/src/client_api_framework/pkg/fir/fir_custom_field_field_attr.rs index 16758efe..be542fae 100644 --- a/wtx-macros/src/pkg/fir/fir_custom_field_field_attr.rs +++ b/wtx-macros/src/client_api_framework/pkg/fir/fir_custom_field_field_attr.rs @@ -1,4 +1,4 @@ -use crate::pkg::keywords; +use crate::client_api_framework::pkg::keywords; use proc_macro2::Ident; use syn::{ parse::{Parse, ParseStream}, diff --git a/wtx-macros/src/pkg/fir/fir_custom_item_values.rs b/wtx-macros/src/client_api_framework/pkg/fir/fir_custom_item_values.rs similarity index 97% rename from wtx-macros/src/pkg/fir/fir_custom_item_values.rs rename to wtx-macros/src/client_api_framework/pkg/fir/fir_custom_item_values.rs index 8b1567a5..1e92f610 100644 --- a/wtx-macros/src/pkg/fir/fir_custom_item_values.rs +++ b/wtx-macros/src/client_api_framework/pkg/fir/fir_custom_item_values.rs @@ -1,4 +1,4 @@ -use crate::pkg::{ +use crate::client_api_framework::pkg::{ enum_struct_or_type::EnumStructOrType, fir::fir_custom_field_field_attr::FirCustomFieldFieldAttr, }; use syn::{punctuated::Punctuated, GenericParam, Ident, Token, Type, WherePredicate}; @@ -26,9 +26,9 @@ macro_rules! create_fir_custom_item_values { $($cb:expr)?, ) => { use crate::{ - item_with_attr_span::ItemWithAttrSpan, + client_api_framework::item_with_attr_span::ItemWithAttrSpan, misc::push_doc_if_inexistent, - pkg::{ + client_api_framework::pkg::{ enum_struct_or_type::EnumStructOrType, fir::{ fir_custom_field_attr::FirCustomFieldAttr, diff --git a/wtx-macros/src/pkg/fir/fir_hook_item_values.rs b/wtx-macros/src/client_api_framework/pkg/fir/fir_hook_item_values.rs similarity index 95% rename from wtx-macros/src/pkg/fir/fir_hook_item_values.rs rename to wtx-macros/src/client_api_framework/pkg/fir/fir_hook_item_values.rs index 52e4a452..076a4281 100644 --- a/wtx-macros/src/pkg/fir/fir_hook_item_values.rs +++ b/wtx-macros/src/client_api_framework/pkg/fir/fir_hook_item_values.rs @@ -7,7 +7,7 @@ macro_rules! create_fir_hook_item_values { $fn_args_idents:expr, $error:ident, ) => { - use crate::item_with_attr_span::ItemWithAttrSpan; + use crate::client_api_framework::item_with_attr_span::ItemWithAttrSpan; use proc_macro2::TokenStream; use syn::{punctuated::Punctuated, FnArg, Item, ItemFn, Pat, Token}; diff --git a/wtx-macros/src/pkg/fir/fir_item_attr.rs b/wtx-macros/src/client_api_framework/pkg/fir/fir_item_attr.rs similarity index 97% rename from wtx-macros/src/pkg/fir/fir_item_attr.rs rename to wtx-macros/src/client_api_framework/pkg/fir/fir_item_attr.rs index 85834c1f..eb334d5f 100644 --- a/wtx-macros/src/pkg/fir/fir_item_attr.rs +++ b/wtx-macros/src/client_api_framework/pkg/fir/fir_item_attr.rs @@ -1,4 +1,4 @@ -use crate::pkg::keywords; +use crate::client_api_framework::pkg::keywords; use proc_macro2::Span; use syn::{ parse::{Parse, ParseBuffer, ParseStream}, diff --git a/wtx-macros/src/pkg/fir/fir_items_values.rs b/wtx-macros/src/client_api_framework/pkg/fir/fir_items_values.rs similarity index 98% rename from wtx-macros/src/pkg/fir/fir_items_values.rs rename to wtx-macros/src/client_api_framework/pkg/fir/fir_items_values.rs index 8de2d4c8..b62f5c3d 100644 --- a/wtx-macros/src/pkg/fir/fir_items_values.rs +++ b/wtx-macros/src/client_api_framework/pkg/fir/fir_items_values.rs @@ -1,4 +1,4 @@ -use crate::{ +use crate::client_api_framework::{ contained_attrs::ContainedAttrs, item_with_attr_span::ItemWithAttrSpan, pkg::{ diff --git a/wtx-macros/src/pkg/fir/fir_params_items_values.rs b/wtx-macros/src/client_api_framework/pkg/fir/fir_params_items_values.rs similarity index 100% rename from wtx-macros/src/pkg/fir/fir_params_items_values.rs rename to wtx-macros/src/client_api_framework/pkg/fir/fir_params_items_values.rs diff --git a/wtx-macros/src/pkg/fir/fir_pkg_attr.rs b/wtx-macros/src/client_api_framework/pkg/fir/fir_pkg_attr.rs similarity index 100% rename from wtx-macros/src/pkg/fir/fir_pkg_attr.rs rename to wtx-macros/src/client_api_framework/pkg/fir/fir_pkg_attr.rs diff --git a/wtx-macros/src/pkg/fir/fir_req_item_values.rs b/wtx-macros/src/client_api_framework/pkg/fir/fir_req_item_values.rs similarity index 100% rename from wtx-macros/src/pkg/fir/fir_req_item_values.rs rename to wtx-macros/src/client_api_framework/pkg/fir/fir_req_item_values.rs diff --git a/wtx-macros/src/pkg/fir/fir_res_item_values.rs b/wtx-macros/src/client_api_framework/pkg/fir/fir_res_item_values.rs similarity index 90% rename from wtx-macros/src/pkg/fir/fir_res_item_values.rs rename to wtx-macros/src/client_api_framework/pkg/fir/fir_res_item_values.rs index c3e055f3..ddacd134 100644 --- a/wtx-macros/src/pkg/fir/fir_res_item_values.rs +++ b/wtx-macros/src/client_api_framework/pkg/fir/fir_res_item_values.rs @@ -1,4 +1,6 @@ -use crate::{item_with_attr_span::ItemWithAttrSpan, misc::push_doc_if_inexistent}; +use crate::{ + client_api_framework::item_with_attr_span::ItemWithAttrSpan, misc::push_doc_if_inexistent, +}; use proc_macro2::Ident; use syn::Item; diff --git a/wtx-macros/src/pkg/keywords.rs b/wtx-macros/src/client_api_framework/pkg/keywords.rs similarity index 100% rename from wtx-macros/src/pkg/keywords.rs rename to wtx-macros/src/client_api_framework/pkg/keywords.rs diff --git a/wtx-macros/src/pkg/misc.rs b/wtx-macros/src/client_api_framework/pkg/misc.rs similarity index 100% rename from wtx-macros/src/pkg/misc.rs rename to wtx-macros/src/client_api_framework/pkg/misc.rs diff --git a/wtx-macros/src/pkg/sir.rs b/wtx-macros/src/client_api_framework/pkg/sir.rs similarity index 100% rename from wtx-macros/src/pkg/sir.rs rename to wtx-macros/src/client_api_framework/pkg/sir.rs diff --git a/wtx-macros/src/pkg/sir/sir_aux_item_values.rs b/wtx-macros/src/client_api_framework/pkg/sir/sir_aux_item_values.rs similarity index 99% rename from wtx-macros/src/pkg/sir/sir_aux_item_values.rs rename to wtx-macros/src/client_api_framework/pkg/sir/sir_aux_item_values.rs index 24c73daf..999fd433 100644 --- a/wtx-macros/src/pkg/sir/sir_aux_item_values.rs +++ b/wtx-macros/src/client_api_framework/pkg/sir/sir_aux_item_values.rs @@ -2,8 +2,7 @@ mod sir_items_values_creators; mod sir_items_values_pushers; use crate::{ - misc::{create_ident, extend_with_tmp_suffix}, - pkg::{ + client_api_framework::pkg::{ fir::{ fir_aux_item_values::FirAuxItemValues, fir_custom_item_values::FirCustomItemValuesRef, fir_params_items_values::FirParamsItemValues, fir_req_item_values::FirReqItemValues, @@ -11,6 +10,7 @@ use crate::{ misc::{from_camel_case_to_snake_case, split_params, EMPTY_GEN_PARAMS}, sir::sir_pkg_attr::SirPkaAttr, }, + misc::{create_ident, extend_with_tmp_suffix}, }; use proc_macro2::{Ident, Span, TokenStream}; use syn::{ diff --git a/wtx-macros/src/pkg/sir/sir_aux_item_values/sir_items_values_creators.rs b/wtx-macros/src/client_api_framework/pkg/sir/sir_aux_item_values/sir_items_values_creators.rs similarity index 99% rename from wtx-macros/src/pkg/sir/sir_aux_item_values/sir_items_values_creators.rs rename to wtx-macros/src/client_api_framework/pkg/sir/sir_aux_item_values/sir_items_values_creators.rs index bbb663b4..e8c3fae0 100644 --- a/wtx-macros/src/pkg/sir/sir_aux_item_values/sir_items_values_creators.rs +++ b/wtx-macros/src/client_api_framework/pkg/sir/sir_aux_item_values/sir_items_values_creators.rs @@ -1,4 +1,4 @@ -use crate::pkg::{ +use crate::client_api_framework::pkg::{ enum_struct_or_type::EnumStructOrType, fir::{fir_aux_item_values::FirAuxItemValues, fir_custom_item_values::FirCustomItemValuesRef}, misc::{ diff --git a/wtx-macros/src/pkg/sir/sir_aux_item_values/sir_items_values_pushers.rs b/wtx-macros/src/client_api_framework/pkg/sir/sir_aux_item_values/sir_items_values_pushers.rs similarity index 99% rename from wtx-macros/src/pkg/sir/sir_aux_item_values/sir_items_values_pushers.rs rename to wtx-macros/src/client_api_framework/pkg/sir/sir_aux_item_values/sir_items_values_pushers.rs index 70aed661..a272d6f0 100644 --- a/wtx-macros/src/pkg/sir/sir_aux_item_values/sir_items_values_pushers.rs +++ b/wtx-macros/src/client_api_framework/pkg/sir/sir_aux_item_values/sir_items_values_pushers.rs @@ -1,4 +1,4 @@ -use crate::pkg::{ +use crate::client_api_framework::pkg::{ data_format::DataFormat, fir::{ fir_aux_item_values::FirAuxItemValues, fir_params_items_values::FirParamsItemValues, diff --git a/wtx-macros/src/pkg/sir/sir_final_values.rs b/wtx-macros/src/client_api_framework/pkg/sir/sir_final_values.rs similarity index 99% rename from wtx-macros/src/pkg/sir/sir_final_values.rs rename to wtx-macros/src/client_api_framework/pkg/sir/sir_final_values.rs index 2986f9f0..f8d969fc 100644 --- a/wtx-macros/src/pkg/sir/sir_final_values.rs +++ b/wtx-macros/src/client_api_framework/pkg/sir/sir_final_values.rs @@ -1,4 +1,4 @@ -use crate::{ +use crate::client_api_framework::{ pkg::{ data_format_elems::DataFormatElems, fir::{ diff --git a/wtx-macros/src/pkg/sir/sir_pkg_attr.rs b/wtx-macros/src/client_api_framework/pkg/sir/sir_pkg_attr.rs similarity index 96% rename from wtx-macros/src/pkg/sir/sir_pkg_attr.rs rename to wtx-macros/src/client_api_framework/pkg/sir/sir_pkg_attr.rs index ab702f2a..e45940e8 100644 --- a/wtx-macros/src/pkg/sir/sir_pkg_attr.rs +++ b/wtx-macros/src/client_api_framework/pkg/sir/sir_pkg_attr.rs @@ -1,4 +1,4 @@ -use crate::{ +use crate::client_api_framework::{ pkg::{data_format::DataFormat, fir::fir_pkg_attr::FirPkgAttr}, transport_group::TransportGroup, }; diff --git a/wtx-macros/src/transport_group.rs b/wtx-macros/src/client_api_framework/transport_group.rs similarity index 100% rename from wtx-macros/src/transport_group.rs rename to wtx-macros/src/client_api_framework/transport_group.rs diff --git a/wtx-macros/src/error.rs b/wtx-macros/src/error.rs index 102bcf7e..aa0fd7ef 100644 --- a/wtx-macros/src/error.rs +++ b/wtx-macros/src/error.rs @@ -21,6 +21,7 @@ pub(crate) enum Error { Syn(syn::Error), UnknownDataFormat, UnknownTransport(Span), + UnsupportedStructure, } impl From for Error { @@ -98,6 +99,7 @@ impl From for syn::Error { Error::Syn(error) => error, Error::UnknownDataFormat => syn::Error::new(Span::call_site(), "Unknown data format."), Error::UnknownTransport(span) => syn::Error::new(span, "Unknown transport."), + Error::UnsupportedStructure => syn::Error::new(Span::call_site(), "Unsupported structure."), } } } diff --git a/wtx-macros/src/http.rs b/wtx-macros/src/http.rs new file mode 100644 index 00000000..0f98935a --- /dev/null +++ b/wtx-macros/src/http.rs @@ -0,0 +1,48 @@ +use quote::quote; +use syn::{parse_macro_input, Data, DeriveInput, Fields}; + +pub(crate) fn conn_aux(item: proc_macro::TokenStream) -> crate::Result { + let input = parse_macro_input::parse::(item)?; + let name = input.ident; + let mut field_names = Vec::new(); + let mut field_tys = Vec::new(); + match input.data { + Data::Struct(data) => match data.fields { + Fields::Named(fields) => { + for elem in fields.named { + field_names.push(elem.ident); + field_tys.push(elem.ty); + } + } + _ => return Err(crate::Error::UnsupportedStructure), + }, + _ => return Err(crate::Error::UnsupportedStructure), + } + let expanded = quote! { + impl wtx::http::server_framework::ConnAux for #name { + type Init = Self; + + #[inline] + fn conn_aux(init: Self::Init) -> wtx::Result { + Ok(init) + } + } + + #( + impl wtx::misc::Lease<#field_tys> for #name { + #[inline] + fn lease(&self) -> &#field_tys { + &self.#field_names + } + } + + impl wtx::misc::LeaseMut<#field_tys> for #name { + #[inline] + fn lease_mut(&mut self) -> &mut #field_tys { + &mut self.#field_names + } + } + )* + }; + Ok(proc_macro::TokenStream::from(expanded)) +} diff --git a/wtx-macros/src/lib.rs b/wtx-macros/src/lib.rs index 549d029f..444f6ddf 100644 --- a/wtx-macros/src/lib.rs +++ b/wtx-macros/src/lib.rs @@ -2,14 +2,10 @@ #![expect(clippy::too_many_lines, reason = "Unimportant")] -mod api_params; -mod contained_attrs; +mod client_api_framework; mod error; -mod item_with_attr_span; +mod http; mod misc; -mod owned_or_ref; -mod pkg; -mod transport_group; use error::Error; @@ -24,7 +20,16 @@ pub fn api_params( attrs: proc_macro::TokenStream, item: proc_macro::TokenStream, ) -> proc_macro::TokenStream { - match api_params::api_params(attrs, item) { + match client_api_framework::api_params::api_params(attrs, item) { + Err(err) => syn::Error::from(err).to_compile_error().into(), + Ok(elem) => elem, + } +} + +/// Connection Auxiliary +#[proc_macro_derive(ConnAux)] +pub fn conn_aux(item: proc_macro::TokenStream) -> proc_macro::TokenStream { + match http::conn_aux(item) { Err(err) => syn::Error::from(err).to_compile_error().into(), Ok(elem) => elem, } @@ -56,7 +61,7 @@ pub fn pkg( attr: proc_macro::TokenStream, item: proc_macro::TokenStream, ) -> proc_macro::TokenStream { - match pkg::pkg(attr, item) { + match client_api_framework::pkg::pkg(attr, item) { Err(err) => syn::Error::from(err).to_compile_error().into(), Ok(elem) => elem, } diff --git a/wtx-ui/src/http_client.rs b/wtx-ui/src/http_client.rs index e31e9c62..051b927a 100644 --- a/wtx-ui/src/http_client.rs +++ b/wtx-ui/src/http_client.rs @@ -23,7 +23,7 @@ pub(crate) async fn http_client(http_client: HttpClient) { rrb .headers .push_from_iter(Header::from_name_and_value( - name.trim().as_bytes(), + name.trim(), values.split(',').map(|el| el.trim().as_bytes()), )) .unwrap(); diff --git a/wtx/Cargo.toml b/wtx/Cargo.toml index f9816e56..e4f304b7 100644 --- a/wtx/Cargo.toml +++ b/wtx/Cargo.toml @@ -8,8 +8,9 @@ chrono = { default-features = false, optional = true, version = "0.4" } cl-aux = { default-features = false, optional = true, features = ["alloc"], version = "5.0" } crypto-common = { default-features = false, optional = true, version = "0.1" } digest = { default-features = false, features = ["mac"], optional = true, version = "0.10" } +embassy-net = { default-features = false, features = ["tcp"], optional = true, version = "0.4" } embedded-io-async = { default-features = false, optional = true, version = "0.6" } -embedded-tls = { default-features = false, optional = true, version = "0.17" } +embedded-tls = { default-features = false, optional = true, git = "https://github.com/drogue-iot/embedded-tls" } fastrand = { default-features = false, optional = true, version = "2.0" } flate2 = { default-features = false, features = ["zlib-rs"], optional = true, version = "1.0" } foldhash = { default-features = false, optional = true, version = "0.1" } @@ -129,6 +130,7 @@ x509-certificate = ["dep:x509-certificate"] _async-tests = ["tokio/macros", "tokio/net", "tokio/rt-multi-thread", "tokio/time"] _bench = [] +_hack = ["embassy-net?/medium-ip", "embassy-net?/proto-ipv4"] _integration-tests = ["serde_json?/raw_value"] _tracing-tree = ["tracing", "tracing-subscriber", "dep:tracing-tree"] diff --git a/wtx/src/database/client/postgres/config.rs b/wtx/src/database/client/postgres/config.rs index a2f393bd..61685cf2 100644 --- a/wtx/src/database/client/postgres/config.rs +++ b/wtx/src/database/client/postgres/config.rs @@ -1,6 +1,6 @@ use crate::{ database::client::postgres::PostgresError, - misc::{into_rslt, str_split1, FromRadix10, UriRef}, + misc::{into_rslt, str_split1, str_split_once1, FromRadix10, UriRef}, }; use core::time::Duration; @@ -29,8 +29,8 @@ impl<'data> Config<'data> { app_name: "", channel_binding: ChannelBinding::Prefer, connect_timeout: Duration::ZERO, - db: uri.relative_reference().get(1..).unwrap_or_default(), - host: uri.host(), + db: uri.path().get(1..).unwrap_or_default(), + host: uri.hostname(), keepalives: true, load_balance_hosts: LoadBalanceHosts::Disable, password: uri.password(), @@ -39,9 +39,15 @@ impl<'data> Config<'data> { tcp_user_timeout: Duration::ZERO, user: uri.user(), }; - for key_value in str_split1(uri.query_and_fragment(), b'&') { - let mut iter = str_split1(key_value, b':'); - if let [Some(key), Some(value)] = [iter.next(), iter.next()] { + let mut pair_iter = str_split1(uri.query_and_fragment(), b'&'); + if let Some(mut key_value) = pair_iter.next() { + key_value = key_value.get(1..).unwrap_or_default(); + if let Some((key, value)) = str_split_once1(key_value, b'=') { + this.set_param(key, value)?; + } + } + for key_value in pair_iter { + if let Some((key, value)) = str_split_once1(key_value, b'=') { this.set_param(key, value)?; } } @@ -111,3 +117,23 @@ pub(crate) enum TargetSessionAttrs { Any, ReadWrite, } + +#[cfg(test)] +mod tests { + use crate::{ + database::client::postgres::{config::ChannelBinding, Config}, + misc::Uri, + }; + + #[test] + fn from_uri() { + let uri = Uri::new("postgres://ab:cd@ef:5432/gh?application_name=ij&channel_binding=disable"); + let config = Config::from_uri(&uri).unwrap(); + assert_eq!(config.app_name, "ij"); + assert_eq!(config.channel_binding, ChannelBinding::Disable); + assert_eq!(config.db, "gh"); + assert_eq!(config.host, "ef"); + assert_eq!(config.password, "cd"); + assert_eq!(config.user, "ab"); + } +} diff --git a/wtx/src/database/client/postgres/executor.rs b/wtx/src/database/client/postgres/executor.rs index e65d8ce1..8b314c6e 100644 --- a/wtx/src/database/client/postgres/executor.rs +++ b/wtx/src/database/client/postgres/executor.rs @@ -54,8 +54,8 @@ where pub async fn connect_encrypted( config: &Config<'_>, mut eb: EB, - mut initial_stream: IS, rng: &mut RNG, + mut stream: IS, cb: impl FnOnce(IS) -> F, ) -> crate::Result where @@ -68,14 +68,14 @@ where { let mut fbw = FilledBufferWriter::from(&mut eb.lease_mut().nb); encrypted_conn(&mut fbw)?; - initial_stream.write_all(fbw._curr_bytes()).await?; + stream.write_all(fbw._curr_bytes()).await?; } let mut buf = [0]; - let _ = initial_stream.read(&mut buf).await?; + let _ = stream.read(&mut buf).await?; if buf[0] != b'S' { return Err(PostgresError::ServerDoesNotSupportEncryption.into()); } - let stream = cb(initial_stream).await?; + let stream = cb(stream).await?; let tls_server_end_point = stream.tls_server_end_point()?; Self::do_connect(config, eb, rng, stream, tls_server_end_point.as_ref().map(Lease::lease)).await } diff --git a/wtx/src/database/client/postgres/executor/authentication.rs b/wtx/src/database/client/postgres/executor/authentication.rs index 8ce9c7b4..178c3f1e 100644 --- a/wtx/src/database/client/postgres/executor/authentication.rs +++ b/wtx/src/database/client/postgres/executor/authentication.rs @@ -76,10 +76,10 @@ where let (method_bytes, method_header) = match (is_scram, is_scram_plus, config.channel_binding) { (false, false, _) => return Err(PostgresError::UnknownAuthenticationMethod.into()), - (true, false | true, ChannelBinding::Disable) | (true, false, ChannelBinding::Prefer) => { + (true, _, ChannelBinding::Disable) | (true, false, ChannelBinding::Prefer) => { (scram_sha_256!().as_slice(), b"n,,".as_slice()) } - (false | true, true, ChannelBinding::Prefer | ChannelBinding::Require) => { + (_, true, ChannelBinding::Prefer | ChannelBinding::Require) => { (scram_sha_256_plus!().as_slice(), b"p=tls-server-end-point,,".as_slice()) } (false, true, ChannelBinding::Disable) => { diff --git a/wtx/src/database/client/postgres/integration_tests.rs b/wtx/src/database/client/postgres/integration_tests.rs index bbba3e08..f2459f47 100644 --- a/wtx/src/database/client/postgres/integration_tests.rs +++ b/wtx/src/database/client/postgres/integration_tests.rs @@ -21,8 +21,8 @@ async fn conn_scram_tls() { let _executor = Executor::::connect_encrypted( &Config::from_uri(&uri).unwrap(), ExecutorBuffer::new(usize::MAX, &mut rng), - TcpStream::connect(uri.hostname_with_implied_port()).await.unwrap(), &mut rng, + TcpStream::connect(uri.hostname_with_implied_port()).await.unwrap(), |stream| async { Ok( crate::misc::TokioRustlsConnector::from_auto() diff --git a/wtx/src/error.rs b/wtx/src/error.rs index 2617e5cf..496c28af 100644 --- a/wtx/src/error.rs +++ b/wtx/src/error.rs @@ -36,6 +36,8 @@ pub enum Error { DecodeError(base64::DecodeError), #[cfg(feature = "base64")] DecodeSliceError(base64::DecodeSliceError), + #[cfg(feature = "embassy-net")] + EmbassyNet(embassy_net::tcp::Error), #[cfg(feature = "base64")] EncodeSliceError(base64::EncodeSliceError), #[cfg(feature = "flate2")] @@ -244,6 +246,14 @@ impl From for Error { } } +#[cfg(feature = "embassy-net")] +impl From for Error { + #[inline] + fn from(from: embassy_net::tcp::Error) -> Self { + Self::EmbassyNet(from) + } +} + #[cfg(feature = "base64")] impl From for Error { #[inline] diff --git a/wtx/src/grpc/grpc_middleware.rs b/wtx/src/grpc/grpc_middleware.rs index fb4394ad..0c3b2b02 100644 --- a/wtx/src/grpc/grpc_middleware.rs +++ b/wtx/src/grpc/grpc_middleware.rs @@ -38,7 +38,7 @@ where Header { is_sensitive: false, is_trailer: true, - name: b"grpc-status", + name: "grpc-status", value: [stream_aux.status_code_mut().number_as_str().as_bytes()].into_iter(), }, ])?; diff --git a/wtx/src/http/header_name.rs b/wtx/src/http/header_name.rs index b36141b6..364855dc 100644 --- a/wtx/src/http/header_name.rs +++ b/wtx/src/http/header_name.rs @@ -40,17 +40,10 @@ macro_rules! create_statics { } } - impl From for HeaderNameStaticBytes { + impl From for HeaderName<&str> { #[inline] fn from(from: KnownHeaderName) -> Self { - HeaderNameStaticBytes::new(<&[u8]>::from(from)) - } - } - - impl From for HeaderNameStaticStr { - #[inline] - fn from(from: KnownHeaderName) -> Self { - HeaderNameStaticStr::new(<&str>::from(from)) + Self(<&str>::from(from)) } } @@ -71,21 +64,23 @@ macro_rules! create_statics { impl TryFrom> for KnownHeaderName where - S: Lease<[u8]> + S: Lease { type Error = HttpError; #[inline] fn try_from(from: HeaderName) -> Result { - KnownHeaderName::try_from(from.bytes()) + KnownHeaderName::try_from(from.str().as_bytes()) } } }; } +use core::str; + use crate::misc::Lease; -const HTTP2P_TABLE: &[u8; 256] = &[ +const TABLE: &[u8; 256] = &[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, b'!', b'"', b'#', b'$', b'%', b'&', b'\'', 0, 0, b'*', b'+', 0, b'-', b'.', 0, b'0', b'1', b'2', b'3', b'4', b'5', b'6', b'7', b'8', b'9', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -100,57 +95,69 @@ const HTTP2P_TABLE: &[u8; 256] = &[ use crate::http::HttpError; -/// [`HeaderName`] composed by static bytes. -pub type HeaderNameStaticBytes = HeaderName<&'static [u8]>; -/// [`HeaderName`] composed by a static string. -pub type HeaderNameStaticStr = HeaderName<&'static str>; - /// HTTP header name -#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] +#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] pub struct HeaderName(S); impl HeaderName where - S: Lease<[u8]>, + S: Lease, { - /// HTTP/2 Plus - /// - /// Expects a valid HTTP/2 or HTTP/3 content. + /// New generic instance that doesn't verify `content`. #[inline] - pub fn http2p(content: S) -> Result { - _iter4!(content.lease(), {}, |byte| { - if let Some(elem) = HTTP2P_TABLE.get(usize::from(*byte)).copied() { - if elem == 0 { - return Err(HttpError::InvalidHttp2pContent); - } - } - }); - Ok(Self(content)) + pub const fn new_unchecked(content: S) -> Self { + Self(content) } - /// Instance from a generic type content. + /// Generic type content. #[inline] - pub const fn new(content: S) -> Self { - Self(content) + pub const fn content(&self) -> &S { + &self.0 } - /// Generic type content in bytes form + /// Generic type content in string form #[inline] - pub fn bytes(&self) -> &[u8] { + pub fn str(&self) -> &str { self.0.lease() } - /// Generic type content. #[inline] - pub const fn content(&self) -> &S { - &self.0 + const fn check(content: &[u8]) -> Result<&str, HttpError> { + let mut idx: usize = 0; + loop { + if idx >= content.len() { + break; + } + #[expect(clippy::as_conversions, reason = "Lack of const traits")] + #[expect( + clippy::indexing_slicing, + reason = "An array of 256 elements will never panic with a 0..255 index" + )] + if TABLE[content[idx] as usize] == 0 { + return Err(HttpError::InvalidHttp2pContent); + } + idx = idx.wrapping_add(1); + } + // SAFETY: `TABLE` is a subset of ASCII + unsafe { Ok(str::from_utf8_unchecked(content)) } + } +} + +impl<'this> HeaderName<&'this str> { + /// New instance from a set of bytes + #[inline] + pub const fn from_bytes(content: &'this [u8]) -> Result { + match Self::check(content) { + Ok(elem) => Ok(HeaderName(elem)), + Err(err) => Err(err), + } } } impl<'hn> From> for HeaderName<&'hn [u8]> { #[inline] fn from(from: HeaderName<&'hn str>) -> Self { - Self::new(from.0.as_bytes()) + Self(from.0.as_bytes()) } } @@ -230,20 +237,3 @@ create_statics! { Warning = "warning"; WwwAuthenticate = "www-authenticate"; } - -#[cfg(all(feature = "_bench", test))] -mod bench { - use crate::{http::HeaderName, misc::Vector}; - - #[bench] - fn http2p(b: &mut test::Bencher) { - const LEN: usize = 32; - let mut data: Vector = Vector::with_capacity(LEN).unwrap(); - for idx in 0..LEN { - data.push(idx.clamp(106, 122).try_into().unwrap()).unwrap(); - } - b.iter(|| { - let _ = HeaderName::http2p(&data).unwrap(); - }); - } -} diff --git a/wtx/src/http/headers.rs b/wtx/src/http/headers.rs index 71fb3c20..d95b266d 100644 --- a/wtx/src/http/headers.rs +++ b/wtx/src/http/headers.rs @@ -1,5 +1,5 @@ -use crate::misc::{BytesFmt, Lease, LeaseMut, Vector}; -use core::{fmt::Arguments, ptr}; +use crate::misc::{Lease, LeaseMut, Vector}; +use core::{fmt::Arguments, ptr, str}; /// Determines how trailers are placed in the headers #[derive(Clone, Copy, Debug)] @@ -58,7 +58,7 @@ impl Headers { /// ```rust /// use wtx::http::{Header, Headers}; /// let mut headers = Headers::new(); - /// headers.push_from_iter(Header::from_name_and_value(b"name", ["value".as_bytes()])).unwrap(); + /// headers.push_from_iter(Header::from_name_and_value("name", ["value".as_bytes()])).unwrap(); /// assert_eq!(headers.bytes_len(), 9); /// assert_eq!(headers.headers_len(), 1); /// headers.clear(); @@ -82,7 +82,7 @@ impl Headers { /// Returns the header that is referenced by `name`, if any. #[inline] pub fn get_by_name(&self, name: &[u8]) -> Option> { - self.iter().find(|el| el.name == name) + self.iter().find(|el| el.name.as_bytes() == name) } /// Returns all first optional headers that are referenced by `names`. @@ -90,7 +90,7 @@ impl Headers { /// ```rust /// use wtx::http::{Header, Headers}; /// let mut headers = Headers::new(); - /// headers.push_from_iter(Header::from_name_and_value(b"name0", [])).unwrap(); + /// headers.push_from_iter(Header::from_name_and_value("name0", [])).unwrap(); /// let array = headers.get_many_by_name(["name0".as_bytes(), "name1".as_bytes()]); /// assert!(array[0].is_some()); /// assert!(array[1].is_none()); @@ -103,7 +103,7 @@ impl Headers { let mut rslt = [None; N]; for header in self.iter() { for (name, opt) in names.into_iter().zip(&mut rslt) { - if name == header.name { + if name == header.name.as_bytes() { *opt = Some(header); break; } @@ -129,7 +129,7 @@ impl Headers { /// ```rust /// use wtx::http::{Header, Headers}; /// let mut headers = Headers::new(); - /// headers.push_from_iter(Header::from_name_and_value(b"name", ["value".as_bytes()])).unwrap(); + /// headers.push_from_iter(Header::from_name_and_value("name", ["value".as_bytes()])).unwrap(); /// assert_eq!(headers.bytes_len(), 9); /// assert_eq!(headers.headers_len(), 1); /// let _ = headers.pop(); @@ -154,8 +154,8 @@ impl Headers { /// ```rust /// use wtx::http::{Header, Headers}; /// let mut headers = Headers::new(); - /// headers.push_from_fmt(Header::from_name_and_value(b"name", format_args!("{}", 1))).unwrap(); - /// assert_eq!(headers.get_by_idx(0).unwrap(), Header::from_name_and_value(b"name", "1".as_bytes())); + /// headers.push_from_fmt(Header::from_name_and_value("name", format_args!("{}", 1))).unwrap(); + /// assert_eq!(headers.get_by_idx(0).unwrap(), Header::from_name_and_value("name", "1".as_bytes())); /// ``` #[inline(always)] pub fn push_from_fmt(&mut self, header: Header<'_, Arguments<'_>>) -> crate::Result<()> { @@ -163,12 +163,12 @@ impl Headers { #[cfg(feature = "std")] { use std::io::Write; - self.bytes.write_fmt(format_args!("{}{}", BytesFmt(header.name), header.value))?; + self.bytes.write_fmt(format_args!("{}{}", header.name, header.value))?; } #[cfg(not(feature = "std"))] { use core::fmt::Write; - self.bytes.write_fmt(format_args!("{}{}", BytesFmt(header.name), header.value))?; + self.bytes.write_fmt(format_args!("{}{}", header.name, header.value))?; } let prev_len = self.headers_parts.len(); self.headers_parts.push(HeaderParts { @@ -188,8 +188,8 @@ impl Headers { /// ```rust /// use wtx::http::{Header, Headers}; /// let mut headers = Headers::new(); - /// headers.push_from_iter(Header::from_name_and_value(b"name", ["value0".as_bytes(), "_value1".as_bytes()])).unwrap(); - /// assert_eq!(headers.get_by_idx(0).unwrap(), Header::from_name_and_value(b"name", "value0_value1".as_bytes())); + /// headers.push_from_iter(Header::from_name_and_value("name", ["value0".as_bytes(), "_value1".as_bytes()])).unwrap(); + /// assert_eq!(headers.get_by_idx(0).unwrap(), Header::from_name_and_value("name", "value0_value1".as_bytes())); /// ``` #[inline(always)] pub fn push_from_iter<'bytes, V>(&mut self, header: Header<'bytes, V>) -> crate::Result<()> @@ -214,7 +214,7 @@ impl Headers { let header_begin = self.bytes.len(); let ptr = self.bytes.as_ptr_mut(); let mut header_end = header_begin; - copy(&mut header_end, ptr, header.name); + copy(&mut header_end, ptr, header.name.as_bytes()); let header_name_end = header_end; for value in iter { copy(&mut header_end, ptr, value); @@ -274,7 +274,7 @@ impl Headers { } #[inline] - fn header_len<'bytes>(header_name: &[u8], iter: impl Iterator) -> usize { + fn header_len<'bytes>(header_name: &str, iter: impl Iterator) -> usize { let mut header_len = header_name.len(); for elem in iter { header_len = header_len.wrapping_add(elem.len()); @@ -311,7 +311,11 @@ impl Headers { Header { is_sensitive, is_trailer, - name: bytes.get(header_begin..header_name_end).unwrap_or_default(), + name: { + let str = bytes.get(header_begin..header_name_end).unwrap_or_default(); + // SAFETY: Input methods only accept UTF-8 data + unsafe { str::from_utf8_unchecked(str) } + }, value: bytes.get(header_name_end..header_end).unwrap_or_default(), } } @@ -350,7 +354,7 @@ pub struct Header<'any, V> { /// The applicability and semantics depends on the HTTP version. pub is_trailer: bool, /// Header name - pub name: &'any [u8], + pub name: &'any str, /// Header value pub value: V, } @@ -358,7 +362,7 @@ pub struct Header<'any, V> { impl<'any, V> Header<'any, V> { /// Sets `is_sensitive` and `is_trailer` to `false`. #[inline] - pub fn from_name_and_value(name: &'any [u8], value: V) -> Self { + pub fn from_name_and_value(name: &'any str, value: V) -> Self { Self { is_sensitive: false, is_trailer: false, name, value } } } diff --git a/wtx/src/http/server_framework/methods.rs b/wtx/src/http/server_framework/methods.rs index dbfcb1a3..63445f3f 100644 --- a/wtx/src/http/server_framework/methods.rs +++ b/wtx/src/http/server_framework/methods.rs @@ -10,7 +10,6 @@ fn check_method(expected: Method, received: Method) -> Result<(), E> where E: From, { - std::dbg!(received); if expected != received { return Err(E::from(crate::Error::from(HttpError::UnexpectedHttpMethod { expected }))); } diff --git a/wtx/src/http/server_framework/server_framework_builder.rs b/wtx/src/http/server_framework/server_framework_builder.rs index 8bc2ce30..bac42e15 100644 --- a/wtx/src/http/server_framework/server_framework_builder.rs +++ b/wtx/src/http/server_framework/server_framework_builder.rs @@ -79,7 +79,7 @@ impl ServerFrameworkBuilder where CA: ConnAux, { - /// Sets the initializing strut for `CAA` and sets the request auxiliary to `()`. + /// Sets the initializing strut for `CA` and sets the request auxiliary to `()`. #[inline] pub fn with_conn_aux( self, diff --git a/wtx/src/http/server_framework/tokio.rs b/wtx/src/http/server_framework/tokio.rs index bad53771..5d021c7b 100644 --- a/wtx/src/http/server_framework/tokio.rs +++ b/wtx/src/http/server_framework/tokio.rs @@ -44,7 +44,7 @@ where host: &str, rng: RNG, err_cb: impl Clone + Fn(E) + Send + 'static, - operation_mode: impl Clone + Fn(Request<&mut ReqResBuffer>) -> Result<(), E> + Send + Sync + 'static, + headers_cb: impl Clone + Fn(Request<&mut ReqResBuffer>) -> Result<(), E> + Send + Sync + 'static, ) -> crate::Result<()> where RNG: Clone + Rng + Send + 'static, @@ -58,7 +58,7 @@ where Self::tokio_manual, move |_, _, req, sa| { let rslt = Self::_route_params(req.rrd.uri.path(), &sa.1)?; - operation_mode(req)?; + headers_cb(req)?; Ok(rslt) }, move || Ok(((_sa_cb.clone(), Arc::clone(&_router)), ReqResBuffer::empty())), @@ -116,7 +116,7 @@ where host: &str, rng: RNG, err_cb: impl Clone + Fn(E) + Send + 'static, - operation_mode: impl Clone + Fn(Request<&mut ReqResBuffer>) -> Result<(), E> + Send + Sync + 'static, + headers_cb: impl Clone + Fn(Request<&mut ReqResBuffer>) -> Result<(), E> + Send + Sync + 'static, ) -> crate::Result<()> where RNG: Clone + Rng + Send + 'static, @@ -130,7 +130,7 @@ where Self::tokio_rustls_manual, move |_, _, req, sa| { let rslt = Self::_route_params(req.rrd.uri.path(), &sa.1)?; - operation_mode(req)?; + headers_cb(req)?; Ok(rslt) }, move || Ok(((_sa_cb.clone(), Arc::clone(&_router)), ReqResBuffer::empty())), diff --git a/wtx/src/http/session.rs b/wtx/src/http/session.rs index 5985c2be..cbe87593 100644 --- a/wtx/src/http/session.rs +++ b/wtx/src/http/session.rs @@ -1,49 +1,18 @@ -mod session_builder; mod session_decoder; mod session_enforcer; mod session_error; mod session_manager; +mod session_manager_builder; mod session_state; mod session_store; -use crate::http::server_framework::ConnAux; -pub use session_builder::SessionBuilder; pub use session_decoder::SessionDecoder; pub use session_enforcer::SessionEnforcer; pub use session_error::SessionError; -pub use session_manager::{SessionManager, SessionManagerInner}; +pub use session_manager::{SessionManager, SessionManagerInner, SessionManagerTokio}; +pub use session_manager_builder::SessionManagerBuilder; pub use session_state::SessionState; pub use session_store::SessionStore; type SessionId = [u8; 16]; type SessionKey = [u8; 32]; -/// [`Session`] backed by `tokio` -#[cfg(feature = "tokio")] -pub type SessionTokio = - Session>>, S>; - -/// Allows the management of state across requests within a connection. -#[derive(Clone, Debug)] -pub struct Session { - /// Manager - pub manager: SessionManager, - /// Store - pub store: S, -} - -impl Session { - /// Allows the specification of custom parameters. - #[inline] - pub fn builder(store: S) -> SessionBuilder { - SessionBuilder::new(store) - } -} - -impl ConnAux for Session { - type Init = Self; - - #[inline] - fn conn_aux(init: Self::Init) -> crate::Result { - Ok(init) - } -} diff --git a/wtx/src/http/session/session_decoder.rs b/wtx/src/http/session/session_decoder.rs index 1d23f4b7..537a02a6 100644 --- a/wtx/src/http/session/session_decoder.rs +++ b/wtx/src/http/session/session_decoder.rs @@ -2,10 +2,10 @@ use crate::{ http::{ cookie::{decrypt, CookieBytes}, server_framework::Middleware, - KnownHeaderName, ReqResBuffer, Request, Response, Session, SessionError, SessionManagerInner, - SessionState, SessionStore, StatusCode, + KnownHeaderName, ReqResBuffer, Request, Response, SessionError, SessionManager, + SessionManagerInner, SessionState, SessionStore, StatusCode, }, - misc::{GenericTime, LeaseMut, Lock}, + misc::{GenericTime, Lease, LeaseMut, Lock}, pool::{Pool, ResourceManager}, }; use chrono::DateTime; @@ -18,19 +18,21 @@ use serde::de::DeserializeOwned; /// is a NO-OP. #[derive(Debug)] pub struct SessionDecoder { - session: Session, + session_manager: SessionManager, + session_store: S, } impl SessionDecoder { /// New instance #[inline] - pub fn new(session: Session) -> Self { - Self { session } + pub fn new(session_manager: SessionManager, session_store: S) -> Self { + Self { session_manager, session_store } } } impl Middleware for SessionDecoder where + CA: LeaseMut>>, CS: DeserializeOwned + PartialEq, E: From, I: Lock>, @@ -49,26 +51,26 @@ where #[inline] async fn req( &self, - _: &mut CA, + ca: &mut CA, _: &mut Self::Aux, req: &mut Request, _: &mut SA, ) -> Result, E> { - let mut guard = self.session.manager.inner.lock().await; - let SessionManagerInner { cookie_def, key, state, .. } = &mut *guard; - if let Some(elem) = state { + let mut session_guard = self.session_manager.inner.lock().await; + let SessionManagerInner { cookie_def, key, .. } = &mut *session_guard; + if let Some(elem) = ca.lease() { if let Some(expire) = &elem.expire { let millis = i64::try_from(GenericTime::timestamp()?.as_millis()).unwrap_or_default(); let date_time = DateTime::from_timestamp_millis(millis).unwrap_or_default(); if expire >= &date_time { - let _rslt = self.session.store.get(&(), &()).await?.lease_mut().delete(&elem.id).await; + let _rslt = self.session_store.get(&(), &()).await?.lease_mut().delete(&elem.id).await; return Err(crate::Error::from(SessionError::ExpiredSession).into()); } } return Ok(ControlFlow::Continue(())); } for header in req.rrd.headers.iter() { - if header.name != <&[u8]>::from(KnownHeaderName::Cookie) { + if header.name != <&str>::from(KnownHeaderName::Cookie) { continue; } let cookie = CookieBytes::parse(header.value, &mut cookie_def.value)?; @@ -88,16 +90,19 @@ where let rslt_des = serde_json::from_slice(&cookie_def.value).map_err(Into::into); cookie_def.value.clear(); let state_des: SessionState = rslt_des?; - let state_db_opt = - self.session.store.get(&(), &()).await?.lease_mut().read(&state_des.id).await?; + let state_db_opt = { + let mut guard = self.session_store.get(&(), &()).await?; + guard.lease_mut().read(&state_des.id).await? + }; let Some(state_db) = state_db_opt else { return Err(crate::Error::from(SessionError::MissingStoredSession).into()); }; if state_db != state_des { - self.session.store.get(&(), &()).await?.lease_mut().delete(&state_des.id).await?; + self.session_store.get(&(), &()).await?.lease_mut().delete(&state_des.id).await?; return Err(crate::Error::from(SessionError::InvalidStoredSession).into()); } - *state = Some(state_des); + let session_state: &mut Option<_> = ca.lease_mut(); + *session_state = Some(state_des); break; } Ok(ControlFlow::Continue(())) diff --git a/wtx/src/http/session/session_enforcer.rs b/wtx/src/http/session/session_enforcer.rs index 660fe27e..9d6dd25f 100644 --- a/wtx/src/http/session/session_enforcer.rs +++ b/wtx/src/http/session/session_enforcer.rs @@ -1,33 +1,31 @@ use crate::{ http::{ - server_framework::Middleware, ReqResBuffer, Request, Response, Session, SessionError, - SessionManagerInner, StatusCode, + server_framework::Middleware, ReqResBuffer, Request, Response, SessionError, SessionState, + StatusCode, }, - misc::Lock, + misc::LeaseMut, }; -use core::ops::ControlFlow; +use core::{marker::PhantomData, ops::ControlFlow}; /// Enforces stored session in all requests. -/// -/// #[derive(Debug)] -pub struct SessionEnforcer { +pub struct SessionEnforcer { denied: [&'static str; N], - session: Session, + phantom: PhantomData, } -impl SessionEnforcer { +impl SessionEnforcer { /// Creates a new instance with paths that are not taken into consideration. #[inline] - pub fn new(denied: [&'static str; N], session: Session) -> Self { - Self { denied, session } + pub fn new(denied: [&'static str; N]) -> Self { + Self { denied, phantom: PhantomData } } } -impl Middleware for SessionEnforcer +impl Middleware for SessionEnforcer where + CA: LeaseMut>>, E: From, - I: Lock>, { type Aux = (); @@ -39,7 +37,7 @@ where #[inline] async fn req( &self, - _: &mut CA, + ca: &mut CA, _: &mut Self::Aux, req: &mut Request, _: &mut SA, @@ -48,7 +46,7 @@ where if self.denied.iter().all(|elem| *elem != path) { return Ok(ControlFlow::Continue(())); } - if self.session.manager.inner.lock().await.state().is_none() { + if ca.lease_mut().is_none() { return Err(crate::Error::from(SessionError::RequiredSessionInPath).into()); } Ok(ControlFlow::Continue(())) diff --git a/wtx/src/http/session/session_manager.rs b/wtx/src/http/session/session_manager.rs index 398f2abe..990ce179 100644 --- a/wtx/src/http/session/session_manager.rs +++ b/wtx/src/http/session/session_manager.rs @@ -2,7 +2,8 @@ use crate::{ http::{ cookie::{encrypt, CookieGeneric}, session::SessionKey, - Header, KnownHeaderName, ReqResBuffer, ReqResDataMut, SessionState, SessionStore, + Header, KnownHeaderName, ReqResBuffer, ReqResDataMut, SessionManagerBuilder, SessionState, + SessionStore, }, misc::{GenericTime, Lease, LeaseMut, Lock, Rng, Vector}, }; @@ -10,6 +11,11 @@ use chrono::DateTime; use core::marker::PhantomData; use serde::Serialize; +/// [`Session`] backed by `tokio` +#[cfg(feature = "tokio")] +pub type SessionManagerTokio = + SessionManager>>>; + /// Manages sessions #[derive(Clone, Debug)] pub struct SessionManager { @@ -22,28 +28,37 @@ where E: From, I: Lock>, { + /// Allows the specification of custom parameters. + #[inline] + pub fn builder() -> SessionManagerBuilder { + SessionManagerBuilder::new() + } + /// Removes the session from the store and also modifies headers. #[inline] pub async fn delete_session_cookie( &mut self, rrd: &mut RRD, + state: &mut Option>, store: &mut S, ) -> Result<(), E> where RRD: ReqResDataMut, S: SessionStore, { - let SessionManagerInner { cookie_def, phantom: _, key: _, state } = - &mut *self.inner.lock().await; + let SessionManagerInner { cookie_def, phantom: _, key: _ } = &mut *self.inner.lock().await; if let Some(elem) = state.take() { store.delete(&elem.id).await?; } + let prev_expire = cookie_def.expire; cookie_def.expire = Some(DateTime::from_timestamp_nanos(0)); cookie_def.value.clear(); - rrd.headers_mut().push_from_fmt(Header::from_name_and_value( + let rslt = rrd.headers_mut().push_from_fmt(Header::from_name_and_value( KnownHeaderName::SetCookie.into(), format_args!("{cookie_def}"), - ))?; + )); + cookie_def.expire = prev_expire; + rslt?; Ok(()) } @@ -64,7 +79,7 @@ where RRD: LeaseMut, S: SessionStore, { - let SessionManagerInner { cookie_def, phantom: _, key, state } = &mut *self.inner.lock().await; + let SessionManagerInner { cookie_def, phantom: _, key } = &mut *self.inner.lock().await; cookie_def.value.clear(); let id = GenericTime::timestamp().map_err(Into::into)?.as_nanos().to_be_bytes(); let local_state = if let Some(elem) = cookie_def.expire { @@ -76,7 +91,6 @@ where }; let idx = rrd.lease().body.len(); serde_json::to_writer(&mut rrd.lease_mut().body, &local_state).map_err(Into::into)?; - *state = Some(local_state); let rslt = encrypt( &mut cookie_def.value, key, @@ -98,14 +112,5 @@ where pub struct SessionManagerInner { pub(crate) cookie_def: CookieGeneric<&'static [u8], Vector>, pub(crate) key: SessionKey, - pub(crate) phantom: PhantomData, - pub(crate) state: Option>, -} - -impl SessionManagerInner { - /// State saved in the store or in the current session. - #[inline] - pub fn state(&self) -> &Option> { - &self.state - } + pub(crate) phantom: PhantomData<(CS, E)>, } diff --git a/wtx/src/http/session/session_builder.rs b/wtx/src/http/session/session_manager_builder.rs similarity index 85% rename from wtx/src/http/session/session_builder.rs rename to wtx/src/http/session/session_manager_builder.rs index 95088506..bad46428 100644 --- a/wtx/src/http/session/session_builder.rs +++ b/wtx/src/http/session/session_manager_builder.rs @@ -2,7 +2,7 @@ use crate::{ http::{ cookie::{CookieGeneric, SameSite}, session::{SessionKey, SessionManagerInner}, - Session, SessionManager, SessionStore, + SessionManager, SessionStore, }, misc::{sleep, Lock, Rng, Vector}, }; @@ -11,15 +11,14 @@ use core::{future::Future, marker::PhantomData, time::Duration}; /// Default and optional parameters for the construction of a [`Session`]. #[derive(Debug)] -pub struct SessionBuilder { +pub struct SessionManagerBuilder { pub(crate) cookie_def: CookieGeneric<&'static [u8], Vector>, pub(crate) inspection_interval: Duration, - pub(crate) store: SS, } -impl SessionBuilder { +impl SessionManagerBuilder { #[inline] - pub(crate) const fn new(store: SS) -> Self { + pub(crate) const fn new() -> Self { Self { cookie_def: CookieGeneric { domain: &[], @@ -33,7 +32,6 @@ impl SessionBuilder { value: Vector::new(), }, inspection_interval: Duration::from_secs(60 * 30), - store, } } @@ -46,10 +44,11 @@ impl SessionBuilder { /// If the backing store already has a system that automatically removes outdated sessions like /// SQL triggers, then the [`Future`] can be ignored. #[inline] - pub fn build_generating_key( + pub fn build_generating_key( self, rng: &mut RNG, - ) -> (impl Future>, Session) + session_store: &mut SS, + ) -> (impl Future>, SessionManager) where E: From, I: Lock>, @@ -58,7 +57,7 @@ impl SessionBuilder { { let mut key = [0; 32]; rng.fill_slice(&mut key); - Self::build_with_key(self, key) + Self::build_with_key(self, key, session_store) } /// Creates a new [`Session`] with the provided `key`. @@ -69,17 +68,18 @@ impl SessionBuilder { /// If the backing store already has a system that automatically removes outdated sessions like /// SQL triggers, then the [`Future`] can be ignored. #[inline] - pub fn build_with_key( + pub fn build_with_key( self, key: SessionKey, - ) -> (impl Future>, Session) + session_store: &mut SS, + ) -> (impl Future>, SessionManager) where E: From, I: Lock>, SS: Clone + SessionStore, { - let Self { cookie_def, inspection_interval, store } = self; - let mut local_store = store.clone(); + let Self { cookie_def, inspection_interval } = self; + let mut local_store = session_store.clone(); ( async move { loop { @@ -87,11 +87,8 @@ impl SessionBuilder { sleep(inspection_interval).await.map_err(Into::into)?; } }, - Session { - manager: SessionManager { - inner: I::new(SessionManagerInner { cookie_def, phantom: PhantomData, key, state: None }), - }, - store, + SessionManager { + inner: I::new(SessionManagerInner { cookie_def, phantom: PhantomData, key }), }, ) } diff --git a/wtx/src/http/session/session_state.rs b/wtx/src/http/session/session_state.rs index 4e13d8e7..bd0f6ad3 100644 --- a/wtx/src/http/session/session_state.rs +++ b/wtx/src/http/session/session_state.rs @@ -2,7 +2,7 @@ use crate::http::session::SessionId; use chrono::{DateTime, Utc}; /// Data that is saved in the corresponding store. -#[derive(Debug, PartialEq, serde::Deserialize, serde::Serialize)] +#[derive(Clone, Copy, Debug, PartialEq, serde::Deserialize, serde::Serialize)] pub struct SessionState { /// Custom state pub custom_state: CS, diff --git a/wtx/src/http2/headers_frame.rs b/wtx/src/http2/headers_frame.rs index e51c992d..378f023a 100644 --- a/wtx/src/http2/headers_frame.rs +++ b/wtx/src/http2/headers_frame.rs @@ -1,5 +1,5 @@ use crate::{ - http::{Header, HeaderName, KnownHeaderName, Method, ReqResBuffer}, + http::{Header, KnownHeaderName, Method, ReqResBuffer}, http2::{ common_flags::CommonFlags, frame_init::{FrameInit, FrameInitTy}, @@ -100,11 +100,11 @@ impl<'uri> HeadersFrame<'uri> { &mut is_malformed, &mut is_over_size, max_headers_len, - name, + name.str(), value, ); } - HpackHeaderBasic::Field => match KnownHeaderName::try_from(name) { + HpackHeaderBasic::Field => match KnownHeaderName::try_from(name.str().as_bytes()) { Ok( KnownHeaderName::Connection | KnownHeaderName::KeepAlive @@ -118,24 +118,20 @@ impl<'uri> HeadersFrame<'uri> { is_malformed = true; } _ => { - let header_name = match HeaderName::http2p(name) { - Ok(header_name) => header_name, - Err(_err) => { - return Err(protocol_err(Http2Error::InvalidHeaderData)); - } - }; has_fields = true; - let len = decoded_header_size(name.len(), value.len()); + let len = decoded_header_size(name.str().len(), value.len()); expanded_headers_len = expanded_headers_len.wrapping_add(len); is_over_size = expanded_headers_len >= max_headers_len; if !is_over_size { - if let Ok(KnownHeaderName::ContentLength) = KnownHeaderName::try_from(header_name) { + if let Ok(KnownHeaderName::ContentLength) = + KnownHeaderName::try_from(name.str().as_bytes()) + { content_length = Some(usize::from_radix_10(value)?); } rrb_headers.push_from_iter(Header { is_sensitive: false, is_trailer: IS_TRAILER, - name, + name: name.str(), value: [value], })?; } @@ -149,7 +145,7 @@ impl<'uri> HeadersFrame<'uri> { &mut is_over_size, method.is_some(), max_headers_len, - name, + name.str(), value, ) { method = Some(local_method); @@ -163,7 +159,7 @@ impl<'uri> HeadersFrame<'uri> { &mut is_malformed, &mut is_over_size, max_headers_len, - name, + name.str(), value, ); } @@ -175,7 +171,7 @@ impl<'uri> HeadersFrame<'uri> { &mut is_over_size, protocol.is_some(), max_headers_len, - name, + name.str(), value, ) { protocol = Some(local_protocol); @@ -189,7 +185,7 @@ impl<'uri> HeadersFrame<'uri> { &mut is_malformed, &mut is_over_size, max_headers_len, - name, + name.str(), value, ); } @@ -201,7 +197,7 @@ impl<'uri> HeadersFrame<'uri> { &mut is_over_size, status.is_some(), max_headers_len, - name, + name.str(), value, ) { status = Some(local_status); @@ -282,7 +278,7 @@ fn push_enum( is_over_size: &mut bool, is_some: bool, max_headers_len: usize, - name: &[u8], + name: &str, value: &[u8], ) -> bool { if *has_fields || is_some { @@ -304,7 +300,7 @@ fn push_uri( is_malformed: &mut bool, is_over_size: &mut bool, max_headers_len: usize, - name: &[u8], + name: &str, value: &[u8], ) { if *has_fields || !buffer.is_empty() { diff --git a/wtx/src/http2/hpack_decoder.rs b/wtx/src/http2/hpack_decoder.rs index ef9dfdb3..a22a05ac 100644 --- a/wtx/src/http2/hpack_decoder.rs +++ b/wtx/src/http2/hpack_decoder.rs @@ -1,5 +1,5 @@ use crate::{ - http::{KnownHeaderName, Method, StatusCode, _HeaderNameBuffer, _HeaderValueBuffer}, + http::{HeaderName, KnownHeaderName, Method, StatusCode, _HeaderNameBuffer, _HeaderValueBuffer}, http2::{ hpack_header::{HpackHeaderBasic, HpackHeaderName}, hpack_headers::HpackHeaders, @@ -13,7 +13,8 @@ use alloc::boxed::Box; const DYN_IDX_OFFSET: usize = 62; -type RawHeaderPair<'value> = (&'static [u8], &'value [u8]); +type RawHeaderName<'value> = (HeaderName<&'static str>, HeaderName<&'value str>); +type RawHeaderValue<'value> = (&'static [u8], &'value [u8]); #[derive(Debug)] pub(crate) struct HpackDecoder { @@ -45,7 +46,7 @@ impl HpackDecoder { pub(crate) fn decode( &mut self, mut data: &[u8], - mut cb: impl FnMut((HpackHeaderBasic, &[u8], &[u8])) -> crate::Result<()>, + mut cb: impl FnMut((HpackHeaderBasic, HeaderName<&str>, &[u8])) -> crate::Result<()>, ) -> crate::Result<()> { if let Some(elem) = self.max_bytes.1.take() { self.dyn_headers.set_max_bytes(*Usize::from(elem), |_| {}); @@ -131,11 +132,11 @@ impl HpackDecoder { &mut self, data: &mut &[u8], mask: u8, - elem_cb: &mut impl FnMut((HpackHeaderBasic, &[u8], &[u8])) -> crate::Result<()>, + elem_cb: &mut impl FnMut((HpackHeaderBasic, HeaderName<&str>, &[u8])) -> crate::Result<()>, ) -> crate::Result<()> { let idx = Self::decode_integer(data, mask)?.1; let has_indexed_name = idx != 0; - let (hhb, name, value) = if has_indexed_name { + if has_indexed_name { let value = Self::decode_string_value(&mut self.header_buffers.1, data)?; let (hhb, (static_name, dyn_name), _) = Self::get(&self.dyn_headers, *Usize::from(idx))?; let new_hhb = match hhb { @@ -147,25 +148,28 @@ impl HpackDecoder { HpackHeaderBasic::Scheme => HpackHeaderBasic::Scheme, HpackHeaderBasic::StatusCode(_) => HpackHeaderBasic::StatusCode(value.try_into()?), }; - let name = if static_name.is_empty() { + let name = if static_name.str().is_empty() { elem_cb((new_hhb, dyn_name, value))?; self.header_buffers.0.clear(); - self.header_buffers.0.extend_from_copyable_slice(dyn_name)?; - self.header_buffers.0.get_mut(..dyn_name.len()).unwrap_or_default() + self.header_buffers.0.extend_from_copyable_slice(dyn_name.str().as_bytes())?; + let bytes = self.header_buffers.0.get_mut(..dyn_name.str().len()).unwrap_or_default(); + // SAFETY: Just a temporary copy of an already existing string + unsafe { core::str::from_utf8_unchecked(bytes) } } else { elem_cb((new_hhb, static_name, value))?; - static_name + static_name.str() }; - (new_hhb, name, value) + if STORE { + self.dyn_headers.push_front(new_hhb, name, [value].into_iter(), false, |_| {})?; + } } else { let (hhn, name) = Self::decode_string_name(&mut self.header_buffers.0, data)?; let value = Self::decode_string_value(&mut self.header_buffers.1, data)?; let hhb = HpackHeaderBasic::try_from((hhn, value))?; elem_cb((hhb, name, value))?; - (hhb, name, value) - }; - if STORE { - self.dyn_headers.push_front(hhb, name, [value].into_iter(), false, |_| {})?; + if STORE { + self.dyn_headers.push_front(hhb, name.str(), [value].into_iter(), false, |_| {})?; + } } Ok(()) } @@ -189,13 +193,13 @@ impl HpackDecoder { fn decode_string_name<'buffer, 'data, 'rslt>( buffer: &'buffer mut _HeaderNameBuffer, data: &mut &'data [u8], - ) -> crate::Result<(HpackHeaderName, &'rslt [u8])> + ) -> crate::Result<(HpackHeaderName, HeaderName<&'rslt str>)> where 'buffer: 'rslt, 'data: 'rslt, { let (before, after, is_encoded) = Self::decode_string_init(data)?; - let rslt = if is_encoded { + let (hhn, bytes) = if is_encoded { huffman_decode(before, buffer)?; (HpackHeaderName::new(buffer)?, &**buffer) } else { @@ -207,7 +211,7 @@ impl HpackDecoder { } }; *data = after; - Ok(rslt) + Ok((hhn, HeaderName::from_bytes(bytes)?)) } #[inline] @@ -235,111 +239,184 @@ impl HpackDecoder { fn get( dyn_headers: &HpackHeaders, idx: usize, - ) -> crate::Result<(HpackHeaderBasic, RawHeaderPair<'_>, RawHeaderPair<'_>)> { - Ok(match idx { + ) -> crate::Result<(HpackHeaderBasic, RawHeaderName<'_>, RawHeaderValue<'_>)> { + let (hhb, name, value) = match idx { 0 => { return Err(crate::Error::Http2ErrorGoAway( Http2ErrorCode::CompressionError, Some(Http2Error::InvalidHpackIdx(Some(0))), )) } - 1 => (HpackHeaderBasic::Authority, (b":authority", &[]), (&[], &[])), - 2 => (HpackHeaderBasic::Method(Method::Get), (b":method", &[]), (b"GET", &[])), - 3 => (HpackHeaderBasic::Method(Method::Post), (b":method", &[]), (b"POST", &[])), - 4 => (HpackHeaderBasic::Path, (b":path", &[]), (b"/", &[])), - 5 => (HpackHeaderBasic::Path, (b":path", &[]), (b"/index.html", &[])), - 6 => (HpackHeaderBasic::Scheme, (b":scheme", &[]), (b"http", &[])), - 7 => (HpackHeaderBasic::Scheme, (b":scheme", &[]), (b"https", &[])), - 8 => (HpackHeaderBasic::StatusCode(StatusCode::Ok), (b":status", &[]), (b"200", &[])), - 9 => (HpackHeaderBasic::StatusCode(StatusCode::NoContent), (b":status", &[]), (b"204", &[])), - 10 => { - (HpackHeaderBasic::StatusCode(StatusCode::PartialContent), (b":status", &[]), (b"206", &[])) - } - 11 => { - (HpackHeaderBasic::StatusCode(StatusCode::NotModified), (b":status", &[]), (b"304", &[])) - } - 12 => { - (HpackHeaderBasic::StatusCode(StatusCode::BadRequest), (b":status", &[]), (b"400", &[])) + 1 => (HpackHeaderBasic::Authority, (":authority", ""), (&[][..], &[][..])), + 2 => (HpackHeaderBasic::Method(Method::Get), (":method", ""), ("GET".as_bytes(), &[][..])), + 3 => (HpackHeaderBasic::Method(Method::Post), (":method", ""), ("POST".as_bytes(), &[][..])), + 4 => (HpackHeaderBasic::Path, (":path", ""), ("/".as_bytes(), &[][..])), + 5 => (HpackHeaderBasic::Path, (":path", ""), ("/index.html".as_bytes(), &[][..])), + 6 => (HpackHeaderBasic::Scheme, (":scheme", ""), ("http".as_bytes(), &[][..])), + 7 => (HpackHeaderBasic::Scheme, (":scheme", ""), ("https".as_bytes(), &[][..])), + 8 => { + (HpackHeaderBasic::StatusCode(StatusCode::Ok), (":status", ""), ("200".as_bytes(), &[][..])) } - 13 => (HpackHeaderBasic::StatusCode(StatusCode::NotFound), (b":status", &[]), (b"404", &[])), + 9 => ( + HpackHeaderBasic::StatusCode(StatusCode::NoContent), + (":status", ""), + ("204".as_bytes(), &[][..]), + ), + 10 => ( + HpackHeaderBasic::StatusCode(StatusCode::PartialContent), + (":status", ""), + ("206".as_bytes(), &[][..]), + ), + 11 => ( + HpackHeaderBasic::StatusCode(StatusCode::NotModified), + (":status", ""), + ("304".as_bytes(), &[][..]), + ), + 12 => ( + HpackHeaderBasic::StatusCode(StatusCode::BadRequest), + (":status", ""), + ("400".as_bytes(), &[][..]), + ), + 13 => ( + HpackHeaderBasic::StatusCode(StatusCode::NotFound), + (":status", ""), + ("404".as_bytes(), &[][..]), + ), 14 => ( HpackHeaderBasic::StatusCode(StatusCode::InternalServerError), - (b":status", &[]), - (b"500", &[]), + (":status", ""), + ("500".as_bytes(), &[][..]), ), - 15 => (HpackHeaderBasic::Field, (KnownHeaderName::AcceptCharset.into(), &[]), (&[], &[])), + 15 => { + (HpackHeaderBasic::Field, (KnownHeaderName::AcceptCharset.into(), ""), (&[][..], &[][..])) + } 16 => ( HpackHeaderBasic::Field, - (KnownHeaderName::AcceptEncoding.into(), &[]), - (b"gzip, deflate", &[]), + (KnownHeaderName::AcceptEncoding.into(), ""), + ("gzip, deflate".as_bytes(), &[][..]), ), - 17 => (HpackHeaderBasic::Field, (KnownHeaderName::AcceptLanguage.into(), &[]), (&[], &[])), - 18 => (HpackHeaderBasic::Field, (KnownHeaderName::AcceptRanges.into(), &[]), (&[], &[])), - 19 => (HpackHeaderBasic::Field, (KnownHeaderName::Accept.into(), &[]), (&[], &[])), + 17 => { + (HpackHeaderBasic::Field, (KnownHeaderName::AcceptLanguage.into(), ""), (&[][..], &[][..])) + } + 18 => { + (HpackHeaderBasic::Field, (KnownHeaderName::AcceptRanges.into(), ""), (&[][..], &[][..])) + } + 19 => (HpackHeaderBasic::Field, (KnownHeaderName::Accept.into(), ""), (&[][..], &[][..])), 20 => ( HpackHeaderBasic::Field, - (KnownHeaderName::AccessControlAllowOrigin.into(), &[]), - (&[], &[]), + (KnownHeaderName::AccessControlAllowOrigin.into(), ""), + (&[][..], &[][..]), + ), + 21 => (HpackHeaderBasic::Field, (KnownHeaderName::Age.into(), ""), (&[][..], &[][..])), + 22 => (HpackHeaderBasic::Field, (KnownHeaderName::Allow.into(), ""), (&[][..], &[][..])), + 23 => { + (HpackHeaderBasic::Field, (KnownHeaderName::Authorization.into(), ""), (&[][..], &[][..])) + } + 24 => { + (HpackHeaderBasic::Field, (KnownHeaderName::CacheControl.into(), ""), (&[][..], &[][..])) + } + 25 => ( + HpackHeaderBasic::Field, + (KnownHeaderName::ContentDisposition.into(), ""), + (&[][..], &[][..]), ), - 21 => (HpackHeaderBasic::Field, (KnownHeaderName::Age.into(), &[]), (&[], &[])), - 22 => (HpackHeaderBasic::Field, (KnownHeaderName::Allow.into(), &[]), (&[], &[])), - 23 => (HpackHeaderBasic::Field, (KnownHeaderName::Authorization.into(), &[]), (&[], &[])), - 24 => (HpackHeaderBasic::Field, (KnownHeaderName::CacheControl.into(), &[]), (&[], &[])), - 25 => { - (HpackHeaderBasic::Field, (KnownHeaderName::ContentDisposition.into(), &[]), (&[], &[])) + 26 => { + (HpackHeaderBasic::Field, (KnownHeaderName::ContentEncoding.into(), ""), (&[][..], &[][..])) + } + 27 => { + (HpackHeaderBasic::Field, (KnownHeaderName::ContentLanguage.into(), ""), (&[][..], &[][..])) + } + 28 => { + (HpackHeaderBasic::Field, (KnownHeaderName::ContentLength.into(), ""), (&[][..], &[][..])) + } + 29 => { + (HpackHeaderBasic::Field, (KnownHeaderName::ContentLocation.into(), ""), (&[][..], &[][..])) + } + 30 => { + (HpackHeaderBasic::Field, (KnownHeaderName::ContentRange.into(), ""), (&[][..], &[][..])) + } + 31 => { + (HpackHeaderBasic::Field, (KnownHeaderName::ContentType.into(), ""), (&[][..], &[][..])) + } + 32 => (HpackHeaderBasic::Field, (KnownHeaderName::Cookie.into(), ""), (&[][..], &[][..])), + 33 => (HpackHeaderBasic::Field, (KnownHeaderName::Date.into(), ""), (&[][..], &[][..])), + 34 => (HpackHeaderBasic::Field, (KnownHeaderName::Etag.into(), ""), (&[][..], &[][..])), + 35 => (HpackHeaderBasic::Field, (KnownHeaderName::Expect.into(), ""), (&[][..], &[][..])), + 36 => (HpackHeaderBasic::Field, (KnownHeaderName::Expires.into(), ""), (&[][..], &[][..])), + 37 => (HpackHeaderBasic::Field, (KnownHeaderName::From.into(), ""), (&[][..], &[][..])), + 38 => (HpackHeaderBasic::Field, (KnownHeaderName::Host.into(), ""), (&[][..], &[][..])), + 39 => (HpackHeaderBasic::Field, (KnownHeaderName::IfMatch.into(), ""), (&[][..], &[][..])), + 40 => { + (HpackHeaderBasic::Field, (KnownHeaderName::IfModifiedSince.into(), ""), (&[][..], &[][..])) } - 26 => (HpackHeaderBasic::Field, (KnownHeaderName::ContentEncoding.into(), &[]), (&[], &[])), - 27 => (HpackHeaderBasic::Field, (KnownHeaderName::ContentLanguage.into(), &[]), (&[], &[])), - 28 => (HpackHeaderBasic::Field, (KnownHeaderName::ContentLength.into(), &[]), (&[], &[])), - 29 => (HpackHeaderBasic::Field, (KnownHeaderName::ContentLocation.into(), &[]), (&[], &[])), - 30 => (HpackHeaderBasic::Field, (KnownHeaderName::ContentRange.into(), &[]), (&[], &[])), - 31 => (HpackHeaderBasic::Field, (KnownHeaderName::ContentType.into(), &[]), (&[], &[])), - 32 => (HpackHeaderBasic::Field, (KnownHeaderName::Cookie.into(), &[]), (&[], &[])), - 33 => (HpackHeaderBasic::Field, (KnownHeaderName::Date.into(), &[]), (&[], &[])), - 34 => (HpackHeaderBasic::Field, (KnownHeaderName::Etag.into(), &[]), (&[], &[])), - 35 => (HpackHeaderBasic::Field, (KnownHeaderName::Expect.into(), &[]), (&[], &[])), - 36 => (HpackHeaderBasic::Field, (KnownHeaderName::Expires.into(), &[]), (&[], &[])), - 37 => (HpackHeaderBasic::Field, (KnownHeaderName::From.into(), &[]), (&[], &[])), - 38 => (HpackHeaderBasic::Field, (KnownHeaderName::Host.into(), &[]), (&[], &[])), - 39 => (HpackHeaderBasic::Field, (KnownHeaderName::IfMatch.into(), &[]), (&[], &[])), - 40 => (HpackHeaderBasic::Field, (KnownHeaderName::IfModifiedSince.into(), &[]), (&[], &[])), - 41 => (HpackHeaderBasic::Field, (KnownHeaderName::IfNoneMatch.into(), &[]), (&[], &[])), - 42 => (HpackHeaderBasic::Field, (KnownHeaderName::IfRange.into(), &[]), (&[], &[])), - 43 => (HpackHeaderBasic::Field, (KnownHeaderName::IfUnmodifiedSince.into(), &[]), (&[], &[])), - 44 => (HpackHeaderBasic::Field, (KnownHeaderName::LastModified.into(), &[]), (&[], &[])), - 45 => (HpackHeaderBasic::Field, (KnownHeaderName::Link.into(), &[]), (&[], &[])), - 46 => (HpackHeaderBasic::Field, (KnownHeaderName::Location.into(), &[]), (&[], &[])), - 47 => (HpackHeaderBasic::Field, (KnownHeaderName::MaxForwards.into(), &[]), (&[], &[])), - 48 => (HpackHeaderBasic::Field, (KnownHeaderName::ProxyAuthenticate.into(), &[]), (&[], &[])), - 49 => { - (HpackHeaderBasic::Field, (KnownHeaderName::ProxyAuthorization.into(), &[]), (&[], &[])) + 41 => { + (HpackHeaderBasic::Field, (KnownHeaderName::IfNoneMatch.into(), ""), (&[][..], &[][..])) } - 50 => (HpackHeaderBasic::Field, (KnownHeaderName::Range.into(), &[]), (&[], &[])), - 51 => (HpackHeaderBasic::Field, (KnownHeaderName::Referer.into(), &[]), (&[], &[])), - 52 => (HpackHeaderBasic::Field, (KnownHeaderName::Refresh.into(), &[]), (&[], &[])), - 53 => (HpackHeaderBasic::Field, (KnownHeaderName::RetryAfter.into(), &[]), (&[], &[])), - 54 => (HpackHeaderBasic::Field, (KnownHeaderName::Server.into(), &[]), (&[], &[])), - 55 => (HpackHeaderBasic::Field, (KnownHeaderName::SetCookie.into(), &[]), (&[], &[])), + 42 => (HpackHeaderBasic::Field, (KnownHeaderName::IfRange.into(), ""), (&[][..], &[][..])), + 43 => ( + HpackHeaderBasic::Field, + (KnownHeaderName::IfUnmodifiedSince.into(), ""), + (&[][..], &[][..]), + ), + 44 => { + (HpackHeaderBasic::Field, (KnownHeaderName::LastModified.into(), ""), (&[][..], &[][..])) + } + 45 => (HpackHeaderBasic::Field, (KnownHeaderName::Link.into(), ""), (&[][..], &[][..])), + 46 => (HpackHeaderBasic::Field, (KnownHeaderName::Location.into(), ""), (&[][..], &[][..])), + 47 => { + (HpackHeaderBasic::Field, (KnownHeaderName::MaxForwards.into(), ""), (&[][..], &[][..])) + } + 48 => ( + HpackHeaderBasic::Field, + (KnownHeaderName::ProxyAuthenticate.into(), ""), + (&[][..], &[][..]), + ), + 49 => ( + HpackHeaderBasic::Field, + (KnownHeaderName::ProxyAuthorization.into(), ""), + (&[][..], &[][..]), + ), + 50 => (HpackHeaderBasic::Field, (KnownHeaderName::Range.into(), ""), (&[][..], &[][..])), + 51 => (HpackHeaderBasic::Field, (KnownHeaderName::Referer.into(), ""), (&[][..], &[][..])), + 52 => (HpackHeaderBasic::Field, (KnownHeaderName::Refresh.into(), ""), (&[][..], &[][..])), + 53 => (HpackHeaderBasic::Field, (KnownHeaderName::RetryAfter.into(), ""), (&[][..], &[][..])), + 54 => (HpackHeaderBasic::Field, (KnownHeaderName::Server.into(), ""), (&[][..], &[][..])), + 55 => (HpackHeaderBasic::Field, (KnownHeaderName::SetCookie.into(), ""), (&[][..], &[][..])), 56 => ( HpackHeaderBasic::Field, - (KnownHeaderName::StrictTransportSecurity.into(), &[]), - (&[], &[]), + (KnownHeaderName::StrictTransportSecurity.into(), ""), + (&[][..], &[][..]), ), - 57 => (HpackHeaderBasic::Field, (KnownHeaderName::TransferEncoding.into(), &[]), (&[], &[])), - 58 => (HpackHeaderBasic::Field, (KnownHeaderName::UserAgent.into(), &[]), (&[], &[])), - 59 => (HpackHeaderBasic::Field, (KnownHeaderName::Vary.into(), &[]), (&[], &[])), - 60 => (HpackHeaderBasic::Field, (KnownHeaderName::Via.into(), &[]), (&[], &[])), - 61 => (HpackHeaderBasic::Field, (KnownHeaderName::WwwAuthenticate.into(), &[]), (&[], &[])), - dyn_idx_with_offset => dyn_headers - .get_by_idx(dyn_idx_with_offset.wrapping_sub(DYN_IDX_OFFSET)) - .ok_or_else(|| { - crate::Error::Http2ErrorGoAway( - Http2ErrorCode::CompressionError, - Some(Http2Error::InvalidHpackIdx(dyn_idx_with_offset.try_into().ok())), - ) - }) - .map(|el| (*el.misc, (&[][..], el.name_bytes), (&[][..], el.value_bytes)))?, - }) + 57 => ( + HpackHeaderBasic::Field, + (KnownHeaderName::TransferEncoding.into(), ""), + (&[][..], &[][..]), + ), + 58 => (HpackHeaderBasic::Field, (KnownHeaderName::UserAgent.into(), ""), (&[][..], &[][..])), + 59 => (HpackHeaderBasic::Field, (KnownHeaderName::Vary.into(), ""), (&[][..], &[][..])), + 60 => (HpackHeaderBasic::Field, (KnownHeaderName::Via.into(), ""), (&[][..], &[][..])), + 61 => { + (HpackHeaderBasic::Field, (KnownHeaderName::WwwAuthenticate.into(), ""), (&[][..], &[][..])) + } + dyn_idx_with_offset => { + return dyn_headers + .get_by_idx(dyn_idx_with_offset.wrapping_sub(DYN_IDX_OFFSET)) + .map(|el| { + ( + *el.misc, + (HeaderName::new_unchecked(""), HeaderName::new_unchecked(el.name_bytes)), + (&[][..], el.value_bytes), + ) + }) + .ok_or_else(|| { + crate::Error::Http2ErrorGoAway( + Http2ErrorCode::CompressionError, + Some(Http2Error::InvalidHpackIdx(dyn_idx_with_offset.try_into().ok())), + ) + }) + } + }; + Ok((hhb, (HeaderName::new_unchecked(name.0), HeaderName::new_unchecked(name.1)), value)) } #[inline] @@ -347,7 +424,7 @@ impl HpackDecoder { &mut self, byte: u8, data: &mut &[u8], - elem_cb: &mut impl FnMut((HpackHeaderBasic, &[u8], &[u8])) -> crate::Result<()>, + elem_cb: &mut impl FnMut((HpackHeaderBasic, HeaderName<&str>, &[u8])) -> crate::Result<()>, mut size_update_cb: impl FnMut() -> crate::Result<()>, ) -> crate::Result<()> { match DecodeIdx::try_from(byte)? { @@ -356,7 +433,7 @@ impl HpackDecoder { elem_cb(Self::get(&self.dyn_headers, *Usize::from(idx)).map(|(hhb, name, value)| { ( hhb, - if name.0.is_empty() { name.1 } else { name.0 }, + if name.0.str().is_empty() { name.1 } else { name.0 }, if value.0.is_empty() { value.1 } else { value.0 }, ) })?)?; @@ -414,37 +491,3 @@ impl TryFrom for DecodeIdx { }) } } - -#[cfg(all(feature = "_bench", test))] -mod bench { - use crate::{ - http::Header, - http2::{hpack_decoder::HpackDecoder, hpack_encoder::HpackEncoder}, - misc::{simple_seed, Usize, Vector, Xorshift64}, - }; - - #[bench] - fn decode(b: &mut test::Bencher) { - const N: u32 = 1024 * 1024; - let data = { - let mut rslt = crate::bench::_data(*Usize::from(N)); - rslt.iter_mut().filter(|el| **el == b':').for_each(|el| { - *el = 0; - }); - rslt - }; - let mut buffer = Vector::with_capacity(*Usize::from(N)).unwrap(); - let mut he = HpackEncoder::new(Xorshift64::from(simple_seed())); - he.set_max_dyn_super_bytes(N); - he.encode(&mut buffer, [].into_iter(), { - data.chunks_exact(128).map(|el| Header::from_name_and_value(&el[..64], &el[64..])) - }) - .unwrap(); - let mut hd = HpackDecoder::new(); - hd.set_max_bytes(N); - b.iter(|| { - hd.decode(&buffer, |_| Ok(())).unwrap(); - hd.clear(); - }); - } -} diff --git a/wtx/src/http2/hpack_encoder.rs b/wtx/src/http2/hpack_encoder.rs index b9a510b0..0e2b9967 100644 --- a/wtx/src/http2/hpack_encoder.rs +++ b/wtx/src/http2/hpack_encoder.rs @@ -82,8 +82,8 @@ impl HpackEncoder { buffer.reserve(reserve)?; self.manage_size_update(buffer)?; for (hhb, value) in pseudo_headers_iter { - let idx = self.encode_idx((&[], value, false), hhb, Self::shi_pseudo((hhb, value)))?; - Self::manage_encode(buffer, (&[], value), idx)?; + let idx = self.encode_idx(("", value, false), hhb, Self::shi_pseudo((hhb, value)))?; + Self::manage_encode(buffer, ("", value), idx)?; } for Header { is_sensitive, name, value, .. } in user_headers_iter { let idx = self.encode_idx( @@ -147,13 +147,13 @@ impl HpackEncoder { #[inline] fn dyn_idx( &mut self, - header: (&[u8], &[u8], bool), + header: (&str, &[u8], bool), should_not_index: bool, ) -> crate::Result { let (name, value, is_sensitive) = header; let mut name_hasher = self.rs.build_hasher(); - name_hasher.write(name); + name_hasher.write(name.as_bytes()); let mut pair_hasher = name_hasher.clone(); pair_hasher.write(value); @@ -191,7 +191,7 @@ impl HpackEncoder { #[inline] fn dyn_idx_with_static_name( &mut self, - header: (&[u8], &[u8], bool), + header: (&str, &[u8], bool), name_idx: u32, ) -> crate::Result { let (name, value, is_sensitive) = header; @@ -205,7 +205,7 @@ impl HpackEncoder { #[inline] fn encode_idx( &mut self, - header: (&[u8], &[u8], bool), + header: (&str, &[u8], bool), hhb: HpackHeaderBasic, static_header: Option, ) -> crate::Result { @@ -330,10 +330,10 @@ impl HpackEncoder { // Regardless of the "sensitive" flag set by users, these headers may carry sensitive content // that shouldn't be indexed. #[inline] - fn header_is_naturally_sensitive(hhb: HpackHeaderBasic, name: &[u8]) -> bool { + fn header_is_naturally_sensitive(hhb: HpackHeaderBasic, name: &str) -> bool { match hhb { HpackHeaderBasic::Field => matches!( - KnownHeaderName::try_from(name), + KnownHeaderName::try_from(name.as_bytes()), Ok( KnownHeaderName::Age | KnownHeaderName::Authorization @@ -353,7 +353,7 @@ impl HpackEncoder { // Very large headers are not good candidates for indexing. #[inline] - fn header_is_very_large(&self, hhb: HpackHeaderBasic, name: &[u8], value: &[u8]) -> bool { + fn header_is_very_large(&self, hhb: HpackHeaderBasic, name: &str, value: &[u8]) -> bool { hhb.len(name, value) >= (self.dyn_headers.max_bytes() / 4).wrapping_mul(3) } @@ -365,7 +365,7 @@ impl HpackEncoder { #[inline] fn manage_encode( buffer: &mut Vector, - header: (&[u8], &[u8]), + header: (&str, &[u8]), idx: EncodeIdx, ) -> crate::Result<()> { let (name, value) = header; @@ -383,12 +383,12 @@ impl HpackEncoder { } EncodeIdx::SavedNameSavedValue => { buffer.push(0b0100_0000)?; - Self::encode_str(buffer, name)?; + Self::encode_str(buffer, name.as_bytes())?; Self::encode_str(buffer, value)?; } EncodeIdx::UnsavedNameUnsavedValue => { buffer.push(0b0001_0000)?; - Self::encode_str(buffer, name)?; + Self::encode_str(buffer, name.as_bytes())?; Self::encode_str(buffer, value)?; } } @@ -428,7 +428,7 @@ impl HpackEncoder { #[inline] fn push_dyn_headers( &mut self, - (name, value, is_sensitive): (&[u8], &[u8], bool), + (name, value, is_sensitive): (&str, &[u8], bool), (name_hash, pair_hash): (Option, u64), ) -> crate::Result<()> { self.idx = self.idx.wrapping_add(1); @@ -446,10 +446,10 @@ impl HpackEncoder { #[inline] fn shi_pseudo((hhb, value): (HpackHeaderBasic, &[u8])) -> Option { - let (has_value, idx, name): (_, _, &[u8]) = match hhb { - HpackHeaderBasic::Authority => (false, 1, b":authority"), + let (has_value, idx, name): (_, _, &str) = match hhb { + HpackHeaderBasic::Authority => (false, 1, ":authority"), HpackHeaderBasic::Method(method) => { - let name = b":method"; + let name = ":method"; let (has_value, idx) = match method { Method::Get => (true, 2), Method::Post => (true, 3), @@ -458,7 +458,7 @@ impl HpackEncoder { (has_value, idx, name) } HpackHeaderBasic::Path => { - let name = b":path"; + let name = ":path"; let (has_value, idx) = match value { b"/" => (true, 4), b"/index.html" => (true, 5), @@ -467,7 +467,7 @@ impl HpackEncoder { (has_value, idx, name) } HpackHeaderBasic::Scheme => { - let name = b":path"; + let name = ":path"; let (has_value, idx) = match value { b"http" => (true, 6), b"https" => (true, 7), @@ -476,7 +476,7 @@ impl HpackEncoder { (has_value, idx, name) } HpackHeaderBasic::StatusCode(status) => { - let name = b":status"; + let name = ":status"; let (has_value, idx) = match status { StatusCode::Ok => (true, 8), StatusCode::NoContent => (true, 9), @@ -495,8 +495,8 @@ impl HpackEncoder { } #[inline] - fn shi_user((name, value): (&[u8], &[u8])) -> Option { - let (has_value, idx, local_name) = match KnownHeaderName::try_from(name) { + fn shi_user((name, value): (&str, &[u8])) -> Option { + let (has_value, idx, local_name) = match KnownHeaderName::try_from(name.as_bytes()) { Ok(KnownHeaderName::AcceptCharset) => (false, 15, KnownHeaderName::AcceptCharset.into()), Ok(KnownHeaderName::AcceptEncoding) => { if value == b"gzip, deflate" { @@ -572,7 +572,7 @@ impl HpackEncoder { #[inline] fn should_not_index( &self, - (name, value, is_sensitive): (&[u8], &[u8], bool), + (name, value, is_sensitive): (&str, &[u8], bool), hhb: HpackHeaderBasic, ) -> bool { is_sensitive @@ -591,7 +591,7 @@ impl HpackEncoder { #[inline] fn store_header_with_ref_name( &mut self, - (name, value, is_sensitive): (&[u8], &[u8], bool), + (name, value, is_sensitive): (&str, &[u8], bool), name_idx: u32, pair_hash: u64, ) -> crate::Result { @@ -647,31 +647,5 @@ struct Metadata { struct StaticHeader { has_value: bool, idx: u32, - name: &'static [u8], -} - -#[cfg(all(feature = "_bench", test))] -mod bench { - use crate::{ - http::Header, - http2::hpack_encoder::HpackEncoder, - misc::{simple_seed, Usize, Vector, Xorshift64}, - }; - - #[bench] - fn encode(b: &mut test::Bencher) { - const N: u32 = 1024 * 1024 * 4; - let data = crate::bench::_data(*Usize::from(N)); - let mut he = HpackEncoder::new(Xorshift64::from(simple_seed())); - he.set_max_dyn_super_bytes(N); - let mut buffer = Vector::new(); - b.iter(|| { - he.encode( - &mut buffer, - [].into_iter(), - data.chunks_exact(128).map(|el| Header::from_name_and_value(&el[..64], &el[64..])), - ) - .unwrap(); - }); - } + name: &'static str, } diff --git a/wtx/src/http2/hpack_header.rs b/wtx/src/http2/hpack_header.rs index bce4ee35..e1ccc168 100644 --- a/wtx/src/http2/hpack_header.rs +++ b/wtx/src/http2/hpack_header.rs @@ -15,7 +15,7 @@ pub(crate) enum HpackHeaderBasic { } impl HpackHeaderBasic { - pub(crate) const fn len(self, name: &[u8], value: &[u8]) -> usize { + pub(crate) const fn len(self, name: &str, value: &[u8]) -> usize { match self { HpackHeaderBasic::Authority => 10usize.wrapping_add(value.len()).wrapping_add(32), HpackHeaderBasic::Field => name.len().wrapping_add(value.len()).wrapping_add(32), diff --git a/wtx/src/http2/hpack_headers.rs b/wtx/src/http2/hpack_headers.rs index c4897df2..1d7d2638 100644 --- a/wtx/src/http2/hpack_headers.rs +++ b/wtx/src/http2/hpack_headers.rs @@ -1,4 +1,5 @@ use crate::misc::{Block, BlocksDeque}; +use core::str; #[derive(Debug)] pub(crate) struct HpackHeaders { @@ -44,7 +45,7 @@ where pub(crate) fn push_front<'bytes, I>( &mut self, misc: M, - name: &'bytes [u8], + name: &'bytes str, values: I, is_sensitive: bool, cb: impl FnMut(M), @@ -64,7 +65,7 @@ where } self.remove_until_max_bytes(local_len, cb); self.bq.push_front_from_coyable_data( - [name].into_iter().chain(iter), + [name.as_bytes()].into_iter().chain(iter), Metadata { is_sensitive, misc, name_len: name.len() }, )?; Ok(()) @@ -87,7 +88,11 @@ where AbstractHeader { is_sensitive: block.misc.is_sensitive, misc: &block.misc.misc, - name_bytes: block.data.get(..block.misc.name_len).unwrap_or_default(), + name_bytes: { + let str = block.data.get(..block.misc.name_len).unwrap_or_default(); + // SAFETY: Input methods only accept UTF-8 data + unsafe { str::from_utf8_unchecked(str) } + }, value_bytes: block.data.get(block.misc.name_len..).unwrap_or_default(), } } @@ -111,7 +116,7 @@ where pub(crate) struct AbstractHeader<'ah, M> { pub(crate) is_sensitive: bool, pub(crate) misc: &'ah M, - pub(crate) name_bytes: &'ah [u8], + pub(crate) name_bytes: &'ah str, pub(crate) value_bytes: &'ah [u8], } diff --git a/wtx/src/http2/tests/connections.rs b/wtx/src/http2/tests/connections.rs index 15657c36..b596ae97 100644 --- a/wtx/src/http2/tests/connections.rs +++ b/wtx/src/http2/tests/connections.rs @@ -34,7 +34,7 @@ async fn client(uri: &UriString) { _0(rrb.body(), rrb.headers()); rrb.clear(); - rrb.headers.push_from_iter(Header::from_name_and_value(b"123", ["456".as_bytes()])).unwrap(); + rrb.headers.push_from_iter(Header::from_name_and_value("123", ["456".as_bytes()])).unwrap(); rrb = stream_client(&mut http2, rrb, &uri_ref).await; _1(rrb.body(), rrb.headers()); @@ -45,7 +45,7 @@ async fn client(uri: &UriString) { rrb.clear(); rrb.body.extend_from_copyable_slice(b"123").unwrap(); - rrb.headers.push_from_iter(Header::from_name_and_value(b"123", ["456".as_bytes()])).unwrap(); + rrb.headers.push_from_iter(Header::from_name_and_value("123", ["456".as_bytes()])).unwrap(); rrb = stream_client(&mut http2, rrb, &uri_ref).await; _3(rrb.body(), rrb.headers()); diff --git a/wtx/src/http2/tests/hpack.rs b/wtx/src/http2/tests/hpack.rs index ab914055..44f0b252 100644 --- a/wtx/src/http2/tests/hpack.rs +++ b/wtx/src/http2/tests/hpack.rs @@ -111,15 +111,15 @@ fn fetch_project() { .unwrap(); } -pub(crate) const fn hhb_name<'name>(hhb: HpackHeaderBasic, name: &'name [u8]) -> &'name [u8] { +pub(crate) const fn hhb_name<'name>(hhb: HpackHeaderBasic, name: &'name str) -> &'name str { match hhb { - HpackHeaderBasic::Authority => b":authority", + HpackHeaderBasic::Authority => ":authority", HpackHeaderBasic::Field => name, - HpackHeaderBasic::Method(_) => b":method", - HpackHeaderBasic::Path => b":path", - HpackHeaderBasic::Protocol(_) => b":protocol", - HpackHeaderBasic::Scheme => b":scheme", - HpackHeaderBasic::StatusCode(_) => b":status", + HpackHeaderBasic::Method(_) => ":method", + HpackHeaderBasic::Path => ":path", + HpackHeaderBasic::Protocol(_) => ":protocol", + HpackHeaderBasic::Scheme => ":scheme", + HpackHeaderBasic::StatusCode(_) => ":status", } } @@ -142,12 +142,12 @@ fn parse_hex(hex: &[u8]) -> Vector { fn strs<'key, 'value>( hhb: HpackHeaderBasic, - name: &'key [u8], + name: &'key str, value: &'value [u8], ) -> (&'key str, &'value str) { match hhb { HpackHeaderBasic::Authority => (":authority", from_utf8_basic(value).unwrap()), - HpackHeaderBasic::Field => (from_utf8_basic(name).unwrap(), from_utf8_basic(value).unwrap()), + HpackHeaderBasic::Field => (name, from_utf8_basic(value).unwrap()), HpackHeaderBasic::Method(elem) => (":method", elem.strings().custom[0]), HpackHeaderBasic::Path => (":path", from_utf8_basic(value).unwrap()), HpackHeaderBasic::Protocol(elem) => (":protocol", elem.strings().custom[0]), @@ -220,7 +220,7 @@ fn test_story_encoding_and_decoding( if header.name.starts_with(":") { None } else { - Some((HpackHeaderBasic::Field, header.name.as_bytes(), header.value.as_bytes())) + Some((HpackHeaderBasic::Field, header.name.as_str(), header.value.as_bytes())) } })) .unwrap(); @@ -236,7 +236,7 @@ fn test_story_encoding_and_decoding( decoder .decode(&buffer, |(hhb, name, value)| { if pseudo_headers.is_empty() { - assert_eq!((hhb, hhb_name(hhb, name), value), user_headers.remove(0).unwrap()); + assert_eq!((hhb, hhb_name(hhb, name.str()), value), user_headers.remove(0).unwrap()); } else { assert_eq!((hhb, value), pseudo_headers.remove(0).unwrap()); } @@ -263,7 +263,7 @@ fn test_story_wired_decoding(cases: &mut Vector, decoder: &mut HpackDecode decoder .decode(&parse_hex(wire.as_bytes()), |(hhb, name, value)| { let case_header = case.headers.remove(0).unwrap(); - let (name, value) = strs(hhb, name, value); + let (name, value) = strs(hhb, name.str(), value); assert_eq!(case_header.name, name); assert_eq!(case_header.value, value); Ok(()) diff --git a/wtx/src/misc/rng/rand_chacha.rs b/wtx/src/misc/rng/rand_chacha.rs index c18c4a49..51268b5e 100644 --- a/wtx/src/misc/rng/rand_chacha.rs +++ b/wtx/src/misc/rng/rand_chacha.rs @@ -2,16 +2,6 @@ use rand_core::RngCore; macro_rules! implement { ($struct:ty) => { - #[cfg(feature = "http-server-framework")] - impl crate::http::server_framework::ConnAux for $struct { - type Init = Self; - - #[inline] - fn conn_aux(init: Self::Init) -> crate::Result { - Ok(init) - } - } - impl crate::misc::rng::Rng for $struct { #[inline] fn u8(&mut self) -> u8 { diff --git a/wtx/src/misc/rng/xorshift.rs b/wtx/src/misc/rng/xorshift.rs index e0d0d3ab..137ffc91 100644 --- a/wtx/src/misc/rng/xorshift.rs +++ b/wtx/src/misc/rng/xorshift.rs @@ -7,16 +7,6 @@ pub struct Xorshift64 { value: u64, } -#[cfg(feature = "http-server-framework")] -impl crate::http::server_framework::ConnAux for Xorshift64 { - type Init = Self; - - #[inline] - fn conn_aux(init: Self::Init) -> crate::Result { - Ok(init) - } -} - impl Rng for Xorshift64 { #[inline] fn u8(&mut self) -> u8 { diff --git a/wtx/src/misc/stream.rs b/wtx/src/misc/stream.rs index 62ec0d54..7e475817 100644 --- a/wtx/src/misc/stream.rs +++ b/wtx/src/misc/stream.rs @@ -1,3 +1,15 @@ +macro_rules! _local_write_all { + ($bytes:expr, $write:expr) => {{ + while !$bytes.is_empty() { + match $write { + Err(e) => return Err(e.into()), + Ok(0) => return { Err(crate::Error::UnexpectedStreamWriteEOF) }, + Ok(n) => $bytes = $bytes.get(n..).unwrap_or_default(), + } + } + }}; +} + macro_rules! _local_write_all_vectored { ($bytes:expr, $this:ident, |$io_slices:ident| $write_many:expr) => { if let [single] = $bytes { @@ -17,6 +29,8 @@ macro_rules! _local_write_all_vectored { } mod bytes_stream; +#[cfg(feature = "embassy-net")] +mod embassy_net; #[cfg(feature = "embedded-tls")] mod embedded_tls; #[cfg(feature = "std")] diff --git a/wtx/src/misc/stream/embassy_net.rs b/wtx/src/misc/stream/embassy_net.rs new file mode 100644 index 00000000..bd1a93c2 --- /dev/null +++ b/wtx/src/misc/stream/embassy_net.rs @@ -0,0 +1,25 @@ +use crate::misc::{StreamReader, StreamWriter}; +use embassy_net::tcp::TcpSocket; + +impl StreamReader for TcpSocket<'_> { + #[inline] + async fn read(&mut self, bytes: &mut [u8]) -> crate::Result { + Ok((*self).read(bytes).await?) + } +} + +impl StreamWriter for TcpSocket<'_> { + #[inline] + async fn write_all(&mut self, mut bytes: &[u8]) -> crate::Result<()> { + _local_write_all!(bytes, Self::write(self, bytes).await); + Ok(()) + } + + #[inline] + async fn write_all_vectored(&mut self, bytes: &[&[u8]]) -> crate::Result<()> { + for elem in bytes { + self.write_all(elem).await?; + } + Ok(()) + } +} diff --git a/wtx/src/misc/stream/tokio_rustls.rs b/wtx/src/misc/stream/tokio_rustls.rs index ff6f8eaf..8d8042be 100644 --- a/wtx/src/misc/stream/tokio_rustls.rs +++ b/wtx/src/misc/stream/tokio_rustls.rs @@ -1,5 +1,6 @@ use crate::misc::{StreamReader, StreamWithTls, StreamWriter}; use ring::digest::{self, Digest}; +use rustls_pki_types::CertificateDer; use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt}; impl StreamReader for tokio_rustls::client::TlsStream @@ -21,35 +22,7 @@ where #[inline] fn tls_server_end_point(&self) -> crate::Result> { let (_, conn) = self.get_ref(); - Ok(match conn.peer_certificates() { - Some([cert, ..]) => { - #[cfg(feature = "x509-certificate")] - let algorithm = { - use x509_certificate::{DigestAlgorithm, SignatureAlgorithm}; - let x509_cer = x509_certificate::X509Certificate::from_der(cert)?; - let Some(sa) = x509_cer.signature_algorithm() else { - return Ok(None); - }; - match sa { - SignatureAlgorithm::EcdsaSha256 - | SignatureAlgorithm::RsaSha1 - | SignatureAlgorithm::RsaSha256 => &digest::SHA256, - SignatureAlgorithm::EcdsaSha384 | SignatureAlgorithm::RsaSha384 => &digest::SHA384, - SignatureAlgorithm::Ed25519 => &digest::SHA512, - SignatureAlgorithm::NoSignature(da) => match da { - DigestAlgorithm::Sha1 | DigestAlgorithm::Sha256 => &digest::SHA256, - DigestAlgorithm::Sha384 => &digest::SHA384, - DigestAlgorithm::Sha512 => &digest::SHA512, - }, - SignatureAlgorithm::RsaSha512 => &digest::SHA512, - } - }; - #[cfg(not(feature = "x509-certificate"))] - let algorithm = &digest::SHA256; - Some(digest::digest(algorithm, cert.as_ref())) - } - _ => None, - }) + tls_server_end_point(conn.peer_certificates()) } } @@ -89,10 +62,7 @@ where #[inline] fn tls_server_end_point(&self) -> crate::Result> { let (_, conn) = self.get_ref(); - Ok(match conn.peer_certificates() { - Some([cert, ..]) => Some(digest::digest(&digest::SHA256, cert)), - _ => None, - }) + tls_server_end_point(conn.peer_certificates()) } } @@ -112,3 +82,38 @@ where Ok(()) } } + +#[inline] +fn tls_server_end_point( + certs: Option<&[CertificateDer<'static>]>, +) -> crate::Result> { + Ok(match certs { + Some([cert, ..]) => { + #[cfg(feature = "x509-certificate")] + let algorithm = { + use x509_certificate::{DigestAlgorithm, SignatureAlgorithm}; + let x509_cer = x509_certificate::X509Certificate::from_der(cert)?; + let Some(sa) = x509_cer.signature_algorithm() else { + return Ok(None); + }; + match sa { + SignatureAlgorithm::EcdsaSha256 + | SignatureAlgorithm::RsaSha1 + | SignatureAlgorithm::RsaSha256 => &digest::SHA256, + SignatureAlgorithm::EcdsaSha384 | SignatureAlgorithm::RsaSha384 => &digest::SHA384, + SignatureAlgorithm::Ed25519 => &digest::SHA512, + SignatureAlgorithm::NoSignature(da) => match da { + DigestAlgorithm::Sha1 | DigestAlgorithm::Sha256 => &digest::SHA256, + DigestAlgorithm::Sha384 => &digest::SHA384, + DigestAlgorithm::Sha512 => &digest::SHA512, + }, + SignatureAlgorithm::RsaSha512 => &digest::SHA512, + } + }; + #[cfg(not(feature = "x509-certificate"))] + let algorithm = &digest::SHA256; + Some(digest::digest(algorithm, cert.as_ref())) + } + _ => None, + }) +} diff --git a/wtx/src/pool/resource_manager.rs b/wtx/src/pool/resource_manager.rs index fd239068..1330ae12 100644 --- a/wtx/src/pool/resource_manager.rs +++ b/wtx/src/pool/resource_manager.rs @@ -238,8 +238,8 @@ pub(crate) mod database { Executor::connect_encrypted( &config, ExecutorBuffer::new(self.max_stmts, &mut &self.rng), - TcpStream::connect(uri.hostname_with_implied_port()).await.map_err(Into::into)?, &mut &self.rng, + TcpStream::connect(uri.hostname_with_implied_port()).await.map_err(Into::into)?, |stream| async { let mut rslt = TokioRustlsConnector::from_auto()?; if let Some(elem) = self._certs { @@ -268,8 +268,8 @@ pub(crate) mod database { Executor::connect_encrypted( &config, buffer, - TcpStream::connect(uri.hostname_with_implied_port()).await.map_err(Into::into)?, &mut &self.rng, + TcpStream::connect(uri.hostname_with_implied_port()).await.map_err(Into::into)?, |stream| async { let mut rslt = TokioRustlsConnector::from_auto()?; if let Some(elem) = self._certs { diff --git a/wtx/src/web_socket/handshake/tests.rs b/wtx/src/web_socket/handshake/tests.rs index 184d8d0c..9ffcb03d 100644 --- a/wtx/src/web_socket/handshake/tests.rs +++ b/wtx/src/web_socket/handshake/tests.rs @@ -15,7 +15,6 @@ use crate::{ WebSocketClient, WebSocketClientOwned, WebSocketServer, WebSocketServerOwned, }, }; -use alloc::vec; use core::{ sync::atomic::{AtomicBool, Ordering}, time::Duration, @@ -177,7 +176,7 @@ where NC: NegotiatedCompression, { async fn client(ws: &mut WebSocketClientOwned) { - let bytes = || vec![b'1'; 256 * 1024]; + let bytes = || _vector![b'1'; 256 * 1024]; ws.write_frame(&mut Frame::new_unfin(OpCode::Text, &mut bytes())).await.unwrap(); ws.write_frame(&mut Frame::new_unfin(OpCode::Continuation, &mut bytes())).await.unwrap(); ws.write_frame(&mut Frame::new_unfin(OpCode::Continuation, &mut bytes())).await.unwrap(); @@ -193,7 +192,7 @@ where async fn server(ws: &mut WebSocketServerOwned) { let text = ws.read_frame().await.unwrap(); assert_eq!(OpCode::Text, text.op_code()); - assert_eq!(&vec![b'1'; 10 * 256 * 1024], text.payload()); + assert_eq!(_vector![b'1'; 10 * 256 * 1024].as_slice(), *text.payload()); } }