hyper-1.5.2/.cargo_vcs_info.json0000644000000001360000000000100121700ustar { "git": { "sha1": "30f2961e89eb306780d856e6e2c1ee10ffbbafd2" }, "path_in_vcs": "" }hyper-1.5.2/Cargo.lock0000644000000455530000000000100101570ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 4 [[package]] name = "addr2line" version = "0.24.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" dependencies = [ "gimli", ] [[package]] name = "adler2" version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" [[package]] name = "aho-corasick" version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" dependencies = [ "memchr", ] [[package]] name = "async-stream" version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b5a71a6f37880a80d1d7f19efd781e4b5de42c88f0722cc13bcb6cc2cfe8476" dependencies = [ "async-stream-impl", "futures-core", "pin-project-lite", ] [[package]] name = "async-stream-impl" version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "atomic-waker" version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" [[package]] name = "autocfg" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" [[package]] name = "backtrace" version = "0.3.74" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" dependencies = [ "addr2line", "cfg-if", "libc", "miniz_oxide", "object", "rustc-demangle", "windows-targets", ] [[package]] name = "bytes" version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ac0150caa2ae65ca5bd83f25c7de183dea78d4d366469f148435e2acfbad0da" [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "env_logger" version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4cd405aab171cb85d6735e5c8d9db038c17d3ca007a4d2c25f337935c3d90580" dependencies = [ "humantime", "is-terminal", "log", "regex", "termcolor", ] [[package]] name = "equivalent" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "fnv" version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "form_urlencoded" version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" dependencies = [ "percent-encoding", ] [[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-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-sink", "futures-task", "pin-project-lite", "pin-utils", ] [[package]] name = "gimli" version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" [[package]] name = "h2" version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ccae279728d634d083c00f6099cb58f01cc99c145b84b8be2f6c74618d79922e" dependencies = [ "atomic-waker", "bytes", "fnv", "futures-core", "futures-sink", "http", "indexmap", "slab", "tokio", "tokio-util", "tracing", ] [[package]] name = "hashbrown" version = "0.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3a9bfc1af68b1726ea47d3d5109de126281def866b33970e10fbab11b5dafab3" [[package]] name = "hermit-abi" version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" [[package]] name = "hermit-abi" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc" [[package]] name = "http" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f16ca2af56261c99fba8bac40a10251ce8188205a4c448fbb745a2e4daa76fea" dependencies = [ "bytes", "fnv", "itoa", ] [[package]] name = "http-body" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" dependencies = [ "bytes", "http", ] [[package]] name = "http-body-util" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f" dependencies = [ "bytes", "futures-util", "http", "http-body", "pin-project-lite", ] [[package]] name = "httparse" version = "1.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7d71d3574edd2771538b901e6549113b4006ece66150fb69c0fb6d9a2adae946" [[package]] name = "httpdate" version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" [[package]] name = "humantime" version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "hyper" version = "1.5.2" dependencies = [ "bytes", "form_urlencoded", "futures-channel", "futures-util", "h2", "http", "http-body", "http-body-util", "httparse", "httpdate", "itoa", "pin-project-lite", "pretty_env_logger", "serde", "serde_json", "smallvec", "spmc", "tokio", "tokio-test", "tokio-util", "tracing", "want", ] [[package]] name = "indexmap" version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" dependencies = [ "equivalent", "hashbrown", ] [[package]] name = "is-terminal" version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "261f68e344040fbd0edea105bef17c66edf46f984ddb1115b775ce31be948f4b" dependencies = [ "hermit-abi 0.4.0", "libc", "windows-sys 0.52.0", ] [[package]] name = "itoa" version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" [[package]] name = "libc" version = "0.2.164" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "433bfe06b8c75da9b2e3fbea6e5329ff87748f0b144ef75306e674c3f6f7c13f" [[package]] name = "log" version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" [[package]] name = "memchr" version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "miniz_oxide" version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" dependencies = [ "adler2", ] [[package]] name = "mio" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" dependencies = [ "hermit-abi 0.3.9", "libc", "wasi", "windows-sys 0.52.0", ] [[package]] name = "object" version = "0.36.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aedf0a2d09c573ed1d8d85b30c119153926a2b36dce0ab28322c09a117a4683e" dependencies = [ "memchr", ] [[package]] name = "once_cell" version = "1.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" [[package]] name = "percent-encoding" version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "pin-project-lite" 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 = "pretty_env_logger" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "865724d4dbe39d9f3dd3b52b88d859d66bcb2d6a0acfd5ea68a65fb66d4bdc1c" dependencies = [ "env_logger", "log", ] [[package]] name = "proc-macro2" version = "1.0.89" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e" dependencies = [ "unicode-ident", ] [[package]] name = "quote" version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" dependencies = [ "proc-macro2", ] [[package]] name = "regex" version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" dependencies = [ "aho-corasick", "memchr", "regex-automata", "regex-syntax", ] [[package]] name = "regex-automata" version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" dependencies = [ "aho-corasick", "memchr", "regex-syntax", ] [[package]] name = "regex-syntax" version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] name = "rustc-demangle" version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" [[package]] name = "ryu" version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" [[package]] name = "serde" version = "1.0.215" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6513c1ad0b11a9376da888e3e0baa0077f1aed55c17f50e7b2397136129fb88f" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" version = "1.0.215" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ad1e866f866923f252f05c889987993144fb74e722403468a4ebd70c3cd756c0" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "serde_json" version = "1.0.133" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c7fceb2473b9166b2294ef05efcb65a3db80803f0b03ef86a5fc88a2b85ee377" dependencies = [ "itoa", "memchr", "ryu", "serde", ] [[package]] name = "slab" version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" dependencies = [ "autocfg", ] [[package]] name = "smallvec" version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" [[package]] name = "socket2" version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" dependencies = [ "libc", "windows-sys 0.52.0", ] [[package]] name = "spmc" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "02a8428da277a8e3a15271d79943e80ccc2ef254e78813a166a08d65e4c3ece5" [[package]] name = "syn" version = "2.0.87" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "25aa4ce346d03a6dcd68dd8b4010bcb74e54e62c90c573f394c46eae99aba32d" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "termcolor" version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" dependencies = [ "winapi-util", ] [[package]] name = "tokio" version = "1.41.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22cfb5bee7a6a52939ca9224d6ac897bb669134078daa8735560897f69de4d33" dependencies = [ "backtrace", "bytes", "libc", "mio", "pin-project-lite", "socket2", "tokio-macros", "windows-sys 0.52.0", ] [[package]] name = "tokio-macros" version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "tokio-stream" version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4f4e6ce100d0eb49a2734f8c0812bcd324cf357d21810932c5df6b96ef2b86f1" dependencies = [ "futures-core", "pin-project-lite", "tokio", ] [[package]] name = "tokio-test" version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2468baabc3311435b55dd935f702f42cd1b8abb7e754fb7dfb16bd36aa88f9f7" dependencies = [ "async-stream", "bytes", "futures-core", "tokio", "tokio-stream", ] [[package]] name = "tokio-util" version = "0.7.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "61e7c3654c13bcd040d4a03abee2c75b1d14a37b423cf5a813ceae1cc903ec6a" dependencies = [ "bytes", "futures-core", "futures-sink", "pin-project-lite", "tokio", ] [[package]] name = "tracing" version = "0.1.40" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" dependencies = [ "pin-project-lite", "tracing-core", ] [[package]] name = "tracing-core" version = "0.1.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" dependencies = [ "once_cell", ] [[package]] name = "try-lock" version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" [[package]] name = "unicode-ident" version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" [[package]] name = "want" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" dependencies = [ "try-lock", ] [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "winapi-util" version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" dependencies = [ "windows-sys 0.59.0", ] [[package]] name = "windows-sys" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ "windows-targets", ] [[package]] name = "windows-sys" version = "0.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" dependencies = [ "windows-targets", ] [[package]] name = "windows-targets" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ "windows_aarch64_gnullvm", "windows_aarch64_msvc", "windows_i686_gnu", "windows_i686_gnullvm", "windows_i686_msvc", "windows_x86_64_gnu", "windows_x86_64_gnullvm", "windows_x86_64_msvc", ] [[package]] name = "windows_aarch64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] name = "windows_aarch64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] name = "windows_i686_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" [[package]] name = "windows_i686_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] name = "windows_i686_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] name = "windows_x86_64_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] name = "windows_x86_64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" hyper-1.5.2/Cargo.toml0000644000000100620000000000100101650ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2021" rust-version = "1.63" name = "hyper" version = "1.5.2" authors = ["Sean McArthur "] build = false include = [ "Cargo.toml", "LICENSE", "src/**/*", ] autolib = false autobins = false autoexamples = false autotests = false autobenches = false description = "A protective and efficient HTTP library for all." homepage = "https://hyper.rs" documentation = "https://docs.rs/hyper" readme = "README.md" keywords = [ "http", "hyper", "hyperium", ] categories = [ "network-programming", "web-programming::http-client", "web-programming::http-server", ] license = "MIT" repository = "https://github.com/hyperium/hyper" [package.metadata.capi.header] generation = false subdirectory = false [[package.metadata.capi.install.include.asset]] from = "capi/include/hyper.h" [package.metadata.docs.rs] features = [ "ffi", "full", "tracing", ] rustdoc-args = [ "--cfg", "hyper_unstable_ffi", "--cfg", "hyper_unstable_tracing", ] [package.metadata.playground] features = ["full"] [features] capi = [] client = [ "dep:want", "dep:pin-project-lite", "dep:smallvec", ] default = [] ffi = [ "dep:http-body-util", "futures-util?/alloc", ] full = [ "client", "http1", "http2", "server", ] http1 = [ "dep:futures-channel", "dep:futures-util", "dep:httparse", "dep:itoa", ] http2 = [ "dep:futures-channel", "dep:futures-util", "dep:h2", ] nightly = [] server = [ "dep:httpdate", "dep:pin-project-lite", "dep:smallvec", ] tracing = ["dep:tracing"] [lib] name = "hyper" path = "src/lib.rs" [dependencies.bytes] version = "1.2" [dependencies.futures-channel] version = "0.3" optional = true [dependencies.futures-util] version = "0.3" optional = true default-features = false [dependencies.h2] version = "0.4.2" optional = true [dependencies.http] version = "1" [dependencies.http-body] version = "1" [dependencies.http-body-util] version = "0.1" optional = true [dependencies.httparse] version = "1.8" optional = true [dependencies.httpdate] version = "1.0" optional = true [dependencies.itoa] version = "1" optional = true [dependencies.pin-project-lite] version = "0.2.4" optional = true [dependencies.smallvec] version = "1.12" features = [ "const_generics", "const_new", ] optional = true [dependencies.tokio] version = "1" features = ["sync"] [dependencies.tracing] version = "0.1" features = ["std"] optional = true default-features = false [dependencies.want] version = "0.3" optional = true [dev-dependencies.form_urlencoded] version = "1" [dev-dependencies.futures-channel] version = "0.3" features = ["sink"] [dev-dependencies.futures-util] version = "0.3" features = [ "alloc", "sink", ] default-features = false [dev-dependencies.http-body-util] version = "0.1" [dev-dependencies.pin-project-lite] version = "0.2.4" [dev-dependencies.pretty_env_logger] version = "0.5" [dev-dependencies.serde] version = "1.0" features = ["derive"] [dev-dependencies.serde_json] version = "1.0" [dev-dependencies.spmc] version = "0.3" [dev-dependencies.tokio] version = "1" features = [ "fs", "macros", "net", "io-std", "io-util", "rt", "rt-multi-thread", "sync", "time", "test-util", ] [dev-dependencies.tokio-test] version = "0.4" [dev-dependencies.tokio-util] version = "0.7.10" [lints.rust.unexpected_cfgs] level = "warn" priority = 0 check-cfg = [ "cfg(hyper_unstable_tracing)", "cfg(hyper_unstable_ffi)", ] [profile.bench] codegen-units = 1 incremental = false [profile.release] codegen-units = 1 incremental = false hyper-1.5.2/Cargo.toml.orig000064400000000000000000000122621046102023000136520ustar 00000000000000[package] name = "hyper" version = "1.5.2" description = "A protective and efficient HTTP library for all." readme = "README.md" homepage = "https://hyper.rs" documentation = "https://docs.rs/hyper" repository = "https://github.com/hyperium/hyper" license = "MIT" authors = ["Sean McArthur "] keywords = ["http", "hyper", "hyperium"] categories = ["network-programming", "web-programming::http-client", "web-programming::http-server"] edition = "2021" rust-version = "1.63" # keep in sync with MSRV.md dev doc include = [ "Cargo.toml", "LICENSE", "src/**/*", ] [dependencies] bytes = "1.2" http = "1" http-body = "1" tokio = { version = "1", features = ["sync"] } # Optional futures-channel = { version = "0.3", optional = true } futures-util = { version = "0.3", default-features = false, optional = true } h2 = { version = "0.4.2", optional = true } http-body-util = { version = "0.1", optional = true } httparse = { version = "1.8", optional = true } httpdate = { version = "1.0", optional = true } itoa = { version = "1", optional = true } pin-project-lite = { version = "0.2.4", optional = true } smallvec = { version = "1.12", features = ["const_generics", "const_new"], optional = true } tracing = { version = "0.1", default-features = false, features = ["std"], optional = true } want = { version = "0.3", optional = true } [dev-dependencies] form_urlencoded = "1" futures-channel = { version = "0.3", features = ["sink"] } futures-util = { version = "0.3", default-features = false, features = ["alloc", "sink"] } http-body-util = "0.1" pretty_env_logger = "0.5" pin-project-lite = "0.2.4" spmc = "0.3" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" tokio = { version = "1", features = [ "fs", "macros", "net", "io-std", "io-util", "rt", "rt-multi-thread", # so examples can use #[tokio::main] "sync", "time", "test-util", ] } tokio-test = "0.4" tokio-util = "0.7.10" [features] # Nothing by default default = [] # Easily turn it all on full = [ "client", "http1", "http2", "server", ] # HTTP versions http1 = ["dep:futures-channel", "dep:futures-util", "dep:httparse", "dep:itoa"] http2 = ["dep:futures-channel", "dep:futures-util", "dep:h2"] # Client/Server client = ["dep:want", "dep:pin-project-lite", "dep:smallvec"] server = ["dep:httpdate", "dep:pin-project-lite", "dep:smallvec"] # C-API support (currently unstable (no semver)) ffi = ["dep:http-body-util", "futures-util?/alloc"] capi = [] # Utilize tracing (currently unstable) tracing = ["dep:tracing"] # internal features used in CI nightly = [] [lints.rust.unexpected_cfgs] level = "warn" check-cfg = [ 'cfg(hyper_unstable_tracing)', 'cfg(hyper_unstable_ffi)' ] [package.metadata.docs.rs] features = ["ffi", "full", "tracing"] rustdoc-args = ["--cfg", "hyper_unstable_ffi", "--cfg", "hyper_unstable_tracing"] [package.metadata.playground] features = ["full"] [package.metadata.capi.header] generation = false subdirectory = false [package.metadata.capi.install.include] asset = [{ from="capi/include/hyper.h" }] [profile.release] codegen-units = 1 incremental = false [profile.bench] codegen-units = 1 incremental = false [[example]] name = "client" path = "examples/client.rs" required-features = ["full"] [[example]] name = "client_json" path = "examples/client_json.rs" required-features = ["full"] [[example]] name = "echo" path = "examples/echo.rs" required-features = ["full"] [[example]] name = "gateway" path = "examples/gateway.rs" required-features = ["full"] [[example]] name = "graceful_shutdown" path = "examples/graceful_shutdown.rs" required-features = ["full"] [[example]] name = "hello" path = "examples/hello.rs" required-features = ["full"] [[example]] name = "http_proxy" path = "examples/http_proxy.rs" required-features = ["full"] [[example]] name = "multi_server" path = "examples/multi_server.rs" required-features = ["full"] [[example]] name = "params" path = "examples/params.rs" required-features = ["full"] [[example]] name = "send_file" path = "examples/send_file.rs" required-features = ["full"] [[example]] name = "service_struct_impl" path = "examples/service_struct_impl.rs" required-features = ["full"] [[example]] name = "single_threaded" path = "examples/single_threaded.rs" required-features = ["full"] [[example]] name = "state" path = "examples/state.rs" required-features = ["full"] [[example]] name = "upgrades" path = "examples/upgrades.rs" required-features = ["full"] [[example]] name = "web_api" path = "examples/web_api.rs" required-features = ["full"] [[bench]] name = "body" path = "benches/body.rs" required-features = ["full"] [[bench]] name = "connect" path = "benches/connect.rs" required-features = ["full"] [[bench]] name = "end_to_end" path = "benches/end_to_end.rs" required-features = ["full"] [[bench]] name = "pipeline" path = "benches/pipeline.rs" required-features = ["full"] [[bench]] name = "server" path = "benches/server.rs" required-features = ["full"] [[test]] name = "client" path = "tests/client.rs" required-features = ["full"] [[test]] name = "integration" path = "tests/integration.rs" required-features = ["full"] [[test]] name = "server" path = "tests/server.rs" required-features = ["full"] hyper-1.5.2/LICENSE000064400000000000000000000020461046102023000117670ustar 00000000000000Copyright (c) 2014-2021 Sean McArthur Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. hyper-1.5.2/README.md000064400000000000000000000031061046102023000122370ustar 00000000000000# [hyper](https://hyper.rs) [![crates.io](https://img.shields.io/crates/v/hyper.svg)](https://crates.io/crates/hyper) [![Released API docs](https://docs.rs/hyper/badge.svg)](https://docs.rs/hyper) [![MIT licensed](https://img.shields.io/badge/license-MIT-blue.svg)](./LICENSE) [![CI](https://github.com/hyperium/hyper/workflows/CI/badge.svg)](https://github.com/hyperium/hyper/actions?query=workflow%3ACI) [![Discord chat][discord-badge]][discord-url] A protective and efficient HTTP library for all. - HTTP/1 and HTTP/2 - Asynchronous design - Leading in performance - Tested and **correct** - Extensive production use - Client and Server APIs **Get started** by looking over the [guides](https://hyper.rs/guides/1/). ## "Low-level" hyper is a relatively low-level library, meant to be a building block for libraries and applications. If you are looking for a convenient HTTP client, then you may wish to consider [reqwest](https://github.com/seanmonstar/reqwest). If you are not sure what HTTP server to choose, then you may want to consider [axum](https://github.com/tokio-rs/axum) or [warp](https://github.com/seanmonstar/warp), the latter taking a more functional approach. Both are built on top of this library. ## Contributing To get involved, take a look at [CONTRIBUTING](CONTRIBUTING.md). If you prefer chatting, there is an active community in the [Discord server][discord-url]. ## License hyper is provided under the MIT license. See [LICENSE](LICENSE). [discord-badge]: https://img.shields.io/discord/500028886025895936.svg?logo=discord [discord-url]: https://discord.gg/kkwpueZ hyper-1.5.2/src/body/incoming.rs000064400000000000000000000471771046102023000146750ustar 00000000000000use std::fmt; #[cfg(all(feature = "http1", any(feature = "client", feature = "server")))] use std::future::Future; use std::pin::Pin; use std::task::{Context, Poll}; use bytes::Bytes; #[cfg(all(feature = "http1", any(feature = "client", feature = "server")))] use futures_channel::{mpsc, oneshot}; #[cfg(all( any(feature = "http1", feature = "http2"), any(feature = "client", feature = "server") ))] use futures_util::ready; #[cfg(all(feature = "http1", any(feature = "client", feature = "server")))] use futures_util::{stream::FusedStream, Stream}; // for mpsc::Receiver #[cfg(all(feature = "http1", any(feature = "client", feature = "server")))] use http::HeaderMap; use http_body::{Body, Frame, SizeHint}; #[cfg(all( any(feature = "http1", feature = "http2"), any(feature = "client", feature = "server") ))] use super::DecodedLength; #[cfg(all(feature = "http1", any(feature = "client", feature = "server")))] use crate::common::watch; #[cfg(all(feature = "http2", any(feature = "client", feature = "server")))] use crate::proto::h2::ping; #[cfg(all(feature = "http1", any(feature = "client", feature = "server")))] type BodySender = mpsc::Sender>; #[cfg(all(feature = "http1", any(feature = "client", feature = "server")))] type TrailersSender = oneshot::Sender; /// A stream of `Bytes`, used when receiving bodies from the network. /// /// Note that Users should not instantiate this struct directly. When working with the hyper client, /// `Incoming` is returned to you in responses. Similarly, when operating with the hyper server, /// it is provided within requests. /// /// # Examples /// /// ```rust,ignore /// async fn echo( /// req: Request, /// ) -> Result>, hyper::Error> { /// //Here, you can process `Incoming` /// } /// ``` #[must_use = "streams do nothing unless polled"] pub struct Incoming { kind: Kind, } enum Kind { Empty, #[cfg(all(feature = "http1", any(feature = "client", feature = "server")))] Chan { content_length: DecodedLength, want_tx: watch::Sender, data_rx: mpsc::Receiver>, trailers_rx: oneshot::Receiver, }, #[cfg(all(feature = "http2", any(feature = "client", feature = "server")))] H2 { content_length: DecodedLength, data_done: bool, ping: ping::Recorder, recv: h2::RecvStream, }, #[cfg(feature = "ffi")] Ffi(crate::ffi::UserBody), } /// A sender half created through [`Body::channel()`]. /// /// Useful when wanting to stream chunks from another thread. /// /// ## Body Closing /// /// Note that the request body will always be closed normally when the sender is dropped (meaning /// that the empty terminating chunk will be sent to the remote). If you desire to close the /// connection with an incomplete response (e.g. in the case of an error during asynchronous /// processing), call the [`Sender::abort()`] method to abort the body in an abnormal fashion. /// /// [`Body::channel()`]: struct.Body.html#method.channel /// [`Sender::abort()`]: struct.Sender.html#method.abort #[must_use = "Sender does nothing unless sent on"] #[cfg(all(feature = "http1", any(feature = "client", feature = "server")))] pub(crate) struct Sender { want_rx: watch::Receiver, data_tx: BodySender, trailers_tx: Option, } #[cfg(all(feature = "http1", any(feature = "client", feature = "server")))] const WANT_PENDING: usize = 1; #[cfg(all(feature = "http1", any(feature = "client", feature = "server")))] const WANT_READY: usize = 2; impl Incoming { /// Create a `Body` stream with an associated sender half. /// /// Useful when wanting to stream chunks from another thread. #[inline] #[cfg(test)] pub(crate) fn channel() -> (Sender, Incoming) { Self::new_channel(DecodedLength::CHUNKED, /*wanter =*/ false) } #[cfg(all(feature = "http1", any(feature = "client", feature = "server")))] pub(crate) fn new_channel(content_length: DecodedLength, wanter: bool) -> (Sender, Incoming) { let (data_tx, data_rx) = mpsc::channel(0); let (trailers_tx, trailers_rx) = oneshot::channel(); // If wanter is true, `Sender::poll_ready()` won't becoming ready // until the `Body` has been polled for data once. let want = if wanter { WANT_PENDING } else { WANT_READY }; let (want_tx, want_rx) = watch::channel(want); let tx = Sender { want_rx, data_tx, trailers_tx: Some(trailers_tx), }; let rx = Incoming::new(Kind::Chan { content_length, want_tx, data_rx, trailers_rx, }); (tx, rx) } fn new(kind: Kind) -> Incoming { Incoming { kind } } #[allow(dead_code)] pub(crate) fn empty() -> Incoming { Incoming::new(Kind::Empty) } #[cfg(feature = "ffi")] pub(crate) fn ffi() -> Incoming { Incoming::new(Kind::Ffi(crate::ffi::UserBody::new())) } #[cfg(all(feature = "http2", any(feature = "client", feature = "server")))] pub(crate) fn h2( recv: h2::RecvStream, mut content_length: DecodedLength, ping: ping::Recorder, ) -> Self { // If the stream is already EOS, then the "unknown length" is clearly // actually ZERO. if !content_length.is_exact() && recv.is_end_stream() { content_length = DecodedLength::ZERO; } Incoming::new(Kind::H2 { data_done: false, ping, content_length, recv, }) } #[cfg(feature = "ffi")] pub(crate) fn as_ffi_mut(&mut self) -> &mut crate::ffi::UserBody { match self.kind { Kind::Ffi(ref mut body) => return body, _ => { self.kind = Kind::Ffi(crate::ffi::UserBody::new()); } } match self.kind { Kind::Ffi(ref mut body) => body, _ => unreachable!(), } } } impl Body for Incoming { type Data = Bytes; type Error = crate::Error; fn poll_frame( #[cfg_attr( not(all( any(feature = "http1", feature = "http2"), any(feature = "client", feature = "server") )), allow(unused_mut) )] mut self: Pin<&mut Self>, #[cfg_attr( not(all( any(feature = "http1", feature = "http2"), any(feature = "client", feature = "server") )), allow(unused_variables) )] cx: &mut Context<'_>, ) -> Poll, Self::Error>>> { match self.kind { Kind::Empty => Poll::Ready(None), #[cfg(all(feature = "http1", any(feature = "client", feature = "server")))] Kind::Chan { content_length: ref mut len, ref mut data_rx, ref mut want_tx, ref mut trailers_rx, } => { want_tx.send(WANT_READY); if !data_rx.is_terminated() { if let Some(chunk) = ready!(Pin::new(data_rx).poll_next(cx)?) { len.sub_if(chunk.len() as u64); return Poll::Ready(Some(Ok(Frame::data(chunk)))); } } // check trailers after data is terminated match ready!(Pin::new(trailers_rx).poll(cx)) { Ok(t) => Poll::Ready(Some(Ok(Frame::trailers(t)))), Err(_) => Poll::Ready(None), } } #[cfg(all(feature = "http2", any(feature = "client", feature = "server")))] Kind::H2 { ref mut data_done, ref ping, recv: ref mut h2, content_length: ref mut len, } => { if !*data_done { match ready!(h2.poll_data(cx)) { Some(Ok(bytes)) => { let _ = h2.flow_control().release_capacity(bytes.len()); len.sub_if(bytes.len() as u64); ping.record_data(bytes.len()); return Poll::Ready(Some(Ok(Frame::data(bytes)))); } Some(Err(e)) => { return match e.reason() { // These reasons should cause the body reading to stop, but not fail it. // The same logic as for `Read for H2Upgraded` is applied here. Some(h2::Reason::NO_ERROR) | Some(h2::Reason::CANCEL) => { Poll::Ready(None) } _ => Poll::Ready(Some(Err(crate::Error::new_body(e)))), }; } None => { *data_done = true; // fall through to trailers } } } // after data, check trailers match ready!(h2.poll_trailers(cx)) { Ok(t) => { ping.record_non_data(); Poll::Ready(Ok(t.map(Frame::trailers)).transpose()) } Err(e) => Poll::Ready(Some(Err(crate::Error::new_h2(e)))), } } #[cfg(feature = "ffi")] Kind::Ffi(ref mut body) => body.poll_data(cx), } } fn is_end_stream(&self) -> bool { match self.kind { Kind::Empty => true, #[cfg(all(feature = "http1", any(feature = "client", feature = "server")))] Kind::Chan { content_length, .. } => content_length == DecodedLength::ZERO, #[cfg(all(feature = "http2", any(feature = "client", feature = "server")))] Kind::H2 { recv: ref h2, .. } => h2.is_end_stream(), #[cfg(feature = "ffi")] Kind::Ffi(..) => false, } } fn size_hint(&self) -> SizeHint { #[cfg(all( any(feature = "http1", feature = "http2"), any(feature = "client", feature = "server") ))] fn opt_len(decoded_length: DecodedLength) -> SizeHint { if let Some(content_length) = decoded_length.into_opt() { SizeHint::with_exact(content_length) } else { SizeHint::default() } } match self.kind { Kind::Empty => SizeHint::with_exact(0), #[cfg(all(feature = "http1", any(feature = "client", feature = "server")))] Kind::Chan { content_length, .. } => opt_len(content_length), #[cfg(all(feature = "http2", any(feature = "client", feature = "server")))] Kind::H2 { content_length, .. } => opt_len(content_length), #[cfg(feature = "ffi")] Kind::Ffi(..) => SizeHint::default(), } } } impl fmt::Debug for Incoming { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { #[cfg(any( all( any(feature = "http1", feature = "http2"), any(feature = "client", feature = "server") ), feature = "ffi" ))] #[derive(Debug)] struct Streaming; #[derive(Debug)] struct Empty; let mut builder = f.debug_tuple("Body"); match self.kind { Kind::Empty => builder.field(&Empty), #[cfg(any( all( any(feature = "http1", feature = "http2"), any(feature = "client", feature = "server") ), feature = "ffi" ))] _ => builder.field(&Streaming), }; builder.finish() } } #[cfg(all(feature = "http1", any(feature = "client", feature = "server")))] impl Sender { /// Check to see if this `Sender` can send more data. pub(crate) fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { // Check if the receiver end has tried polling for the body yet ready!(self.poll_want(cx)?); self.data_tx .poll_ready(cx) .map_err(|_| crate::Error::new_closed()) } fn poll_want(&mut self, cx: &mut Context<'_>) -> Poll> { match self.want_rx.load(cx) { WANT_READY => Poll::Ready(Ok(())), WANT_PENDING => Poll::Pending, watch::CLOSED => Poll::Ready(Err(crate::Error::new_closed())), unexpected => unreachable!("want_rx value: {}", unexpected), } } #[cfg(test)] async fn ready(&mut self) -> crate::Result<()> { futures_util::future::poll_fn(|cx| self.poll_ready(cx)).await } /// Send data on data channel when it is ready. #[cfg(test)] #[allow(unused)] pub(crate) async fn send_data(&mut self, chunk: Bytes) -> crate::Result<()> { self.ready().await?; self.data_tx .try_send(Ok(chunk)) .map_err(|_| crate::Error::new_closed()) } /// Send trailers on trailers channel. #[allow(unused)] pub(crate) async fn send_trailers(&mut self, trailers: HeaderMap) -> crate::Result<()> { let tx = match self.trailers_tx.take() { Some(tx) => tx, None => return Err(crate::Error::new_closed()), }; tx.send(trailers).map_err(|_| crate::Error::new_closed()) } /// Try to send data on this channel. /// /// # Errors /// /// Returns `Err(Bytes)` if the channel could not (currently) accept /// another `Bytes`. /// /// # Note /// /// This is mostly useful for when trying to send from some other thread /// that doesn't have an async context. If in an async context, prefer /// `send_data()` instead. #[cfg(feature = "http1")] pub(crate) fn try_send_data(&mut self, chunk: Bytes) -> Result<(), Bytes> { self.data_tx .try_send(Ok(chunk)) .map_err(|err| err.into_inner().expect("just sent Ok")) } #[cfg(feature = "http1")] pub(crate) fn try_send_trailers( &mut self, trailers: HeaderMap, ) -> Result<(), Option> { let tx = match self.trailers_tx.take() { Some(tx) => tx, None => return Err(None), }; tx.send(trailers).map_err(Some) } #[cfg(test)] pub(crate) fn abort(mut self) { self.send_error(crate::Error::new_body_write_aborted()); } pub(crate) fn send_error(&mut self, err: crate::Error) { let _ = self .data_tx // clone so the send works even if buffer is full .clone() .try_send(Err(err)); } } #[cfg(all(feature = "http1", any(feature = "client", feature = "server")))] impl fmt::Debug for Sender { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { #[derive(Debug)] struct Open; #[derive(Debug)] struct Closed; let mut builder = f.debug_tuple("Sender"); match self.want_rx.peek() { watch::CLOSED => builder.field(&Closed), _ => builder.field(&Open), }; builder.finish() } } #[cfg(test)] mod tests { use std::mem; use std::task::Poll; use super::{Body, DecodedLength, Incoming, Sender, SizeHint}; use http_body_util::BodyExt; #[test] fn test_size_of() { // These are mostly to help catch *accidentally* increasing // the size by too much. let body_size = mem::size_of::(); let body_expected_size = mem::size_of::() * 5; assert!( body_size <= body_expected_size, "Body size = {} <= {}", body_size, body_expected_size, ); //assert_eq!(body_size, mem::size_of::>(), "Option"); assert_eq!( mem::size_of::(), mem::size_of::() * 5, "Sender" ); assert_eq!( mem::size_of::(), mem::size_of::>(), "Option" ); } #[test] fn size_hint() { fn eq(body: Incoming, b: SizeHint, note: &str) { let a = body.size_hint(); assert_eq!(a.lower(), b.lower(), "lower for {:?}", note); assert_eq!(a.upper(), b.upper(), "upper for {:?}", note); } eq(Incoming::empty(), SizeHint::with_exact(0), "empty"); eq(Incoming::channel().1, SizeHint::new(), "channel"); eq( Incoming::new_channel(DecodedLength::new(4), /*wanter =*/ false).1, SizeHint::with_exact(4), "channel with length", ); } #[cfg(not(miri))] #[tokio::test] async fn channel_abort() { let (tx, mut rx) = Incoming::channel(); tx.abort(); let err = rx.frame().await.unwrap().unwrap_err(); assert!(err.is_body_write_aborted(), "{:?}", err); } #[cfg(all(not(miri), feature = "http1"))] #[tokio::test] async fn channel_abort_when_buffer_is_full() { let (mut tx, mut rx) = Incoming::channel(); tx.try_send_data("chunk 1".into()).expect("send 1"); // buffer is full, but can still send abort tx.abort(); let chunk1 = rx .frame() .await .expect("item 1") .expect("chunk 1") .into_data() .unwrap(); assert_eq!(chunk1, "chunk 1"); let err = rx.frame().await.unwrap().unwrap_err(); assert!(err.is_body_write_aborted(), "{:?}", err); } #[cfg(feature = "http1")] #[test] fn channel_buffers_one() { let (mut tx, _rx) = Incoming::channel(); tx.try_send_data("chunk 1".into()).expect("send 1"); // buffer is now full let chunk2 = tx.try_send_data("chunk 2".into()).expect_err("send 2"); assert_eq!(chunk2, "chunk 2"); } #[cfg(not(miri))] #[tokio::test] async fn channel_empty() { let (_, mut rx) = Incoming::channel(); assert!(rx.frame().await.is_none()); } #[test] fn channel_ready() { let (mut tx, _rx) = Incoming::new_channel(DecodedLength::CHUNKED, /*wanter = */ false); let mut tx_ready = tokio_test::task::spawn(tx.ready()); assert!(tx_ready.poll().is_ready(), "tx is ready immediately"); } #[test] fn channel_wanter() { let (mut tx, mut rx) = Incoming::new_channel(DecodedLength::CHUNKED, /*wanter = */ true); let mut tx_ready = tokio_test::task::spawn(tx.ready()); let mut rx_data = tokio_test::task::spawn(rx.frame()); assert!( tx_ready.poll().is_pending(), "tx isn't ready before rx has been polled" ); assert!(rx_data.poll().is_pending(), "poll rx.data"); assert!(tx_ready.is_woken(), "rx poll wakes tx"); assert!( tx_ready.poll().is_ready(), "tx is ready after rx has been polled" ); } #[test] fn channel_notices_closure() { let (mut tx, rx) = Incoming::new_channel(DecodedLength::CHUNKED, /*wanter = */ true); let mut tx_ready = tokio_test::task::spawn(tx.ready()); assert!( tx_ready.poll().is_pending(), "tx isn't ready before rx has been polled" ); drop(rx); assert!(tx_ready.is_woken(), "dropping rx wakes tx"); match tx_ready.poll() { Poll::Ready(Err(ref e)) if e.is_closed() => (), unexpected => panic!("tx poll ready unexpected: {:?}", unexpected), } } } hyper-1.5.2/src/body/length.rs000064400000000000000000000076551046102023000143500ustar 00000000000000use std::fmt; #[derive(Clone, Copy, PartialEq, Eq)] pub(crate) struct DecodedLength(u64); #[cfg(any(feature = "http1", feature = "http2"))] impl From> for DecodedLength { fn from(len: Option) -> Self { len.and_then(|len| { // If the length is u64::MAX, oh well, just reported chunked. Self::checked_new(len).ok() }) .unwrap_or(DecodedLength::CHUNKED) } } #[cfg(any(feature = "http1", feature = "http2", test))] const MAX_LEN: u64 = u64::MAX - 2; impl DecodedLength { pub(crate) const CLOSE_DELIMITED: DecodedLength = DecodedLength(u64::MAX); pub(crate) const CHUNKED: DecodedLength = DecodedLength(u64::MAX - 1); pub(crate) const ZERO: DecodedLength = DecodedLength(0); #[cfg(test)] pub(crate) fn new(len: u64) -> Self { debug_assert!(len <= MAX_LEN); DecodedLength(len) } /// Takes the length as a content-length without other checks. /// /// Should only be called if previously confirmed this isn't /// CLOSE_DELIMITED or CHUNKED. #[inline] #[cfg(all(any(feature = "client", feature = "server"), feature = "http1"))] pub(crate) fn danger_len(self) -> u64 { debug_assert!(self.0 < Self::CHUNKED.0); self.0 } /// Converts to an Option representing a Known or Unknown length. #[cfg(all( any(feature = "http1", feature = "http2"), any(feature = "client", feature = "server") ))] pub(crate) fn into_opt(self) -> Option { match self { DecodedLength::CHUNKED | DecodedLength::CLOSE_DELIMITED => None, DecodedLength(known) => Some(known), } } /// Checks the `u64` is within the maximum allowed for content-length. #[cfg(any(feature = "http1", feature = "http2"))] pub(crate) fn checked_new(len: u64) -> Result { if len <= MAX_LEN { Ok(DecodedLength(len)) } else { warn!("content-length bigger than maximum: {} > {}", len, MAX_LEN); Err(crate::error::Parse::TooLarge) } } #[cfg(all( any(feature = "http1", feature = "http2"), any(feature = "client", feature = "server") ))] pub(crate) fn sub_if(&mut self, amt: u64) { match *self { DecodedLength::CHUNKED | DecodedLength::CLOSE_DELIMITED => (), DecodedLength(ref mut known) => { *known -= amt; } } } /// Returns whether this represents an exact length. /// /// This includes 0, which of course is an exact known length. /// /// It would return false if "chunked" or otherwise size-unknown. #[cfg(all(any(feature = "client", feature = "server"), feature = "http2"))] pub(crate) fn is_exact(&self) -> bool { self.0 <= MAX_LEN } } impl fmt::Debug for DecodedLength { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match *self { DecodedLength::CLOSE_DELIMITED => f.write_str("CLOSE_DELIMITED"), DecodedLength::CHUNKED => f.write_str("CHUNKED"), DecodedLength(n) => f.debug_tuple("DecodedLength").field(&n).finish(), } } } impl fmt::Display for DecodedLength { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match *self { DecodedLength::CLOSE_DELIMITED => f.write_str("close-delimited"), DecodedLength::CHUNKED => f.write_str("chunked encoding"), DecodedLength::ZERO => f.write_str("empty"), DecodedLength(n) => write!(f, "content-length ({} bytes)", n), } } } #[cfg(test)] mod tests { use super::*; #[test] fn sub_if_known() { let mut len = DecodedLength::new(30); len.sub_if(20); assert_eq!(len.0, 10); } #[test] fn sub_if_chunked() { let mut len = DecodedLength::CHUNKED; len.sub_if(20); assert_eq!(len, DecodedLength::CHUNKED); } } hyper-1.5.2/src/body/mod.rs000064400000000000000000000032021046102023000136260ustar 00000000000000//! Streaming bodies for Requests and Responses //! //! For both [Clients](crate::client) and [Servers](crate::server), requests and //! responses use streaming bodies, instead of complete buffering. This //! allows applications to not use memory they don't need, and allows exerting //! back-pressure on connections by only reading when asked. //! //! There are two pieces to this in hyper: //! //! - **The [`Body`] trait** describes all possible bodies. //! hyper allows any body type that implements `Body`, allowing //! applications to have fine-grained control over their streaming. //! - **The [`Incoming`] concrete type**, which is an implementation //! of `Body`, and returned by hyper as a "receive stream" (so, for server //! requests and client responses). //! //! There are additional implementations available in [`http-body-util`][], //! such as a `Full` or `Empty` body. //! //! [`http-body-util`]: https://docs.rs/http-body-util pub use bytes::{Buf, Bytes}; pub use http_body::Body; pub use http_body::Frame; pub use http_body::SizeHint; pub use self::incoming::Incoming; #[cfg(all(any(feature = "client", feature = "server"), feature = "http1"))] pub(crate) use self::incoming::Sender; #[cfg(all( any(feature = "http1", feature = "http2"), any(feature = "client", feature = "server") ))] pub(crate) use self::length::DecodedLength; mod incoming; #[cfg(all( any(feature = "http1", feature = "http2"), any(feature = "client", feature = "server") ))] mod length; fn _assert_send_sync() { fn _assert_send() {} fn _assert_sync() {} _assert_send::(); _assert_sync::(); } hyper-1.5.2/src/cfg.rs000064400000000000000000000015401046102023000126540ustar 00000000000000macro_rules! cfg_feature { ( #![$meta:meta] $($item:item)* ) => { $( #[cfg($meta)] #[cfg_attr(docsrs, doc(cfg($meta)))] $item )* } } macro_rules! cfg_proto { ($($item:item)*) => { cfg_feature! { #![all( any(feature = "http1", feature = "http2"), any(feature = "client", feature = "server"), )] $($item)* } } } cfg_proto! { macro_rules! cfg_client { ($($item:item)*) => { cfg_feature! { #![feature = "client"] $($item)* } } } macro_rules! cfg_server { ($($item:item)*) => { cfg_feature! { #![feature = "server"] $($item)* } } } } hyper-1.5.2/src/client/conn/http1.rs000064400000000000000000000527141046102023000154010ustar 00000000000000//! HTTP/1 client connections use std::error::Error as StdError; use std::fmt; use std::future::Future; use std::pin::Pin; use std::task::{Context, Poll}; use crate::rt::{Read, Write}; use bytes::Bytes; use futures_util::ready; use http::{Request, Response}; use httparse::ParserConfig; use super::super::dispatch::{self, TrySendError}; use crate::body::{Body, Incoming as IncomingBody}; use crate::proto; type Dispatcher = proto::dispatch::Dispatcher, B, T, proto::h1::ClientTransaction>; /// The sender side of an established connection. pub struct SendRequest { dispatch: dispatch::Sender, Response>, } /// Deconstructed parts of a `Connection`. /// /// This allows taking apart a `Connection` at a later time, in order to /// reclaim the IO object, and additional related pieces. #[derive(Debug)] #[non_exhaustive] pub struct Parts { /// The original IO object used in the handshake. pub io: T, /// A buffer of bytes that have been read but not processed as HTTP. /// /// For instance, if the `Connection` is used for an HTTP upgrade request, /// it is possible the server sent back the first bytes of the new protocol /// along with the response upgrade. /// /// You will want to check for any existing bytes if you plan to continue /// communicating on the IO object. pub read_buf: Bytes, } /// A future that processes all HTTP state for the IO object. /// /// In most cases, this should just be spawned into an executor, so that it /// can process incoming and outgoing messages, notice hangups, and the like. /// /// Instances of this type are typically created via the [`handshake`] function #[must_use = "futures do nothing unless polled"] pub struct Connection where T: Read + Write, B: Body + 'static, { inner: Dispatcher, } impl Connection where T: Read + Write + Unpin, B: Body + 'static, B::Error: Into>, { /// Return the inner IO object, and additional information. /// /// Only works for HTTP/1 connections. HTTP/2 connections will panic. pub fn into_parts(self) -> Parts { let (io, read_buf, _) = self.inner.into_inner(); Parts { io, read_buf } } /// Poll the connection for completion, but without calling `shutdown` /// on the underlying IO. /// /// This is useful to allow running a connection while doing an HTTP /// upgrade. Once the upgrade is completed, the connection would be "done", /// but it is not desired to actually shutdown the IO object. Instead you /// would take it back using `into_parts`. /// /// Use [`poll_fn`](https://docs.rs/futures/0.1.25/futures/future/fn.poll_fn.html) /// and [`try_ready!`](https://docs.rs/futures/0.1.25/futures/macro.try_ready.html) /// to work with this function; or use the `without_shutdown` wrapper. pub fn poll_without_shutdown(&mut self, cx: &mut Context<'_>) -> Poll> { self.inner.poll_without_shutdown(cx) } /// Prevent shutdown of the underlying IO object at the end of service the request, /// instead run `into_parts`. This is a convenience wrapper over `poll_without_shutdown`. pub async fn without_shutdown(self) -> crate::Result> { let mut conn = Some(self); futures_util::future::poll_fn(move |cx| -> Poll>> { ready!(conn.as_mut().unwrap().poll_without_shutdown(cx))?; Poll::Ready(Ok(conn.take().unwrap().into_parts())) }) .await } } /// A builder to configure an HTTP connection. /// /// After setting options, the builder is used to create a handshake future. /// /// **Note**: The default values of options are *not considered stable*. They /// are subject to change at any time. #[derive(Clone, Debug)] pub struct Builder { h09_responses: bool, h1_parser_config: ParserConfig, h1_writev: Option, h1_title_case_headers: bool, h1_preserve_header_case: bool, h1_max_headers: Option, #[cfg(feature = "ffi")] h1_preserve_header_order: bool, h1_read_buf_exact_size: Option, h1_max_buf_size: Option, } /// Returns a handshake future over some IO. /// /// This is a shortcut for `Builder::new().handshake(io)`. /// See [`client::conn`](crate::client::conn) for more. pub async fn handshake(io: T) -> crate::Result<(SendRequest, Connection)> where T: Read + Write + Unpin, B: Body + 'static, B::Data: Send, B::Error: Into>, { Builder::new().handshake(io).await } // ===== impl SendRequest impl SendRequest { /// Polls to determine whether this sender can be used yet for a request. /// /// If the associated connection is closed, this returns an Error. pub fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { self.dispatch.poll_ready(cx) } /// Waits until the dispatcher is ready /// /// If the associated connection is closed, this returns an Error. pub async fn ready(&mut self) -> crate::Result<()> { futures_util::future::poll_fn(|cx| self.poll_ready(cx)).await } /// Checks if the connection is currently ready to send a request. /// /// # Note /// /// This is mostly a hint. Due to inherent latency of networks, it is /// possible that even after checking this is ready, sending a request /// may still fail because the connection was closed in the meantime. pub fn is_ready(&self) -> bool { self.dispatch.is_ready() } /// Checks if the connection side has been closed. pub fn is_closed(&self) -> bool { self.dispatch.is_closed() } } impl SendRequest where B: Body + 'static, { /// Sends a `Request` on the associated connection. /// /// Returns a future that if successful, yields the `Response`. /// /// `req` must have a `Host` header. /// /// # Uri /// /// The `Uri` of the request is serialized as-is. /// /// - Usually you want origin-form (`/path?query`). /// - For sending to an HTTP proxy, you want to send in absolute-form /// (`https://hyper.rs/guides`). /// /// This is however not enforced or validated and it is up to the user /// of this method to ensure the `Uri` is correct for their intended purpose. pub fn send_request( &mut self, req: Request, ) -> impl Future>> { let sent = self.dispatch.send(req); async move { match sent { Ok(rx) => match rx.await { Ok(Ok(resp)) => Ok(resp), Ok(Err(err)) => Err(err), // this is definite bug if it happens, but it shouldn't happen! Err(_canceled) => panic!("dispatch dropped without returning error"), }, Err(_req) => { debug!("connection was not ready"); Err(crate::Error::new_canceled().with("connection was not ready")) } } } } /// Sends a `Request` on the associated connection. /// /// Returns a future that if successful, yields the `Response`. /// /// # Error /// /// If there was an error before trying to serialize the request to the /// connection, the message will be returned as part of this error. pub fn try_send_request( &mut self, req: Request, ) -> impl Future, TrySendError>>> { let sent = self.dispatch.try_send(req); async move { match sent { Ok(rx) => match rx.await { Ok(Ok(res)) => Ok(res), Ok(Err(err)) => Err(err), // this is definite bug if it happens, but it shouldn't happen! Err(_) => panic!("dispatch dropped without returning error"), }, Err(req) => { debug!("connection was not ready"); let error = crate::Error::new_canceled().with("connection was not ready"); Err(TrySendError { error, message: Some(req), }) } } } } } impl fmt::Debug for SendRequest { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("SendRequest").finish() } } // ===== impl Connection impl Connection where T: Read + Write + Unpin + Send, B: Body + 'static, B::Error: Into>, { /// Enable this connection to support higher-level HTTP upgrades. /// /// See [the `upgrade` module](crate::upgrade) for more. pub fn with_upgrades(self) -> upgrades::UpgradeableConnection { upgrades::UpgradeableConnection { inner: Some(self) } } } impl fmt::Debug for Connection where T: Read + Write + fmt::Debug, B: Body + 'static, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("Connection").finish() } } impl Future for Connection where T: Read + Write + Unpin, B: Body + 'static, B::Data: Send, B::Error: Into>, { type Output = crate::Result<()>; fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { match ready!(Pin::new(&mut self.inner).poll(cx))? { proto::Dispatched::Shutdown => Poll::Ready(Ok(())), proto::Dispatched::Upgrade(pending) => { // With no `Send` bound on `I`, we can't try to do // upgrades here. In case a user was trying to use // `upgrade` with this API, send a special // error letting them know about that. pending.manual(); Poll::Ready(Ok(())) } } } } // ===== impl Builder impl Builder { /// Creates a new connection builder. #[inline] pub fn new() -> Builder { Builder { h09_responses: false, h1_writev: None, h1_read_buf_exact_size: None, h1_parser_config: Default::default(), h1_title_case_headers: false, h1_preserve_header_case: false, h1_max_headers: None, #[cfg(feature = "ffi")] h1_preserve_header_order: false, h1_max_buf_size: None, } } /// Set whether HTTP/0.9 responses should be tolerated. /// /// Default is false. pub fn http09_responses(&mut self, enabled: bool) -> &mut Builder { self.h09_responses = enabled; self } /// Set whether HTTP/1 connections will accept spaces between header names /// and the colon that follow them in responses. /// /// You probably don't need this, here is what [RFC 7230 Section 3.2.4.] has /// to say about it: /// /// > No whitespace is allowed between the header field-name and colon. In /// > the past, differences in the handling of such whitespace have led to /// > security vulnerabilities in request routing and response handling. A /// > server MUST reject any received request message that contains /// > whitespace between a header field-name and colon with a response code /// > of 400 (Bad Request). A proxy MUST remove any such whitespace from a /// > response message before forwarding the message downstream. /// /// Default is false. /// /// [RFC 7230 Section 3.2.4.]: https://tools.ietf.org/html/rfc7230#section-3.2.4 pub fn allow_spaces_after_header_name_in_responses(&mut self, enabled: bool) -> &mut Builder { self.h1_parser_config .allow_spaces_after_header_name_in_responses(enabled); self } /// Set whether HTTP/1 connections will accept obsolete line folding for /// header values. /// /// Newline codepoints (`\r` and `\n`) will be transformed to spaces when /// parsing. /// /// You probably don't need this, here is what [RFC 7230 Section 3.2.4.] has /// to say about it: /// /// > A server that receives an obs-fold in a request message that is not /// > within a message/http container MUST either reject the message by /// > sending a 400 (Bad Request), preferably with a representation /// > explaining that obsolete line folding is unacceptable, or replace /// > each received obs-fold with one or more SP octets prior to /// > interpreting the field value or forwarding the message downstream. /// /// > A proxy or gateway that receives an obs-fold in a response message /// > that is not within a message/http container MUST either discard the /// > message and replace it with a 502 (Bad Gateway) response, preferably /// > with a representation explaining that unacceptable line folding was /// > received, or replace each received obs-fold with one or more SP /// > octets prior to interpreting the field value or forwarding the /// > message downstream. /// /// > A user agent that receives an obs-fold in a response message that is /// > not within a message/http container MUST replace each received /// > obs-fold with one or more SP octets prior to interpreting the field /// > value. /// /// Default is false. /// /// [RFC 7230 Section 3.2.4.]: https://tools.ietf.org/html/rfc7230#section-3.2.4 pub fn allow_obsolete_multiline_headers_in_responses(&mut self, enabled: bool) -> &mut Builder { self.h1_parser_config .allow_obsolete_multiline_headers_in_responses(enabled); self } /// Set whether HTTP/1 connections will silently ignored malformed header lines. /// /// If this is enabled and a header line does not start with a valid header /// name, or does not include a colon at all, the line will be silently ignored /// and no error will be reported. /// /// Default is false. pub fn ignore_invalid_headers_in_responses(&mut self, enabled: bool) -> &mut Builder { self.h1_parser_config .ignore_invalid_headers_in_responses(enabled); self } /// Set whether HTTP/1 connections should try to use vectored writes, /// or always flatten into a single buffer. /// /// Note that setting this to false may mean more copies of body data, /// but may also improve performance when an IO transport doesn't /// support vectored writes well, such as most TLS implementations. /// /// Setting this to true will force hyper to use queued strategy /// which may eliminate unnecessary cloning on some TLS backends /// /// Default is `auto`. In this mode hyper will try to guess which /// mode to use pub fn writev(&mut self, enabled: bool) -> &mut Builder { self.h1_writev = Some(enabled); self } /// Set whether HTTP/1 connections will write header names as title case at /// the socket level. /// /// Default is false. pub fn title_case_headers(&mut self, enabled: bool) -> &mut Builder { self.h1_title_case_headers = enabled; self } /// Set whether to support preserving original header cases. /// /// Currently, this will record the original cases received, and store them /// in a private extension on the `Response`. It will also look for and use /// such an extension in any provided `Request`. /// /// Since the relevant extension is still private, there is no way to /// interact with the original cases. The only effect this can have now is /// to forward the cases in a proxy-like fashion. /// /// Default is false. pub fn preserve_header_case(&mut self, enabled: bool) -> &mut Builder { self.h1_preserve_header_case = enabled; self } /// Set the maximum number of headers. /// /// When a response is received, the parser will reserve a buffer to store headers for optimal /// performance. /// /// If client receives more headers than the buffer size, the error "message header too large" /// is returned. /// /// Note that headers is allocated on the stack by default, which has higher performance. After /// setting this value, headers will be allocated in heap memory, that is, heap memory /// allocation will occur for each response, and there will be a performance drop of about 5%. /// /// Default is 100. pub fn max_headers(&mut self, val: usize) -> &mut Self { self.h1_max_headers = Some(val); self } /// Set whether to support preserving original header order. /// /// Currently, this will record the order in which headers are received, and store this /// ordering in a private extension on the `Response`. It will also look for and use /// such an extension in any provided `Request`. /// /// Default is false. #[cfg(feature = "ffi")] pub fn preserve_header_order(&mut self, enabled: bool) -> &mut Builder { self.h1_preserve_header_order = enabled; self } /// Sets the exact size of the read buffer to *always* use. /// /// Note that setting this option unsets the `max_buf_size` option. /// /// Default is an adaptive read buffer. pub fn read_buf_exact_size(&mut self, sz: Option) -> &mut Builder { self.h1_read_buf_exact_size = sz; self.h1_max_buf_size = None; self } /// Set the maximum buffer size for the connection. /// /// Default is ~400kb. /// /// Note that setting this option unsets the `read_exact_buf_size` option. /// /// # Panics /// /// The minimum value allowed is 8192. This method panics if the passed `max` is less than the minimum. pub fn max_buf_size(&mut self, max: usize) -> &mut Self { assert!( max >= proto::h1::MINIMUM_MAX_BUFFER_SIZE, "the max_buf_size cannot be smaller than the minimum that h1 specifies." ); self.h1_max_buf_size = Some(max); self.h1_read_buf_exact_size = None; self } /// Constructs a connection with the configured options and IO. /// See [`client::conn`](crate::client::conn) for more. /// /// Note, if [`Connection`] is not `await`-ed, [`SendRequest`] will /// do nothing. pub fn handshake( &self, io: T, ) -> impl Future, Connection)>> where T: Read + Write + Unpin, B: Body + 'static, B::Data: Send, B::Error: Into>, { let opts = self.clone(); async move { trace!("client handshake HTTP/1"); let (tx, rx) = dispatch::channel(); let mut conn = proto::Conn::new(io); conn.set_h1_parser_config(opts.h1_parser_config); if let Some(writev) = opts.h1_writev { if writev { conn.set_write_strategy_queue(); } else { conn.set_write_strategy_flatten(); } } if opts.h1_title_case_headers { conn.set_title_case_headers(); } if opts.h1_preserve_header_case { conn.set_preserve_header_case(); } if let Some(max_headers) = opts.h1_max_headers { conn.set_http1_max_headers(max_headers); } #[cfg(feature = "ffi")] if opts.h1_preserve_header_order { conn.set_preserve_header_order(); } if opts.h09_responses { conn.set_h09_responses(); } if let Some(sz) = opts.h1_read_buf_exact_size { conn.set_read_buf_exact_size(sz); } if let Some(max) = opts.h1_max_buf_size { conn.set_max_buf_size(max); } let cd = proto::h1::dispatch::Client::new(rx); let proto = proto::h1::Dispatcher::new(cd, conn); Ok((SendRequest { dispatch: tx }, Connection { inner: proto })) } } } mod upgrades { use crate::upgrade::Upgraded; use super::*; // A future binding a connection with a Service with Upgrade support. // // This type is unnameable outside the crate. #[must_use = "futures do nothing unless polled"] #[allow(missing_debug_implementations)] pub struct UpgradeableConnection where T: Read + Write + Unpin + Send + 'static, B: Body + 'static, B::Error: Into>, { pub(super) inner: Option>, } impl Future for UpgradeableConnection where I: Read + Write + Unpin + Send + 'static, B: Body + 'static, B::Data: Send, B::Error: Into>, { type Output = crate::Result<()>; fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { match ready!(Pin::new(&mut self.inner.as_mut().unwrap().inner).poll(cx)) { Ok(proto::Dispatched::Shutdown) => Poll::Ready(Ok(())), Ok(proto::Dispatched::Upgrade(pending)) => { let Parts { io, read_buf } = self.inner.take().unwrap().into_parts(); pending.fulfill(Upgraded::new(io, read_buf)); Poll::Ready(Ok(())) } Err(e) => Poll::Ready(Err(e)), } } } } hyper-1.5.2/src/client/conn/http2.rs000064400000000000000000000562171046102023000154040ustar 00000000000000//! HTTP/2 client connections use std::error::Error; use std::fmt; use std::future::Future; use std::marker::PhantomData; use std::pin::Pin; use std::sync::Arc; use std::task::{Context, Poll}; use std::time::Duration; use crate::rt::{Read, Write}; use futures_util::ready; use http::{Request, Response}; use super::super::dispatch::{self, TrySendError}; use crate::body::{Body, Incoming as IncomingBody}; use crate::common::time::Time; use crate::proto; use crate::rt::bounds::Http2ClientConnExec; use crate::rt::Timer; /// The sender side of an established connection. pub struct SendRequest { dispatch: dispatch::UnboundedSender, Response>, } impl Clone for SendRequest { fn clone(&self) -> SendRequest { SendRequest { dispatch: self.dispatch.clone(), } } } /// A future that processes all HTTP state for the IO object. /// /// In most cases, this should just be spawned into an executor, so that it /// can process incoming and outgoing messages, notice hangups, and the like. /// /// Instances of this type are typically created via the [`handshake`] function #[must_use = "futures do nothing unless polled"] pub struct Connection where T: Read + Write + Unpin, B: Body + 'static, E: Http2ClientConnExec + Unpin, B::Error: Into>, { inner: (PhantomData, proto::h2::ClientTask), } /// A builder to configure an HTTP connection. /// /// After setting options, the builder is used to create a handshake future. /// /// **Note**: The default values of options are *not considered stable*. They /// are subject to change at any time. #[derive(Clone, Debug)] pub struct Builder { pub(super) exec: Ex, pub(super) timer: Time, h2_builder: proto::h2::client::Config, } /// Returns a handshake future over some IO. /// /// This is a shortcut for `Builder::new(exec).handshake(io)`. /// See [`client::conn`](crate::client::conn) for more. pub async fn handshake( exec: E, io: T, ) -> crate::Result<(SendRequest, Connection)> where T: Read + Write + Unpin, B: Body + 'static, B::Data: Send, B::Error: Into>, E: Http2ClientConnExec + Unpin + Clone, { Builder::new(exec).handshake(io).await } // ===== impl SendRequest impl SendRequest { /// Polls to determine whether this sender can be used yet for a request. /// /// If the associated connection is closed, this returns an Error. pub fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll> { if self.is_closed() { Poll::Ready(Err(crate::Error::new_closed())) } else { Poll::Ready(Ok(())) } } /// Waits until the dispatcher is ready /// /// If the associated connection is closed, this returns an Error. pub async fn ready(&mut self) -> crate::Result<()> { futures_util::future::poll_fn(|cx| self.poll_ready(cx)).await } /// Checks if the connection is currently ready to send a request. /// /// # Note /// /// This is mostly a hint. Due to inherent latency of networks, it is /// possible that even after checking this is ready, sending a request /// may still fail because the connection was closed in the meantime. pub fn is_ready(&self) -> bool { self.dispatch.is_ready() } /// Checks if the connection side has been closed. pub fn is_closed(&self) -> bool { self.dispatch.is_closed() } } impl SendRequest where B: Body + 'static, { /// Sends a `Request` on the associated connection. /// /// Returns a future that if successful, yields the `Response`. /// /// `req` must have a `Host` header. /// /// Absolute-form `Uri`s are not required. If received, they will be serialized /// as-is. pub fn send_request( &mut self, req: Request, ) -> impl Future>> { let sent = self.dispatch.send(req); async move { match sent { Ok(rx) => match rx.await { Ok(Ok(resp)) => Ok(resp), Ok(Err(err)) => Err(err), // this is definite bug if it happens, but it shouldn't happen! Err(_canceled) => panic!("dispatch dropped without returning error"), }, Err(_req) => { debug!("connection was not ready"); Err(crate::Error::new_canceled().with("connection was not ready")) } } } } /// Sends a `Request` on the associated connection. /// /// Returns a future that if successful, yields the `Response`. /// /// # Error /// /// If there was an error before trying to serialize the request to the /// connection, the message will be returned as part of this error. pub fn try_send_request( &mut self, req: Request, ) -> impl Future, TrySendError>>> { let sent = self.dispatch.try_send(req); async move { match sent { Ok(rx) => match rx.await { Ok(Ok(res)) => Ok(res), Ok(Err(err)) => Err(err), // this is definite bug if it happens, but it shouldn't happen! Err(_) => panic!("dispatch dropped without returning error"), }, Err(req) => { debug!("connection was not ready"); let error = crate::Error::new_canceled().with("connection was not ready"); Err(TrySendError { error, message: Some(req), }) } } } } } impl fmt::Debug for SendRequest { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("SendRequest").finish() } } // ===== impl Connection impl Connection where T: Read + Write + Unpin + 'static, B: Body + Unpin + 'static, B::Data: Send, B::Error: Into>, E: Http2ClientConnExec + Unpin, { /// Returns whether the [extended CONNECT protocol][1] is enabled or not. /// /// This setting is configured by the server peer by sending the /// [`SETTINGS_ENABLE_CONNECT_PROTOCOL` parameter][2] in a `SETTINGS` frame. /// This method returns the currently acknowledged value received from the /// remote. /// /// [1]: https://datatracker.ietf.org/doc/html/rfc8441#section-4 /// [2]: https://datatracker.ietf.org/doc/html/rfc8441#section-3 pub fn is_extended_connect_protocol_enabled(&self) -> bool { self.inner.1.is_extended_connect_protocol_enabled() } } impl fmt::Debug for Connection where T: Read + Write + fmt::Debug + 'static + Unpin, B: Body + 'static, E: Http2ClientConnExec + Unpin, B::Error: Into>, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("Connection").finish() } } impl Future for Connection where T: Read + Write + Unpin + 'static, B: Body + 'static + Unpin, B::Data: Send, E: Unpin, B::Error: Into>, E: Http2ClientConnExec + Unpin, { type Output = crate::Result<()>; fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { match ready!(Pin::new(&mut self.inner.1).poll(cx))? { proto::Dispatched::Shutdown => Poll::Ready(Ok(())), #[cfg(feature = "http1")] proto::Dispatched::Upgrade(_pending) => unreachable!("http2 cannot upgrade"), } } } // ===== impl Builder impl Builder where Ex: Clone, { /// Creates a new connection builder. #[inline] pub fn new(exec: Ex) -> Builder { Builder { exec, timer: Time::Empty, h2_builder: Default::default(), } } /// Provide a timer to execute background HTTP2 tasks. pub fn timer(&mut self, timer: M) -> &mut Builder where M: Timer + Send + Sync + 'static, { self.timer = Time::Timer(Arc::new(timer)); self } /// Sets the [`SETTINGS_INITIAL_WINDOW_SIZE`][spec] option for HTTP2 /// stream-level flow control. /// /// Passing `None` will do nothing. /// /// If not set, hyper will use a default. /// /// [spec]: https://httpwg.org/specs/rfc9113.html#SETTINGS_INITIAL_WINDOW_SIZE pub fn initial_stream_window_size(&mut self, sz: impl Into>) -> &mut Self { if let Some(sz) = sz.into() { self.h2_builder.adaptive_window = false; self.h2_builder.initial_stream_window_size = sz; } self } /// Sets the max connection-level flow control for HTTP2 /// /// Passing `None` will do nothing. /// /// If not set, hyper will use a default. pub fn initial_connection_window_size(&mut self, sz: impl Into>) -> &mut Self { if let Some(sz) = sz.into() { self.h2_builder.adaptive_window = false; self.h2_builder.initial_conn_window_size = sz; } self } /// Sets the initial maximum of locally initiated (send) streams. /// /// This value will be overwritten by the value included in the initial /// SETTINGS frame received from the peer as part of a [connection preface]. /// /// Passing `None` will do nothing. /// /// If not set, hyper will use a default. /// /// [connection preface]: https://httpwg.org/specs/rfc9113.html#preface pub fn initial_max_send_streams(&mut self, initial: impl Into>) -> &mut Self { if let Some(initial) = initial.into() { self.h2_builder.initial_max_send_streams = initial; } self } /// Sets whether to use an adaptive flow control. /// /// Enabling this will override the limits set in /// `initial_stream_window_size` and /// `initial_connection_window_size`. pub fn adaptive_window(&mut self, enabled: bool) -> &mut Self { use proto::h2::SPEC_WINDOW_SIZE; self.h2_builder.adaptive_window = enabled; if enabled { self.h2_builder.initial_conn_window_size = SPEC_WINDOW_SIZE; self.h2_builder.initial_stream_window_size = SPEC_WINDOW_SIZE; } self } /// Sets the maximum frame size to use for HTTP2. /// /// Default is currently 16KB, but can change. pub fn max_frame_size(&mut self, sz: impl Into>) -> &mut Self { self.h2_builder.max_frame_size = sz.into(); self } /// Sets the max size of received header frames. /// /// Default is currently 16KB, but can change. pub fn max_header_list_size(&mut self, max: u32) -> &mut Self { self.h2_builder.max_header_list_size = max; self } /// Sets the header table size. /// /// This setting informs the peer of the maximum size of the header compression /// table used to encode header blocks, in octets. The encoder may select any value /// equal to or less than the header table size specified by the sender. /// /// The default value of crate `h2` is 4,096. pub fn header_table_size(&mut self, size: impl Into>) -> &mut Self { self.h2_builder.header_table_size = size.into(); self } /// Sets the maximum number of concurrent streams. /// /// The maximum concurrent streams setting only controls the maximum number /// of streams that can be initiated by the remote peer. In other words, /// when this setting is set to 100, this does not limit the number of /// concurrent streams that can be created by the caller. /// /// It is recommended that this value be no smaller than 100, so as to not /// unnecessarily limit parallelism. However, any value is legal, including /// 0. If `max` is set to 0, then the remote will not be permitted to /// initiate streams. /// /// Note that streams in the reserved state, i.e., push promises that have /// been reserved but the stream has not started, do not count against this /// setting. /// /// Also note that if the remote *does* exceed the value set here, it is not /// a protocol level error. Instead, the `h2` library will immediately reset /// the stream. /// /// See [Section 5.1.2] in the HTTP/2 spec for more details. /// /// [Section 5.1.2]: https://http2.github.io/http2-spec/#rfc.section.5.1.2 pub fn max_concurrent_streams(&mut self, max: impl Into>) -> &mut Self { self.h2_builder.max_concurrent_streams = max.into(); self } /// Sets an interval for HTTP2 Ping frames should be sent to keep a /// connection alive. /// /// Pass `None` to disable HTTP2 keep-alive. /// /// Default is currently disabled. pub fn keep_alive_interval(&mut self, interval: impl Into>) -> &mut Self { self.h2_builder.keep_alive_interval = interval.into(); self } /// Sets a timeout for receiving an acknowledgement of the keep-alive ping. /// /// If the ping is not acknowledged within the timeout, the connection will /// be closed. Does nothing if `keep_alive_interval` is disabled. /// /// Default is 20 seconds. pub fn keep_alive_timeout(&mut self, timeout: Duration) -> &mut Self { self.h2_builder.keep_alive_timeout = timeout; self } /// Sets whether HTTP2 keep-alive should apply while the connection is idle. /// /// If disabled, keep-alive pings are only sent while there are open /// request/responses streams. If enabled, pings are also sent when no /// streams are active. Does nothing if `keep_alive_interval` is /// disabled. /// /// Default is `false`. pub fn keep_alive_while_idle(&mut self, enabled: bool) -> &mut Self { self.h2_builder.keep_alive_while_idle = enabled; self } /// Sets the maximum number of HTTP2 concurrent locally reset streams. /// /// See the documentation of [`h2::client::Builder::max_concurrent_reset_streams`] for more /// details. /// /// The default value is determined by the `h2` crate. /// /// [`h2::client::Builder::max_concurrent_reset_streams`]: https://docs.rs/h2/client/struct.Builder.html#method.max_concurrent_reset_streams pub fn max_concurrent_reset_streams(&mut self, max: usize) -> &mut Self { self.h2_builder.max_concurrent_reset_streams = Some(max); self } /// Set the maximum write buffer size for each HTTP/2 stream. /// /// Default is currently 1MB, but may change. /// /// # Panics /// /// The value must be no larger than `u32::MAX`. pub fn max_send_buf_size(&mut self, max: usize) -> &mut Self { assert!(max <= u32::MAX as usize); self.h2_builder.max_send_buffer_size = max; self } /// Configures the maximum number of pending reset streams allowed before a GOAWAY will be sent. /// /// This will default to the default value set by the [`h2` crate](https://crates.io/crates/h2). /// As of v0.4.0, it is 20. /// /// See for more information. pub fn max_pending_accept_reset_streams(&mut self, max: impl Into>) -> &mut Self { self.h2_builder.max_pending_accept_reset_streams = max.into(); self } /// Constructs a connection with the configured options and IO. /// See [`client::conn`](crate::client::conn) for more. /// /// Note, if [`Connection`] is not `await`-ed, [`SendRequest`] will /// do nothing. pub fn handshake( &self, io: T, ) -> impl Future, Connection)>> where T: Read + Write + Unpin, B: Body + 'static, B::Data: Send, B::Error: Into>, Ex: Http2ClientConnExec + Unpin, { let opts = self.clone(); async move { trace!("client handshake HTTP/2"); let (tx, rx) = dispatch::channel(); let h2 = proto::h2::client::handshake(io, rx, &opts.h2_builder, opts.exec, opts.timer) .await?; Ok(( SendRequest { dispatch: tx.unbound(), }, Connection { inner: (PhantomData, h2), }, )) } } } #[cfg(test)] mod tests { #[tokio::test] #[ignore] // only compilation is checked async fn send_sync_executor_of_non_send_futures() { #[derive(Clone)] struct LocalTokioExecutor; impl crate::rt::Executor for LocalTokioExecutor where F: std::future::Future + 'static, // not requiring `Send` { fn execute(&self, fut: F) { // This will spawn into the currently running `LocalSet`. tokio::task::spawn_local(fut); } } #[allow(unused)] async fn run(io: impl crate::rt::Read + crate::rt::Write + Unpin + 'static) { let (_sender, conn) = crate::client::conn::http2::handshake::< _, _, http_body_util::Empty, >(LocalTokioExecutor, io) .await .unwrap(); tokio::task::spawn_local(async move { conn.await.unwrap(); }); } } #[tokio::test] #[ignore] // only compilation is checked async fn not_send_not_sync_executor_of_not_send_futures() { #[derive(Clone)] struct LocalTokioExecutor { _x: std::marker::PhantomData>, } impl crate::rt::Executor for LocalTokioExecutor where F: std::future::Future + 'static, // not requiring `Send` { fn execute(&self, fut: F) { // This will spawn into the currently running `LocalSet`. tokio::task::spawn_local(fut); } } #[allow(unused)] async fn run(io: impl crate::rt::Read + crate::rt::Write + Unpin + 'static) { let (_sender, conn) = crate::client::conn::http2::handshake::<_, _, http_body_util::Empty>( LocalTokioExecutor { _x: Default::default(), }, io, ) .await .unwrap(); tokio::task::spawn_local(async move { conn.await.unwrap(); }); } } #[tokio::test] #[ignore] // only compilation is checked async fn send_not_sync_executor_of_not_send_futures() { #[derive(Clone)] struct LocalTokioExecutor { _x: std::marker::PhantomData>, } impl crate::rt::Executor for LocalTokioExecutor where F: std::future::Future + 'static, // not requiring `Send` { fn execute(&self, fut: F) { // This will spawn into the currently running `LocalSet`. tokio::task::spawn_local(fut); } } #[allow(unused)] async fn run(io: impl crate::rt::Read + crate::rt::Write + Unpin + 'static) { let (_sender, conn) = crate::client::conn::http2::handshake::<_, _, http_body_util::Empty>( LocalTokioExecutor { _x: Default::default(), }, io, ) .await .unwrap(); tokio::task::spawn_local(async move { conn.await.unwrap(); }); } } #[tokio::test] #[ignore] // only compilation is checked async fn send_sync_executor_of_send_futures() { #[derive(Clone)] struct TokioExecutor; impl crate::rt::Executor for TokioExecutor where F: std::future::Future + 'static + Send, F::Output: Send + 'static, { fn execute(&self, fut: F) { tokio::task::spawn(fut); } } #[allow(unused)] async fn run(io: impl crate::rt::Read + crate::rt::Write + Send + Unpin + 'static) { let (_sender, conn) = crate::client::conn::http2::handshake::< _, _, http_body_util::Empty, >(TokioExecutor, io) .await .unwrap(); tokio::task::spawn(async move { conn.await.unwrap(); }); } } #[tokio::test] #[ignore] // only compilation is checked async fn not_send_not_sync_executor_of_send_futures() { #[derive(Clone)] struct TokioExecutor { // !Send, !Sync _x: std::marker::PhantomData>, } impl crate::rt::Executor for TokioExecutor where F: std::future::Future + 'static + Send, F::Output: Send + 'static, { fn execute(&self, fut: F) { tokio::task::spawn(fut); } } #[allow(unused)] async fn run(io: impl crate::rt::Read + crate::rt::Write + Send + Unpin + 'static) { let (_sender, conn) = crate::client::conn::http2::handshake::<_, _, http_body_util::Empty>( TokioExecutor { _x: Default::default(), }, io, ) .await .unwrap(); tokio::task::spawn_local(async move { // can't use spawn here because when executor is !Send conn.await.unwrap(); }); } } #[tokio::test] #[ignore] // only compilation is checked async fn send_not_sync_executor_of_send_futures() { #[derive(Clone)] struct TokioExecutor { // !Sync _x: std::marker::PhantomData>, } impl crate::rt::Executor for TokioExecutor where F: std::future::Future + 'static + Send, F::Output: Send + 'static, { fn execute(&self, fut: F) { tokio::task::spawn(fut); } } #[allow(unused)] async fn run(io: impl crate::rt::Read + crate::rt::Write + Send + Unpin + 'static) { let (_sender, conn) = crate::client::conn::http2::handshake::<_, _, http_body_util::Empty>( TokioExecutor { _x: Default::default(), }, io, ) .await .unwrap(); tokio::task::spawn_local(async move { // can't use spawn here because when executor is !Send conn.await.unwrap(); }); } } } hyper-1.5.2/src/client/conn/mod.rs000064400000000000000000000015131046102023000151070ustar 00000000000000//! Lower-level client connection API. //! //! The types in this module are to provide a lower-level API based around a //! single connection. Connecting to a host, pooling connections, and the like //! are not handled at this level. This module provides the building blocks to //! customize those things externally. //! //! If you are looking for a convenient HTTP client, then you may wish to //! consider [reqwest](https://github.com/seanmonstar/reqwest) for a high level //! client or [`hyper-util`'s client](https://docs.rs/hyper-util/latest/hyper_util/client/index.html) //! if you want to keep it more low level / basic. //! //! ## Example //! //! See the [client guide](https://hyper.rs/guides/1/client/basic/). #[cfg(feature = "http1")] pub mod http1; #[cfg(feature = "http2")] pub mod http2; pub use super::dispatch::TrySendError; hyper-1.5.2/src/client/dispatch.rs000064400000000000000000000357171046102023000152070ustar 00000000000000use std::task::{Context, Poll}; #[cfg(feature = "http2")] use std::{future::Future, pin::Pin}; #[cfg(feature = "http2")] use http::{Request, Response}; #[cfg(feature = "http2")] use http_body::Body; #[cfg(feature = "http2")] use pin_project_lite::pin_project; use tokio::sync::{mpsc, oneshot}; #[cfg(feature = "http2")] use crate::{body::Incoming, proto::h2::client::ResponseFutMap}; pub(crate) type RetryPromise = oneshot::Receiver>>; pub(crate) type Promise = oneshot::Receiver>; /// An error when calling `try_send_request`. /// /// There is a possibility of an error occurring on a connection in-between the /// time that a request is queued and when it is actually written to the IO /// transport. If that happens, it is safe to return the request back to the /// caller, as it was never fully sent. #[derive(Debug)] pub struct TrySendError { pub(crate) error: crate::Error, pub(crate) message: Option, } pub(crate) fn channel() -> (Sender, Receiver) { let (tx, rx) = mpsc::unbounded_channel(); let (giver, taker) = want::new(); let tx = Sender { #[cfg(feature = "http1")] buffered_once: false, giver, inner: tx, }; let rx = Receiver { inner: rx, taker }; (tx, rx) } /// A bounded sender of requests and callbacks for when responses are ready. /// /// While the inner sender is unbounded, the Giver is used to determine /// if the Receiver is ready for another request. pub(crate) struct Sender { /// One message is always allowed, even if the Receiver hasn't asked /// for it yet. This boolean keeps track of whether we've sent one /// without notice. #[cfg(feature = "http1")] buffered_once: bool, /// The Giver helps watch that the Receiver side has been polled /// when the queue is empty. This helps us know when a request and /// response have been fully processed, and a connection is ready /// for more. giver: want::Giver, /// Actually bounded by the Giver, plus `buffered_once`. inner: mpsc::UnboundedSender>, } /// An unbounded version. /// /// Cannot poll the Giver, but can still use it to determine if the Receiver /// has been dropped. However, this version can be cloned. #[cfg(feature = "http2")] pub(crate) struct UnboundedSender { /// Only used for `is_closed`, since mpsc::UnboundedSender cannot be checked. giver: want::SharedGiver, inner: mpsc::UnboundedSender>, } impl Sender { #[cfg(feature = "http1")] pub(crate) fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { self.giver .poll_want(cx) .map_err(|_| crate::Error::new_closed()) } #[cfg(feature = "http1")] pub(crate) fn is_ready(&self) -> bool { self.giver.is_wanting() } #[cfg(feature = "http1")] pub(crate) fn is_closed(&self) -> bool { self.giver.is_canceled() } #[cfg(feature = "http1")] fn can_send(&mut self) -> bool { if self.giver.give() || !self.buffered_once { // If the receiver is ready *now*, then of course we can send. // // If the receiver isn't ready yet, but we don't have anything // in the channel yet, then allow one message. self.buffered_once = true; true } else { false } } #[cfg(feature = "http1")] pub(crate) fn try_send(&mut self, val: T) -> Result, T> { if !self.can_send() { return Err(val); } let (tx, rx) = oneshot::channel(); self.inner .send(Envelope(Some((val, Callback::Retry(Some(tx)))))) .map(move |_| rx) .map_err(|mut e| (e.0).0.take().expect("envelope not dropped").0) } #[cfg(feature = "http1")] pub(crate) fn send(&mut self, val: T) -> Result, T> { if !self.can_send() { return Err(val); } let (tx, rx) = oneshot::channel(); self.inner .send(Envelope(Some((val, Callback::NoRetry(Some(tx)))))) .map(move |_| rx) .map_err(|mut e| (e.0).0.take().expect("envelope not dropped").0) } #[cfg(feature = "http2")] pub(crate) fn unbound(self) -> UnboundedSender { UnboundedSender { giver: self.giver.shared(), inner: self.inner, } } } #[cfg(feature = "http2")] impl UnboundedSender { pub(crate) fn is_ready(&self) -> bool { !self.giver.is_canceled() } pub(crate) fn is_closed(&self) -> bool { self.giver.is_canceled() } pub(crate) fn try_send(&mut self, val: T) -> Result, T> { let (tx, rx) = oneshot::channel(); self.inner .send(Envelope(Some((val, Callback::Retry(Some(tx)))))) .map(move |_| rx) .map_err(|mut e| (e.0).0.take().expect("envelope not dropped").0) } pub(crate) fn send(&mut self, val: T) -> Result, T> { let (tx, rx) = oneshot::channel(); self.inner .send(Envelope(Some((val, Callback::NoRetry(Some(tx)))))) .map(move |_| rx) .map_err(|mut e| (e.0).0.take().expect("envelope not dropped").0) } } #[cfg(feature = "http2")] impl Clone for UnboundedSender { fn clone(&self) -> Self { UnboundedSender { giver: self.giver.clone(), inner: self.inner.clone(), } } } pub(crate) struct Receiver { inner: mpsc::UnboundedReceiver>, taker: want::Taker, } impl Receiver { pub(crate) fn poll_recv(&mut self, cx: &mut Context<'_>) -> Poll)>> { match self.inner.poll_recv(cx) { Poll::Ready(item) => { Poll::Ready(item.map(|mut env| env.0.take().expect("envelope not dropped"))) } Poll::Pending => { self.taker.want(); Poll::Pending } } } #[cfg(feature = "http1")] pub(crate) fn close(&mut self) { self.taker.cancel(); self.inner.close(); } #[cfg(feature = "http1")] pub(crate) fn try_recv(&mut self) -> Option<(T, Callback)> { use futures_util::FutureExt; match self.inner.recv().now_or_never() { Some(Some(mut env)) => env.0.take(), _ => None, } } } impl Drop for Receiver { fn drop(&mut self) { // Notify the giver about the closure first, before dropping // the mpsc::Receiver. self.taker.cancel(); } } struct Envelope(Option<(T, Callback)>); impl Drop for Envelope { fn drop(&mut self) { if let Some((val, cb)) = self.0.take() { cb.send(Err(TrySendError { error: crate::Error::new_canceled().with("connection closed"), message: Some(val), })); } } } pub(crate) enum Callback { #[allow(unused)] Retry(Option>>>), NoRetry(Option>>), } impl Drop for Callback { fn drop(&mut self) { match self { Callback::Retry(tx) => { if let Some(tx) = tx.take() { let _ = tx.send(Err(TrySendError { error: dispatch_gone(), message: None, })); } } Callback::NoRetry(tx) => { if let Some(tx) = tx.take() { let _ = tx.send(Err(dispatch_gone())); } } } } } #[cold] fn dispatch_gone() -> crate::Error { // FIXME(nox): What errors do we want here? crate::Error::new_user_dispatch_gone().with(if std::thread::panicking() { "user code panicked" } else { "runtime dropped the dispatch task" }) } impl Callback { #[cfg(feature = "http2")] pub(crate) fn is_canceled(&self) -> bool { match *self { Callback::Retry(Some(ref tx)) => tx.is_closed(), Callback::NoRetry(Some(ref tx)) => tx.is_closed(), _ => unreachable!(), } } pub(crate) fn poll_canceled(&mut self, cx: &mut Context<'_>) -> Poll<()> { match *self { Callback::Retry(Some(ref mut tx)) => tx.poll_closed(cx), Callback::NoRetry(Some(ref mut tx)) => tx.poll_closed(cx), _ => unreachable!(), } } pub(crate) fn send(mut self, val: Result>) { match self { Callback::Retry(ref mut tx) => { let _ = tx.take().unwrap().send(val); } Callback::NoRetry(ref mut tx) => { let _ = tx.take().unwrap().send(val.map_err(|e| e.error)); } } } } impl TrySendError { /// Take the message from this error. /// /// The message will not always have been recovered. If an error occurs /// after the message has been serialized onto the connection, it will not /// be available here. pub fn take_message(&mut self) -> Option { self.message.take() } /// Consumes this to return the inner error. pub fn into_error(self) -> crate::Error { self.error } } #[cfg(feature = "http2")] pin_project! { pub struct SendWhen where B: Body, B: 'static, { #[pin] pub(crate) when: ResponseFutMap, #[pin] pub(crate) call_back: Option, Response>>, } } #[cfg(feature = "http2")] impl Future for SendWhen where B: Body + 'static, { type Output = (); fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { let mut this = self.project(); let mut call_back = this.call_back.take().expect("polled after complete"); match Pin::new(&mut this.when).poll(cx) { Poll::Ready(Ok(res)) => { call_back.send(Ok(res)); Poll::Ready(()) } Poll::Pending => { // check if the callback is canceled match call_back.poll_canceled(cx) { Poll::Ready(v) => v, Poll::Pending => { // Move call_back back to struct before return this.call_back.set(Some(call_back)); return Poll::Pending; } }; trace!("send_when canceled"); Poll::Ready(()) } Poll::Ready(Err((error, message))) => { call_back.send(Err(TrySendError { error, message })); Poll::Ready(()) } } } } #[cfg(test)] mod tests { #[cfg(feature = "nightly")] extern crate test; use std::future::Future; use std::pin::Pin; use std::task::{Context, Poll}; use super::{channel, Callback, Receiver}; #[derive(Debug)] struct Custom(#[allow(dead_code)] i32); impl Future for Receiver { type Output = Option<(T, Callback)>; fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { self.poll_recv(cx) } } /// Helper to check if the future is ready after polling once. struct PollOnce<'a, F>(&'a mut F); impl Future for PollOnce<'_, F> where F: Future + Unpin, { type Output = Option<()>; fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { match Pin::new(&mut self.0).poll(cx) { Poll::Ready(_) => Poll::Ready(Some(())), Poll::Pending => Poll::Ready(None), } } } #[cfg(not(miri))] #[tokio::test] async fn drop_receiver_sends_cancel_errors() { let _ = pretty_env_logger::try_init(); let (mut tx, mut rx) = channel::(); // must poll once for try_send to succeed assert!(PollOnce(&mut rx).await.is_none(), "rx empty"); let promise = tx.try_send(Custom(43)).unwrap(); drop(rx); let fulfilled = promise.await; let err = fulfilled .expect("fulfilled") .expect_err("promise should error"); match (err.error.is_canceled(), err.message) { (true, Some(_)) => (), e => panic!("expected Error::Cancel(_), found {:?}", e), } } #[cfg(not(miri))] #[tokio::test] async fn sender_checks_for_want_on_send() { let (mut tx, mut rx) = channel::(); // one is allowed to buffer, second is rejected let _ = tx.try_send(Custom(1)).expect("1 buffered"); tx.try_send(Custom(2)).expect_err("2 not ready"); assert!(PollOnce(&mut rx).await.is_some(), "rx once"); // Even though 1 has been popped, only 1 could be buffered for the // lifetime of the channel. tx.try_send(Custom(2)).expect_err("2 still not ready"); assert!(PollOnce(&mut rx).await.is_none(), "rx empty"); let _ = tx.try_send(Custom(2)).expect("2 ready"); } #[cfg(feature = "http2")] #[test] fn unbounded_sender_doesnt_bound_on_want() { let (tx, rx) = channel::(); let mut tx = tx.unbound(); let _ = tx.try_send(Custom(1)).unwrap(); let _ = tx.try_send(Custom(2)).unwrap(); let _ = tx.try_send(Custom(3)).unwrap(); drop(rx); let _ = tx.try_send(Custom(4)).unwrap_err(); } #[cfg(feature = "nightly")] #[bench] fn giver_queue_throughput(b: &mut test::Bencher) { use crate::{body::Incoming, Request, Response}; let rt = tokio::runtime::Builder::new_current_thread() .build() .unwrap(); let (mut tx, mut rx) = channel::, Response>(); b.iter(move || { let _ = tx.send(Request::new(Incoming::empty())).unwrap(); rt.block_on(async { loop { let poll_once = PollOnce(&mut rx); let opt = poll_once.await; if opt.is_none() { break; } } }); }) } #[cfg(feature = "nightly")] #[bench] fn giver_queue_not_ready(b: &mut test::Bencher) { let rt = tokio::runtime::Builder::new_current_thread() .build() .unwrap(); let (_tx, mut rx) = channel::(); b.iter(move || { rt.block_on(async { let poll_once = PollOnce(&mut rx); assert!(poll_once.await.is_none()); }); }) } #[cfg(feature = "nightly")] #[bench] fn giver_queue_cancel(b: &mut test::Bencher) { let (_tx, mut rx) = channel::(); b.iter(move || { rx.taker.cancel(); }) } } hyper-1.5.2/src/client/mod.rs000064400000000000000000000013651046102023000141570ustar 00000000000000//! HTTP Client //! //! hyper provides HTTP over a single connection. See the [`conn`] module. //! //! ## Examples //! //! * [`client`] - A simple CLI http client that requests the url passed in parameters and outputs the response content and details to the stdout, reading content chunk-by-chunk. //! //! * [`client_json`] - A simple program that GETs some json, reads the body asynchronously, parses it with serde and outputs the result. //! //! [`client`]: https://github.com/hyperium/hyper/blob/master/examples/client.rs //! [`client_json`]: https://github.com/hyperium/hyper/blob/master/examples/client_json.rs #[cfg(test)] mod tests; cfg_feature! { #![any(feature = "http1", feature = "http2")] pub mod conn; pub(super) mod dispatch; } hyper-1.5.2/src/client/tests.rs000064400000000000000000000210131046102023000145320ustar 00000000000000/* // FIXME: re-implement tests with `async/await` #[test] fn retryable_request() { let _ = pretty_env_logger::try_init(); let mut rt = Runtime::new().expect("new rt"); let mut connector = MockConnector::new(); let sock1 = connector.mock("http://mock.local"); let sock2 = connector.mock("http://mock.local"); let client = Client::builder() .build::<_, crate::Body>(connector); client.pool.no_timer(); { let req = Request::builder() .uri("http://mock.local/a") .body(Default::default()) .unwrap(); let res1 = client.request(req); let srv1 = poll_fn(|| { try_ready!(sock1.read(&mut [0u8; 512])); try_ready!(sock1.write(b"HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n")); Ok(Async::Ready(())) }).map_err(|e: std::io::Error| panic!("srv1 poll_fn error: {}", e)); rt.block_on(res1.join(srv1)).expect("res1"); } drop(sock1); let req = Request::builder() .uri("http://mock.local/b") .body(Default::default()) .unwrap(); let res2 = client.request(req) .map(|res| { assert_eq!(res.status().as_u16(), 222); }); let srv2 = poll_fn(|| { try_ready!(sock2.read(&mut [0u8; 512])); try_ready!(sock2.write(b"HTTP/1.1 222 OK\r\nContent-Length: 0\r\n\r\n")); Ok(Async::Ready(())) }).map_err(|e: std::io::Error| panic!("srv2 poll_fn error: {}", e)); rt.block_on(res2.join(srv2)).expect("res2"); } #[test] fn conn_reset_after_write() { let _ = pretty_env_logger::try_init(); let mut rt = Runtime::new().expect("new rt"); let mut connector = MockConnector::new(); let sock1 = connector.mock("http://mock.local"); let client = Client::builder() .build::<_, crate::Body>(connector); client.pool.no_timer(); { let req = Request::builder() .uri("http://mock.local/a") .body(Default::default()) .unwrap(); let res1 = client.request(req); let srv1 = poll_fn(|| { try_ready!(sock1.read(&mut [0u8; 512])); try_ready!(sock1.write(b"HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n")); Ok(Async::Ready(())) }).map_err(|e: std::io::Error| panic!("srv1 poll_fn error: {}", e)); rt.block_on(res1.join(srv1)).expect("res1"); } let req = Request::builder() .uri("http://mock.local/a") .body(Default::default()) .unwrap(); let res2 = client.request(req); let mut sock1 = Some(sock1); let srv2 = poll_fn(|| { // We purposefully keep the socket open until the client // has written the second request, and THEN disconnect. // // Not because we expect servers to be jerks, but to trigger // state where we write on an assumedly good connection, and // only reset the close AFTER we wrote bytes. try_ready!(sock1.as_mut().unwrap().read(&mut [0u8; 512])); sock1.take(); Ok(Async::Ready(())) }).map_err(|e: std::io::Error| panic!("srv2 poll_fn error: {}", e)); let err = rt.block_on(res2.join(srv2)).expect_err("res2"); assert!(err.is_incomplete_message(), "{:?}", err); } #[test] fn checkout_win_allows_connect_future_to_be_pooled() { let _ = pretty_env_logger::try_init(); let mut rt = Runtime::new().expect("new rt"); let mut connector = MockConnector::new(); let (tx, rx) = oneshot::channel::<()>(); let sock1 = connector.mock("http://mock.local"); let sock2 = connector.mock_fut("http://mock.local", rx); let client = Client::builder() .build::<_, crate::Body>(connector); client.pool.no_timer(); let uri = "http://mock.local/a".parse::().expect("uri parse"); // First request just sets us up to have a connection able to be put // back in the pool. *However*, it doesn't insert immediately. The // body has 1 pending byte, and we will only drain in request 2, once // the connect future has been started. let mut body = { let res1 = client.get(uri.clone()) .map(|res| res.into_body().concat2()); let srv1 = poll_fn(|| { try_ready!(sock1.read(&mut [0u8; 512])); // Chunked is used so as to force 2 body reads. try_ready!(sock1.write(b"\ HTTP/1.1 200 OK\r\n\ transfer-encoding: chunked\r\n\ \r\n\ 1\r\nx\r\n\ 0\r\n\r\n\ ")); Ok(Async::Ready(())) }).map_err(|e: std::io::Error| panic!("srv1 poll_fn error: {}", e)); rt.block_on(res1.join(srv1)).expect("res1").0 }; // The second request triggers the only mocked connect future, but then // the drained body allows the first socket to go back to the pool, // "winning" the checkout race. { let res2 = client.get(uri.clone()); let drain = poll_fn(move || { body.poll() }); let srv2 = poll_fn(|| { try_ready!(sock1.read(&mut [0u8; 512])); try_ready!(sock1.write(b"HTTP/1.1 200 OK\r\nConnection: close\r\n\r\nx")); Ok(Async::Ready(())) }).map_err(|e: std::io::Error| panic!("srv2 poll_fn error: {}", e)); rt.block_on(res2.join(drain).join(srv2)).expect("res2"); } // "Release" the mocked connect future, and let the runtime spin once so // it's all setup... { let mut tx = Some(tx); let client = &client; let key = client.pool.h1_key("http://mock.local"); let mut tick_cnt = 0; let fut = poll_fn(move || { tx.take(); if client.pool.idle_count(&key) == 0 { tick_cnt += 1; assert!(tick_cnt < 10, "ticked too many times waiting for idle"); trace!("no idle yet; tick count: {}", tick_cnt); ::futures::task::current().notify(); Ok(Async::NotReady) } else { Ok::<_, ()>(Async::Ready(())) } }); rt.block_on(fut).unwrap(); } // Third request just tests out that the "loser" connection was pooled. If // it isn't, this will panic since the MockConnector doesn't have any more // mocks to give out. { let res3 = client.get(uri); let srv3 = poll_fn(|| { try_ready!(sock2.read(&mut [0u8; 512])); try_ready!(sock2.write(b"HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n")); Ok(Async::Ready(())) }).map_err(|e: std::io::Error| panic!("srv3 poll_fn error: {}", e)); rt.block_on(res3.join(srv3)).expect("res3"); } } #[cfg(feature = "nightly")] #[bench] fn bench_http1_get_0b(b: &mut test::Bencher) { let _ = pretty_env_logger::try_init(); let mut rt = Runtime::new().expect("new rt"); let mut connector = MockConnector::new(); let client = Client::builder() .build::<_, crate::Body>(connector.clone()); client.pool.no_timer(); let uri = Uri::from_static("http://mock.local/a"); b.iter(move || { let sock1 = connector.mock("http://mock.local"); let res1 = client .get(uri.clone()) .and_then(|res| { res.into_body().for_each(|_| Ok(())) }); let srv1 = poll_fn(|| { try_ready!(sock1.read(&mut [0u8; 512])); try_ready!(sock1.write(b"HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n")); Ok(Async::Ready(())) }).map_err(|e: std::io::Error| panic!("srv1 poll_fn error: {}", e)); rt.block_on(res1.join(srv1)).expect("res1"); }); } #[cfg(feature = "nightly")] #[bench] fn bench_http1_get_10b(b: &mut test::Bencher) { let _ = pretty_env_logger::try_init(); let mut rt = Runtime::new().expect("new rt"); let mut connector = MockConnector::new(); let client = Client::builder() .build::<_, crate::Body>(connector.clone()); client.pool.no_timer(); let uri = Uri::from_static("http://mock.local/a"); b.iter(move || { let sock1 = connector.mock("http://mock.local"); let res1 = client .get(uri.clone()) .and_then(|res| { res.into_body().for_each(|_| Ok(())) }); let srv1 = poll_fn(|| { try_ready!(sock1.read(&mut [0u8; 512])); try_ready!(sock1.write(b"HTTP/1.1 200 OK\r\nContent-Length: 10\r\n\r\n0123456789")); Ok(Async::Ready(())) }).map_err(|e: std::io::Error| panic!("srv1 poll_fn error: {}", e)); rt.block_on(res1.join(srv1)).expect("res1"); }); } */ hyper-1.5.2/src/common/buf.rs000064400000000000000000000076561046102023000141770ustar 00000000000000use std::collections::VecDeque; use std::io::IoSlice; use bytes::{Buf, BufMut, Bytes, BytesMut}; pub(crate) struct BufList { bufs: VecDeque, } impl BufList { pub(crate) fn new() -> BufList { BufList { bufs: VecDeque::new(), } } #[inline] pub(crate) fn push(&mut self, buf: T) { debug_assert!(buf.has_remaining()); self.bufs.push_back(buf); } #[inline] pub(crate) fn bufs_cnt(&self) -> usize { self.bufs.len() } } impl Buf for BufList { #[inline] fn remaining(&self) -> usize { self.bufs.iter().map(|buf| buf.remaining()).sum() } #[inline] fn chunk(&self) -> &[u8] { self.bufs.front().map(Buf::chunk).unwrap_or_default() } #[inline] fn advance(&mut self, mut cnt: usize) { while cnt > 0 { { let front = &mut self.bufs[0]; let rem = front.remaining(); if rem > cnt { front.advance(cnt); return; } else { front.advance(rem); cnt -= rem; } } self.bufs.pop_front(); } } #[inline] fn chunks_vectored<'t>(&'t self, dst: &mut [IoSlice<'t>]) -> usize { if dst.is_empty() { return 0; } let mut vecs = 0; for buf in &self.bufs { vecs += buf.chunks_vectored(&mut dst[vecs..]); if vecs == dst.len() { break; } } vecs } #[inline] fn copy_to_bytes(&mut self, len: usize) -> Bytes { // Our inner buffer may have an optimized version of copy_to_bytes, and if the whole // request can be fulfilled by the front buffer, we can take advantage. match self.bufs.front_mut() { Some(front) if front.remaining() == len => { let b = front.copy_to_bytes(len); self.bufs.pop_front(); b } Some(front) if front.remaining() > len => front.copy_to_bytes(len), _ => { assert!(len <= self.remaining(), "`len` greater than remaining"); let mut bm = BytesMut::with_capacity(len); bm.put(self.take(len)); bm.freeze() } } } } #[cfg(test)] mod tests { use std::ptr; use super::*; fn hello_world_buf() -> BufList { BufList { bufs: vec![Bytes::from("Hello"), Bytes::from(" "), Bytes::from("World")].into(), } } #[test] fn to_bytes_shorter() { let mut bufs = hello_world_buf(); let old_ptr = bufs.chunk().as_ptr(); let start = bufs.copy_to_bytes(4); assert_eq!(start, "Hell"); assert!(ptr::eq(old_ptr, start.as_ptr())); assert_eq!(bufs.chunk(), b"o"); assert!(ptr::eq(old_ptr.wrapping_add(4), bufs.chunk().as_ptr())); assert_eq!(bufs.remaining(), 7); } #[test] fn to_bytes_eq() { let mut bufs = hello_world_buf(); let old_ptr = bufs.chunk().as_ptr(); let start = bufs.copy_to_bytes(5); assert_eq!(start, "Hello"); assert!(ptr::eq(old_ptr, start.as_ptr())); assert_eq!(bufs.chunk(), b" "); assert_eq!(bufs.remaining(), 6); } #[test] fn to_bytes_longer() { let mut bufs = hello_world_buf(); let start = bufs.copy_to_bytes(7); assert_eq!(start, "Hello W"); assert_eq!(bufs.remaining(), 4); } #[test] fn one_long_buf_to_bytes() { let mut buf = BufList::new(); buf.push(b"Hello World" as &[_]); assert_eq!(buf.copy_to_bytes(5), "Hello"); assert_eq!(buf.chunk(), b" World"); } #[test] #[should_panic(expected = "`len` greater than remaining")] fn buf_to_bytes_too_many() { hello_world_buf().copy_to_bytes(42); } } hyper-1.5.2/src/common/date.rs000064400000000000000000000063101046102023000143220ustar 00000000000000use std::cell::RefCell; use std::fmt::{self, Write}; use std::str; use std::time::{Duration, SystemTime}; #[cfg(feature = "http2")] use http::header::HeaderValue; use httpdate::HttpDate; // "Sun, 06 Nov 1994 08:49:37 GMT".len() pub(crate) const DATE_VALUE_LENGTH: usize = 29; #[cfg(feature = "http1")] pub(crate) fn extend(dst: &mut Vec) { CACHED.with(|cache| { dst.extend_from_slice(cache.borrow().buffer()); }) } #[cfg(feature = "http1")] pub(crate) fn update() { CACHED.with(|cache| { cache.borrow_mut().check(); }) } #[cfg(feature = "http2")] pub(crate) fn update_and_header_value() -> HeaderValue { CACHED.with(|cache| { let mut cache = cache.borrow_mut(); cache.check(); cache.header_value.clone() }) } struct CachedDate { bytes: [u8; DATE_VALUE_LENGTH], pos: usize, #[cfg(feature = "http2")] header_value: HeaderValue, next_update: SystemTime, } thread_local!(static CACHED: RefCell = RefCell::new(CachedDate::new())); impl CachedDate { fn new() -> Self { let mut cache = CachedDate { bytes: [0; DATE_VALUE_LENGTH], pos: 0, #[cfg(feature = "http2")] header_value: HeaderValue::from_static(""), next_update: SystemTime::now(), }; cache.update(cache.next_update); cache } fn buffer(&self) -> &[u8] { &self.bytes[..] } fn check(&mut self) { let now = SystemTime::now(); if now > self.next_update { self.update(now); } } fn update(&mut self, now: SystemTime) { self.render(now); self.next_update = now + Duration::new(1, 0); } fn render(&mut self, now: SystemTime) { self.pos = 0; let _ = write!(self, "{}", HttpDate::from(now)); debug_assert!(self.pos == DATE_VALUE_LENGTH); self.render_http2(); } #[cfg(feature = "http2")] fn render_http2(&mut self) { self.header_value = HeaderValue::from_bytes(self.buffer()) .expect("Date format should be valid HeaderValue"); } #[cfg(not(feature = "http2"))] fn render_http2(&mut self) {} } impl fmt::Write for CachedDate { fn write_str(&mut self, s: &str) -> fmt::Result { let len = s.len(); self.bytes[self.pos..self.pos + len].copy_from_slice(s.as_bytes()); self.pos += len; Ok(()) } } #[cfg(test)] mod tests { use super::*; #[cfg(feature = "nightly")] use test::Bencher; #[test] fn test_date_len() { assert_eq!(DATE_VALUE_LENGTH, "Sun, 06 Nov 1994 08:49:37 GMT".len()); } #[cfg(feature = "nightly")] #[bench] fn bench_date_check(b: &mut Bencher) { let mut date = CachedDate::new(); // cache the first update date.check(); b.iter(|| { date.check(); }); } #[cfg(feature = "nightly")] #[bench] fn bench_date_render(b: &mut Bencher) { let mut date = CachedDate::new(); let now = SystemTime::now(); date.render(now); b.bytes = date.buffer().len() as u64; b.iter(|| { date.render(now); test::black_box(&date); }); } } hyper-1.5.2/src/common/io/compat.rs000064400000000000000000000100501046102023000152730ustar 00000000000000use std::pin::Pin; use std::task::{Context, Poll}; /// This adapts from `hyper` IO traits to the ones in Tokio. /// /// This is currently used by `h2`, and by hyper internal unit tests. #[derive(Debug)] pub(crate) struct Compat(pub(crate) T); impl Compat { pub(crate) fn new(io: T) -> Self { Compat(io) } fn p(self: Pin<&mut Self>) -> Pin<&mut T> { // SAFETY: The simplest of projections. This is just // a wrapper, we don't do anything that would undo the projection. unsafe { self.map_unchecked_mut(|me| &mut me.0) } } } impl tokio::io::AsyncRead for Compat where T: crate::rt::Read, { fn poll_read( self: Pin<&mut Self>, cx: &mut Context<'_>, tbuf: &mut tokio::io::ReadBuf<'_>, ) -> Poll> { let init = tbuf.initialized().len(); let filled = tbuf.filled().len(); let (new_init, new_filled) = unsafe { let mut buf = crate::rt::ReadBuf::uninit(tbuf.inner_mut()); buf.set_init(init); buf.set_filled(filled); match crate::rt::Read::poll_read(self.p(), cx, buf.unfilled()) { Poll::Ready(Ok(())) => (buf.init_len(), buf.len()), other => return other, } }; let n_init = new_init - init; unsafe { tbuf.assume_init(n_init); tbuf.set_filled(new_filled); } Poll::Ready(Ok(())) } } impl tokio::io::AsyncWrite for Compat where T: crate::rt::Write, { fn poll_write( self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8], ) -> Poll> { crate::rt::Write::poll_write(self.p(), cx, buf) } fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { crate::rt::Write::poll_flush(self.p(), cx) } fn poll_shutdown( self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll> { crate::rt::Write::poll_shutdown(self.p(), cx) } fn is_write_vectored(&self) -> bool { crate::rt::Write::is_write_vectored(&self.0) } fn poll_write_vectored( self: Pin<&mut Self>, cx: &mut Context<'_>, bufs: &[std::io::IoSlice<'_>], ) -> Poll> { crate::rt::Write::poll_write_vectored(self.p(), cx, bufs) } } #[cfg(test)] impl crate::rt::Read for Compat where T: tokio::io::AsyncRead, { fn poll_read( self: Pin<&mut Self>, cx: &mut Context<'_>, mut buf: crate::rt::ReadBufCursor<'_>, ) -> Poll> { let n = unsafe { let mut tbuf = tokio::io::ReadBuf::uninit(buf.as_mut()); match tokio::io::AsyncRead::poll_read(self.p(), cx, &mut tbuf) { Poll::Ready(Ok(())) => tbuf.filled().len(), other => return other, } }; unsafe { buf.advance(n); } Poll::Ready(Ok(())) } } #[cfg(test)] impl crate::rt::Write for Compat where T: tokio::io::AsyncWrite, { fn poll_write( self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8], ) -> Poll> { tokio::io::AsyncWrite::poll_write(self.p(), cx, buf) } fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { tokio::io::AsyncWrite::poll_flush(self.p(), cx) } fn poll_shutdown( self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll> { tokio::io::AsyncWrite::poll_shutdown(self.p(), cx) } fn is_write_vectored(&self) -> bool { tokio::io::AsyncWrite::is_write_vectored(&self.0) } fn poll_write_vectored( self: Pin<&mut Self>, cx: &mut Context<'_>, bufs: &[std::io::IoSlice<'_>], ) -> Poll> { tokio::io::AsyncWrite::poll_write_vectored(self.p(), cx, bufs) } } hyper-1.5.2/src/common/io/mod.rs000064400000000000000000000003731046102023000145760ustar 00000000000000#[cfg(all(any(feature = "client", feature = "server"), feature = "http2"))] mod compat; mod rewind; #[cfg(all(any(feature = "client", feature = "server"), feature = "http2"))] pub(crate) use self::compat::Compat; pub(crate) use self::rewind::Rewind; hyper-1.5.2/src/common/io/rewind.rs000064400000000000000000000103721046102023000153070ustar 00000000000000use std::pin::Pin; use std::task::{Context, Poll}; use std::{cmp, io}; use bytes::{Buf, Bytes}; use crate::rt::{Read, ReadBufCursor, Write}; /// Combine a buffer with an IO, rewinding reads to use the buffer. #[derive(Debug)] pub(crate) struct Rewind { pre: Option, inner: T, } impl Rewind { #[cfg(test)] pub(crate) fn new(io: T) -> Self { Rewind { pre: None, inner: io, } } pub(crate) fn new_buffered(io: T, buf: Bytes) -> Self { Rewind { pre: Some(buf), inner: io, } } #[cfg(test)] pub(crate) fn rewind(&mut self, bs: Bytes) { debug_assert!(self.pre.is_none()); self.pre = Some(bs); } pub(crate) fn into_inner(self) -> (T, Bytes) { (self.inner, self.pre.unwrap_or_default()) } // pub(crate) fn get_mut(&mut self) -> &mut T { // &mut self.inner // } } impl Read for Rewind where T: Read + Unpin, { fn poll_read( mut self: Pin<&mut Self>, cx: &mut Context<'_>, mut buf: ReadBufCursor<'_>, ) -> Poll> { if let Some(mut prefix) = self.pre.take() { // If there are no remaining bytes, let the bytes get dropped. if !prefix.is_empty() { let copy_len = cmp::min(prefix.len(), buf.remaining()); // TODO: There should be a way to do following two lines cleaner... buf.put_slice(&prefix[..copy_len]); prefix.advance(copy_len); // Put back what's left if !prefix.is_empty() { self.pre = Some(prefix); } return Poll::Ready(Ok(())); } } Pin::new(&mut self.inner).poll_read(cx, buf) } } impl Write for Rewind where T: Write + Unpin, { fn poll_write( mut self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8], ) -> Poll> { Pin::new(&mut self.inner).poll_write(cx, buf) } fn poll_write_vectored( mut self: Pin<&mut Self>, cx: &mut Context<'_>, bufs: &[io::IoSlice<'_>], ) -> Poll> { Pin::new(&mut self.inner).poll_write_vectored(cx, bufs) } fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { Pin::new(&mut self.inner).poll_flush(cx) } fn poll_shutdown(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { Pin::new(&mut self.inner).poll_shutdown(cx) } fn is_write_vectored(&self) -> bool { self.inner.is_write_vectored() } } #[cfg(all( any(feature = "client", feature = "server"), any(feature = "http1", feature = "http2"), ))] #[cfg(test)] mod tests { use super::super::Compat; use super::Rewind; use bytes::Bytes; use tokio::io::AsyncReadExt; #[cfg(not(miri))] #[tokio::test] async fn partial_rewind() { let underlying = [104, 101, 108, 108, 111]; let mock = tokio_test::io::Builder::new().read(&underlying).build(); let mut stream = Compat::new(Rewind::new(Compat::new(mock))); // Read off some bytes, ensure we filled o1 let mut buf = [0; 2]; stream.read_exact(&mut buf).await.expect("read1"); // Rewind the stream so that it is as if we never read in the first place. stream.0.rewind(Bytes::copy_from_slice(&buf[..])); let mut buf = [0; 5]; stream.read_exact(&mut buf).await.expect("read1"); // At this point we should have read everything that was in the MockStream assert_eq!(&buf, &underlying); } #[cfg(not(miri))] #[tokio::test] async fn full_rewind() { let underlying = [104, 101, 108, 108, 111]; let mock = tokio_test::io::Builder::new().read(&underlying).build(); let mut stream = Compat::new(Rewind::new(Compat::new(mock))); let mut buf = [0; 5]; stream.read_exact(&mut buf).await.expect("read1"); // Rewind the stream so that it is as if we never read in the first place. stream.0.rewind(Bytes::copy_from_slice(&buf[..])); let mut buf = [0; 5]; stream.read_exact(&mut buf).await.expect("read1"); } } hyper-1.5.2/src/common/mod.rs000064400000000000000000000010631046102023000141640ustar 00000000000000#[cfg(all(any(feature = "client", feature = "server"), feature = "http1"))] pub(crate) mod buf; #[cfg(all(feature = "server", any(feature = "http1", feature = "http2")))] pub(crate) mod date; pub(crate) mod io; #[cfg(all(any(feature = "client", feature = "server"), feature = "http1"))] pub(crate) mod task; #[cfg(any( all(feature = "server", feature = "http1"), all(any(feature = "client", feature = "server"), feature = "http2"), ))] pub(crate) mod time; #[cfg(all(any(feature = "client", feature = "server"), feature = "http1"))] pub(crate) mod watch; hyper-1.5.2/src/common/task.rs000064400000000000000000000004771046102023000143570ustar 00000000000000use std::task::{Context, Poll}; /// A function to help "yield" a future, such that it is re-scheduled immediately. /// /// Useful for spin counts, so a future doesn't hog too much time. pub(crate) fn yield_now(cx: &mut Context<'_>) -> Poll { cx.waker().wake_by_ref(); Poll::Pending } hyper-1.5.2/src/common/time.rs000064400000000000000000000045131046102023000143460ustar 00000000000000#[cfg(any( all(any(feature = "client", feature = "server"), feature = "http2"), all(feature = "server", feature = "http1"), ))] use std::time::Duration; use std::{fmt, sync::Arc}; use std::{pin::Pin, time::Instant}; use crate::rt::Sleep; use crate::rt::Timer; /// A user-provided timer to time background tasks. #[derive(Clone)] pub(crate) enum Time { Timer(Arc), Empty, } #[cfg(all(feature = "server", feature = "http1"))] #[derive(Clone, Copy, Debug)] pub(crate) enum Dur { Default(Option), Configured(Option), } impl fmt::Debug for Time { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("Time").finish() } } impl Time { #[cfg(all(any(feature = "client", feature = "server"), feature = "http2"))] pub(crate) fn sleep(&self, duration: Duration) -> Pin> { match *self { Time::Empty => { panic!("You must supply a timer.") } Time::Timer(ref t) => t.sleep(duration), } } #[cfg(feature = "http1")] pub(crate) fn sleep_until(&self, deadline: Instant) -> Pin> { match *self { Time::Empty => { panic!("You must supply a timer.") } Time::Timer(ref t) => t.sleep_until(deadline), } } pub(crate) fn reset(&self, sleep: &mut Pin>, new_deadline: Instant) { match *self { Time::Empty => { panic!("You must supply a timer.") } Time::Timer(ref t) => t.reset(sleep, new_deadline), } } #[cfg(all(feature = "server", feature = "http1"))] pub(crate) fn check(&self, dur: Dur, name: &'static str) -> Option { match dur { Dur::Default(Some(dur)) => match self { Time::Empty => { warn!("timeout `{}` has default, but no timer set", name,); None } Time::Timer(..) => Some(dur), }, Dur::Configured(Some(dur)) => match self { Time::Empty => panic!("timeout `{}` set, but no timer set", name,), Time::Timer(..) => Some(dur), }, Dur::Default(None) | Dur::Configured(None) => None, } } } hyper-1.5.2/src/common/watch.rs000064400000000000000000000027421046102023000145200ustar 00000000000000//! An SPSC broadcast channel. //! //! - The value can only be a `usize`. //! - The consumer is only notified if the value is different. //! - The value `0` is reserved for closed. use futures_util::task::AtomicWaker; use std::sync::{ atomic::{AtomicUsize, Ordering}, Arc, }; use std::task; type Value = usize; pub(crate) const CLOSED: usize = 0; pub(crate) fn channel(initial: Value) -> (Sender, Receiver) { debug_assert!( initial != CLOSED, "watch::channel initial state of 0 is reserved" ); let shared = Arc::new(Shared { value: AtomicUsize::new(initial), waker: AtomicWaker::new(), }); ( Sender { shared: shared.clone(), }, Receiver { shared }, ) } pub(crate) struct Sender { shared: Arc, } pub(crate) struct Receiver { shared: Arc, } struct Shared { value: AtomicUsize, waker: AtomicWaker, } impl Sender { pub(crate) fn send(&mut self, value: Value) { if self.shared.value.swap(value, Ordering::SeqCst) != value { self.shared.waker.wake(); } } } impl Drop for Sender { fn drop(&mut self) { self.send(CLOSED); } } impl Receiver { pub(crate) fn load(&mut self, cx: &mut task::Context<'_>) -> Value { self.shared.waker.register(cx.waker()); self.shared.value.load(Ordering::SeqCst) } pub(crate) fn peek(&self) -> Value { self.shared.value.load(Ordering::Relaxed) } } hyper-1.5.2/src/error.rs000064400000000000000000000540311046102023000132510ustar 00000000000000//! Error and Result module. use std::error::Error as StdError; use std::fmt; /// Result type often returned from methods that can have hyper `Error`s. pub type Result = std::result::Result; type Cause = Box; /// Represents errors that can occur handling HTTP streams. /// /// # Formatting /// /// The `Display` implementation of this type will only print the details of /// this level of error, even though it may have been caused by another error /// and contain that error in its source. To print all the relevant /// information, including the source chain, using something like /// `std::error::Report`, or equivalent 3rd party types. /// /// The contents of the formatted error message of this specific `Error` type /// is unspecified. **You must not depend on it.** The wording and details may /// change in any version, with the goal of improving error messages. /// /// # Source /// /// A `hyper::Error` may be caused by another error. To aid in debugging, /// those are exposed in `Error::source()` as erased types. While it is /// possible to check the exact type of the sources, they **can not be depended /// on**. They may come from private internal dependencies, and are subject to /// change at any moment. pub struct Error { inner: Box, } struct ErrorImpl { kind: Kind, cause: Option, } #[derive(Debug)] pub(super) enum Kind { Parse(Parse), User(User), /// A message reached EOF, but is not complete. #[cfg(all(any(feature = "client", feature = "server"), feature = "http1"))] IncompleteMessage, /// A connection received a message (or bytes) when not waiting for one. #[cfg(all(any(feature = "client", feature = "server"), feature = "http1"))] UnexpectedMessage, /// A pending item was dropped before ever being processed. Canceled, /// Indicates a channel (client or body sender) is closed. #[cfg(any( all(feature = "http1", any(feature = "client", feature = "server")), all(feature = "http2", feature = "client") ))] ChannelClosed, /// An `io::Error` that occurred while trying to read or write to a network stream. #[cfg(all( any(feature = "client", feature = "server"), any(feature = "http1", feature = "http2") ))] Io, /// User took too long to send headers #[cfg(all(feature = "http1", feature = "server"))] HeaderTimeout, /// Error while reading a body from connection. #[cfg(all( any(feature = "client", feature = "server"), any(feature = "http1", feature = "http2") ))] Body, /// Error while writing a body to connection. #[cfg(all( any(feature = "client", feature = "server"), any(feature = "http1", feature = "http2") ))] BodyWrite, /// Error calling AsyncWrite::shutdown() #[cfg(all(any(feature = "client", feature = "server"), feature = "http1"))] Shutdown, /// A general error from h2. #[cfg(all(any(feature = "client", feature = "server"), feature = "http2"))] Http2, } #[derive(Debug)] pub(super) enum Parse { Method, #[cfg(feature = "http1")] Version, #[cfg(all(any(feature = "client", feature = "server"), feature = "http1"))] VersionH2, Uri, #[cfg(all(feature = "http1", feature = "server"))] UriTooLong, #[cfg(feature = "http1")] Header(Header), #[cfg(any(feature = "http1", feature = "http2"))] #[cfg_attr(feature = "http2", allow(unused))] TooLarge, Status, #[cfg(all(any(feature = "client", feature = "server"), feature = "http1"))] Internal, } #[derive(Debug)] #[cfg(feature = "http1")] pub(super) enum Header { Token, #[cfg(any(feature = "client", feature = "server"))] ContentLengthInvalid, #[cfg(feature = "server")] TransferEncodingInvalid, #[cfg(any(feature = "client", feature = "server"))] TransferEncodingUnexpected, } #[derive(Debug)] pub(super) enum User { /// Error calling user's Body::poll_data(). #[cfg(all( any(feature = "client", feature = "server"), any(feature = "http1", feature = "http2") ))] Body, /// The user aborted writing of the outgoing body. #[cfg(any( all(feature = "http1", any(feature = "client", feature = "server")), feature = "ffi" ))] BodyWriteAborted, /// Error from future of user's Service. #[cfg(any( all(any(feature = "client", feature = "server"), feature = "http1"), all(feature = "server", feature = "http2") ))] Service, /// User tried to send a certain header in an unexpected context. /// /// For example, sending both `content-length` and `transfer-encoding`. #[cfg(any(feature = "http1", feature = "http2"))] #[cfg(feature = "server")] UnexpectedHeader, /// User tried to respond with a 1xx (not 101) response code. #[cfg(feature = "http1")] #[cfg(feature = "server")] UnsupportedStatusCode, /// User tried polling for an upgrade that doesn't exist. NoUpgrade, /// User polled for an upgrade, but low-level API is not using upgrades. #[cfg(all(any(feature = "client", feature = "server"), feature = "http1"))] ManualUpgrade, /// The dispatch task is gone. #[cfg(all(feature = "client", any(feature = "http1", feature = "http2")))] DispatchGone, /// User aborted in an FFI callback. #[cfg(feature = "ffi")] AbortedByCallback, } // Sentinel type to indicate the error was caused by a timeout. #[derive(Debug)] pub(super) struct TimedOut; impl Error { /// Returns true if this was an HTTP parse error. pub fn is_parse(&self) -> bool { matches!(self.inner.kind, Kind::Parse(_)) } /// Returns true if this was an HTTP parse error caused by a message that was too large. #[cfg(all(feature = "http1", feature = "server"))] pub fn is_parse_too_large(&self) -> bool { matches!( self.inner.kind, Kind::Parse(Parse::TooLarge) | Kind::Parse(Parse::UriTooLong) ) } /// Returns true if this was an HTTP parse error caused by an invalid response status code or /// reason phrase. pub fn is_parse_status(&self) -> bool { matches!(self.inner.kind, Kind::Parse(Parse::Status)) } /// Returns true if this error was caused by user code. pub fn is_user(&self) -> bool { matches!(self.inner.kind, Kind::User(_)) } /// Returns true if this was about a `Request` that was canceled. pub fn is_canceled(&self) -> bool { matches!(self.inner.kind, Kind::Canceled) } /// Returns true if a sender's channel is closed. pub fn is_closed(&self) -> bool { #[cfg(not(any( all(feature = "http1", any(feature = "client", feature = "server")), all(feature = "http2", feature = "client") )))] return false; #[cfg(any( all(feature = "http1", any(feature = "client", feature = "server")), all(feature = "http2", feature = "client") ))] matches!(self.inner.kind, Kind::ChannelClosed) } /// Returns true if the connection closed before a message could complete. pub fn is_incomplete_message(&self) -> bool { #[cfg(not(all(any(feature = "client", feature = "server"), feature = "http1")))] return false; #[cfg(all(any(feature = "client", feature = "server"), feature = "http1"))] matches!(self.inner.kind, Kind::IncompleteMessage) } /// Returns true if the body write was aborted. pub fn is_body_write_aborted(&self) -> bool { #[cfg(not(any( all(feature = "http1", any(feature = "client", feature = "server")), feature = "ffi" )))] return false; #[cfg(any( all(feature = "http1", any(feature = "client", feature = "server")), feature = "ffi" ))] matches!(self.inner.kind, Kind::User(User::BodyWriteAborted)) } /// Returns true if the error was caused by a timeout. pub fn is_timeout(&self) -> bool { self.find_source::().is_some() } pub(super) fn new(kind: Kind) -> Error { Error { inner: Box::new(ErrorImpl { kind, cause: None }), } } pub(super) fn with>(mut self, cause: C) -> Error { self.inner.cause = Some(cause.into()); self } #[cfg(any(all(feature = "http1", feature = "server"), feature = "ffi"))] pub(super) fn kind(&self) -> &Kind { &self.inner.kind } pub(crate) fn find_source(&self) -> Option<&E> { let mut cause = self.source(); while let Some(err) = cause { if let Some(typed) = err.downcast_ref() { return Some(typed); } cause = err.source(); } // else None } #[cfg(all(any(feature = "client", feature = "server"), feature = "http2"))] pub(super) fn h2_reason(&self) -> h2::Reason { // Find an h2::Reason somewhere in the cause stack, if it exists, // otherwise assume an INTERNAL_ERROR. self.find_source::() .and_then(|h2_err| h2_err.reason()) .unwrap_or(h2::Reason::INTERNAL_ERROR) } pub(super) fn new_canceled() -> Error { Error::new(Kind::Canceled) } #[cfg(all(any(feature = "client", feature = "server"), feature = "http1"))] pub(super) fn new_incomplete() -> Error { Error::new(Kind::IncompleteMessage) } #[cfg(all(any(feature = "client", feature = "server"), feature = "http1"))] pub(super) fn new_too_large() -> Error { Error::new(Kind::Parse(Parse::TooLarge)) } #[cfg(all(any(feature = "client", feature = "server"), feature = "http1"))] pub(super) fn new_version_h2() -> Error { Error::new(Kind::Parse(Parse::VersionH2)) } #[cfg(all(any(feature = "client", feature = "server"), feature = "http1"))] pub(super) fn new_unexpected_message() -> Error { Error::new(Kind::UnexpectedMessage) } #[cfg(all( any(feature = "client", feature = "server"), any(feature = "http1", feature = "http2") ))] pub(super) fn new_io(cause: std::io::Error) -> Error { Error::new(Kind::Io).with(cause) } #[cfg(any( all(feature = "http1", any(feature = "client", feature = "server")), all(feature = "http2", feature = "client") ))] pub(super) fn new_closed() -> Error { Error::new(Kind::ChannelClosed) } #[cfg(all( any(feature = "client", feature = "server"), any(feature = "http1", feature = "http2") ))] pub(super) fn new_body>(cause: E) -> Error { Error::new(Kind::Body).with(cause) } #[cfg(all( any(feature = "client", feature = "server"), any(feature = "http1", feature = "http2") ))] pub(super) fn new_body_write>(cause: E) -> Error { Error::new(Kind::BodyWrite).with(cause) } #[cfg(any( all(feature = "http1", any(feature = "client", feature = "server")), feature = "ffi" ))] pub(super) fn new_body_write_aborted() -> Error { Error::new(Kind::User(User::BodyWriteAborted)) } fn new_user(user: User) -> Error { Error::new(Kind::User(user)) } #[cfg(any(feature = "http1", feature = "http2"))] #[cfg(feature = "server")] pub(super) fn new_user_header() -> Error { Error::new_user(User::UnexpectedHeader) } #[cfg(all(feature = "http1", feature = "server"))] pub(super) fn new_header_timeout() -> Error { Error::new(Kind::HeaderTimeout) } #[cfg(feature = "http1")] #[cfg(feature = "server")] pub(super) fn new_user_unsupported_status_code() -> Error { Error::new_user(User::UnsupportedStatusCode) } pub(super) fn new_user_no_upgrade() -> Error { Error::new_user(User::NoUpgrade) } #[cfg(all(any(feature = "client", feature = "server"), feature = "http1"))] pub(super) fn new_user_manual_upgrade() -> Error { Error::new_user(User::ManualUpgrade) } #[cfg(any( all(any(feature = "client", feature = "server"), feature = "http1"), all(feature = "server", feature = "http2") ))] pub(super) fn new_user_service>(cause: E) -> Error { Error::new_user(User::Service).with(cause) } #[cfg(all( any(feature = "client", feature = "server"), any(feature = "http1", feature = "http2") ))] pub(super) fn new_user_body>(cause: E) -> Error { Error::new_user(User::Body).with(cause) } #[cfg(all(any(feature = "client", feature = "server"), feature = "http1"))] pub(super) fn new_shutdown(cause: std::io::Error) -> Error { Error::new(Kind::Shutdown).with(cause) } #[cfg(feature = "ffi")] pub(super) fn new_user_aborted_by_callback() -> Error { Error::new_user(User::AbortedByCallback) } #[cfg(all(feature = "client", any(feature = "http1", feature = "http2")))] pub(super) fn new_user_dispatch_gone() -> Error { Error::new(Kind::User(User::DispatchGone)) } #[cfg(all(any(feature = "client", feature = "server"), feature = "http2"))] pub(super) fn new_h2(cause: ::h2::Error) -> Error { if cause.is_io() { Error::new_io(cause.into_io().expect("h2::Error::is_io")) } else { Error::new(Kind::Http2).with(cause) } } fn description(&self) -> &str { match self.inner.kind { Kind::Parse(Parse::Method) => "invalid HTTP method parsed", #[cfg(feature = "http1")] Kind::Parse(Parse::Version) => "invalid HTTP version parsed", #[cfg(all(any(feature = "client", feature = "server"), feature = "http1"))] Kind::Parse(Parse::VersionH2) => "invalid HTTP version parsed (found HTTP2 preface)", Kind::Parse(Parse::Uri) => "invalid URI", #[cfg(all(feature = "http1", feature = "server"))] Kind::Parse(Parse::UriTooLong) => "URI too long", #[cfg(feature = "http1")] Kind::Parse(Parse::Header(Header::Token)) => "invalid HTTP header parsed", #[cfg(all(any(feature = "client", feature = "server"), feature = "http1"))] Kind::Parse(Parse::Header(Header::ContentLengthInvalid)) => { "invalid content-length parsed" } #[cfg(all(feature = "http1", feature = "server"))] Kind::Parse(Parse::Header(Header::TransferEncodingInvalid)) => { "invalid transfer-encoding parsed" } #[cfg(all(feature = "http1", any(feature = "client", feature = "server")))] Kind::Parse(Parse::Header(Header::TransferEncodingUnexpected)) => { "unexpected transfer-encoding parsed" } #[cfg(any(feature = "http1", feature = "http2"))] Kind::Parse(Parse::TooLarge) => "message head is too large", Kind::Parse(Parse::Status) => "invalid HTTP status-code parsed", #[cfg(all(any(feature = "client", feature = "server"), feature = "http1"))] Kind::Parse(Parse::Internal) => { "internal error inside Hyper and/or its dependencies, please report" } #[cfg(all(any(feature = "client", feature = "server"), feature = "http1"))] Kind::IncompleteMessage => "connection closed before message completed", #[cfg(all(any(feature = "client", feature = "server"), feature = "http1"))] Kind::UnexpectedMessage => "received unexpected message from connection", #[cfg(any( all(feature = "http1", any(feature = "client", feature = "server")), all(feature = "http2", feature = "client") ))] Kind::ChannelClosed => "channel closed", Kind::Canceled => "operation was canceled", #[cfg(all(feature = "http1", feature = "server"))] Kind::HeaderTimeout => "read header from client timeout", #[cfg(all( any(feature = "client", feature = "server"), any(feature = "http1", feature = "http2") ))] Kind::Body => "error reading a body from connection", #[cfg(all( any(feature = "client", feature = "server"), any(feature = "http1", feature = "http2") ))] Kind::BodyWrite => "error writing a body to connection", #[cfg(all(any(feature = "client", feature = "server"), feature = "http1"))] Kind::Shutdown => "error shutting down connection", #[cfg(all(any(feature = "client", feature = "server"), feature = "http2"))] Kind::Http2 => "http2 error", #[cfg(all( any(feature = "client", feature = "server"), any(feature = "http1", feature = "http2") ))] Kind::Io => "connection error", #[cfg(all( any(feature = "client", feature = "server"), any(feature = "http1", feature = "http2") ))] Kind::User(User::Body) => "error from user's Body stream", #[cfg(any( all(feature = "http1", any(feature = "client", feature = "server")), feature = "ffi" ))] Kind::User(User::BodyWriteAborted) => "user body write aborted", #[cfg(any( all(any(feature = "client", feature = "server"), feature = "http1"), all(feature = "server", feature = "http2") ))] Kind::User(User::Service) => "error from user's Service", #[cfg(any(feature = "http1", feature = "http2"))] #[cfg(feature = "server")] Kind::User(User::UnexpectedHeader) => "user sent unexpected header", #[cfg(feature = "http1")] #[cfg(feature = "server")] Kind::User(User::UnsupportedStatusCode) => { "response has 1xx status code, not supported by server" } Kind::User(User::NoUpgrade) => "no upgrade available", #[cfg(all(any(feature = "client", feature = "server"), feature = "http1"))] Kind::User(User::ManualUpgrade) => "upgrade expected but low level API in use", #[cfg(all(feature = "client", any(feature = "http1", feature = "http2")))] Kind::User(User::DispatchGone) => "dispatch task is gone", #[cfg(feature = "ffi")] Kind::User(User::AbortedByCallback) => "operation aborted by an application callback", } } } impl fmt::Debug for Error { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let mut f = f.debug_tuple("hyper::Error"); f.field(&self.inner.kind); if let Some(ref cause) = self.inner.cause { f.field(cause); } f.finish() } } impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str(self.description()) } } impl StdError for Error { fn source(&self) -> Option<&(dyn StdError + 'static)> { self.inner .cause .as_ref() .map(|cause| &**cause as &(dyn StdError + 'static)) } } #[doc(hidden)] impl From for Error { fn from(err: Parse) -> Error { Error::new(Kind::Parse(err)) } } #[cfg(feature = "http1")] impl Parse { #[cfg(any(feature = "client", feature = "server"))] pub(crate) fn content_length_invalid() -> Self { Parse::Header(Header::ContentLengthInvalid) } #[cfg(feature = "server")] pub(crate) fn transfer_encoding_invalid() -> Self { Parse::Header(Header::TransferEncodingInvalid) } #[cfg(any(feature = "client", feature = "server"))] pub(crate) fn transfer_encoding_unexpected() -> Self { Parse::Header(Header::TransferEncodingUnexpected) } } #[cfg(feature = "http1")] impl From for Parse { fn from(err: httparse::Error) -> Parse { match err { httparse::Error::HeaderName | httparse::Error::HeaderValue | httparse::Error::NewLine | httparse::Error::Token => Parse::Header(Header::Token), httparse::Error::Status => Parse::Status, httparse::Error::TooManyHeaders => Parse::TooLarge, httparse::Error::Version => Parse::Version, } } } impl From for Parse { fn from(_: http::method::InvalidMethod) -> Parse { Parse::Method } } impl From for Parse { fn from(_: http::status::InvalidStatusCode) -> Parse { Parse::Status } } impl From for Parse { fn from(_: http::uri::InvalidUri) -> Parse { Parse::Uri } } impl From for Parse { fn from(_: http::uri::InvalidUriParts) -> Parse { Parse::Uri } } // ===== impl TimedOut ==== impl fmt::Display for TimedOut { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str("operation timed out") } } impl StdError for TimedOut {} #[cfg(test)] mod tests { use super::*; use std::mem; fn assert_send_sync() {} #[test] fn error_satisfies_send_sync() { assert_send_sync::() } #[test] fn error_size_of() { assert_eq!(mem::size_of::(), mem::size_of::()); } #[cfg(feature = "http2")] #[test] fn h2_reason_unknown() { let closed = Error::new_closed(); assert_eq!(closed.h2_reason(), h2::Reason::INTERNAL_ERROR); } #[cfg(feature = "http2")] #[test] fn h2_reason_one_level() { let body_err = Error::new_user_body(h2::Error::from(h2::Reason::ENHANCE_YOUR_CALM)); assert_eq!(body_err.h2_reason(), h2::Reason::ENHANCE_YOUR_CALM); } #[cfg(feature = "http2")] #[test] fn h2_reason_nested() { let recvd = Error::new_h2(h2::Error::from(h2::Reason::HTTP_1_1_REQUIRED)); // Suppose a user were proxying the received error let svc_err = Error::new_user_service(recvd); assert_eq!(svc_err.h2_reason(), h2::Reason::HTTP_1_1_REQUIRED); } } hyper-1.5.2/src/ext/h1_reason_phrase.rs000064400000000000000000000147221046102023000161440ustar 00000000000000use bytes::Bytes; /// A reason phrase in an HTTP/1 response. /// /// # Clients /// /// For clients, a `ReasonPhrase` will be present in the extensions of the `http::Response` returned /// for a request if the reason phrase is different from the canonical reason phrase for the /// response's status code. For example, if a server returns `HTTP/1.1 200 Awesome`, the /// `ReasonPhrase` will be present and contain `Awesome`, but if a server returns `HTTP/1.1 200 OK`, /// the response will not contain a `ReasonPhrase`. /// /// ```no_run /// # #[cfg(all(feature = "tcp", feature = "client", feature = "http1"))] /// # async fn fake_fetch() -> hyper::Result<()> { /// use hyper::{Client, Uri}; /// use hyper::ext::ReasonPhrase; /// /// let res = Client::new().get(Uri::from_static("http://example.com/non_canonical_reason")).await?; /// /// // Print out the non-canonical reason phrase, if it has one... /// if let Some(reason) = res.extensions().get::() { /// println!("non-canonical reason: {}", std::str::from_utf8(reason.as_bytes()).unwrap()); /// } /// # Ok(()) /// # } /// ``` /// /// # Servers /// /// When a `ReasonPhrase` is present in the extensions of the `http::Response` written by a server, /// its contents will be written in place of the canonical reason phrase when responding via HTTP/1. #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct ReasonPhrase(Bytes); impl ReasonPhrase { /// Gets the reason phrase as bytes. pub fn as_bytes(&self) -> &[u8] { &self.0 } /// Converts a static byte slice to a reason phrase. pub const fn from_static(reason: &'static [u8]) -> Self { // TODO: this can be made const once MSRV is >= 1.57.0 if find_invalid_byte(reason).is_some() { panic!("invalid byte in static reason phrase"); } Self(Bytes::from_static(reason)) } // Not public on purpose. /// Converts a `Bytes` directly into a `ReasonPhrase` without validating. /// /// Use with care; invalid bytes in a reason phrase can cause serious security problems if /// emitted in a response. #[cfg(feature = "client")] pub(crate) fn from_bytes_unchecked(reason: Bytes) -> Self { Self(reason) } } impl TryFrom<&[u8]> for ReasonPhrase { type Error = InvalidReasonPhrase; fn try_from(reason: &[u8]) -> Result { if let Some(bad_byte) = find_invalid_byte(reason) { Err(InvalidReasonPhrase { bad_byte }) } else { Ok(Self(Bytes::copy_from_slice(reason))) } } } impl TryFrom> for ReasonPhrase { type Error = InvalidReasonPhrase; fn try_from(reason: Vec) -> Result { if let Some(bad_byte) = find_invalid_byte(&reason) { Err(InvalidReasonPhrase { bad_byte }) } else { Ok(Self(Bytes::from(reason))) } } } impl TryFrom for ReasonPhrase { type Error = InvalidReasonPhrase; fn try_from(reason: String) -> Result { if let Some(bad_byte) = find_invalid_byte(reason.as_bytes()) { Err(InvalidReasonPhrase { bad_byte }) } else { Ok(Self(Bytes::from(reason))) } } } impl TryFrom for ReasonPhrase { type Error = InvalidReasonPhrase; fn try_from(reason: Bytes) -> Result { if let Some(bad_byte) = find_invalid_byte(&reason) { Err(InvalidReasonPhrase { bad_byte }) } else { Ok(Self(reason)) } } } impl From for Bytes { fn from(reason: ReasonPhrase) -> Self { reason.0 } } impl AsRef<[u8]> for ReasonPhrase { fn as_ref(&self) -> &[u8] { &self.0 } } /// Error indicating an invalid byte when constructing a `ReasonPhrase`. /// /// See [the spec][spec] for details on allowed bytes. /// /// [spec]: https://httpwg.org/http-core/draft-ietf-httpbis-messaging-latest.html#rfc.section.4.p.7 #[derive(Debug)] pub struct InvalidReasonPhrase { bad_byte: u8, } impl std::fmt::Display for InvalidReasonPhrase { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "Invalid byte in reason phrase: {}", self.bad_byte) } } impl std::error::Error for InvalidReasonPhrase {} const fn is_valid_byte(b: u8) -> bool { // See https://www.rfc-editor.org/rfc/rfc5234.html#appendix-B.1 const fn is_vchar(b: u8) -> bool { 0x21 <= b && b <= 0x7E } // See https://httpwg.org/http-core/draft-ietf-httpbis-semantics-latest.html#fields.values // // The 0xFF comparison is technically redundant, but it matches the text of the spec more // clearly and will be optimized away. #[allow(unused_comparisons, clippy::absurd_extreme_comparisons)] const fn is_obs_text(b: u8) -> bool { 0x80 <= b && b <= 0xFF } // See https://httpwg.org/http-core/draft-ietf-httpbis-messaging-latest.html#rfc.section.4.p.7 b == b'\t' || b == b' ' || is_vchar(b) || is_obs_text(b) } const fn find_invalid_byte(bytes: &[u8]) -> Option { let mut i = 0; while i < bytes.len() { let b = bytes[i]; if !is_valid_byte(b) { return Some(b); } i += 1; } None } #[cfg(test)] mod tests { use super::*; #[test] fn basic_valid() { const PHRASE: &[u8] = b"OK"; assert_eq!(ReasonPhrase::from_static(PHRASE).as_bytes(), PHRASE); assert_eq!(ReasonPhrase::try_from(PHRASE).unwrap().as_bytes(), PHRASE); } #[test] fn empty_valid() { const PHRASE: &[u8] = b""; assert_eq!(ReasonPhrase::from_static(PHRASE).as_bytes(), PHRASE); assert_eq!(ReasonPhrase::try_from(PHRASE).unwrap().as_bytes(), PHRASE); } #[test] fn obs_text_valid() { const PHRASE: &[u8] = b"hyp\xe9r"; assert_eq!(ReasonPhrase::from_static(PHRASE).as_bytes(), PHRASE); assert_eq!(ReasonPhrase::try_from(PHRASE).unwrap().as_bytes(), PHRASE); } const NEWLINE_PHRASE: &[u8] = b"hyp\ner"; #[test] #[should_panic] fn newline_invalid_panic() { ReasonPhrase::from_static(NEWLINE_PHRASE); } #[test] fn newline_invalid_err() { assert!(ReasonPhrase::try_from(NEWLINE_PHRASE).is_err()); } const CR_PHRASE: &[u8] = b"hyp\rer"; #[test] #[should_panic] fn cr_invalid_panic() { ReasonPhrase::from_static(CR_PHRASE); } #[test] fn cr_invalid_err() { assert!(ReasonPhrase::try_from(CR_PHRASE).is_err()); } } hyper-1.5.2/src/ext/mod.rs000064400000000000000000000161741046102023000135050ustar 00000000000000//! HTTP extensions. #[cfg(all(any(feature = "client", feature = "server"), feature = "http1"))] use bytes::Bytes; #[cfg(any( all(any(feature = "client", feature = "server"), feature = "http1"), feature = "ffi" ))] use http::header::HeaderName; #[cfg(all(any(feature = "client", feature = "server"), feature = "http1"))] use http::header::{HeaderMap, IntoHeaderName, ValueIter}; #[cfg(feature = "ffi")] use std::collections::HashMap; #[cfg(feature = "http2")] use std::fmt; #[cfg(any(feature = "http1", feature = "ffi"))] mod h1_reason_phrase; #[cfg(any(feature = "http1", feature = "ffi"))] pub use h1_reason_phrase::ReasonPhrase; #[cfg(feature = "http2")] /// Represents the `:protocol` pseudo-header used by /// the [Extended CONNECT Protocol]. /// /// [Extended CONNECT Protocol]: https://datatracker.ietf.org/doc/html/rfc8441#section-4 #[derive(Clone, Eq, PartialEq)] pub struct Protocol { inner: h2::ext::Protocol, } #[cfg(feature = "http2")] impl Protocol { /// Converts a static string to a protocol name. pub const fn from_static(value: &'static str) -> Self { Self { inner: h2::ext::Protocol::from_static(value), } } /// Returns a str representation of the header. pub fn as_str(&self) -> &str { self.inner.as_str() } #[cfg(feature = "server")] pub(crate) fn from_inner(inner: h2::ext::Protocol) -> Self { Self { inner } } #[cfg(all(feature = "client", feature = "http2"))] pub(crate) fn into_inner(self) -> h2::ext::Protocol { self.inner } } #[cfg(feature = "http2")] impl<'a> From<&'a str> for Protocol { fn from(value: &'a str) -> Self { Self { inner: h2::ext::Protocol::from(value), } } } #[cfg(feature = "http2")] impl AsRef<[u8]> for Protocol { fn as_ref(&self) -> &[u8] { self.inner.as_ref() } } #[cfg(feature = "http2")] impl fmt::Debug for Protocol { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.inner.fmt(f) } } /// A map from header names to their original casing as received in an HTTP message. /// /// If an HTTP/1 response `res` is parsed on a connection whose option /// [`preserve_header_case`] was set to true and the response included /// the following headers: /// /// ```ignore /// x-Bread: Baguette /// X-BREAD: Pain /// x-bread: Ficelle /// ``` /// /// Then `res.extensions().get::()` will return a map with: /// /// ```ignore /// HeaderCaseMap({ /// "x-bread": ["x-Bread", "X-BREAD", "x-bread"], /// }) /// ``` /// /// [`preserve_header_case`]: /client/struct.Client.html#method.preserve_header_case #[cfg(all(any(feature = "client", feature = "server"), feature = "http1"))] #[derive(Clone, Debug)] pub(crate) struct HeaderCaseMap(HeaderMap); #[cfg(all(any(feature = "client", feature = "server"), feature = "http1"))] impl HeaderCaseMap { /// Returns a view of all spellings associated with that header name, /// in the order they were found. #[cfg(feature = "client")] pub(crate) fn get_all<'a>( &'a self, name: &HeaderName, ) -> impl Iterator + 'a> + 'a { self.get_all_internal(name) } /// Returns a view of all spellings associated with that header name, /// in the order they were found. #[cfg(any(feature = "client", feature = "server"))] pub(crate) fn get_all_internal(&self, name: &HeaderName) -> ValueIter<'_, Bytes> { self.0.get_all(name).into_iter() } #[cfg(any(feature = "client", feature = "server"))] pub(crate) fn default() -> Self { Self(Default::default()) } #[cfg(any(test, feature = "ffi"))] pub(crate) fn insert(&mut self, name: HeaderName, orig: Bytes) { self.0.insert(name, orig); } #[cfg(any(feature = "client", feature = "server"))] pub(crate) fn append(&mut self, name: N, orig: Bytes) where N: IntoHeaderName, { self.0.append(name, orig); } } #[cfg(feature = "ffi")] #[derive(Clone, Debug)] /// Hashmap pub(crate) struct OriginalHeaderOrder { /// Stores how many entries a Headername maps to. This is used /// for accounting. num_entries: HashMap, /// Stores the ordering of the headers. ex: `vec[i] = (headerName, idx)`, /// The vector is ordered such that the ith element /// represents the ith header that came in off the line. /// The `HeaderName` and `idx` are then used elsewhere to index into /// the multi map that stores the header values. entry_order: Vec<(HeaderName, usize)>, } #[cfg(all(feature = "http1", feature = "ffi"))] impl OriginalHeaderOrder { pub(crate) fn default() -> Self { OriginalHeaderOrder { num_entries: HashMap::new(), entry_order: Vec::new(), } } pub(crate) fn insert(&mut self, name: HeaderName) { if !self.num_entries.contains_key(&name) { let idx = 0; self.num_entries.insert(name.clone(), 1); self.entry_order.push((name, idx)); } // Replacing an already existing element does not // change ordering, so we only care if its the first // header name encountered } pub(crate) fn append(&mut self, name: N) where N: IntoHeaderName + Into + Clone, { let name: HeaderName = name.into(); let idx; if self.num_entries.contains_key(&name) { idx = self.num_entries[&name]; *self.num_entries.get_mut(&name).unwrap() += 1; } else { idx = 0; self.num_entries.insert(name.clone(), 1); } self.entry_order.push((name, idx)); } // No doc test is run here because `RUSTFLAGS='--cfg hyper_unstable_ffi'` // is needed to compile. Once ffi is stabilized `no_run` should be removed // here. /// This returns an iterator that provides header names and indexes /// in the original order received. /// /// # Examples /// ```no_run /// use hyper::ext::OriginalHeaderOrder; /// use hyper::header::{HeaderName, HeaderValue, HeaderMap}; /// /// let mut h_order = OriginalHeaderOrder::default(); /// let mut h_map = Headermap::new(); /// /// let name1 = b"Set-CookiE"; /// let value1 = b"a=b"; /// h_map.append(name1); /// h_order.append(name1); /// /// let name2 = b"Content-Encoding"; /// let value2 = b"gzip"; /// h_map.append(name2, value2); /// h_order.append(name2); /// /// let name3 = b"SET-COOKIE"; /// let value3 = b"c=d"; /// h_map.append(name3, value3); /// h_order.append(name3) /// /// let mut iter = h_order.get_in_order() /// /// let (name, idx) = iter.next(); /// assert_eq!(b"a=b", h_map.get_all(name).nth(idx).unwrap()); /// /// let (name, idx) = iter.next(); /// assert_eq!(b"gzip", h_map.get_all(name).nth(idx).unwrap()); /// /// let (name, idx) = iter.next(); /// assert_eq!(b"c=d", h_map.get_all(name).nth(idx).unwrap()); /// ``` pub(crate) fn get_in_order(&self) -> impl Iterator { self.entry_order.iter() } } hyper-1.5.2/src/ffi/body.rs000064400000000000000000000260641046102023000136260ustar 00000000000000use std::ffi::{c_int, c_void}; use std::mem::ManuallyDrop; use std::ptr; use std::task::{Context, Poll}; use http_body_util::BodyExt as _; use super::task::{hyper_context, hyper_task, hyper_task_return_type, AsTaskType}; use super::{UserDataPointer, HYPER_ITER_CONTINUE}; use crate::body::{Bytes, Frame, Incoming as IncomingBody}; use crate::ffi::size_t; /// A streaming HTTP body. /// /// This is used both for sending requests (with `hyper_request_set_body`) and /// for receiving responses (with `hyper_response_body`). /// /// For outgoing request bodies, call `hyper_body_set_data_func` to provide the /// data. /// /// For incoming response bodies, call `hyper_body_data` to get a task that will /// yield a chunk of data each time it is polled. That task must be then be /// added to the executor with `hyper_executor_push`. /// /// Methods: /// /// - hyper_body_new: Create a new “empty” body. /// - hyper_body_set_userdata: Set userdata on this body, which will be passed to callback functions. /// - hyper_body_set_data_func: Set the data callback for this body. /// - hyper_body_data: Creates a task that will poll a response body for the next buffer of data. /// - hyper_body_foreach: Creates a task to execute the callback with each body chunk received. /// - hyper_body_free: Free a body. pub struct hyper_body(pub(super) IncomingBody); /// A buffer of bytes that is sent or received on a `hyper_body`. /// /// Obtain one of these in the callback of `hyper_body_foreach` or by receiving /// a task of type `HYPER_TASK_BUF` from `hyper_executor_poll` (after calling /// `hyper_body_data` and pushing the resulting task). /// /// Methods: /// /// - hyper_buf_bytes: Get a pointer to the bytes in this buffer. /// - hyper_buf_copy: Create a new hyper_buf * by copying the provided bytes. /// - hyper_buf_free: Free this buffer. /// - hyper_buf_len: Get the length of the bytes this buffer contains. pub struct hyper_buf(pub(crate) Bytes); pub(crate) struct UserBody { data_func: hyper_body_data_callback, userdata: *mut c_void, } // ===== Body ===== type hyper_body_foreach_callback = extern "C" fn(*mut c_void, *const hyper_buf) -> c_int; type hyper_body_data_callback = extern "C" fn(*mut c_void, *mut hyper_context<'_>, *mut *mut hyper_buf) -> c_int; ffi_fn! { /// Creates a new "empty" body. /// /// If not configured, this body acts as an empty payload. /// /// To avoid a memory leak, the body must eventually be consumed by /// `hyper_body_free`, `hyper_body_foreach`, or `hyper_request_set_body`. fn hyper_body_new() -> *mut hyper_body { Box::into_raw(Box::new(hyper_body(IncomingBody::ffi()))) } ?= ptr::null_mut() } ffi_fn! { /// Free a body. /// /// This should only be used if the request isn't consumed by /// `hyper_body_foreach` or `hyper_request_set_body`. fn hyper_body_free(body: *mut hyper_body) { drop(non_null!(Box::from_raw(body) ?= ())); } } ffi_fn! { /// Creates a task that will poll a response body for the next buffer of data. /// /// The task may have different types depending on the outcome: /// /// - `HYPER_TASK_BUF`: Success, and more data was received. /// - `HYPER_TASK_ERROR`: An error retrieving the data. /// - `HYPER_TASK_EMPTY`: The body has finished streaming data. /// /// When the application receives the task from `hyper_executor_poll`, /// if the task type is `HYPER_TASK_BUF`, it should cast the task to /// `hyper_buf *` and consume all the bytes in the buffer. Then /// the application should call `hyper_body_data` again for the same /// `hyper_body *`, to create a task for the next buffer of data. /// Repeat until the polled task type is `HYPER_TASK_ERROR` or /// `HYPER_TASK_EMPTY`. /// /// To avoid a memory leak, the task must eventually be consumed by /// `hyper_task_free`, or taken ownership of by `hyper_executor_push` /// without subsequently being given back by `hyper_executor_poll`. /// /// This does not consume the `hyper_body *`, so it may be used again. /// However, the `hyper_body *` MUST NOT be used or freed until the /// related task is returned from `hyper_executor_poll`. /// /// For a more convenient method, see also `hyper_body_foreach`. fn hyper_body_data(body: *mut hyper_body) -> *mut hyper_task { // This doesn't take ownership of the Body, so don't allow destructor let mut body = ManuallyDrop::new(non_null!(Box::from_raw(body) ?= ptr::null_mut())); Box::into_raw(hyper_task::boxed(async move { loop { match body.0.frame().await { Some(Ok(frame)) => { if let Ok(data) = frame.into_data() { return Ok(Some(hyper_buf(data))); } else { continue; } }, Some(Err(e)) => return Err(e), None => return Ok(None), } } })) } ?= ptr::null_mut() } ffi_fn! { /// Creates a task to execute the callback with each body chunk received. /// /// To avoid a memory leak, the task must eventually be consumed by /// `hyper_task_free`, or taken ownership of by `hyper_executor_push` /// without subsequently being given back by `hyper_executor_poll`. /// /// The `hyper_buf` pointer is only a borrowed reference. It cannot live outside /// the execution of the callback. You must make a copy of the bytes to retain them. /// /// The callback should return `HYPER_ITER_CONTINUE` to continue iterating /// chunks as they are received, or `HYPER_ITER_BREAK` to cancel. Each /// invocation of the callback must consume all the bytes it is provided. /// There is no mechanism to signal to Hyper that only a subset of bytes were /// consumed. /// /// This will consume the `hyper_body *`, you shouldn't use it anymore or free it. fn hyper_body_foreach(body: *mut hyper_body, func: hyper_body_foreach_callback, userdata: *mut c_void) -> *mut hyper_task { let mut body = non_null!(Box::from_raw(body) ?= ptr::null_mut()); let userdata = UserDataPointer(userdata); Box::into_raw(hyper_task::boxed(async move { let _ = &userdata; while let Some(item) = body.0.frame().await { let frame = item?; if let Ok(chunk) = frame.into_data() { if HYPER_ITER_CONTINUE != func(userdata.0, &hyper_buf(chunk)) { return Err(crate::Error::new_user_aborted_by_callback()); } } } Ok(()) })) } ?= ptr::null_mut() } ffi_fn! { /// Set userdata on this body, which will be passed to callback functions. fn hyper_body_set_userdata(body: *mut hyper_body, userdata: *mut c_void) { let b = non_null!(&mut *body ?= ()); b.0.as_ffi_mut().userdata = userdata; } } ffi_fn! { /// Set the outgoing data callback for this body. /// /// The callback is called each time hyper needs to send more data for the /// body. It is passed the value from `hyper_body_set_userdata`. /// /// If there is data available, the `hyper_buf **` argument should be set /// to a `hyper_buf *` containing the data, and `HYPER_POLL_READY` should /// be returned. /// /// Returning `HYPER_POLL_READY` while the `hyper_buf **` argument points /// to `NULL` will indicate the body has completed all data. /// /// If there is more data to send, but it isn't yet available, a /// `hyper_waker` should be saved from the `hyper_context *` argument, and /// `HYPER_POLL_PENDING` should be returned. You must wake the saved waker /// to signal the task when data is available. /// /// If some error has occurred, you can return `HYPER_POLL_ERROR` to abort /// the body. fn hyper_body_set_data_func(body: *mut hyper_body, func: hyper_body_data_callback) { let b = non_null!{ &mut *body ?= () }; b.0.as_ffi_mut().data_func = func; } } // ===== impl UserBody ===== impl UserBody { pub(crate) fn new() -> UserBody { UserBody { data_func: data_noop, userdata: std::ptr::null_mut(), } } pub(crate) fn poll_data( &mut self, cx: &mut Context<'_>, ) -> Poll>>> { let mut out = std::ptr::null_mut(); match (self.data_func)(self.userdata, hyper_context::wrap(cx), &mut out) { super::task::HYPER_POLL_READY => { if out.is_null() { Poll::Ready(None) } else { let buf = unsafe { Box::from_raw(out) }; Poll::Ready(Some(Ok(Frame::data(buf.0)))) } } super::task::HYPER_POLL_PENDING => Poll::Pending, super::task::HYPER_POLL_ERROR => { Poll::Ready(Some(Err(crate::Error::new_body_write_aborted()))) } unexpected => Poll::Ready(Some(Err(crate::Error::new_body_write(format!( "unexpected hyper_body_data_func return code {}", unexpected ))))), } } } /// cbindgen:ignore extern "C" fn data_noop( _userdata: *mut c_void, _: *mut hyper_context<'_>, _: *mut *mut hyper_buf, ) -> c_int { super::task::HYPER_POLL_READY } unsafe impl Send for UserBody {} unsafe impl Sync for UserBody {} // ===== Bytes ===== ffi_fn! { /// Create a new `hyper_buf *` by copying the provided bytes. /// /// This makes an owned copy of the bytes, so the `buf` argument can be /// freed (with `hyper_buf_free`) or changed afterwards. /// /// To avoid a memory leak, the copy must eventually be consumed by /// `hyper_buf_free`. /// /// This returns `NULL` if allocating a new buffer fails. fn hyper_buf_copy(buf: *const u8, len: size_t) -> *mut hyper_buf { let slice = unsafe { std::slice::from_raw_parts(buf, len) }; Box::into_raw(Box::new(hyper_buf(Bytes::copy_from_slice(slice)))) } ?= ptr::null_mut() } ffi_fn! { /// Get a pointer to the bytes in this buffer. /// /// This should be used in conjunction with `hyper_buf_len` to get the length /// of the bytes data. /// /// This pointer is borrowed data, and not valid once the `hyper_buf` is /// consumed/freed. fn hyper_buf_bytes(buf: *const hyper_buf) -> *const u8 { unsafe { (*buf).0.as_ptr() } } ?= ptr::null() } ffi_fn! { /// Get the length of the bytes this buffer contains. fn hyper_buf_len(buf: *const hyper_buf) -> size_t { unsafe { (*buf).0.len() } } } ffi_fn! { /// Free this buffer. /// /// This should be used for any buffer once it is no longer needed. fn hyper_buf_free(buf: *mut hyper_buf) { drop(unsafe { Box::from_raw(buf) }); } } unsafe impl AsTaskType for hyper_buf { fn as_task_type(&self) -> hyper_task_return_type { hyper_task_return_type::HYPER_TASK_BUF } } hyper-1.5.2/src/ffi/client.rs000064400000000000000000000251011046102023000141360ustar 00000000000000use std::ffi::c_int; use std::ptr; use std::sync::Arc; use crate::client::conn; use crate::rt::Executor as _; use super::error::hyper_code; use super::http_types::{hyper_request, hyper_response}; use super::io::hyper_io; use super::task::{hyper_executor, hyper_task, hyper_task_return_type, AsTaskType, WeakExec}; /// An options builder to configure an HTTP client connection. /// /// Methods: /// /// - hyper_clientconn_options_new: Creates a new set of HTTP clientconn options to be used in a handshake. /// - hyper_clientconn_options_exec: Set the client background task executor. /// - hyper_clientconn_options_http2: Set whether to use HTTP2. /// - hyper_clientconn_options_set_preserve_header_case: Set whether header case is preserved. /// - hyper_clientconn_options_set_preserve_header_order: Set whether header order is preserved. /// - hyper_clientconn_options_http1_allow_multiline_headers: Set whether HTTP/1 connections accept obsolete line folding for header values. /// - hyper_clientconn_options_free: Free a set of HTTP clientconn options. pub struct hyper_clientconn_options { http1_allow_obsolete_multiline_headers_in_responses: bool, http1_preserve_header_case: bool, http1_preserve_header_order: bool, http2: bool, /// Use a `Weak` to prevent cycles. exec: WeakExec, } /// An HTTP client connection handle. /// /// These are used to send one or more requests on a single connection. /// /// It's possible to send multiple requests on a single connection, such /// as when HTTP/1 keep-alive or HTTP/2 is used. /// /// To create a `hyper_clientconn`: /// /// 1. Create a `hyper_io` with `hyper_io_new`. /// 2. Create a `hyper_clientconn_options` with `hyper_clientconn_options_new`. /// 3. Call `hyper_clientconn_handshake` with the `hyper_io` and `hyper_clientconn_options`. /// This creates a `hyper_task`. /// 5. Call `hyper_task_set_userdata` to assign an application-specific pointer to the task. /// This allows keeping track of multiple connections that may be handshaking /// simultaneously. /// 4. Add the `hyper_task` to an executor with `hyper_executor_push`. /// 5. Poll that executor until it yields a task of type `HYPER_TASK_CLIENTCONN`. /// 6. Extract the `hyper_clientconn` from the task with `hyper_task_value`. /// This will require a cast from `void *` to `hyper_clientconn *`. /// /// This process results in a `hyper_clientconn` that permanently owns the /// `hyper_io`. Because the `hyper_io` in turn owns a TCP or TLS connection, that means /// the `hyper_clientconn` owns the connection for both the clientconn's lifetime /// and the connection's lifetime. /// /// In other words, each connection (`hyper_io`) must have exactly one `hyper_clientconn` /// associated with it. That's because `hyper_clientconn_handshake` sends the /// [HTTP/2 Connection Preface] (for HTTP/2 connections). Since that preface can't /// be sent twice, handshake can't be called twice. /// /// [HTTP/2 Connection Preface]: https://datatracker.ietf.org/doc/html/rfc9113#name-http-2-connection-preface /// /// Methods: /// /// - hyper_clientconn_handshake: Creates an HTTP client handshake task. /// - hyper_clientconn_send: Creates a task to send a request on the client connection. /// - hyper_clientconn_free: Free a hyper_clientconn *. pub struct hyper_clientconn { tx: Tx, } enum Tx { #[cfg(feature = "http1")] Http1(conn::http1::SendRequest), #[cfg(feature = "http2")] Http2(conn::http2::SendRequest), } // ===== impl hyper_clientconn ===== ffi_fn! { /// Creates an HTTP client handshake task. /// /// Both the `io` and the `options` are consumed in this function call. /// They should not be used or freed afterwards. /// /// The returned task must be polled with an executor until the handshake /// completes, at which point the value can be taken. /// /// To avoid a memory leak, the task must eventually be consumed by /// `hyper_task_free`, or taken ownership of by `hyper_executor_push` /// without subsequently being given back by `hyper_executor_poll`. fn hyper_clientconn_handshake(io: *mut hyper_io, options: *mut hyper_clientconn_options) -> *mut hyper_task { let options = non_null! { Box::from_raw(options) ?= ptr::null_mut() }; let io = non_null! { Box::from_raw(io) ?= ptr::null_mut() }; Box::into_raw(hyper_task::boxed(async move { #[cfg(feature = "http2")] { if options.http2 { return conn::http2::Builder::new(options.exec.clone()) .handshake::<_, crate::body::Incoming>(io) .await .map(|(tx, conn)| { options.exec.execute(Box::pin(async move { let _ = conn.await; })); hyper_clientconn { tx: Tx::Http2(tx) } }); } } conn::http1::Builder::new() .allow_obsolete_multiline_headers_in_responses(options.http1_allow_obsolete_multiline_headers_in_responses) .preserve_header_case(options.http1_preserve_header_case) .preserve_header_order(options.http1_preserve_header_order) .handshake::<_, crate::body::Incoming>(io) .await .map(|(tx, conn)| { options.exec.execute(Box::pin(async move { let _ = conn.await; })); hyper_clientconn { tx: Tx::Http1(tx) } }) })) } ?= std::ptr::null_mut() } ffi_fn! { /// Creates a task to send a request on the client connection. /// /// This consumes the request. You should not use or free the request /// afterwards. /// /// Returns a task that needs to be polled until it is ready. When ready, the /// task yields a `hyper_response *`. /// /// To avoid a memory leak, the task must eventually be consumed by /// `hyper_task_free`, or taken ownership of by `hyper_executor_push` /// without subsequently being given back by `hyper_executor_poll`. fn hyper_clientconn_send(conn: *mut hyper_clientconn, req: *mut hyper_request) -> *mut hyper_task { let mut req = non_null! { Box::from_raw(req) ?= ptr::null_mut() }; // Update request with original-case map of headers req.finalize_request(); let fut = match non_null! { &mut *conn ?= ptr::null_mut() }.tx { Tx::Http1(ref mut tx) => futures_util::future::Either::Left(tx.send_request(req.0)), Tx::Http2(ref mut tx) => futures_util::future::Either::Right(tx.send_request(req.0)), }; let fut = async move { fut.await.map(hyper_response::wrap) }; Box::into_raw(hyper_task::boxed(fut)) } ?= std::ptr::null_mut() } ffi_fn! { /// Free a `hyper_clientconn *`. /// /// This should be used for any connection once it is no longer needed. fn hyper_clientconn_free(conn: *mut hyper_clientconn) { drop(non_null! { Box::from_raw(conn) ?= () }); } } unsafe impl AsTaskType for hyper_clientconn { fn as_task_type(&self) -> hyper_task_return_type { hyper_task_return_type::HYPER_TASK_CLIENTCONN } } // ===== impl hyper_clientconn_options ===== ffi_fn! { /// Creates a new set of HTTP clientconn options to be used in a handshake. /// /// To avoid a memory leak, the options must eventually be consumed by /// `hyper_clientconn_options_free` or `hyper_clientconn_handshake`. fn hyper_clientconn_options_new() -> *mut hyper_clientconn_options { Box::into_raw(Box::new(hyper_clientconn_options { http1_allow_obsolete_multiline_headers_in_responses: false, http1_preserve_header_case: false, http1_preserve_header_order: false, http2: false, exec: WeakExec::new(), })) } ?= std::ptr::null_mut() } ffi_fn! { /// Set whether header case is preserved. /// /// Pass `0` to allow lowercase normalization (default), `1` to retain original case. fn hyper_clientconn_options_set_preserve_header_case(opts: *mut hyper_clientconn_options, enabled: c_int) { let opts = non_null! { &mut *opts ?= () }; opts.http1_preserve_header_case = enabled != 0; } } ffi_fn! { /// Set whether header order is preserved. /// /// Pass `0` to allow reordering (default), `1` to retain original ordering. fn hyper_clientconn_options_set_preserve_header_order(opts: *mut hyper_clientconn_options, enabled: c_int) { let opts = non_null! { &mut *opts ?= () }; opts.http1_preserve_header_order = enabled != 0; } } ffi_fn! { /// Free a set of HTTP clientconn options. /// /// This should only be used if the options aren't consumed by /// `hyper_clientconn_handshake`. fn hyper_clientconn_options_free(opts: *mut hyper_clientconn_options) { drop(non_null! { Box::from_raw(opts) ?= () }); } } ffi_fn! { /// Set the client background task executor. /// /// This does not consume the `options` or the `exec`. fn hyper_clientconn_options_exec(opts: *mut hyper_clientconn_options, exec: *const hyper_executor) { let opts = non_null! { &mut *opts ?= () }; let exec = non_null! { Arc::from_raw(exec) ?= () }; let weak_exec = hyper_executor::downgrade(&exec); std::mem::forget(exec); opts.exec = weak_exec; } } ffi_fn! { /// Set whether to use HTTP2. /// /// Pass `0` to disable, `1` to enable. fn hyper_clientconn_options_http2(opts: *mut hyper_clientconn_options, enabled: c_int) -> hyper_code { #[cfg(feature = "http2")] { let opts = non_null! { &mut *opts ?= hyper_code::HYPERE_INVALID_ARG }; opts.http2 = enabled != 0; hyper_code::HYPERE_OK } #[cfg(not(feature = "http2"))] { drop(opts); drop(enabled); hyper_code::HYPERE_FEATURE_NOT_ENABLED } } } ffi_fn! { /// Set whether HTTP/1 connections accept obsolete line folding for header values. /// /// Newline codepoints (\r and \n) will be transformed to spaces when parsing. /// /// Pass `0` to disable, `1` to enable. /// fn hyper_clientconn_options_http1_allow_multiline_headers(opts: *mut hyper_clientconn_options, enabled: c_int) -> hyper_code { let opts = non_null! { &mut *opts ?= hyper_code::HYPERE_INVALID_ARG }; opts.http1_allow_obsolete_multiline_headers_in_responses = enabled != 0; hyper_code::HYPERE_OK } } hyper-1.5.2/src/ffi/error.rs000064400000000000000000000060401046102023000140120ustar 00000000000000use crate::ffi::size_t; /// A more detailed error object returned by some hyper functions. /// /// Compare with `hyper_code`, which is a simpler error returned from /// some hyper functions. /// /// Methods: /// /// - hyper_error_code: Get an equivalent hyper_code from this error. /// - hyper_error_print: Print the details of this error to a buffer. /// - hyper_error_free: Frees a hyper_error. pub struct hyper_error(crate::Error); /// A return code for many of hyper's methods. #[repr(C)] pub enum hyper_code { /// All is well. HYPERE_OK, /// General error, details in the `hyper_error *`. HYPERE_ERROR, /// A function argument was invalid. HYPERE_INVALID_ARG, /// The IO transport returned an EOF when one wasn't expected. /// /// This typically means an HTTP request or response was expected, but the /// connection closed cleanly without sending (all of) it. HYPERE_UNEXPECTED_EOF, /// Aborted by a user supplied callback. HYPERE_ABORTED_BY_CALLBACK, /// An optional hyper feature was not enabled. #[cfg_attr(feature = "http2", allow(unused))] HYPERE_FEATURE_NOT_ENABLED, /// The peer sent an HTTP message that could not be parsed. HYPERE_INVALID_PEER_MESSAGE, } // ===== impl hyper_error ===== impl hyper_error { fn code(&self) -> hyper_code { use crate::error::Kind as ErrorKind; use crate::error::User; match self.0.kind() { ErrorKind::Parse(_) => hyper_code::HYPERE_INVALID_PEER_MESSAGE, ErrorKind::IncompleteMessage => hyper_code::HYPERE_UNEXPECTED_EOF, ErrorKind::User(User::AbortedByCallback) => hyper_code::HYPERE_ABORTED_BY_CALLBACK, // TODO: add more variants _ => hyper_code::HYPERE_ERROR, } } fn print_to(&self, dst: &mut [u8]) -> usize { use std::io::Write; let mut dst = std::io::Cursor::new(dst); // A write! error doesn't matter. As much as possible will have been // written, and the Cursor position will know how far that is (even // if that is zero). let _ = write!(dst, "{}", &self.0); dst.position() as usize } } ffi_fn! { /// Frees a `hyper_error`. /// /// This should be used for any error once it is no longer needed. fn hyper_error_free(err: *mut hyper_error) { drop(non_null!(Box::from_raw(err) ?= ())); } } ffi_fn! { /// Get an equivalent `hyper_code` from this error. fn hyper_error_code(err: *const hyper_error) -> hyper_code { non_null!(&*err ?= hyper_code::HYPERE_INVALID_ARG).code() } } ffi_fn! { /// Print the details of this error to a buffer. /// /// The `dst_len` value must be the maximum length that the buffer can /// store. /// /// The return value is number of bytes that were written to `dst`. fn hyper_error_print(err: *const hyper_error, dst: *mut u8, dst_len: size_t) -> size_t { let dst = unsafe { std::slice::from_raw_parts_mut(dst, dst_len) }; non_null!(&*err ?= 0).print_to(dst) } } hyper-1.5.2/src/ffi/http_types.rs000064400000000000000000000600571046102023000150740ustar 00000000000000use std::ffi::{c_int, c_void}; use bytes::Bytes; use super::body::hyper_body; use super::error::hyper_code; use super::task::{hyper_task_return_type, AsTaskType}; use super::{UserDataPointer, HYPER_ITER_CONTINUE}; use crate::body::Incoming as IncomingBody; use crate::ext::{HeaderCaseMap, OriginalHeaderOrder, ReasonPhrase}; use crate::ffi::size_t; use crate::header::{HeaderName, HeaderValue}; use crate::{HeaderMap, Method, Request, Response, Uri}; /// An HTTP request. /// /// Once you've finished constructing a request, you can send it with /// `hyper_clientconn_send`. /// /// Methods: /// /// - hyper_request_new: Construct a new HTTP request. /// - hyper_request_headers: Gets a mutable reference to the HTTP headers of this request /// - hyper_request_set_body: Set the body of the request. /// - hyper_request_set_method: Set the HTTP Method of the request. /// - hyper_request_set_uri: Set the URI of the request. /// - hyper_request_set_uri_parts: Set the URI of the request with separate scheme, authority, and path/query strings. /// - hyper_request_set_version: Set the preferred HTTP version of the request. /// - hyper_request_on_informational: Set an informational (1xx) response callback. /// - hyper_request_free: Free an HTTP request. pub struct hyper_request(pub(super) Request); /// An HTTP response. /// /// Obtain one of these by making a request with `hyper_clientconn_send`, then /// polling the executor unntil you get a `hyper_task` of type /// `HYPER_TASK_RESPONSE`. To figure out which request this response /// corresponds to, check the userdata of the task, which you should /// previously have set to an application-specific identifier for the /// request. /// /// Methods: /// /// - hyper_response_status: Get the HTTP-Status code of this response. /// - hyper_response_version: Get the HTTP version used by this response. /// - hyper_response_reason_phrase: Get a pointer to the reason-phrase of this response. /// - hyper_response_reason_phrase_len: Get the length of the reason-phrase of this response. /// - hyper_response_headers: Gets a reference to the HTTP headers of this response. /// - hyper_response_body: Take ownership of the body of this response. /// - hyper_response_free: Free an HTTP response. pub struct hyper_response(pub(super) Response); /// An HTTP header map. /// /// These can be part of a request or response. /// /// Obtain a pointer to read or modify these from `hyper_request_headers` /// or `hyper_response_headers`. /// /// Methods: /// /// - hyper_headers_add: Adds the provided value to the list of the provided name. /// - hyper_headers_foreach: Iterates the headers passing each name and value pair to the callback. /// - hyper_headers_set: Sets the header with the provided name to the provided value. #[derive(Clone)] pub struct hyper_headers { pub(super) headers: HeaderMap, orig_casing: HeaderCaseMap, orig_order: OriginalHeaderOrder, } #[derive(Clone)] pub(crate) struct OnInformational { func: hyper_request_on_informational_callback, data: UserDataPointer, } type hyper_request_on_informational_callback = extern "C" fn(*mut c_void, *mut hyper_response); // ===== impl hyper_request ===== ffi_fn! { /// Construct a new HTTP request. /// /// The default request has an empty body. To send a body, call `hyper_request_set_body`. /// /// /// To avoid a memory leak, the request must eventually be consumed by /// `hyper_request_free` or `hyper_clientconn_send`. fn hyper_request_new() -> *mut hyper_request { Box::into_raw(Box::new(hyper_request(Request::new(IncomingBody::empty())))) } ?= std::ptr::null_mut() } ffi_fn! { /// Free an HTTP request. /// /// This should only be used if the request isn't consumed by /// `hyper_clientconn_send`. fn hyper_request_free(req: *mut hyper_request) { drop(non_null!(Box::from_raw(req) ?= ())); } } ffi_fn! { /// Set the HTTP Method of the request. fn hyper_request_set_method(req: *mut hyper_request, method: *const u8, method_len: size_t) -> hyper_code { let bytes = unsafe { std::slice::from_raw_parts(method, method_len as usize) }; let req = non_null!(&mut *req ?= hyper_code::HYPERE_INVALID_ARG); match Method::from_bytes(bytes) { Ok(m) => { *req.0.method_mut() = m; hyper_code::HYPERE_OK }, Err(_) => { hyper_code::HYPERE_INVALID_ARG } } } } ffi_fn! { /// Set the URI of the request. /// /// The request's URI is best described as the `request-target` from the RFCs. So in HTTP/1, /// whatever is set will get sent as-is in the first line (GET $uri HTTP/1.1). It /// supports the 4 defined variants, origin-form, absolute-form, authority-form, and /// asterisk-form. /// /// The underlying type was built to efficiently support HTTP/2 where the request-target is /// split over :scheme, :authority, and :path. As such, each part can be set explicitly, or the /// type can parse a single contiguous string and if a scheme is found, that slot is "set". If /// the string just starts with a path, only the path portion is set. All pseudo headers that /// have been parsed/set are sent when the connection type is HTTP/2. /// /// To set each slot explicitly, use `hyper_request_set_uri_parts`. fn hyper_request_set_uri(req: *mut hyper_request, uri: *const u8, uri_len: size_t) -> hyper_code { let bytes = unsafe { std::slice::from_raw_parts(uri, uri_len as usize) }; let req = non_null!(&mut *req ?= hyper_code::HYPERE_INVALID_ARG); match Uri::from_maybe_shared(bytes) { Ok(u) => { *req.0.uri_mut() = u; hyper_code::HYPERE_OK }, Err(_) => { hyper_code::HYPERE_INVALID_ARG } } } } ffi_fn! { /// Set the URI of the request with separate scheme, authority, and /// path/query strings. /// /// Each of `scheme`, `authority`, and `path_and_query` should either be /// null, to skip providing a component, or point to a UTF-8 encoded /// string. If any string pointer argument is non-null, its corresponding /// `len` parameter must be set to the string's length. fn hyper_request_set_uri_parts( req: *mut hyper_request, scheme: *const u8, scheme_len: size_t, authority: *const u8, authority_len: size_t, path_and_query: *const u8, path_and_query_len: size_t ) -> hyper_code { let mut builder = Uri::builder(); if !scheme.is_null() { let scheme_bytes = unsafe { std::slice::from_raw_parts(scheme, scheme_len as usize) }; builder = builder.scheme(scheme_bytes); } if !authority.is_null() { let authority_bytes = unsafe { std::slice::from_raw_parts(authority, authority_len as usize) }; builder = builder.authority(authority_bytes); } if !path_and_query.is_null() { let path_and_query_bytes = unsafe { std::slice::from_raw_parts(path_and_query, path_and_query_len as usize) }; builder = builder.path_and_query(path_and_query_bytes); } match builder.build() { Ok(u) => { *unsafe { &mut *req }.0.uri_mut() = u; hyper_code::HYPERE_OK }, Err(_) => { hyper_code::HYPERE_INVALID_ARG } } } } ffi_fn! { /// Set the preferred HTTP version of the request. /// /// The version value should be one of the `HYPER_HTTP_VERSION_` constants. /// /// Note that this won't change the major HTTP version of the connection, /// since that is determined at the handshake step. fn hyper_request_set_version(req: *mut hyper_request, version: c_int) -> hyper_code { use http::Version; let req = non_null!(&mut *req ?= hyper_code::HYPERE_INVALID_ARG); *req.0.version_mut() = match version { super::HYPER_HTTP_VERSION_NONE => Version::HTTP_11, super::HYPER_HTTP_VERSION_1_0 => Version::HTTP_10, super::HYPER_HTTP_VERSION_1_1 => Version::HTTP_11, super::HYPER_HTTP_VERSION_2 => Version::HTTP_2, _ => { // We don't know this version return hyper_code::HYPERE_INVALID_ARG; } }; hyper_code::HYPERE_OK } } ffi_fn! { /// Gets a mutable reference to the HTTP headers of this request /// /// This is not an owned reference, so it should not be accessed after the /// `hyper_request` has been consumed. fn hyper_request_headers(req: *mut hyper_request) -> *mut hyper_headers { hyper_headers::get_or_default(unsafe { &mut *req }.0.extensions_mut()) } ?= std::ptr::null_mut() } ffi_fn! { /// Set the body of the request. /// /// You can get a `hyper_body` by calling `hyper_body_new`. /// /// This takes ownership of the `hyper_body *`, you must not use it or /// free it after setting it on the request. fn hyper_request_set_body(req: *mut hyper_request, body: *mut hyper_body) -> hyper_code { let body = non_null!(Box::from_raw(body) ?= hyper_code::HYPERE_INVALID_ARG); let req = non_null!(&mut *req ?= hyper_code::HYPERE_INVALID_ARG); *req.0.body_mut() = body.0; hyper_code::HYPERE_OK } } ffi_fn! { /// Set an informational (1xx) response callback. /// /// The callback is called each time hyper receives an informational (1xx) /// response for this request. /// /// The third argument is an opaque user data pointer, which is passed to /// the callback each time. /// /// The callback is passed the `void *` data pointer, and a /// `hyper_response *` which can be inspected as any other response. The /// body of the response will always be empty. /// /// NOTE: The `hyper_response *` is just borrowed data, and will not /// be valid after the callback finishes. You must copy any data you wish /// to persist. fn hyper_request_on_informational(req: *mut hyper_request, callback: hyper_request_on_informational_callback, data: *mut c_void) -> hyper_code { let ext = OnInformational { func: callback, data: UserDataPointer(data), }; let req = non_null!(&mut *req ?= hyper_code::HYPERE_INVALID_ARG); req.0.extensions_mut().insert(ext); hyper_code::HYPERE_OK } } impl hyper_request { pub(super) fn finalize_request(&mut self) { if let Some(headers) = self.0.extensions_mut().remove::() { *self.0.headers_mut() = headers.headers; self.0.extensions_mut().insert(headers.orig_casing); self.0.extensions_mut().insert(headers.orig_order); } } } // ===== impl hyper_response ===== ffi_fn! { /// Free an HTTP response. /// /// This should be used for any response once it is no longer needed. fn hyper_response_free(resp: *mut hyper_response) { drop(non_null!(Box::from_raw(resp) ?= ())); } } ffi_fn! { /// Get the HTTP-Status code of this response. /// /// It will always be within the range of 100-599. fn hyper_response_status(resp: *const hyper_response) -> u16 { non_null!(&*resp ?= 0).0.status().as_u16() } } ffi_fn! { /// Get a pointer to the reason-phrase of this response. /// /// This buffer is not null-terminated. /// /// This buffer is owned by the response, and should not be used after /// the response has been freed. /// /// Use `hyper_response_reason_phrase_len()` to get the length of this /// buffer. fn hyper_response_reason_phrase(resp: *const hyper_response) -> *const u8 { non_null!(&*resp ?= std::ptr::null()).reason_phrase().as_ptr() } ?= std::ptr::null() } ffi_fn! { /// Get the length of the reason-phrase of this response. /// /// Use `hyper_response_reason_phrase()` to get the buffer pointer. fn hyper_response_reason_phrase_len(resp: *const hyper_response) -> size_t { non_null!(&*resp ?= 0).reason_phrase().len() } } ffi_fn! { /// Get the HTTP version used by this response. /// /// The returned value could be: /// /// - `HYPER_HTTP_VERSION_1_0` /// - `HYPER_HTTP_VERSION_1_1` /// - `HYPER_HTTP_VERSION_2` /// - `HYPER_HTTP_VERSION_NONE` if newer (or older). fn hyper_response_version(resp: *const hyper_response) -> c_int { use http::Version; match non_null!(&*resp ?= 0).0.version() { Version::HTTP_10 => super::HYPER_HTTP_VERSION_1_0, Version::HTTP_11 => super::HYPER_HTTP_VERSION_1_1, Version::HTTP_2 => super::HYPER_HTTP_VERSION_2, _ => super::HYPER_HTTP_VERSION_NONE, } } } ffi_fn! { /// Gets a reference to the HTTP headers of this response. /// /// This is not an owned reference, so it should not be accessed after the /// `hyper_response` has been freed. fn hyper_response_headers(resp: *mut hyper_response) -> *mut hyper_headers { hyper_headers::get_or_default(unsafe { &mut *resp }.0.extensions_mut()) } ?= std::ptr::null_mut() } ffi_fn! { /// Take ownership of the body of this response. /// /// It is safe to free the response even after taking ownership of its body. /// /// To avoid a memory leak, the body must eventually be consumed by /// `hyper_body_free`, `hyper_body_foreach`, or `hyper_request_set_body`. fn hyper_response_body(resp: *mut hyper_response) -> *mut hyper_body { let body = std::mem::replace(non_null!(&mut *resp ?= std::ptr::null_mut()).0.body_mut(), IncomingBody::empty()); Box::into_raw(Box::new(hyper_body(body))) } ?= std::ptr::null_mut() } impl hyper_response { pub(super) fn wrap(mut resp: Response) -> hyper_response { let headers = std::mem::take(resp.headers_mut()); let orig_casing = resp .extensions_mut() .remove::() .unwrap_or_else(HeaderCaseMap::default); let orig_order = resp .extensions_mut() .remove::() .unwrap_or_else(OriginalHeaderOrder::default); resp.extensions_mut().insert(hyper_headers { headers, orig_casing, orig_order, }); hyper_response(resp) } fn reason_phrase(&self) -> &[u8] { if let Some(reason) = self.0.extensions().get::() { return reason.as_bytes(); } if let Some(reason) = self.0.status().canonical_reason() { return reason.as_bytes(); } &[] } } unsafe impl AsTaskType for hyper_response { fn as_task_type(&self) -> hyper_task_return_type { hyper_task_return_type::HYPER_TASK_RESPONSE } } // ===== impl Headers ===== type hyper_headers_foreach_callback = extern "C" fn(*mut c_void, *const u8, size_t, *const u8, size_t) -> c_int; impl hyper_headers { pub(super) fn get_or_default(ext: &mut http::Extensions) -> &mut hyper_headers { if let None = ext.get_mut::() { ext.insert(hyper_headers::default()); } ext.get_mut::().unwrap() } } ffi_fn! { /// Iterates the headers passing each name and value pair to the callback. /// /// The `userdata` pointer is also passed to the callback. /// /// The callback should return `HYPER_ITER_CONTINUE` to keep iterating, or /// `HYPER_ITER_BREAK` to stop. fn hyper_headers_foreach(headers: *const hyper_headers, func: hyper_headers_foreach_callback, userdata: *mut c_void) { let headers = non_null!(&*headers ?= ()); // For each header name/value pair, there may be a value in the casemap // that corresponds to the HeaderValue. So, we iterator all the keys, // and for each one, try to pair the originally cased name with the value. // // TODO: consider adding http::HeaderMap::entries() iterator let mut ordered_iter = headers.orig_order.get_in_order().peekable(); if ordered_iter.peek().is_some() { for (name, idx) in ordered_iter { let (name_ptr, name_len) = if let Some(orig_name) = headers.orig_casing.get_all(name).nth(*idx) { (orig_name.as_ref().as_ptr(), orig_name.as_ref().len()) } else { ( name.as_str().as_bytes().as_ptr(), name.as_str().as_bytes().len(), ) }; let val_ptr; let val_len; if let Some(value) = headers.headers.get_all(name).iter().nth(*idx) { val_ptr = value.as_bytes().as_ptr(); val_len = value.as_bytes().len(); } else { // Stop iterating, something has gone wrong. return; } if HYPER_ITER_CONTINUE != func(userdata, name_ptr, name_len, val_ptr, val_len) { return; } } } else { for name in headers.headers.keys() { let mut names = headers.orig_casing.get_all(name); for value in headers.headers.get_all(name) { let (name_ptr, name_len) = if let Some(orig_name) = names.next() { (orig_name.as_ref().as_ptr(), orig_name.as_ref().len()) } else { ( name.as_str().as_bytes().as_ptr(), name.as_str().as_bytes().len(), ) }; let val_ptr = value.as_bytes().as_ptr(); let val_len = value.as_bytes().len(); if HYPER_ITER_CONTINUE != func(userdata, name_ptr, name_len, val_ptr, val_len) { return; } } } } } } ffi_fn! { /// Sets the header with the provided name to the provided value. /// /// This overwrites any previous value set for the header. fn hyper_headers_set(headers: *mut hyper_headers, name: *const u8, name_len: size_t, value: *const u8, value_len: size_t) -> hyper_code { let headers = non_null!(&mut *headers ?= hyper_code::HYPERE_INVALID_ARG); match unsafe { raw_name_value(name, name_len, value, value_len) } { Ok((name, value, orig_name)) => { headers.headers.insert(&name, value); headers.orig_casing.insert(name.clone(), orig_name.clone()); headers.orig_order.insert(name); hyper_code::HYPERE_OK } Err(code) => code, } } } ffi_fn! { /// Adds the provided value to the list of the provided name. /// /// If there were already existing values for the name, this will append the /// new value to the internal list. fn hyper_headers_add(headers: *mut hyper_headers, name: *const u8, name_len: size_t, value: *const u8, value_len: size_t) -> hyper_code { let headers = non_null!(&mut *headers ?= hyper_code::HYPERE_INVALID_ARG); match unsafe { raw_name_value(name, name_len, value, value_len) } { Ok((name, value, orig_name)) => { headers.headers.append(&name, value); headers.orig_casing.append(&name, orig_name.clone()); headers.orig_order.append(name); hyper_code::HYPERE_OK } Err(code) => code, } } } impl Default for hyper_headers { fn default() -> Self { Self { headers: Default::default(), orig_casing: HeaderCaseMap::default(), orig_order: OriginalHeaderOrder::default(), } } } unsafe fn raw_name_value( name: *const u8, name_len: size_t, value: *const u8, value_len: size_t, ) -> Result<(HeaderName, HeaderValue, Bytes), hyper_code> { let name = std::slice::from_raw_parts(name, name_len); let orig_name = Bytes::copy_from_slice(name); let name = match HeaderName::from_bytes(name) { Ok(name) => name, Err(_) => return Err(hyper_code::HYPERE_INVALID_ARG), }; let value = std::slice::from_raw_parts(value, value_len); let value = match HeaderValue::from_bytes(value) { Ok(val) => val, Err(_) => return Err(hyper_code::HYPERE_INVALID_ARG), }; Ok((name, value, orig_name)) } // ===== impl OnInformational ===== impl OnInformational { pub(crate) fn call(&mut self, resp: Response) { let mut resp = hyper_response::wrap(resp); (self.func)(self.data.0, &mut resp); } } #[cfg(test)] mod tests { use super::*; #[test] fn test_headers_foreach_cases_preserved() { let mut headers = hyper_headers::default(); let name1 = b"Set-CookiE"; let value1 = b"a=b"; hyper_headers_add( &mut headers, name1.as_ptr(), name1.len(), value1.as_ptr(), value1.len(), ); let name2 = b"SET-COOKIE"; let value2 = b"c=d"; hyper_headers_add( &mut headers, name2.as_ptr(), name2.len(), value2.as_ptr(), value2.len(), ); let mut vec = Vec::::new(); hyper_headers_foreach(&headers, concat, &mut vec as *mut _ as *mut c_void); assert_eq!(vec, b"Set-CookiE: a=b\r\nSET-COOKIE: c=d\r\n"); extern "C" fn concat( vec: *mut c_void, name: *const u8, name_len: usize, value: *const u8, value_len: usize, ) -> c_int { unsafe { let vec = &mut *(vec as *mut Vec); let name = std::slice::from_raw_parts(name, name_len); let value = std::slice::from_raw_parts(value, value_len); vec.extend(name); vec.extend(b": "); vec.extend(value); vec.extend(b"\r\n"); } HYPER_ITER_CONTINUE } } #[cfg(all(feature = "http1", feature = "ffi"))] #[test] fn test_headers_foreach_order_preserved() { let mut headers = hyper_headers::default(); let name1 = b"Set-CookiE"; let value1 = b"a=b"; hyper_headers_add( &mut headers, name1.as_ptr(), name1.len(), value1.as_ptr(), value1.len(), ); let name2 = b"Content-Encoding"; let value2 = b"gzip"; hyper_headers_add( &mut headers, name2.as_ptr(), name2.len(), value2.as_ptr(), value2.len(), ); let name3 = b"SET-COOKIE"; let value3 = b"c=d"; hyper_headers_add( &mut headers, name3.as_ptr(), name3.len(), value3.as_ptr(), value3.len(), ); let mut vec = Vec::::new(); hyper_headers_foreach(&headers, concat, &mut vec as *mut _ as *mut c_void); println!("{}", std::str::from_utf8(&vec).unwrap()); assert_eq!( vec, b"Set-CookiE: a=b\r\nContent-Encoding: gzip\r\nSET-COOKIE: c=d\r\n" ); extern "C" fn concat( vec: *mut c_void, name: *const u8, name_len: usize, value: *const u8, value_len: usize, ) -> c_int { unsafe { let vec = &mut *(vec as *mut Vec); let name = std::slice::from_raw_parts(name, name_len); let value = std::slice::from_raw_parts(value, value_len); vec.extend(name); vec.extend(b": "); vec.extend(value); vec.extend(b"\r\n"); } HYPER_ITER_CONTINUE } } } hyper-1.5.2/src/ffi/io.rs000064400000000000000000000154401046102023000132740ustar 00000000000000use std::ffi::c_void; use std::pin::Pin; use std::task::{Context, Poll}; use super::task::hyper_context; use crate::ffi::size_t; use crate::rt::{Read, Write}; /// Sentinel value to return from a read or write callback that the operation /// is pending. pub const HYPER_IO_PENDING: size_t = 0xFFFFFFFF; /// Sentinel value to return from a read or write callback that the operation /// has errored. pub const HYPER_IO_ERROR: size_t = 0xFFFFFFFE; type hyper_io_read_callback = extern "C" fn(*mut c_void, *mut hyper_context<'_>, *mut u8, size_t) -> size_t; type hyper_io_write_callback = extern "C" fn(*mut c_void, *mut hyper_context<'_>, *const u8, size_t) -> size_t; /// A read/write handle for a specific connection. /// /// This owns a specific TCP or TLS connection for the lifetime of /// that connection. It contains a read and write callback, as well as a /// void *userdata. Typically the userdata will point to a struct /// containing a file descriptor and a TLS context. /// /// Methods: /// /// - hyper_io_new: Create a new IO type used to represent a transport. /// - hyper_io_set_read: Set the read function for this IO transport. /// - hyper_io_set_write: Set the write function for this IO transport. /// - hyper_io_set_userdata: Set the user data pointer for this IO to some value. /// - hyper_io_free: Free an IO handle. pub struct hyper_io { read: hyper_io_read_callback, write: hyper_io_write_callback, userdata: *mut c_void, } ffi_fn! { /// Create a new IO type used to represent a transport. /// /// The read and write functions of this transport should be set with /// `hyper_io_set_read` and `hyper_io_set_write`. /// /// It is expected that the underlying transport is non-blocking. When /// a read or write callback can't make progress because there is no /// data available yet, it should use the `hyper_waker` mechanism to /// arrange to be called again when data is available. /// /// To avoid a memory leak, the IO handle must eventually be consumed by /// `hyper_io_free` or `hyper_clientconn_handshake`. fn hyper_io_new() -> *mut hyper_io { Box::into_raw(Box::new(hyper_io { read: read_noop, write: write_noop, userdata: std::ptr::null_mut(), })) } ?= std::ptr::null_mut() } ffi_fn! { /// Free an IO handle. /// /// This should only be used if the request isn't consumed by /// `hyper_clientconn_handshake`. fn hyper_io_free(io: *mut hyper_io) { drop(non_null!(Box::from_raw(io) ?= ())); } } ffi_fn! { /// Set the user data pointer for this IO to some value. /// /// This value is passed as an argument to the read and write callbacks. fn hyper_io_set_userdata(io: *mut hyper_io, data: *mut c_void) { non_null!(&mut *io ?= ()).userdata = data; } } ffi_fn! { /// Set the read function for this IO transport. /// /// Data that is read from the transport should be put in the `buf` pointer, /// up to `buf_len` bytes. The number of bytes read should be the return value. /// /// It is undefined behavior to try to access the bytes in the `buf` pointer, /// unless you have already written them yourself. It is also undefined behavior /// to return that more bytes have been written than actually set on the `buf`. /// /// If there is no data currently available, the callback should create a /// `hyper_waker` from its `hyper_context` argument and register the waker /// with whatever polling mechanism is used to signal when data is available /// later on. The return value should be `HYPER_IO_PENDING`. See the /// documentation for `hyper_waker`. /// /// If there is an irrecoverable error reading data, then `HYPER_IO_ERROR` /// should be the return value. fn hyper_io_set_read(io: *mut hyper_io, func: hyper_io_read_callback) { non_null!(&mut *io ?= ()).read = func; } } ffi_fn! { /// Set the write function for this IO transport. /// /// Data from the `buf` pointer should be written to the transport, up to /// `buf_len` bytes. The number of bytes written should be the return value. /// /// If there is no data currently available, the callback should create a /// `hyper_waker` from its `hyper_context` argument and register the waker /// with whatever polling mechanism is used to signal when data is available /// later on. The return value should be `HYPER_IO_PENDING`. See the documentation /// for `hyper_waker`. /// /// If there is an irrecoverable error reading data, then `HYPER_IO_ERROR` /// should be the return value. fn hyper_io_set_write(io: *mut hyper_io, func: hyper_io_write_callback) { non_null!(&mut *io ?= ()).write = func; } } /// cbindgen:ignore extern "C" fn read_noop( _userdata: *mut c_void, _: *mut hyper_context<'_>, _buf: *mut u8, _buf_len: size_t, ) -> size_t { 0 } /// cbindgen:ignore extern "C" fn write_noop( _userdata: *mut c_void, _: *mut hyper_context<'_>, _buf: *const u8, _buf_len: size_t, ) -> size_t { 0 } impl Read for hyper_io { fn poll_read( self: Pin<&mut Self>, cx: &mut Context<'_>, mut buf: crate::rt::ReadBufCursor<'_>, ) -> Poll> { let buf_ptr = unsafe { buf.as_mut() }.as_mut_ptr() as *mut u8; let buf_len = buf.remaining(); match (self.read)(self.userdata, hyper_context::wrap(cx), buf_ptr, buf_len) { HYPER_IO_PENDING => Poll::Pending, HYPER_IO_ERROR => Poll::Ready(Err(std::io::Error::new( std::io::ErrorKind::Other, "io error", ))), ok => { // We have to trust that the user's read callback actually // filled in that many bytes... :( unsafe { buf.advance(ok) }; Poll::Ready(Ok(())) } } } } impl Write for hyper_io { fn poll_write( self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8], ) -> Poll> { let buf_ptr = buf.as_ptr(); let buf_len = buf.len(); match (self.write)(self.userdata, hyper_context::wrap(cx), buf_ptr, buf_len) { HYPER_IO_PENDING => Poll::Pending, HYPER_IO_ERROR => Poll::Ready(Err(std::io::Error::new( std::io::ErrorKind::Other, "io error", ))), ok => Poll::Ready(Ok(ok)), } } fn poll_flush(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll> { Poll::Ready(Ok(())) } fn poll_shutdown(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll> { Poll::Ready(Ok(())) } } unsafe impl Send for hyper_io {} unsafe impl Sync for hyper_io {} hyper-1.5.2/src/ffi/macros.rs000064400000000000000000000033651046102023000141540ustar 00000000000000macro_rules! ffi_fn { ($(#[$doc:meta])* fn $name:ident($($arg:ident: $arg_ty:ty),*) -> $ret:ty $body:block ?= $default:expr) => { $(#[$doc])* #[no_mangle] pub extern fn $name($($arg: $arg_ty),*) -> $ret { use std::panic::{self, AssertUnwindSafe}; match panic::catch_unwind(AssertUnwindSafe(move || $body)) { Ok(v) => v, Err(_) => { $default } } } }; ($(#[$doc:meta])* fn $name:ident($($arg:ident: $arg_ty:ty),*) -> $ret:ty $body:block) => { ffi_fn!($(#[$doc])* fn $name($($arg: $arg_ty),*) -> $ret $body ?= { eprintln!("panic unwind caught, aborting"); std::process::abort() }); }; ($(#[$doc:meta])* fn $name:ident($($arg:ident: $arg_ty:ty),*) $body:block ?= $default:expr) => { ffi_fn!($(#[$doc])* fn $name($($arg: $arg_ty),*) -> () $body ?= $default); }; ($(#[$doc:meta])* fn $name:ident($($arg:ident: $arg_ty:ty),*) $body:block) => { ffi_fn!($(#[$doc])* fn $name($($arg: $arg_ty),*) -> () $body); }; } macro_rules! non_null { ($ptr:ident, $eval:expr, $err:expr) => {{ debug_assert!(!$ptr.is_null(), "{:?} must not be null", stringify!($ptr)); if $ptr.is_null() { return $err; } unsafe { $eval } }}; (&*$ptr:ident ?= $err:expr) => {{ non_null!($ptr, &*$ptr, $err) }}; (&mut *$ptr:ident ?= $err:expr) => {{ non_null!($ptr, &mut *$ptr, $err) }}; (Box::from_raw($ptr:ident) ?= $err:expr) => {{ non_null!($ptr, Box::from_raw($ptr), $err) }}; (Arc::from_raw($ptr:ident) ?= $err:expr) => {{ non_null!($ptr, Arc::from_raw($ptr), $err) }}; } hyper-1.5.2/src/ffi/mod.rs000064400000000000000000000062531046102023000134460ustar 00000000000000// We have a lot of c-types in here, stop warning about their names! #![allow(non_camel_case_types)] // fmt::Debug isn't helpful on FFI types #![allow(missing_debug_implementations)] // unreachable_pub warns `#[no_mangle] pub extern fn` in private mod. #![allow(unreachable_pub)] //! # hyper C API //! //! This part of the documentation describes the C API for hyper. That is, how //! to *use* the hyper library in C code. This is **not** a regular Rust //! module, and thus it is not accessible in Rust. //! //! ## Unstable //! //! The C API of hyper is currently **unstable**, which means it's not part of //! the semver contract as the rest of the Rust API is. Because of that, it's //! only accessible if `--cfg hyper_unstable_ffi` is passed to `rustc` when //! compiling. The easiest way to do that is setting the `RUSTFLAGS` //! environment variable. //! //! ## Building //! //! The C API is part of the Rust library, but isn't compiled by default. Using //! `cargo`, staring with `1.64.0`, it can be compiled with the following command: //! //! ```notrust //! RUSTFLAGS="--cfg hyper_unstable_ffi" cargo rustc --crate-type cdylib --features client,http1,http2,ffi //! ``` // We may eventually allow the FFI to be enabled without `client` or `http1`, // that is why we don't auto enable them as `ffi = ["client", "http1"]` in // the `Cargo.toml`. // // But for now, give a clear message that this compile error is expected. #[cfg(not(all(feature = "client", feature = "http1")))] compile_error!("The `ffi` feature currently requires the `client` and `http1` features."); #[cfg(not(hyper_unstable_ffi))] compile_error!( "\ The `ffi` feature is unstable, and requires the \ `RUSTFLAGS='--cfg hyper_unstable_ffi'` environment variable to be set.\ " ); #[macro_use] mod macros; mod body; mod client; mod error; mod http_types; mod io; mod task; pub use self::body::*; pub use self::client::*; pub use self::error::*; pub use self::http_types::*; pub use self::io::*; pub use self::task::*; /// Return in iter functions to continue iterating. pub const HYPER_ITER_CONTINUE: std::ffi::c_int = 0; /// Return in iter functions to stop iterating. #[allow(unused)] pub const HYPER_ITER_BREAK: std::ffi::c_int = 1; /// An HTTP Version that is unspecified. pub const HYPER_HTTP_VERSION_NONE: std::ffi::c_int = 0; /// The HTTP/1.0 version. pub const HYPER_HTTP_VERSION_1_0: std::ffi::c_int = 10; /// The HTTP/1.1 version. pub const HYPER_HTTP_VERSION_1_1: std::ffi::c_int = 11; /// The HTTP/2 version. pub const HYPER_HTTP_VERSION_2: std::ffi::c_int = 20; #[derive(Clone)] struct UserDataPointer(*mut std::ffi::c_void); // We don't actually know anything about this pointer, it's up to the user // to do the right thing. unsafe impl Send for UserDataPointer {} unsafe impl Sync for UserDataPointer {} /// cbindgen:ignore static VERSION_CSTR: &str = concat!(env!("CARGO_PKG_VERSION"), "\0"); // `core::ffi::c_size_t` is a nightly-only experimental API. // https://github.com/rust-lang/rust/issues/88345 type size_t = usize; ffi_fn! { /// Returns a static ASCII (null terminated) string of the hyper version. fn hyper_version() -> *const std::ffi::c_char { VERSION_CSTR.as_ptr() as _ } ?= std::ptr::null() } hyper-1.5.2/src/ffi/task.rs000064400000000000000000000450401046102023000136260ustar 00000000000000use std::ffi::{c_int, c_void}; use std::future::Future; use std::pin::Pin; use std::ptr; use std::sync::{ atomic::{AtomicBool, Ordering}, Arc, Mutex, Weak, }; use std::task::{Context, Poll}; use futures_util::stream::{FuturesUnordered, Stream}; use super::error::hyper_code; use super::UserDataPointer; type BoxFuture = Pin + Send>>; type BoxAny = Box; /// Return in a poll function to indicate it was ready. pub const HYPER_POLL_READY: c_int = 0; /// Return in a poll function to indicate it is still pending. /// /// The passed in `hyper_waker` should be registered to wake up the task at /// some later point. pub const HYPER_POLL_PENDING: c_int = 1; /// Return in a poll function indicate an error. pub const HYPER_POLL_ERROR: c_int = 3; /// A task executor for `hyper_task`s. /// /// A task is a unit of work that may be blocked on IO, and can be polled to /// make progress on that work. /// /// An executor can hold many tasks, included from unrelated HTTP connections. /// An executor is single threaded. Typically you might have one executor per /// thread. Or, for simplicity, you may choose one executor per connection. /// /// Progress on tasks happens only when `hyper_executor_poll` is called, and only /// on tasks whose corresponding `hyper_waker` has been called to indicate they /// are ready to make progress (for instance, because the OS has indicated there /// is more data to read or more buffer space available to write). /// /// Deadlock potential: `hyper_executor_poll` must not be called from within a task's /// callback. Doing so will result in a deadlock. /// /// Methods: /// /// - hyper_executor_new: Creates a new task executor. /// - hyper_executor_push: Push a task onto the executor. /// - hyper_executor_poll: Polls the executor, trying to make progress on any tasks that have notified that they are ready again. /// - hyper_executor_free: Frees an executor and any incomplete tasks still part of it. pub struct hyper_executor { /// The executor of all task futures. /// /// There should never be contention on the mutex, as it is only locked /// to drive the futures. However, we cannot guarantee proper usage from /// `hyper_executor_poll()`, which in C could potentially be called inside /// one of the stored futures. The mutex isn't re-entrant, so doing so /// would result in a deadlock, but that's better than data corruption. driver: Mutex>, /// The queue of futures that need to be pushed into the `driver`. /// /// This is has a separate mutex since `spawn` could be called from inside /// a future, which would mean the driver's mutex is already locked. spawn_queue: Mutex>, /// This is used to track when a future calls `wake` while we are within /// `hyper_executor::poll_next`. is_woken: Arc, } #[derive(Clone)] pub(crate) struct WeakExec(Weak); struct ExecWaker(AtomicBool); /// An async task. /// /// A task represents a chunk of work that will eventually yield exactly one /// `hyper_task_value`. Tasks are pushed onto an executor, and that executor is /// responsible for calling the necessary private functions on the task to make /// progress. In most cases those private functions will eventually cause read /// or write callbacks on a `hyper_io` object to be called. /// /// Tasks are created by various functions: /// /// - hyper_clientconn_handshake: Creates an HTTP client handshake task. /// - hyper_clientconn_send: Creates a task to send a request on the client connection. /// - hyper_body_data: Creates a task that will poll a response body for the next buffer of data. /// - hyper_body_foreach: Creates a task to execute the callback with each body chunk received. /// /// Tasks then have a userdata associated with them using `hyper_task_set_userdata``. This /// is important, for instance, to associate a request id with a given request. When multiple /// tasks are running on the same executor, this allows distinguishing tasks for different /// requests. /// /// Tasks are then pushed onto an executor, and eventually yielded from hyper_executor_poll: /// /// - hyper_executor_push: Push a task onto the executor. /// - hyper_executor_poll: Polls the executor, trying to make progress on any tasks that have notified that they are ready again. /// /// Once a task is yielded from poll, retrieve its userdata, check its type, /// and extract its value. This will require a case from void* to the appropriate type. /// /// Methods on hyper_task: /// /// - hyper_task_type: Query the return type of this task. /// - hyper_task_value: Takes the output value of this task. /// - hyper_task_set_userdata: Set a user data pointer to be associated with this task. /// - hyper_task_userdata: Retrieve the userdata that has been set via hyper_task_set_userdata. /// - hyper_task_free: Free a task. pub struct hyper_task { future: BoxFuture, output: Option, userdata: UserDataPointer, } struct TaskFuture { task: Option>, } /// An async context for a task that contains the related waker. /// /// This is provided to `hyper_io`'s read and write callbacks. Currently /// its only purpose is to provide access to the waker. See `hyper_waker`. /// /// Corresponding Rust type: pub struct hyper_context<'a>(Context<'a>); /// A waker that is saved and used to waken a pending task. /// /// This is provided to `hyper_io`'s read and write callbacks via `hyper_context` /// and `hyper_context_waker`. /// /// When nonblocking I/O in one of those callbacks can't make progress (returns /// `EAGAIN` or `EWOULDBLOCK`), the callback has to return to avoid blocking the /// executor. But it also has to arrange to get called in the future when more /// data is available. That's the role of the async context and the waker. The /// waker can be used to tell the executor "this task is ready to make progress." /// /// The read or write callback, upon finding it can't make progress, must get a /// waker from the context (`hyper_context_waker`), arrange for that waker to be /// called in the future, and then return `HYPER_POLL_PENDING`. /// /// The arrangements for the waker to be called in the future are up to the /// application, but usually it will involve one big `select(2)` loop that checks which /// FDs are ready, and a correspondence between FDs and waker objects. For each /// FD that is ready, the corresponding waker must be called. Then `hyper_executor_poll` /// must be called. That will cause the executor to attempt to make progress on each /// woken task. /// /// Corresponding Rust type: pub struct hyper_waker { waker: std::task::Waker, } /// A descriptor for what type a `hyper_task` value is. #[repr(C)] pub enum hyper_task_return_type { /// The value of this task is null (does not imply an error). HYPER_TASK_EMPTY, /// The value of this task is `hyper_error *`. HYPER_TASK_ERROR, /// The value of this task is `hyper_clientconn *`. HYPER_TASK_CLIENTCONN, /// The value of this task is `hyper_response *`. HYPER_TASK_RESPONSE, /// The value of this task is `hyper_buf *`. HYPER_TASK_BUF, } pub(crate) unsafe trait AsTaskType { fn as_task_type(&self) -> hyper_task_return_type; } pub(crate) trait IntoDynTaskType { fn into_dyn_task_type(self) -> BoxAny; } // ===== impl hyper_executor ===== impl hyper_executor { fn new() -> Arc { Arc::new(hyper_executor { driver: Mutex::new(FuturesUnordered::new()), spawn_queue: Mutex::new(Vec::new()), is_woken: Arc::new(ExecWaker(AtomicBool::new(false))), }) } pub(crate) fn downgrade(exec: &Arc) -> WeakExec { WeakExec(Arc::downgrade(exec)) } fn spawn(&self, task: Box) { self.spawn_queue .lock() .unwrap() .push(TaskFuture { task: Some(task) }); } fn poll_next(&self) -> Option> { // Drain the queue first. self.drain_queue(); let waker = futures_util::task::waker_ref(&self.is_woken); let mut cx = Context::from_waker(&waker); loop { { // Scope the lock on the driver to ensure it is dropped before // calling drain_queue below. let mut driver = self.driver.lock().unwrap(); match Pin::new(&mut *driver).poll_next(&mut cx) { Poll::Ready(val) => return val, Poll::Pending => {} }; } // poll_next returned Pending. // Check if any of the pending tasks tried to spawn // some new tasks. If so, drain into the driver and loop. if self.drain_queue() { continue; } // If the driver called `wake` while we were polling, // we should poll again immediately! if self.is_woken.0.swap(false, Ordering::SeqCst) { continue; } return None; } } /// drain_queue locks both self.spawn_queue and self.driver, so it requires /// that neither of them be locked already. fn drain_queue(&self) -> bool { let mut queue = self.spawn_queue.lock().unwrap(); if queue.is_empty() { return false; } let driver = self.driver.lock().unwrap(); for task in queue.drain(..) { driver.push(task); } true } } impl futures_util::task::ArcWake for ExecWaker { fn wake_by_ref(me: &Arc) { me.0.store(true, Ordering::SeqCst); } } // ===== impl WeakExec ===== impl WeakExec { pub(crate) fn new() -> Self { WeakExec(Weak::new()) } } impl crate::rt::Executor for WeakExec where F: Future + Send + 'static, F::Output: Send + Sync + AsTaskType, { fn execute(&self, fut: F) { if let Some(exec) = self.0.upgrade() { exec.spawn(hyper_task::boxed(fut)); } } } ffi_fn! { /// Creates a new task executor. /// /// To avoid a memory leak, the executor must eventually be consumed by /// `hyper_executor_free`. fn hyper_executor_new() -> *const hyper_executor { Arc::into_raw(hyper_executor::new()) } ?= ptr::null() } ffi_fn! { /// Frees an executor and any incomplete tasks still part of it. /// /// This should be used for any executor once it is no longer needed. fn hyper_executor_free(exec: *const hyper_executor) { drop(non_null!(Arc::from_raw(exec) ?= ())); } } ffi_fn! { /// Push a task onto the executor. /// /// The executor takes ownership of the task, which must not be accessed /// again. /// /// Ownership of the task will eventually be returned to the user from /// `hyper_executor_poll`. /// /// To distinguish multiple tasks running on the same executor, use /// hyper_task_set_userdata. fn hyper_executor_push(exec: *const hyper_executor, task: *mut hyper_task) -> hyper_code { let exec = non_null!(&*exec ?= hyper_code::HYPERE_INVALID_ARG); let task = non_null!(Box::from_raw(task) ?= hyper_code::HYPERE_INVALID_ARG); exec.spawn(task); hyper_code::HYPERE_OK } } ffi_fn! { /// Polls the executor, trying to make progress on any tasks that can do so. /// /// If any task from the executor is ready, returns one of them. The way /// tasks signal being finished is internal to Hyper. The order in which tasks /// are returned is not guaranteed. Use userdata to distinguish between tasks. /// /// To avoid a memory leak, the task must eventually be consumed by /// `hyper_task_free`. /// /// If there are no ready tasks, this returns `NULL`. fn hyper_executor_poll(exec: *const hyper_executor) -> *mut hyper_task { let exec = non_null!(&*exec ?= ptr::null_mut()); match exec.poll_next() { Some(task) => Box::into_raw(task), None => ptr::null_mut(), } } ?= ptr::null_mut() } // ===== impl hyper_task ===== impl hyper_task { pub(crate) fn boxed(fut: F) -> Box where F: Future + Send + 'static, F::Output: IntoDynTaskType + Send + Sync + 'static, { Box::new(hyper_task { future: Box::pin(async move { fut.await.into_dyn_task_type() }), output: None, userdata: UserDataPointer(ptr::null_mut()), }) } fn output_type(&self) -> hyper_task_return_type { match self.output { None => hyper_task_return_type::HYPER_TASK_EMPTY, Some(ref val) => val.as_task_type(), } } } impl Future for TaskFuture { type Output = Box; fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { match Pin::new(&mut self.task.as_mut().unwrap().future).poll(cx) { Poll::Ready(val) => { let mut task = self.task.take().unwrap(); task.output = Some(val); Poll::Ready(task) } Poll::Pending => Poll::Pending, } } } ffi_fn! { /// Free a task. /// /// This should only be used if the task isn't consumed by /// `hyper_clientconn_handshake` or taken ownership of by /// `hyper_executor_push`. fn hyper_task_free(task: *mut hyper_task) { drop(non_null!(Box::from_raw(task) ?= ())); } } ffi_fn! { /// Takes the output value of this task. /// /// This must only be called once polling the task on an executor has finished /// this task. /// /// Use `hyper_task_type` to determine the type of the `void *` return value. /// /// To avoid a memory leak, a non-empty return value must eventually be /// consumed by a function appropriate for its type, one of /// `hyper_error_free`, `hyper_clientconn_free`, `hyper_response_free`, or /// `hyper_buf_free`. fn hyper_task_value(task: *mut hyper_task) -> *mut c_void { let task = non_null!(&mut *task ?= ptr::null_mut()); if let Some(val) = task.output.take() { let p = Box::into_raw(val) as *mut c_void; // protect from returning fake pointers to empty types if p == std::ptr::NonNull::::dangling().as_ptr() { ptr::null_mut() } else { p } } else { ptr::null_mut() } } ?= ptr::null_mut() } ffi_fn! { /// Query the return type of this task. fn hyper_task_type(task: *mut hyper_task) -> hyper_task_return_type { // instead of blowing up spectacularly, just say this null task // doesn't have a value to retrieve. non_null!(&*task ?= hyper_task_return_type::HYPER_TASK_EMPTY).output_type() } } ffi_fn! { /// Set a user data pointer to be associated with this task. /// /// This value will be passed to task callbacks, and can be checked later /// with `hyper_task_userdata`. /// /// This is useful for telling apart tasks for different requests that are /// running on the same executor. fn hyper_task_set_userdata(task: *mut hyper_task, userdata: *mut c_void) { if task.is_null() { return; } unsafe { (*task).userdata = UserDataPointer(userdata) }; } } ffi_fn! { /// Retrieve the userdata that has been set via `hyper_task_set_userdata`. fn hyper_task_userdata(task: *mut hyper_task) -> *mut c_void { non_null!(&*task ?= ptr::null_mut()).userdata.0 } ?= ptr::null_mut() } // ===== impl AsTaskType ===== unsafe impl AsTaskType for () { fn as_task_type(&self) -> hyper_task_return_type { hyper_task_return_type::HYPER_TASK_EMPTY } } unsafe impl AsTaskType for crate::Error { fn as_task_type(&self) -> hyper_task_return_type { hyper_task_return_type::HYPER_TASK_ERROR } } impl IntoDynTaskType for T where T: AsTaskType + Send + Sync + 'static, { fn into_dyn_task_type(self) -> BoxAny { Box::new(self) } } impl IntoDynTaskType for crate::Result where T: IntoDynTaskType + Send + Sync + 'static, { fn into_dyn_task_type(self) -> BoxAny { match self { Ok(val) => val.into_dyn_task_type(), Err(err) => Box::new(err), } } } impl IntoDynTaskType for Option where T: IntoDynTaskType + Send + Sync + 'static, { fn into_dyn_task_type(self) -> BoxAny { match self { Some(val) => val.into_dyn_task_type(), None => ().into_dyn_task_type(), } } } // ===== impl hyper_context ===== impl hyper_context<'_> { pub(crate) fn wrap<'a, 'b>(cx: &'a mut Context<'b>) -> &'a mut hyper_context<'b> { // A struct with only one field has the same layout as that field. unsafe { std::mem::transmute::<&mut Context<'_>, &mut hyper_context<'_>>(cx) } } } ffi_fn! { /// Creates a waker associated with the task context. /// /// The waker can be used to inform the task's executor that the task is /// ready to make progress (using `hyper_waker_wake``). /// /// Typically this only needs to be called once, but it can be called /// multiple times, returning a new waker each time. /// /// To avoid a memory leak, the waker must eventually be consumed by /// `hyper_waker_free` or `hyper_waker_wake`. fn hyper_context_waker(cx: *mut hyper_context<'_>) -> *mut hyper_waker { let waker = non_null!(&mut *cx ?= ptr::null_mut()).0.waker().clone(); Box::into_raw(Box::new(hyper_waker { waker })) } ?= ptr::null_mut() } // ===== impl hyper_waker ===== ffi_fn! { /// Free a waker. /// /// This should only be used if the request isn't consumed by /// `hyper_waker_wake`. fn hyper_waker_free(waker: *mut hyper_waker) { drop(non_null!(Box::from_raw(waker) ?= ())); } } ffi_fn! { /// Wake up the task associated with a waker. /// /// This does not do work towards associated task. Instead, it signals /// to the task's executor that the task is ready to make progress. The /// application is responsible for calling hyper_executor_poll, which /// will in turn do work on all tasks that are ready to make progress. /// /// NOTE: This consumes the waker. You should not use or free the waker afterwards. fn hyper_waker_wake(waker: *mut hyper_waker) { let waker = non_null!(Box::from_raw(waker) ?= ()); waker.waker.wake(); } } hyper-1.5.2/src/headers.rs000064400000000000000000000114521046102023000135330ustar 00000000000000#[cfg(all(feature = "client", feature = "http1"))] use bytes::BytesMut; use http::header::HeaderValue; #[cfg(all(feature = "http2", feature = "client"))] use http::Method; #[cfg(any(feature = "client", all(feature = "server", feature = "http2")))] use http::{ header::{ValueIter, CONTENT_LENGTH}, HeaderMap, }; #[cfg(feature = "http1")] pub(super) fn connection_keep_alive(value: &HeaderValue) -> bool { connection_has(value, "keep-alive") } #[cfg(feature = "http1")] pub(super) fn connection_close(value: &HeaderValue) -> bool { connection_has(value, "close") } #[cfg(feature = "http1")] fn connection_has(value: &HeaderValue, needle: &str) -> bool { if let Ok(s) = value.to_str() { for val in s.split(',') { if val.trim().eq_ignore_ascii_case(needle) { return true; } } } false } #[cfg(all(feature = "http1", feature = "server"))] pub(super) fn content_length_parse(value: &HeaderValue) -> Option { from_digits(value.as_bytes()) } #[cfg(any(feature = "client", all(feature = "server", feature = "http2")))] pub(super) fn content_length_parse_all(headers: &HeaderMap) -> Option { content_length_parse_all_values(headers.get_all(CONTENT_LENGTH).into_iter()) } #[cfg(any(feature = "client", all(feature = "server", feature = "http2")))] pub(super) fn content_length_parse_all_values(values: ValueIter<'_, HeaderValue>) -> Option { // If multiple Content-Length headers were sent, everything can still // be alright if they all contain the same value, and all parse // correctly. If not, then it's an error. let mut content_length: Option = None; for h in values { if let Ok(line) = h.to_str() { for v in line.split(',') { if let Some(n) = from_digits(v.trim().as_bytes()) { if content_length.is_none() { content_length = Some(n) } else if content_length != Some(n) { return None; } } else { return None; } } } else { return None; } } content_length } fn from_digits(bytes: &[u8]) -> Option { // cannot use FromStr for u64, since it allows a signed prefix let mut result = 0u64; const RADIX: u64 = 10; if bytes.is_empty() { return None; } for &b in bytes { // can't use char::to_digit, since we haven't verified these bytes // are utf-8. match b { b'0'..=b'9' => { result = result.checked_mul(RADIX)?; result = result.checked_add((b - b'0') as u64)?; } _ => { // not a DIGIT, get outta here! return None; } } } Some(result) } #[cfg(all(feature = "http2", feature = "client"))] pub(super) fn method_has_defined_payload_semantics(method: &Method) -> bool { !matches!( *method, Method::GET | Method::HEAD | Method::DELETE | Method::CONNECT ) } #[cfg(feature = "http2")] pub(super) fn set_content_length_if_missing(headers: &mut HeaderMap, len: u64) { headers .entry(CONTENT_LENGTH) .or_insert_with(|| HeaderValue::from(len)); } #[cfg(all(feature = "client", feature = "http1"))] pub(super) fn transfer_encoding_is_chunked(headers: &HeaderMap) -> bool { is_chunked(headers.get_all(http::header::TRANSFER_ENCODING).into_iter()) } #[cfg(all(feature = "client", feature = "http1"))] pub(super) fn is_chunked(mut encodings: ValueIter<'_, HeaderValue>) -> bool { // chunked must always be the last encoding, according to spec if let Some(line) = encodings.next_back() { return is_chunked_(line); } false } #[cfg(feature = "http1")] pub(super) fn is_chunked_(value: &HeaderValue) -> bool { // chunked must always be the last encoding, according to spec if let Ok(s) = value.to_str() { if let Some(encoding) = s.rsplit(',').next() { return encoding.trim().eq_ignore_ascii_case("chunked"); } } false } #[cfg(all(feature = "client", feature = "http1"))] pub(super) fn add_chunked(mut entry: http::header::OccupiedEntry<'_, HeaderValue>) { const CHUNKED: &str = "chunked"; if let Some(line) = entry.iter_mut().next_back() { // + 2 for ", " let new_cap = line.as_bytes().len() + CHUNKED.len() + 2; let mut buf = BytesMut::with_capacity(new_cap); buf.extend_from_slice(line.as_bytes()); buf.extend_from_slice(b", "); buf.extend_from_slice(CHUNKED.as_bytes()); *line = HeaderValue::from_maybe_shared(buf.freeze()) .expect("original header value plus ascii is valid"); return; } entry.insert(HeaderValue::from_static(CHUNKED)); } hyper-1.5.2/src/lib.rs000064400000000000000000000105131046102023000126630ustar 00000000000000#![deny(missing_docs)] #![deny(missing_debug_implementations)] #![cfg_attr(test, deny(rust_2018_idioms))] #![cfg_attr(all(test, feature = "full"), deny(unreachable_pub))] #![cfg_attr(all(test, feature = "full"), deny(warnings))] #![cfg_attr(all(test, feature = "nightly"), feature(test))] #![cfg_attr(docsrs, feature(doc_cfg))] //! # hyper //! //! hyper is a **fast** and **correct** HTTP implementation written in and for Rust. //! //! ## Features //! //! - HTTP/1 and HTTP/2 //! - Asynchronous design //! - Leading in performance //! - Tested and **correct** //! - Extensive production use //! - [Client](client/index.html) and [Server](server/index.html) APIs //! //! If just starting out, **check out the [Guides](https://hyper.rs/guides/1/) //! first.** //! //! ## "Low-level" //! //! hyper is a lower-level HTTP library, meant to be a building block //! for libraries and applications. //! //! If looking for just a convenient HTTP client, consider the //! [reqwest](https://crates.io/crates/reqwest) crate. //! //! # Optional Features //! //! hyper uses a set of [feature flags] to reduce the amount of compiled code. //! It is possible to just enable certain features over others. By default, //! hyper does not enable any features but allows one to enable a subset for //! their use case. Below is a list of the available feature flags. You may //! also notice above each function, struct and trait there is listed one or //! more feature flags that are required for that item to be used. //! //! If you are new to hyper it is possible to enable the `full` feature flag //! which will enable all public APIs. Beware though that this will pull in //! many extra dependencies that you may not need. //! //! The following optional features are available: //! //! - `http1`: Enables HTTP/1 support. //! - `http2`: Enables HTTP/2 support. //! - `client`: Enables the HTTP `client`. //! - `server`: Enables the HTTP `server`. //! //! [feature flags]: https://doc.rust-lang.org/cargo/reference/manifest.html#the-features-section //! //! ## Unstable Features //! //! hyper includes a set of unstable optional features that can be enabled through the use of a //! feature flag and a [configuration flag]. //! //! The following is a list of feature flags and their corresponding `RUSTFLAG`: //! //! - `ffi`: Enables C API for hyper `hyper_unstable_ffi`. //! - `tracing`: Enables debug logging with `hyper_unstable_tracing`. //! //! For example: //! //! ```notrust //! RUSTFLAGS="--cfg hyper_unstable_tracing" cargo build //! ``` //! //! [configuration flag]: https://doc.rust-lang.org/reference/conditional-compilation.html //! //! # Stability //! //! It's worth talking a bit about the stability of hyper. hyper's API follows //! [SemVer](https://semver.org). Breaking changes will only be introduced in //! major versions, if ever. New additions to the API, such as new types, //! methods, or traits will only be added in minor versions. //! //! Some parts of hyper are documented as NOT being part of the stable API. The //! following is a brief list, you can read more about each one in the relevant //! part of the documentation. //! //! - Downcasting error types from `Error::source()` is not considered stable. //! - Private dependencies use of global variables is not considered stable. //! So, if a dependency uses `log` or `tracing`, hyper doesn't promise it //! will continue to do so. //! - Behavior from default options is not stable. hyper reserves the right to //! add new options that are enabled by default which might alter the //! behavior, for the purposes of protection. It is also possible to _change_ //! what the default options are set to, also in efforts to protect the //! most people possible. #[doc(hidden)] pub use http; #[cfg(all(test, feature = "nightly"))] extern crate test; #[doc(no_inline)] pub use http::{header, HeaderMap, Method, Request, Response, StatusCode, Uri, Version}; pub use crate::error::{Error, Result}; #[macro_use] mod cfg; #[macro_use] mod trace; pub mod body; mod common; mod error; pub mod ext; #[cfg(test)] mod mock; pub mod rt; pub mod service; pub mod upgrade; #[cfg(feature = "ffi")] #[cfg_attr(docsrs, doc(cfg(all(feature = "ffi", hyper_unstable_ffi))))] pub mod ffi; cfg_proto! { mod headers; mod proto; } cfg_feature! { #![feature = "client"] pub mod client; } cfg_feature! { #![feature = "server"] pub mod server; } hyper-1.5.2/src/mock.rs000064400000000000000000000141051046102023000130470ustar 00000000000000// FIXME: re-implement tests with `async/await` /* #[cfg(feature = "runtime")] use std::collections::HashMap; use std::cmp; use std::io::{self, Read, Write}; #[cfg(feature = "runtime")] use std::sync::{Arc, Mutex}; use bytes::Buf; use futures::{Async, Poll}; #[cfg(feature = "runtime")] use futures::Future; use futures::task::{self, Task}; use tokio_io::{AsyncRead, AsyncWrite}; #[cfg(feature = "runtime")] use crate::client::connect::{Connect, Connected, Destination}; #[cfg(feature = "runtime")] pub struct Duplex { inner: Arc>, } #[cfg(feature = "runtime")] struct DuplexInner { handle_read_task: Option, read: AsyncIo, write: AsyncIo, } #[cfg(feature = "runtime")] impl Duplex { pub(crate) fn channel() -> (Duplex, DuplexHandle) { let mut inner = DuplexInner { handle_read_task: None, read: AsyncIo::new_buf(Vec::new(), 0), write: AsyncIo::new_buf(Vec::new(), std::usize::MAX), }; inner.read.park_tasks(true); inner.write.park_tasks(true); let inner = Arc::new(Mutex::new(inner)); let duplex = Duplex { inner: inner.clone(), }; let handle = DuplexHandle { inner: inner, }; (duplex, handle) } } #[cfg(feature = "runtime")] impl Read for Duplex { fn read(&mut self, buf: &mut [u8]) -> io::Result { self.inner.lock().unwrap().read.read(buf) } } #[cfg(feature = "runtime")] impl Write for Duplex { fn write(&mut self, buf: &[u8]) -> io::Result { let mut inner = self.inner.lock().unwrap(); let ret = inner.write.write(buf); if let Some(task) = inner.handle_read_task.take() { trace!("waking DuplexHandle read"); task.notify(); } ret } fn flush(&mut self) -> io::Result<()> { self.inner.lock().unwrap().write.flush() } } #[cfg(feature = "runtime")] impl AsyncRead for Duplex { } #[cfg(feature = "runtime")] impl AsyncWrite for Duplex { fn shutdown(&mut self) -> Poll<(), io::Error> { Ok(().into()) } fn write_buf(&mut self, buf: &mut B) -> Poll { let mut inner = self.inner.lock().unwrap(); if let Some(task) = inner.handle_read_task.take() { task.notify(); } inner.write.write_buf(buf) } } #[cfg(feature = "runtime")] pub struct DuplexHandle { inner: Arc>, } #[cfg(feature = "runtime")] impl DuplexHandle { pub fn read(&self, buf: &mut [u8]) -> Poll { let mut inner = self.inner.lock().unwrap(); assert!(buf.len() >= inner.write.inner.len()); if inner.write.inner.is_empty() { trace!("DuplexHandle read parking"); inner.handle_read_task = Some(task::current()); return Ok(Async::NotReady); } inner.write.read(buf).map(Async::Ready) } pub fn write(&self, bytes: &[u8]) -> Poll { let mut inner = self.inner.lock().unwrap(); assert_eq!(inner.read.inner.pos, 0); assert_eq!(inner.read.inner.vec.len(), 0, "write but read isn't empty"); inner .read .inner .vec .extend(bytes); inner.read.block_in(bytes.len()); Ok(Async::Ready(bytes.len())) } } #[cfg(feature = "runtime")] impl Drop for DuplexHandle { fn drop(&mut self) { trace!("mock duplex handle drop"); if !::std::thread::panicking() { let mut inner = self.inner.lock().unwrap(); inner.read.close(); inner.write.close(); } } } #[cfg(feature = "runtime")] type BoxedConnectFut = Box + Send>; #[cfg(feature = "runtime")] #[derive(Clone)] pub struct MockConnector { mocks: Arc>, } #[cfg(feature = "runtime")] struct MockedConnections(HashMap>); #[cfg(feature = "runtime")] impl MockConnector { pub fn new() -> MockConnector { MockConnector { mocks: Arc::new(Mutex::new(MockedConnections(HashMap::new()))), } } pub fn mock(&mut self, key: &str) -> DuplexHandle { use futures::future; self.mock_fut(key, future::ok::<_, ()>(())) } pub fn mock_fut(&mut self, key: &str, fut: F) -> DuplexHandle where F: Future + Send + 'static, { self.mock_opts(key, Connected::new(), fut) } pub fn mock_opts(&mut self, key: &str, connected: Connected, fut: F) -> DuplexHandle where F: Future + Send + 'static, { let key = key.to_owned(); let (duplex, handle) = Duplex::channel(); let fut = Box::new(fut.then(move |_| { trace!("MockConnector mocked fut ready"); Ok((duplex, connected)) })); self.mocks.lock().unwrap().0.entry(key) .or_insert(Vec::new()) .push(fut); handle } } #[cfg(feature = "runtime")] impl Connect for MockConnector { type Transport = Duplex; type Error = io::Error; type Future = BoxedConnectFut; fn connect(&self, dst: Destination) -> Self::Future { trace!("mock connect: {:?}", dst); let key = format!("{}://{}{}", dst.scheme(), dst.host(), if let Some(port) = dst.port() { format!(":{}", port) } else { "".to_owned() }); let mut mocks = self.mocks.lock().unwrap(); let mocks = mocks.0.get_mut(&key) .expect(&format!("unknown mocks uri: {}", key)); assert!(!mocks.is_empty(), "no additional mocks for {}", key); mocks.remove(0) } } #[cfg(feature = "runtime")] impl Drop for MockedConnections { fn drop(&mut self) { if !::std::thread::panicking() { for (key, mocks) in self.0.iter() { assert_eq!( mocks.len(), 0, "not all mocked connects for {:?} were used", key, ); } } } } */ hyper-1.5.2/src/proto/h1/conn.rs000064400000000000000000001462721046102023000145410ustar 00000000000000use std::fmt; #[cfg(feature = "server")] use std::future::Future; use std::io; use std::marker::{PhantomData, Unpin}; use std::pin::Pin; use std::task::{Context, Poll}; #[cfg(feature = "server")] use std::time::{Duration, Instant}; use crate::rt::{Read, Write}; use bytes::{Buf, Bytes}; use futures_util::ready; use http::header::{HeaderValue, CONNECTION, TE}; use http::{HeaderMap, Method, Version}; use http_body::Frame; use httparse::ParserConfig; use super::io::Buffered; use super::{Decoder, Encode, EncodedBuf, Encoder, Http1Transaction, ParseContext, Wants}; use crate::body::DecodedLength; #[cfg(feature = "server")] use crate::common::time::Time; use crate::headers; use crate::proto::{BodyLength, MessageHead}; #[cfg(feature = "server")] use crate::rt::Sleep; const H2_PREFACE: &[u8] = b"PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n"; /// This handles a connection, which will have been established over an /// `Read + Write` (like a socket), and will likely include multiple /// `Transaction`s over HTTP. /// /// The connection will determine when a message begins and ends as well as /// determine if this connection can be kept alive after the message, /// or if it is complete. pub(crate) struct Conn { io: Buffered>, state: State, _marker: PhantomData, } impl Conn where I: Read + Write + Unpin, B: Buf, T: Http1Transaction, { pub(crate) fn new(io: I) -> Conn { Conn { io: Buffered::new(io), state: State { allow_half_close: false, cached_headers: None, error: None, keep_alive: KA::Busy, method: None, h1_parser_config: ParserConfig::default(), h1_max_headers: None, #[cfg(feature = "server")] h1_header_read_timeout: None, #[cfg(feature = "server")] h1_header_read_timeout_fut: None, #[cfg(feature = "server")] h1_header_read_timeout_running: false, #[cfg(feature = "server")] date_header: true, #[cfg(feature = "server")] timer: Time::Empty, preserve_header_case: false, #[cfg(feature = "ffi")] preserve_header_order: false, title_case_headers: false, h09_responses: false, #[cfg(feature = "ffi")] on_informational: None, notify_read: false, reading: Reading::Init, writing: Writing::Init, upgrade: None, // We assume a modern world where the remote speaks HTTP/1.1. // If they tell us otherwise, we'll downgrade in `read_head`. version: Version::HTTP_11, allow_trailer_fields: false, }, _marker: PhantomData, } } #[cfg(feature = "server")] pub(crate) fn set_timer(&mut self, timer: Time) { self.state.timer = timer; } #[cfg(feature = "server")] pub(crate) fn set_flush_pipeline(&mut self, enabled: bool) { self.io.set_flush_pipeline(enabled); } pub(crate) fn set_write_strategy_queue(&mut self) { self.io.set_write_strategy_queue(); } pub(crate) fn set_max_buf_size(&mut self, max: usize) { self.io.set_max_buf_size(max); } #[cfg(feature = "client")] pub(crate) fn set_read_buf_exact_size(&mut self, sz: usize) { self.io.set_read_buf_exact_size(sz); } pub(crate) fn set_write_strategy_flatten(&mut self) { self.io.set_write_strategy_flatten(); } #[cfg(feature = "client")] pub(crate) fn set_h1_parser_config(&mut self, parser_config: ParserConfig) { self.state.h1_parser_config = parser_config; } pub(crate) fn set_title_case_headers(&mut self) { self.state.title_case_headers = true; } pub(crate) fn set_preserve_header_case(&mut self) { self.state.preserve_header_case = true; } #[cfg(feature = "ffi")] pub(crate) fn set_preserve_header_order(&mut self) { self.state.preserve_header_order = true; } #[cfg(feature = "client")] pub(crate) fn set_h09_responses(&mut self) { self.state.h09_responses = true; } pub(crate) fn set_http1_max_headers(&mut self, val: usize) { self.state.h1_max_headers = Some(val); } #[cfg(feature = "server")] pub(crate) fn set_http1_header_read_timeout(&mut self, val: Duration) { self.state.h1_header_read_timeout = Some(val); } #[cfg(feature = "server")] pub(crate) fn set_allow_half_close(&mut self) { self.state.allow_half_close = true; } #[cfg(feature = "server")] pub(crate) fn disable_date_header(&mut self) { self.state.date_header = false; } pub(crate) fn into_inner(self) -> (I, Bytes) { self.io.into_inner() } pub(crate) fn pending_upgrade(&mut self) -> Option { self.state.upgrade.take() } pub(crate) fn is_read_closed(&self) -> bool { self.state.is_read_closed() } pub(crate) fn is_write_closed(&self) -> bool { self.state.is_write_closed() } pub(crate) fn can_read_head(&self) -> bool { if !matches!(self.state.reading, Reading::Init) { return false; } if T::should_read_first() { return true; } !matches!(self.state.writing, Writing::Init) } pub(crate) fn can_read_body(&self) -> bool { matches!( self.state.reading, Reading::Body(..) | Reading::Continue(..) ) } #[cfg(feature = "server")] pub(crate) fn has_initial_read_write_state(&self) -> bool { matches!(self.state.reading, Reading::Init) && matches!(self.state.writing, Writing::Init) && self.io.read_buf().is_empty() } fn should_error_on_eof(&self) -> bool { // If we're idle, it's probably just the connection closing gracefully. T::should_error_on_parse_eof() && !self.state.is_idle() } fn has_h2_prefix(&self) -> bool { let read_buf = self.io.read_buf(); read_buf.len() >= 24 && read_buf[..24] == *H2_PREFACE } pub(super) fn poll_read_head( &mut self, cx: &mut Context<'_>, ) -> Poll, DecodedLength, Wants)>>> { debug_assert!(self.can_read_head()); trace!("Conn::read_head"); #[cfg(feature = "server")] if !self.state.h1_header_read_timeout_running { if let Some(h1_header_read_timeout) = self.state.h1_header_read_timeout { let deadline = Instant::now() + h1_header_read_timeout; self.state.h1_header_read_timeout_running = true; match self.state.h1_header_read_timeout_fut { Some(ref mut h1_header_read_timeout_fut) => { trace!("resetting h1 header read timeout timer"); self.state.timer.reset(h1_header_read_timeout_fut, deadline); } None => { trace!("setting h1 header read timeout timer"); self.state.h1_header_read_timeout_fut = Some(self.state.timer.sleep_until(deadline)); } } } } let msg = match self.io.parse::( cx, ParseContext { cached_headers: &mut self.state.cached_headers, req_method: &mut self.state.method, h1_parser_config: self.state.h1_parser_config.clone(), h1_max_headers: self.state.h1_max_headers, preserve_header_case: self.state.preserve_header_case, #[cfg(feature = "ffi")] preserve_header_order: self.state.preserve_header_order, h09_responses: self.state.h09_responses, #[cfg(feature = "ffi")] on_informational: &mut self.state.on_informational, }, ) { Poll::Ready(Ok(msg)) => msg, Poll::Ready(Err(e)) => return self.on_read_head_error(e), Poll::Pending => { #[cfg(feature = "server")] if self.state.h1_header_read_timeout_running { if let Some(ref mut h1_header_read_timeout_fut) = self.state.h1_header_read_timeout_fut { if Pin::new(h1_header_read_timeout_fut).poll(cx).is_ready() { self.state.h1_header_read_timeout_running = false; warn!("read header from client timeout"); return Poll::Ready(Some(Err(crate::Error::new_header_timeout()))); } } } return Poll::Pending; } }; #[cfg(feature = "server")] { self.state.h1_header_read_timeout_running = false; self.state.h1_header_read_timeout_fut = None; } // Note: don't deconstruct `msg` into local variables, it appears // the optimizer doesn't remove the extra copies. debug!("incoming body is {}", msg.decode); // Prevent accepting HTTP/0.9 responses after the initial one, if any. self.state.h09_responses = false; // Drop any OnInformational callbacks, we're done there! #[cfg(feature = "ffi")] { self.state.on_informational = None; } self.state.busy(); self.state.keep_alive &= msg.keep_alive; self.state.version = msg.head.version; let mut wants = if msg.wants_upgrade { Wants::UPGRADE } else { Wants::EMPTY }; if msg.decode == DecodedLength::ZERO { if msg.expect_continue { debug!("ignoring expect-continue since body is empty"); } self.state.reading = Reading::KeepAlive; if !T::should_read_first() { self.try_keep_alive(cx); } } else if msg.expect_continue && msg.head.version.gt(&Version::HTTP_10) { let h1_max_header_size = None; // TODO: remove this when we land h1_max_header_size support self.state.reading = Reading::Continue(Decoder::new( msg.decode, self.state.h1_max_headers, h1_max_header_size, )); wants = wants.add(Wants::EXPECT); } else { let h1_max_header_size = None; // TODO: remove this when we land h1_max_header_size support self.state.reading = Reading::Body(Decoder::new( msg.decode, self.state.h1_max_headers, h1_max_header_size, )); } self.state.allow_trailer_fields = msg .head .headers .get(TE) .map_or(false, |te_header| te_header == "trailers"); Poll::Ready(Some(Ok((msg.head, msg.decode, wants)))) } fn on_read_head_error(&mut self, e: crate::Error) -> Poll>> { // If we are currently waiting on a message, then an empty // message should be reported as an error. If not, it is just // the connection closing gracefully. let must_error = self.should_error_on_eof(); self.close_read(); self.io.consume_leading_lines(); let was_mid_parse = e.is_parse() || !self.io.read_buf().is_empty(); if was_mid_parse || must_error { // We check if the buf contains the h2 Preface debug!( "parse error ({}) with {} bytes", e, self.io.read_buf().len() ); match self.on_parse_error(e) { Ok(()) => Poll::Pending, // XXX: wat? Err(e) => Poll::Ready(Some(Err(e))), } } else { debug!("read eof"); self.close_write(); Poll::Ready(None) } } pub(crate) fn poll_read_body( &mut self, cx: &mut Context<'_>, ) -> Poll>>> { debug_assert!(self.can_read_body()); let (reading, ret) = match self.state.reading { Reading::Body(ref mut decoder) => { match ready!(decoder.decode(cx, &mut self.io)) { Ok(frame) => { if frame.is_data() { let slice = frame.data_ref().unwrap_or_else(|| unreachable!()); let (reading, maybe_frame) = if decoder.is_eof() { debug!("incoming body completed"); ( Reading::KeepAlive, if !slice.is_empty() { Some(Ok(frame)) } else { None }, ) } else if slice.is_empty() { error!("incoming body unexpectedly ended"); // This should be unreachable, since all 3 decoders // either set eof=true or return an Err when reading // an empty slice... (Reading::Closed, None) } else { return Poll::Ready(Some(Ok(frame))); }; (reading, Poll::Ready(maybe_frame)) } else if frame.is_trailers() { (Reading::Closed, Poll::Ready(Some(Ok(frame)))) } else { trace!("discarding unknown frame"); (Reading::Closed, Poll::Ready(None)) } } Err(e) => { debug!("incoming body decode error: {}", e); (Reading::Closed, Poll::Ready(Some(Err(e)))) } } } Reading::Continue(ref decoder) => { // Write the 100 Continue if not already responded... if let Writing::Init = self.state.writing { trace!("automatically sending 100 Continue"); let cont = b"HTTP/1.1 100 Continue\r\n\r\n"; self.io.headers_buf().extend_from_slice(cont); } // And now recurse once in the Reading::Body state... self.state.reading = Reading::Body(decoder.clone()); return self.poll_read_body(cx); } _ => unreachable!("poll_read_body invalid state: {:?}", self.state.reading), }; self.state.reading = reading; self.try_keep_alive(cx); ret } pub(crate) fn wants_read_again(&mut self) -> bool { let ret = self.state.notify_read; self.state.notify_read = false; ret } pub(crate) fn poll_read_keep_alive(&mut self, cx: &mut Context<'_>) -> Poll> { debug_assert!(!self.can_read_head() && !self.can_read_body()); if self.is_read_closed() { Poll::Pending } else if self.is_mid_message() { self.mid_message_detect_eof(cx) } else { self.require_empty_read(cx) } } fn is_mid_message(&self) -> bool { !matches!( (&self.state.reading, &self.state.writing), (&Reading::Init, &Writing::Init) ) } // This will check to make sure the io object read is empty. // // This should only be called for Clients wanting to enter the idle // state. fn require_empty_read(&mut self, cx: &mut Context<'_>) -> Poll> { debug_assert!(!self.can_read_head() && !self.can_read_body() && !self.is_read_closed()); debug_assert!(!self.is_mid_message()); debug_assert!(T::is_client()); if !self.io.read_buf().is_empty() { debug!("received an unexpected {} bytes", self.io.read_buf().len()); return Poll::Ready(Err(crate::Error::new_unexpected_message())); } let num_read = ready!(self.force_io_read(cx)).map_err(crate::Error::new_io)?; if num_read == 0 { let ret = if self.should_error_on_eof() { trace!("found unexpected EOF on busy connection: {:?}", self.state); Poll::Ready(Err(crate::Error::new_incomplete())) } else { trace!("found EOF on idle connection, closing"); Poll::Ready(Ok(())) }; // order is important: should_error needs state BEFORE close_read self.state.close_read(); return ret; } debug!( "received unexpected {} bytes on an idle connection", num_read ); Poll::Ready(Err(crate::Error::new_unexpected_message())) } fn mid_message_detect_eof(&mut self, cx: &mut Context<'_>) -> Poll> { debug_assert!(!self.can_read_head() && !self.can_read_body() && !self.is_read_closed()); debug_assert!(self.is_mid_message()); if self.state.allow_half_close || !self.io.read_buf().is_empty() { return Poll::Pending; } let num_read = ready!(self.force_io_read(cx)).map_err(crate::Error::new_io)?; if num_read == 0 { trace!("found unexpected EOF on busy connection: {:?}", self.state); self.state.close_read(); Poll::Ready(Err(crate::Error::new_incomplete())) } else { Poll::Ready(Ok(())) } } fn force_io_read(&mut self, cx: &mut Context<'_>) -> Poll> { debug_assert!(!self.state.is_read_closed()); let result = ready!(self.io.poll_read_from_io(cx)); Poll::Ready(result.map_err(|e| { trace!(error = %e, "force_io_read; io error"); self.state.close(); e })) } fn maybe_notify(&mut self, cx: &mut Context<'_>) { // its possible that we returned NotReady from poll() without having // exhausted the underlying Io. We would have done this when we // determined we couldn't keep reading until we knew how writing // would finish. match self.state.reading { Reading::Continue(..) | Reading::Body(..) | Reading::KeepAlive | Reading::Closed => { return } Reading::Init => (), }; match self.state.writing { Writing::Body(..) => return, Writing::Init | Writing::KeepAlive | Writing::Closed => (), } if !self.io.is_read_blocked() { if self.io.read_buf().is_empty() { match self.io.poll_read_from_io(cx) { Poll::Ready(Ok(n)) => { if n == 0 { trace!("maybe_notify; read eof"); if self.state.is_idle() { self.state.close(); } else { self.close_read() } return; } } Poll::Pending => { trace!("maybe_notify; read_from_io blocked"); return; } Poll::Ready(Err(e)) => { trace!("maybe_notify; read_from_io error: {}", e); self.state.close(); self.state.error = Some(crate::Error::new_io(e)); } } } self.state.notify_read = true; } } fn try_keep_alive(&mut self, cx: &mut Context<'_>) { self.state.try_keep_alive::(); self.maybe_notify(cx); } pub(crate) fn can_write_head(&self) -> bool { if !T::should_read_first() && matches!(self.state.reading, Reading::Closed) { return false; } match self.state.writing { Writing::Init => self.io.can_headers_buf(), _ => false, } } pub(crate) fn can_write_body(&self) -> bool { match self.state.writing { Writing::Body(..) => true, Writing::Init | Writing::KeepAlive | Writing::Closed => false, } } pub(crate) fn can_buffer_body(&self) -> bool { self.io.can_buffer() } pub(crate) fn write_head(&mut self, head: MessageHead, body: Option) { if let Some(encoder) = self.encode_head(head, body) { self.state.writing = if !encoder.is_eof() { Writing::Body(encoder) } else if encoder.is_last() { Writing::Closed } else { Writing::KeepAlive }; } } fn encode_head( &mut self, mut head: MessageHead, body: Option, ) -> Option { debug_assert!(self.can_write_head()); if !T::should_read_first() { self.state.busy(); } self.enforce_version(&mut head); let buf = self.io.headers_buf(); match super::role::encode_headers::( Encode { head: &mut head, body, #[cfg(feature = "server")] keep_alive: self.state.wants_keep_alive(), req_method: &mut self.state.method, title_case_headers: self.state.title_case_headers, #[cfg(feature = "server")] date_header: self.state.date_header, }, buf, ) { Ok(encoder) => { debug_assert!(self.state.cached_headers.is_none()); debug_assert!(head.headers.is_empty()); self.state.cached_headers = Some(head.headers); #[cfg(feature = "ffi")] { self.state.on_informational = head.extensions.remove::(); } Some(encoder) } Err(err) => { self.state.error = Some(err); self.state.writing = Writing::Closed; None } } } // Fix keep-alive when Connection: keep-alive header is not present fn fix_keep_alive(&mut self, head: &mut MessageHead) { let outgoing_is_keep_alive = head .headers .get(CONNECTION) .map_or(false, headers::connection_keep_alive); if !outgoing_is_keep_alive { match head.version { // If response is version 1.0 and keep-alive is not present in the response, // disable keep-alive so the server closes the connection Version::HTTP_10 => self.state.disable_keep_alive(), // If response is version 1.1 and keep-alive is wanted, add // Connection: keep-alive header when not present Version::HTTP_11 => { if self.state.wants_keep_alive() { head.headers .insert(CONNECTION, HeaderValue::from_static("keep-alive")); } } _ => (), } } } // If we know the remote speaks an older version, we try to fix up any messages // to work with our older peer. fn enforce_version(&mut self, head: &mut MessageHead) { match self.state.version { Version::HTTP_10 => { // Fixes response or connection when keep-alive header is not present self.fix_keep_alive(head); // If the remote only knows HTTP/1.0, we should force ourselves // to do only speak HTTP/1.0 as well. head.version = Version::HTTP_10; } Version::HTTP_11 => { if let KA::Disabled = self.state.keep_alive.status() { head.headers .insert(CONNECTION, HeaderValue::from_static("close")); } } _ => (), } // If the remote speaks HTTP/1.1, then it *should* be fine with // both HTTP/1.0 and HTTP/1.1 from us. So again, we just let // the user's headers be. } pub(crate) fn write_body(&mut self, chunk: B) { debug_assert!(self.can_write_body() && self.can_buffer_body()); // empty chunks should be discarded at Dispatcher level debug_assert!(chunk.remaining() != 0); let state = match self.state.writing { Writing::Body(ref mut encoder) => { self.io.buffer(encoder.encode(chunk)); if !encoder.is_eof() { return; } if encoder.is_last() { Writing::Closed } else { Writing::KeepAlive } } _ => unreachable!("write_body invalid state: {:?}", self.state.writing), }; self.state.writing = state; } pub(crate) fn write_trailers(&mut self, trailers: HeaderMap) { if T::is_server() && !self.state.allow_trailer_fields { debug!("trailers not allowed to be sent"); return; } debug_assert!(self.can_write_body() && self.can_buffer_body()); match self.state.writing { Writing::Body(ref encoder) => { if let Some(enc_buf) = encoder.encode_trailers(trailers, self.state.title_case_headers) { self.io.buffer(enc_buf); self.state.writing = if encoder.is_last() || encoder.is_close_delimited() { Writing::Closed } else { Writing::KeepAlive }; } } _ => unreachable!("write_trailers invalid state: {:?}", self.state.writing), } } pub(crate) fn write_body_and_end(&mut self, chunk: B) { debug_assert!(self.can_write_body() && self.can_buffer_body()); // empty chunks should be discarded at Dispatcher level debug_assert!(chunk.remaining() != 0); let state = match self.state.writing { Writing::Body(ref encoder) => { let can_keep_alive = encoder.encode_and_end(chunk, self.io.write_buf()); if can_keep_alive { Writing::KeepAlive } else { Writing::Closed } } _ => unreachable!("write_body invalid state: {:?}", self.state.writing), }; self.state.writing = state; } pub(crate) fn end_body(&mut self) -> crate::Result<()> { debug_assert!(self.can_write_body()); let encoder = match self.state.writing { Writing::Body(ref mut enc) => enc, _ => return Ok(()), }; // end of stream, that means we should try to eof match encoder.end() { Ok(end) => { if let Some(end) = end { self.io.buffer(end); } self.state.writing = if encoder.is_last() || encoder.is_close_delimited() { Writing::Closed } else { Writing::KeepAlive }; Ok(()) } Err(not_eof) => { self.state.writing = Writing::Closed; Err(crate::Error::new_body_write_aborted().with(not_eof)) } } } // When we get a parse error, depending on what side we are, we might be able // to write a response before closing the connection. // // - Client: there is nothing we can do // - Server: if Response hasn't been written yet, we can send a 4xx response fn on_parse_error(&mut self, err: crate::Error) -> crate::Result<()> { if let Writing::Init = self.state.writing { if self.has_h2_prefix() { return Err(crate::Error::new_version_h2()); } if let Some(msg) = T::on_error(&err) { // Drop the cached headers so as to not trigger a debug // assert in `write_head`... self.state.cached_headers.take(); self.write_head(msg, None); self.state.error = Some(err); return Ok(()); } } // fallback is pass the error back up Err(err) } pub(crate) fn poll_flush(&mut self, cx: &mut Context<'_>) -> Poll> { ready!(Pin::new(&mut self.io).poll_flush(cx))?; self.try_keep_alive(cx); trace!("flushed({}): {:?}", T::LOG, self.state); Poll::Ready(Ok(())) } pub(crate) fn poll_shutdown(&mut self, cx: &mut Context<'_>) -> Poll> { match ready!(Pin::new(self.io.io_mut()).poll_shutdown(cx)) { Ok(()) => { trace!("shut down IO complete"); Poll::Ready(Ok(())) } Err(e) => { debug!("error shutting down IO: {}", e); Poll::Ready(Err(e)) } } } /// If the read side can be cheaply drained, do so. Otherwise, close. pub(super) fn poll_drain_or_close_read(&mut self, cx: &mut Context<'_>) { if let Reading::Continue(ref decoder) = self.state.reading { // skip sending the 100-continue // just move forward to a read, in case a tiny body was included self.state.reading = Reading::Body(decoder.clone()); } let _ = self.poll_read_body(cx); // If still in Reading::Body, just give up match self.state.reading { Reading::Init | Reading::KeepAlive => { trace!("body drained") } _ => self.close_read(), } } pub(crate) fn close_read(&mut self) { self.state.close_read(); } pub(crate) fn close_write(&mut self) { self.state.close_write(); } #[cfg(feature = "server")] pub(crate) fn disable_keep_alive(&mut self) { if self.state.is_idle() { trace!("disable_keep_alive; closing idle connection"); self.state.close(); } else { trace!("disable_keep_alive; in-progress connection"); self.state.disable_keep_alive(); } } pub(crate) fn take_error(&mut self) -> crate::Result<()> { if let Some(err) = self.state.error.take() { Err(err) } else { Ok(()) } } pub(super) fn on_upgrade(&mut self) -> crate::upgrade::OnUpgrade { trace!("{}: prepare possible HTTP upgrade", T::LOG); self.state.prepare_upgrade() } } impl fmt::Debug for Conn { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("Conn") .field("state", &self.state) .field("io", &self.io) .finish() } } // B and T are never pinned impl Unpin for Conn {} struct State { allow_half_close: bool, /// Re-usable HeaderMap to reduce allocating new ones. cached_headers: Option, /// If an error occurs when there wasn't a direct way to return it /// back to the user, this is set. error: Option, /// Current keep-alive status. keep_alive: KA, /// If mid-message, the HTTP Method that started it. /// /// This is used to know things such as if the message can include /// a body or not. method: Option, h1_parser_config: ParserConfig, h1_max_headers: Option, #[cfg(feature = "server")] h1_header_read_timeout: Option, #[cfg(feature = "server")] h1_header_read_timeout_fut: Option>>, #[cfg(feature = "server")] h1_header_read_timeout_running: bool, #[cfg(feature = "server")] date_header: bool, #[cfg(feature = "server")] timer: Time, preserve_header_case: bool, #[cfg(feature = "ffi")] preserve_header_order: bool, title_case_headers: bool, h09_responses: bool, /// If set, called with each 1xx informational response received for /// the current request. MUST be unset after a non-1xx response is /// received. #[cfg(feature = "ffi")] on_informational: Option, /// Set to true when the Dispatcher should poll read operations /// again. See the `maybe_notify` method for more. notify_read: bool, /// State of allowed reads reading: Reading, /// State of allowed writes writing: Writing, /// An expected pending HTTP upgrade. upgrade: Option, /// Either HTTP/1.0 or 1.1 connection version: Version, /// Flag to track if trailer fields are allowed to be sent allow_trailer_fields: bool, } #[derive(Debug)] enum Reading { Init, Continue(Decoder), Body(Decoder), KeepAlive, Closed, } enum Writing { Init, Body(Encoder), KeepAlive, Closed, } impl fmt::Debug for State { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let mut builder = f.debug_struct("State"); builder .field("reading", &self.reading) .field("writing", &self.writing) .field("keep_alive", &self.keep_alive); // Only show error field if it's interesting... if let Some(ref error) = self.error { builder.field("error", error); } if self.allow_half_close { builder.field("allow_half_close", &true); } // Purposefully leaving off other fields.. builder.finish() } } impl fmt::Debug for Writing { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match *self { Writing::Init => f.write_str("Init"), Writing::Body(ref enc) => f.debug_tuple("Body").field(enc).finish(), Writing::KeepAlive => f.write_str("KeepAlive"), Writing::Closed => f.write_str("Closed"), } } } impl std::ops::BitAndAssign for KA { fn bitand_assign(&mut self, enabled: bool) { if !enabled { trace!("remote disabling keep-alive"); *self = KA::Disabled; } } } #[derive(Clone, Copy, Debug, Default)] enum KA { Idle, #[default] Busy, Disabled, } impl KA { fn idle(&mut self) { *self = KA::Idle; } fn busy(&mut self) { *self = KA::Busy; } fn disable(&mut self) { *self = KA::Disabled; } fn status(&self) -> KA { *self } } impl State { fn close(&mut self) { trace!("State::close()"); self.reading = Reading::Closed; self.writing = Writing::Closed; self.keep_alive.disable(); } fn close_read(&mut self) { trace!("State::close_read()"); self.reading = Reading::Closed; self.keep_alive.disable(); } fn close_write(&mut self) { trace!("State::close_write()"); self.writing = Writing::Closed; self.keep_alive.disable(); } fn wants_keep_alive(&self) -> bool { !matches!(self.keep_alive.status(), KA::Disabled) } fn try_keep_alive(&mut self) { match (&self.reading, &self.writing) { (&Reading::KeepAlive, &Writing::KeepAlive) => { if let KA::Busy = self.keep_alive.status() { self.idle::(); } else { trace!( "try_keep_alive({}): could keep-alive, but status = {:?}", T::LOG, self.keep_alive ); self.close(); } } (&Reading::Closed, &Writing::KeepAlive) | (&Reading::KeepAlive, &Writing::Closed) => { self.close() } _ => (), } } fn disable_keep_alive(&mut self) { self.keep_alive.disable() } fn busy(&mut self) { if let KA::Disabled = self.keep_alive.status() { return; } self.keep_alive.busy(); } fn idle(&mut self) { debug_assert!(!self.is_idle(), "State::idle() called while idle"); self.method = None; self.keep_alive.idle(); if !self.is_idle() { self.close(); return; } self.reading = Reading::Init; self.writing = Writing::Init; // !T::should_read_first() means Client. // // If Client connection has just gone idle, the Dispatcher // should try the poll loop one more time, so as to poll the // pending requests stream. if !T::should_read_first() { self.notify_read = true; } } fn is_idle(&self) -> bool { matches!(self.keep_alive.status(), KA::Idle) } fn is_read_closed(&self) -> bool { matches!(self.reading, Reading::Closed) } fn is_write_closed(&self) -> bool { matches!(self.writing, Writing::Closed) } fn prepare_upgrade(&mut self) -> crate::upgrade::OnUpgrade { let (tx, rx) = crate::upgrade::pending(); self.upgrade = Some(tx); rx } } #[cfg(test)] mod tests { #[cfg(all(feature = "nightly", not(miri)))] #[bench] fn bench_read_head_short(b: &mut ::test::Bencher) { use super::*; use crate::common::io::Compat; let s = b"GET / HTTP/1.1\r\nHost: localhost:8080\r\n\r\n"; let len = s.len(); b.bytes = len as u64; // an empty IO, we'll be skipping and using the read buffer anyways let io = Compat(tokio_test::io::Builder::new().build()); let mut conn = Conn::<_, bytes::Bytes, crate::proto::h1::ServerTransaction>::new(io); *conn.io.read_buf_mut() = ::bytes::BytesMut::from(&s[..]); conn.state.cached_headers = Some(HeaderMap::with_capacity(2)); let rt = tokio::runtime::Builder::new_current_thread() .enable_all() .build() .unwrap(); b.iter(|| { rt.block_on(futures_util::future::poll_fn(|cx| { match conn.poll_read_head(cx) { Poll::Ready(Some(Ok(x))) => { ::test::black_box(&x); let mut headers = x.0.headers; headers.clear(); conn.state.cached_headers = Some(headers); } f => panic!("expected Ready(Some(Ok(..))): {:?}", f), } conn.io.read_buf_mut().reserve(1); unsafe { conn.io.read_buf_mut().set_len(len); } conn.state.reading = Reading::Init; Poll::Ready(()) })); }); } /* //TODO: rewrite these using dispatch... someday... use futures::{Async, Future, Stream, Sink}; use futures::future; use proto::{self, ClientTransaction, MessageHead, ServerTransaction}; use super::super::Encoder; use mock::AsyncIo; use super::{Conn, Decoder, Reading, Writing}; use ::uri::Uri; use std::str::FromStr; #[test] fn test_conn_init_read() { let good_message = b"GET / HTTP/1.1\r\n\r\n".to_vec(); let len = good_message.len(); let io = AsyncIo::new_buf(good_message, len); let mut conn = Conn::<_, proto::Bytes, ServerTransaction>::new(io); match conn.poll().unwrap() { Async::Ready(Some(Frame::Message { message, body: false })) => { assert_eq!(message, MessageHead { subject: ::proto::RequestLine(::Get, Uri::from_str("/").unwrap()), .. MessageHead::default() }) }, f => panic!("frame is not Frame::Message: {:?}", f) } } #[test] fn test_conn_parse_partial() { let _: Result<(), ()> = future::lazy(|| { let good_message = b"GET / HTTP/1.1\r\nHost: foo.bar\r\n\r\n".to_vec(); let io = AsyncIo::new_buf(good_message, 10); let mut conn = Conn::<_, proto::Bytes, ServerTransaction>::new(io); assert!(conn.poll().unwrap().is_not_ready()); conn.io.io_mut().block_in(50); let async = conn.poll().unwrap(); assert!(async.is_ready()); match async { Async::Ready(Some(Frame::Message { .. })) => (), f => panic!("frame is not Message: {:?}", f), } Ok(()) }).wait(); } #[test] fn test_conn_init_read_eof_idle() { let io = AsyncIo::new_buf(vec![], 1); let mut conn = Conn::<_, proto::Bytes, ServerTransaction>::new(io); conn.state.idle(); match conn.poll().unwrap() { Async::Ready(None) => {}, other => panic!("frame is not None: {:?}", other) } } #[test] fn test_conn_init_read_eof_idle_partial_parse() { let io = AsyncIo::new_buf(b"GET / HTTP/1.1".to_vec(), 100); let mut conn = Conn::<_, proto::Bytes, ServerTransaction>::new(io); conn.state.idle(); match conn.poll() { Err(ref err) if err.kind() == std::io::ErrorKind::UnexpectedEof => {}, other => panic!("unexpected frame: {:?}", other) } } #[test] fn test_conn_init_read_eof_busy() { let _: Result<(), ()> = future::lazy(|| { // server ignores let io = AsyncIo::new_eof(); let mut conn = Conn::<_, proto::Bytes, ServerTransaction>::new(io); conn.state.busy(); match conn.poll().unwrap() { Async::Ready(None) => {}, other => panic!("unexpected frame: {:?}", other) } // client let io = AsyncIo::new_eof(); let mut conn = Conn::<_, proto::Bytes, ClientTransaction>::new(io); conn.state.busy(); match conn.poll() { Err(ref err) if err.kind() == std::io::ErrorKind::UnexpectedEof => {}, other => panic!("unexpected frame: {:?}", other) } Ok(()) }).wait(); } #[test] fn test_conn_body_finish_read_eof() { let _: Result<(), ()> = future::lazy(|| { let io = AsyncIo::new_eof(); let mut conn = Conn::<_, proto::Bytes, ClientTransaction>::new(io); conn.state.busy(); conn.state.writing = Writing::KeepAlive; conn.state.reading = Reading::Body(Decoder::length(0)); match conn.poll() { Ok(Async::Ready(Some(Frame::Body { chunk: None }))) => (), other => panic!("unexpected frame: {:?}", other) } // conn eofs, but tokio-proto will call poll() again, before calling flush() // the conn eof in this case is perfectly fine match conn.poll() { Ok(Async::Ready(None)) => (), other => panic!("unexpected frame: {:?}", other) } Ok(()) }).wait(); } #[test] fn test_conn_message_empty_body_read_eof() { let _: Result<(), ()> = future::lazy(|| { let io = AsyncIo::new_buf(b"HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n".to_vec(), 1024); let mut conn = Conn::<_, proto::Bytes, ClientTransaction>::new(io); conn.state.busy(); conn.state.writing = Writing::KeepAlive; match conn.poll() { Ok(Async::Ready(Some(Frame::Message { body: false, .. }))) => (), other => panic!("unexpected frame: {:?}", other) } // conn eofs, but tokio-proto will call poll() again, before calling flush() // the conn eof in this case is perfectly fine match conn.poll() { Ok(Async::Ready(None)) => (), other => panic!("unexpected frame: {:?}", other) } Ok(()) }).wait(); } #[test] fn test_conn_read_body_end() { let _: Result<(), ()> = future::lazy(|| { let io = AsyncIo::new_buf(b"POST / HTTP/1.1\r\nContent-Length: 5\r\n\r\n12345".to_vec(), 1024); let mut conn = Conn::<_, proto::Bytes, ServerTransaction>::new(io); conn.state.busy(); match conn.poll() { Ok(Async::Ready(Some(Frame::Message { body: true, .. }))) => (), other => panic!("unexpected frame: {:?}", other) } match conn.poll() { Ok(Async::Ready(Some(Frame::Body { chunk: Some(_) }))) => (), other => panic!("unexpected frame: {:?}", other) } // When the body is done, `poll` MUST return a `Body` frame with chunk set to `None` match conn.poll() { Ok(Async::Ready(Some(Frame::Body { chunk: None }))) => (), other => panic!("unexpected frame: {:?}", other) } match conn.poll() { Ok(Async::NotReady) => (), other => panic!("unexpected frame: {:?}", other) } Ok(()) }).wait(); } #[test] fn test_conn_closed_read() { let io = AsyncIo::new_buf(vec![], 0); let mut conn = Conn::<_, proto::Bytes, ServerTransaction>::new(io); conn.state.close(); match conn.poll().unwrap() { Async::Ready(None) => {}, other => panic!("frame is not None: {:?}", other) } } #[test] fn test_conn_body_write_length() { let _ = pretty_env_logger::try_init(); let _: Result<(), ()> = future::lazy(|| { let io = AsyncIo::new_buf(vec![], 0); let mut conn = Conn::<_, proto::Bytes, ServerTransaction>::new(io); let max = super::super::io::DEFAULT_MAX_BUFFER_SIZE + 4096; conn.state.writing = Writing::Body(Encoder::length((max * 2) as u64)); assert!(conn.start_send(Frame::Body { chunk: Some(vec![b'a'; max].into()) }).unwrap().is_ready()); assert!(!conn.can_buffer_body()); assert!(conn.start_send(Frame::Body { chunk: Some(vec![b'b'; 1024 * 8].into()) }).unwrap().is_not_ready()); conn.io.io_mut().block_in(1024 * 3); assert!(conn.poll_complete().unwrap().is_not_ready()); conn.io.io_mut().block_in(1024 * 3); assert!(conn.poll_complete().unwrap().is_not_ready()); conn.io.io_mut().block_in(max * 2); assert!(conn.poll_complete().unwrap().is_ready()); assert!(conn.start_send(Frame::Body { chunk: Some(vec![b'c'; 1024 * 8].into()) }).unwrap().is_ready()); Ok(()) }).wait(); } #[test] fn test_conn_body_write_chunked() { let _: Result<(), ()> = future::lazy(|| { let io = AsyncIo::new_buf(vec![], 4096); let mut conn = Conn::<_, proto::Bytes, ServerTransaction>::new(io); conn.state.writing = Writing::Body(Encoder::chunked()); assert!(conn.start_send(Frame::Body { chunk: Some("headers".into()) }).unwrap().is_ready()); assert!(conn.start_send(Frame::Body { chunk: Some(vec![b'x'; 8192].into()) }).unwrap().is_ready()); Ok(()) }).wait(); } #[test] fn test_conn_body_flush() { let _: Result<(), ()> = future::lazy(|| { let io = AsyncIo::new_buf(vec![], 1024 * 1024 * 5); let mut conn = Conn::<_, proto::Bytes, ServerTransaction>::new(io); conn.state.writing = Writing::Body(Encoder::length(1024 * 1024)); assert!(conn.start_send(Frame::Body { chunk: Some(vec![b'a'; 1024 * 1024].into()) }).unwrap().is_ready()); assert!(!conn.can_buffer_body()); conn.io.io_mut().block_in(1024 * 1024 * 5); assert!(conn.poll_complete().unwrap().is_ready()); assert!(conn.can_buffer_body()); assert!(conn.io.io_mut().flushed()); Ok(()) }).wait(); } #[test] fn test_conn_parking() { use std::sync::Arc; use futures::executor::Notify; use futures::executor::NotifyHandle; struct Car { permit: bool, } impl Notify for Car { fn notify(&self, _id: usize) { assert!(self.permit, "unparked without permit"); } } fn car(permit: bool) -> NotifyHandle { Arc::new(Car { permit: permit, }).into() } // test that once writing is done, unparks let f = future::lazy(|| { let io = AsyncIo::new_buf(vec![], 4096); let mut conn = Conn::<_, proto::Bytes, ServerTransaction>::new(io); conn.state.reading = Reading::KeepAlive; assert!(conn.poll().unwrap().is_not_ready()); conn.state.writing = Writing::KeepAlive; assert!(conn.poll_complete().unwrap().is_ready()); Ok::<(), ()>(()) }); ::futures::executor::spawn(f).poll_future_notify(&car(true), 0).unwrap(); // test that flushing when not waiting on read doesn't unpark let f = future::lazy(|| { let io = AsyncIo::new_buf(vec![], 4096); let mut conn = Conn::<_, proto::Bytes, ServerTransaction>::new(io); conn.state.writing = Writing::KeepAlive; assert!(conn.poll_complete().unwrap().is_ready()); Ok::<(), ()>(()) }); ::futures::executor::spawn(f).poll_future_notify(&car(false), 0).unwrap(); // test that flushing and writing isn't done doesn't unpark let f = future::lazy(|| { let io = AsyncIo::new_buf(vec![], 4096); let mut conn = Conn::<_, proto::Bytes, ServerTransaction>::new(io); conn.state.reading = Reading::KeepAlive; assert!(conn.poll().unwrap().is_not_ready()); conn.state.writing = Writing::Body(Encoder::length(5_000)); assert!(conn.poll_complete().unwrap().is_ready()); Ok::<(), ()>(()) }); ::futures::executor::spawn(f).poll_future_notify(&car(false), 0).unwrap(); } #[test] fn test_conn_closed_write() { let io = AsyncIo::new_buf(vec![], 0); let mut conn = Conn::<_, proto::Bytes, ServerTransaction>::new(io); conn.state.close(); match conn.start_send(Frame::Body { chunk: Some(b"foobar".to_vec().into()) }) { Err(_e) => {}, other => panic!("did not return Err: {:?}", other) } assert!(conn.state.is_write_closed()); } #[test] fn test_conn_write_empty_chunk() { let io = AsyncIo::new_buf(vec![], 0); let mut conn = Conn::<_, proto::Bytes, ServerTransaction>::new(io); conn.state.writing = Writing::KeepAlive; assert!(conn.start_send(Frame::Body { chunk: None }).unwrap().is_ready()); assert!(conn.start_send(Frame::Body { chunk: Some(Vec::new().into()) }).unwrap().is_ready()); conn.start_send(Frame::Body { chunk: Some(vec![b'a'].into()) }).unwrap_err(); } */ } hyper-1.5.2/src/proto/h1/decode.rs000064400000000000000000001206111046102023000150140ustar 00000000000000use std::error::Error as StdError; use std::fmt; use std::io; use std::task::{Context, Poll}; use bytes::{BufMut, Bytes, BytesMut}; use futures_util::ready; use http::{HeaderMap, HeaderName, HeaderValue}; use http_body::Frame; use super::io::MemRead; use super::role::DEFAULT_MAX_HEADERS; use super::DecodedLength; use self::Kind::{Chunked, Eof, Length}; /// Maximum amount of bytes allowed in chunked extensions. /// /// This limit is currentlty applied for the entire body, not per chunk. const CHUNKED_EXTENSIONS_LIMIT: u64 = 1024 * 16; /// Maximum number of bytes allowed for all trailer fields. /// /// TODO: remove this when we land h1_max_header_size support const TRAILER_LIMIT: usize = 1024 * 16; /// Decoders to handle different Transfer-Encodings. /// /// If a message body does not include a Transfer-Encoding, it *should* /// include a Content-Length header. #[derive(Clone, PartialEq)] pub(crate) struct Decoder { kind: Kind, } #[derive(Debug, Clone, PartialEq)] enum Kind { /// A Reader used when a Content-Length header is passed with a positive integer. Length(u64), /// A Reader used when Transfer-Encoding is `chunked`. Chunked { state: ChunkedState, chunk_len: u64, extensions_cnt: u64, trailers_buf: Option, trailers_cnt: usize, h1_max_headers: Option, h1_max_header_size: Option, }, /// A Reader used for responses that don't indicate a length or chunked. /// /// The bool tracks when EOF is seen on the transport. /// /// Note: This should only used for `Response`s. It is illegal for a /// `Request` to be made with both `Content-Length` and /// `Transfer-Encoding: chunked` missing, as explained from the spec: /// /// > If a Transfer-Encoding header field is present in a response and /// > the chunked transfer coding is not the final encoding, the /// > message body length is determined by reading the connection until /// > it is closed by the server. If a Transfer-Encoding header field /// > is present in a request and the chunked transfer coding is not /// > the final encoding, the message body length cannot be determined /// > reliably; the server MUST respond with the 400 (Bad Request) /// > status code and then close the connection. Eof(bool), } #[derive(Debug, PartialEq, Clone, Copy)] enum ChunkedState { Start, Size, SizeLws, Extension, SizeLf, Body, BodyCr, BodyLf, Trailer, TrailerLf, EndCr, EndLf, End, } impl Decoder { // constructors pub(crate) fn length(x: u64) -> Decoder { Decoder { kind: Kind::Length(x), } } pub(crate) fn chunked( h1_max_headers: Option, h1_max_header_size: Option, ) -> Decoder { Decoder { kind: Kind::Chunked { state: ChunkedState::new(), chunk_len: 0, extensions_cnt: 0, trailers_buf: None, trailers_cnt: 0, h1_max_headers, h1_max_header_size, }, } } pub(crate) fn eof() -> Decoder { Decoder { kind: Kind::Eof(false), } } pub(super) fn new( len: DecodedLength, h1_max_headers: Option, h1_max_header_size: Option, ) -> Self { match len { DecodedLength::CHUNKED => Decoder::chunked(h1_max_headers, h1_max_header_size), DecodedLength::CLOSE_DELIMITED => Decoder::eof(), length => Decoder::length(length.danger_len()), } } // methods pub(crate) fn is_eof(&self) -> bool { matches!( self.kind, Length(0) | Chunked { state: ChunkedState::End, .. } | Eof(true) ) } pub(crate) fn decode( &mut self, cx: &mut Context<'_>, body: &mut R, ) -> Poll, io::Error>> { trace!("decode; state={:?}", self.kind); match self.kind { Length(ref mut remaining) => { if *remaining == 0 { Poll::Ready(Ok(Frame::data(Bytes::new()))) } else { let to_read = *remaining as usize; let buf = ready!(body.read_mem(cx, to_read))?; let num = buf.as_ref().len() as u64; if num > *remaining { *remaining = 0; } else if num == 0 { return Poll::Ready(Err(io::Error::new( io::ErrorKind::UnexpectedEof, IncompleteBody, ))); } else { *remaining -= num; } Poll::Ready(Ok(Frame::data(buf))) } } Chunked { ref mut state, ref mut chunk_len, ref mut extensions_cnt, ref mut trailers_buf, ref mut trailers_cnt, ref h1_max_headers, ref h1_max_header_size, } => { let h1_max_headers = h1_max_headers.unwrap_or(DEFAULT_MAX_HEADERS); let h1_max_header_size = h1_max_header_size.unwrap_or(TRAILER_LIMIT); loop { let mut buf = None; // advances the chunked state *state = ready!(state.step( cx, body, chunk_len, extensions_cnt, &mut buf, trailers_buf, trailers_cnt, h1_max_headers, h1_max_header_size ))?; if *state == ChunkedState::End { trace!("end of chunked"); if trailers_buf.is_some() { trace!("found possible trailers"); // decoder enforces that trailers count will not exceed h1_max_headers if *trailers_cnt >= h1_max_headers { return Poll::Ready(Err(io::Error::new( io::ErrorKind::InvalidData, "chunk trailers count overflow", ))); } match decode_trailers( &mut trailers_buf.take().expect("Trailer is None"), *trailers_cnt, ) { Ok(headers) => { return Poll::Ready(Ok(Frame::trailers(headers))); } Err(e) => { return Poll::Ready(Err(e)); } } } return Poll::Ready(Ok(Frame::data(Bytes::new()))); } if let Some(buf) = buf { return Poll::Ready(Ok(Frame::data(buf))); } } } Eof(ref mut is_eof) => { if *is_eof { Poll::Ready(Ok(Frame::data(Bytes::new()))) } else { // 8192 chosen because its about 2 packets, there probably // won't be that much available, so don't have MemReaders // allocate buffers to big body.read_mem(cx, 8192).map_ok(|slice| { *is_eof = slice.is_empty(); Frame::data(slice) }) } } } } #[cfg(test)] async fn decode_fut(&mut self, body: &mut R) -> Result, io::Error> { futures_util::future::poll_fn(move |cx| self.decode(cx, body)).await } } impl fmt::Debug for Decoder { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Debug::fmt(&self.kind, f) } } macro_rules! byte ( ($rdr:ident, $cx:expr) => ({ let buf = ready!($rdr.read_mem($cx, 1))?; if !buf.is_empty() { buf[0] } else { return Poll::Ready(Err(io::Error::new(io::ErrorKind::UnexpectedEof, "unexpected EOF during chunk size line"))); } }) ); macro_rules! or_overflow { ($e:expr) => ( match $e { Some(val) => val, None => return Poll::Ready(Err(io::Error::new( io::ErrorKind::InvalidData, "invalid chunk size: overflow", ))), } ) } macro_rules! put_u8 { ($trailers_buf:expr, $byte:expr, $limit:expr) => { $trailers_buf.put_u8($byte); if $trailers_buf.len() >= $limit { return Poll::Ready(Err(io::Error::new( io::ErrorKind::InvalidData, "chunk trailers bytes over limit", ))); } }; } impl ChunkedState { fn new() -> ChunkedState { ChunkedState::Start } fn step( &self, cx: &mut Context<'_>, body: &mut R, size: &mut u64, extensions_cnt: &mut u64, buf: &mut Option, trailers_buf: &mut Option, trailers_cnt: &mut usize, h1_max_headers: usize, h1_max_header_size: usize, ) -> Poll> { use self::ChunkedState::*; match *self { Start => ChunkedState::read_start(cx, body, size), Size => ChunkedState::read_size(cx, body, size), SizeLws => ChunkedState::read_size_lws(cx, body), Extension => ChunkedState::read_extension(cx, body, extensions_cnt), SizeLf => ChunkedState::read_size_lf(cx, body, *size), Body => ChunkedState::read_body(cx, body, size, buf), BodyCr => ChunkedState::read_body_cr(cx, body), BodyLf => ChunkedState::read_body_lf(cx, body), Trailer => ChunkedState::read_trailer(cx, body, trailers_buf, h1_max_header_size), TrailerLf => ChunkedState::read_trailer_lf( cx, body, trailers_buf, trailers_cnt, h1_max_headers, h1_max_header_size, ), EndCr => ChunkedState::read_end_cr(cx, body, trailers_buf, h1_max_header_size), EndLf => ChunkedState::read_end_lf(cx, body, trailers_buf, h1_max_header_size), End => Poll::Ready(Ok(ChunkedState::End)), } } fn read_start( cx: &mut Context<'_>, rdr: &mut R, size: &mut u64, ) -> Poll> { trace!("Read chunk start"); let radix = 16; match byte!(rdr, cx) { b @ b'0'..=b'9' => { *size = or_overflow!(size.checked_mul(radix)); *size = or_overflow!(size.checked_add((b - b'0') as u64)); } b @ b'a'..=b'f' => { *size = or_overflow!(size.checked_mul(radix)); *size = or_overflow!(size.checked_add((b + 10 - b'a') as u64)); } b @ b'A'..=b'F' => { *size = or_overflow!(size.checked_mul(radix)); *size = or_overflow!(size.checked_add((b + 10 - b'A') as u64)); } _ => { return Poll::Ready(Err(io::Error::new( io::ErrorKind::InvalidInput, "Invalid chunk size line: missing size digit", ))); } } Poll::Ready(Ok(ChunkedState::Size)) } fn read_size( cx: &mut Context<'_>, rdr: &mut R, size: &mut u64, ) -> Poll> { trace!("Read chunk hex size"); let radix = 16; match byte!(rdr, cx) { b @ b'0'..=b'9' => { *size = or_overflow!(size.checked_mul(radix)); *size = or_overflow!(size.checked_add((b - b'0') as u64)); } b @ b'a'..=b'f' => { *size = or_overflow!(size.checked_mul(radix)); *size = or_overflow!(size.checked_add((b + 10 - b'a') as u64)); } b @ b'A'..=b'F' => { *size = or_overflow!(size.checked_mul(radix)); *size = or_overflow!(size.checked_add((b + 10 - b'A') as u64)); } b'\t' | b' ' => return Poll::Ready(Ok(ChunkedState::SizeLws)), b';' => return Poll::Ready(Ok(ChunkedState::Extension)), b'\r' => return Poll::Ready(Ok(ChunkedState::SizeLf)), _ => { return Poll::Ready(Err(io::Error::new( io::ErrorKind::InvalidInput, "Invalid chunk size line: Invalid Size", ))); } } Poll::Ready(Ok(ChunkedState::Size)) } fn read_size_lws( cx: &mut Context<'_>, rdr: &mut R, ) -> Poll> { trace!("read_size_lws"); match byte!(rdr, cx) { // LWS can follow the chunk size, but no more digits can come b'\t' | b' ' => Poll::Ready(Ok(ChunkedState::SizeLws)), b';' => Poll::Ready(Ok(ChunkedState::Extension)), b'\r' => Poll::Ready(Ok(ChunkedState::SizeLf)), _ => Poll::Ready(Err(io::Error::new( io::ErrorKind::InvalidInput, "Invalid chunk size linear white space", ))), } } fn read_extension( cx: &mut Context<'_>, rdr: &mut R, extensions_cnt: &mut u64, ) -> Poll> { trace!("read_extension"); // We don't care about extensions really at all. Just ignore them. // They "end" at the next CRLF. // // However, some implementations may not check for the CR, so to save // them from themselves, we reject extensions containing plain LF as // well. match byte!(rdr, cx) { b'\r' => Poll::Ready(Ok(ChunkedState::SizeLf)), b'\n' => Poll::Ready(Err(io::Error::new( io::ErrorKind::InvalidData, "invalid chunk extension contains newline", ))), _ => { *extensions_cnt += 1; if *extensions_cnt >= CHUNKED_EXTENSIONS_LIMIT { Poll::Ready(Err(io::Error::new( io::ErrorKind::InvalidData, "chunk extensions over limit", ))) } else { Poll::Ready(Ok(ChunkedState::Extension)) } } // no supported extensions } } fn read_size_lf( cx: &mut Context<'_>, rdr: &mut R, size: u64, ) -> Poll> { trace!("Chunk size is {:?}", size); match byte!(rdr, cx) { b'\n' => { if size == 0 { Poll::Ready(Ok(ChunkedState::EndCr)) } else { debug!("incoming chunked header: {0:#X} ({0} bytes)", size); Poll::Ready(Ok(ChunkedState::Body)) } } _ => Poll::Ready(Err(io::Error::new( io::ErrorKind::InvalidInput, "Invalid chunk size LF", ))), } } fn read_body( cx: &mut Context<'_>, rdr: &mut R, rem: &mut u64, buf: &mut Option, ) -> Poll> { trace!("Chunked read, remaining={:?}", rem); // cap remaining bytes at the max capacity of usize let rem_cap = match *rem { r if r > usize::MAX as u64 => usize::MAX, r => r as usize, }; let to_read = rem_cap; let slice = ready!(rdr.read_mem(cx, to_read))?; let count = slice.len(); if count == 0 { *rem = 0; return Poll::Ready(Err(io::Error::new( io::ErrorKind::UnexpectedEof, IncompleteBody, ))); } *buf = Some(slice); *rem -= count as u64; if *rem > 0 { Poll::Ready(Ok(ChunkedState::Body)) } else { Poll::Ready(Ok(ChunkedState::BodyCr)) } } fn read_body_cr( cx: &mut Context<'_>, rdr: &mut R, ) -> Poll> { match byte!(rdr, cx) { b'\r' => Poll::Ready(Ok(ChunkedState::BodyLf)), _ => Poll::Ready(Err(io::Error::new( io::ErrorKind::InvalidInput, "Invalid chunk body CR", ))), } } fn read_body_lf( cx: &mut Context<'_>, rdr: &mut R, ) -> Poll> { match byte!(rdr, cx) { b'\n' => Poll::Ready(Ok(ChunkedState::Start)), _ => Poll::Ready(Err(io::Error::new( io::ErrorKind::InvalidInput, "Invalid chunk body LF", ))), } } fn read_trailer( cx: &mut Context<'_>, rdr: &mut R, trailers_buf: &mut Option, h1_max_header_size: usize, ) -> Poll> { trace!("read_trailer"); let byte = byte!(rdr, cx); put_u8!( trailers_buf.as_mut().expect("trailers_buf is None"), byte, h1_max_header_size ); match byte { b'\r' => Poll::Ready(Ok(ChunkedState::TrailerLf)), _ => Poll::Ready(Ok(ChunkedState::Trailer)), } } fn read_trailer_lf( cx: &mut Context<'_>, rdr: &mut R, trailers_buf: &mut Option, trailers_cnt: &mut usize, h1_max_headers: usize, h1_max_header_size: usize, ) -> Poll> { let byte = byte!(rdr, cx); match byte { b'\n' => { if *trailers_cnt >= h1_max_headers { return Poll::Ready(Err(io::Error::new( io::ErrorKind::InvalidData, "chunk trailers count overflow", ))); } *trailers_cnt += 1; put_u8!( trailers_buf.as_mut().expect("trailers_buf is None"), byte, h1_max_header_size ); Poll::Ready(Ok(ChunkedState::EndCr)) } _ => Poll::Ready(Err(io::Error::new( io::ErrorKind::InvalidInput, "Invalid trailer end LF", ))), } } fn read_end_cr( cx: &mut Context<'_>, rdr: &mut R, trailers_buf: &mut Option, h1_max_header_size: usize, ) -> Poll> { let byte = byte!(rdr, cx); match byte { b'\r' => { if let Some(trailers_buf) = trailers_buf { put_u8!(trailers_buf, byte, h1_max_header_size); } Poll::Ready(Ok(ChunkedState::EndLf)) } byte => { match trailers_buf { None => { // 64 will fit a single Expires header without reallocating let mut buf = BytesMut::with_capacity(64); buf.put_u8(byte); *trailers_buf = Some(buf); } Some(ref mut trailers_buf) => { put_u8!(trailers_buf, byte, h1_max_header_size); } } Poll::Ready(Ok(ChunkedState::Trailer)) } } } fn read_end_lf( cx: &mut Context<'_>, rdr: &mut R, trailers_buf: &mut Option, h1_max_header_size: usize, ) -> Poll> { let byte = byte!(rdr, cx); match byte { b'\n' => { if let Some(trailers_buf) = trailers_buf { put_u8!(trailers_buf, byte, h1_max_header_size); } Poll::Ready(Ok(ChunkedState::End)) } _ => Poll::Ready(Err(io::Error::new( io::ErrorKind::InvalidInput, "Invalid chunk end LF", ))), } } } // TODO: disallow Transfer-Encoding, Content-Length, Trailer, etc in trailers ?? fn decode_trailers(buf: &mut BytesMut, count: usize) -> Result { let mut trailers = HeaderMap::new(); let mut headers = vec![httparse::EMPTY_HEADER; count]; let res = httparse::parse_headers(buf, &mut headers); match res { Ok(httparse::Status::Complete((_, headers))) => { for header in headers.iter() { use std::convert::TryFrom; let name = match HeaderName::try_from(header.name) { Ok(name) => name, Err(_) => { return Err(io::Error::new( io::ErrorKind::InvalidInput, format!("Invalid header name: {:?}", &header), )); } }; let value = match HeaderValue::from_bytes(header.value) { Ok(value) => value, Err(_) => { return Err(io::Error::new( io::ErrorKind::InvalidInput, format!("Invalid header value: {:?}", &header), )); } }; trailers.insert(name, value); } Ok(trailers) } Ok(httparse::Status::Partial) => Err(io::Error::new( io::ErrorKind::InvalidInput, "Partial header", )), Err(e) => Err(io::Error::new(io::ErrorKind::InvalidInput, e)), } } #[derive(Debug)] struct IncompleteBody; impl fmt::Display for IncompleteBody { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "end of file before message length reached") } } impl StdError for IncompleteBody {} #[cfg(test)] mod tests { use super::*; use crate::rt::{Read, ReadBuf}; use std::pin::Pin; use std::time::Duration; impl MemRead for &[u8] { fn read_mem(&mut self, _: &mut Context<'_>, len: usize) -> Poll> { let n = std::cmp::min(len, self.len()); if n > 0 { let (a, b) = self.split_at(n); let buf = Bytes::copy_from_slice(a); *self = b; Poll::Ready(Ok(buf)) } else { Poll::Ready(Ok(Bytes::new())) } } } impl MemRead for &mut (dyn Read + Unpin) { fn read_mem(&mut self, cx: &mut Context<'_>, len: usize) -> Poll> { let mut v = vec![0; len]; let mut buf = ReadBuf::new(&mut v); ready!(Pin::new(self).poll_read(cx, buf.unfilled())?); Poll::Ready(Ok(Bytes::copy_from_slice(buf.filled()))) } } impl MemRead for Bytes { fn read_mem(&mut self, _: &mut Context<'_>, len: usize) -> Poll> { let n = std::cmp::min(len, self.len()); let ret = self.split_to(n); Poll::Ready(Ok(ret)) } } /* use std::io; use std::io::Write; use super::Decoder; use super::ChunkedState; use futures::{Async, Poll}; use bytes::{BytesMut, Bytes}; use crate::mock::AsyncIo; */ #[cfg(not(miri))] #[tokio::test] async fn test_read_chunk_size() { use std::io::ErrorKind::{InvalidData, InvalidInput, UnexpectedEof}; async fn read(s: &str) -> u64 { let mut state = ChunkedState::new(); let rdr = &mut s.as_bytes(); let mut size = 0; let mut ext_cnt = 0; let mut trailers_cnt = 0; loop { let result = futures_util::future::poll_fn(|cx| { state.step( cx, rdr, &mut size, &mut ext_cnt, &mut None, &mut None, &mut trailers_cnt, DEFAULT_MAX_HEADERS, TRAILER_LIMIT, ) }) .await; let desc = format!("read_size failed for {:?}", s); state = result.expect(&desc); if state == ChunkedState::Body || state == ChunkedState::EndCr { break; } } size } async fn read_err(s: &str, expected_err: io::ErrorKind) { let mut state = ChunkedState::new(); let rdr = &mut s.as_bytes(); let mut size = 0; let mut ext_cnt = 0; let mut trailers_cnt = 0; loop { let result = futures_util::future::poll_fn(|cx| { state.step( cx, rdr, &mut size, &mut ext_cnt, &mut None, &mut None, &mut trailers_cnt, DEFAULT_MAX_HEADERS, TRAILER_LIMIT, ) }) .await; state = match result { Ok(s) => s, Err(e) => { assert!( expected_err == e.kind(), "Reading {:?}, expected {:?}, but got {:?}", s, expected_err, e.kind() ); return; } }; if state == ChunkedState::Body || state == ChunkedState::End { panic!("Was Ok. Expected Err for {:?}", s); } } } assert_eq!(1, read("1\r\n").await); assert_eq!(1, read("01\r\n").await); assert_eq!(0, read("0\r\n").await); assert_eq!(0, read("00\r\n").await); assert_eq!(10, read("A\r\n").await); assert_eq!(10, read("a\r\n").await); assert_eq!(255, read("Ff\r\n").await); assert_eq!(255, read("Ff \r\n").await); // Missing LF or CRLF read_err("F\rF", InvalidInput).await; read_err("F", UnexpectedEof).await; // Missing digit read_err("\r\n\r\n", InvalidInput).await; read_err("\r\n", InvalidInput).await; // Invalid hex digit read_err("X\r\n", InvalidInput).await; read_err("1X\r\n", InvalidInput).await; read_err("-\r\n", InvalidInput).await; read_err("-1\r\n", InvalidInput).await; // Acceptable (if not fully valid) extensions do not influence the size assert_eq!(1, read("1;extension\r\n").await); assert_eq!(10, read("a;ext name=value\r\n").await); assert_eq!(1, read("1;extension;extension2\r\n").await); assert_eq!(1, read("1;;; ;\r\n").await); assert_eq!(2, read("2; extension...\r\n").await); assert_eq!(3, read("3 ; extension=123\r\n").await); assert_eq!(3, read("3 ;\r\n").await); assert_eq!(3, read("3 ; \r\n").await); // Invalid extensions cause an error read_err("1 invalid extension\r\n", InvalidInput).await; read_err("1 A\r\n", InvalidInput).await; read_err("1;no CRLF", UnexpectedEof).await; read_err("1;reject\nnewlines\r\n", InvalidData).await; // Overflow read_err("f0000000000000003\r\n", InvalidData).await; } #[cfg(not(miri))] #[tokio::test] async fn test_read_sized_early_eof() { let mut bytes = &b"foo bar"[..]; let mut decoder = Decoder::length(10); assert_eq!( decoder .decode_fut(&mut bytes) .await .unwrap() .data_ref() .unwrap() .len(), 7 ); let e = decoder.decode_fut(&mut bytes).await.unwrap_err(); assert_eq!(e.kind(), io::ErrorKind::UnexpectedEof); } #[cfg(not(miri))] #[tokio::test] async fn test_read_chunked_early_eof() { let mut bytes = &b"\ 9\r\n\ foo bar\ "[..]; let mut decoder = Decoder::chunked(None, None); assert_eq!( decoder .decode_fut(&mut bytes) .await .unwrap() .data_ref() .unwrap() .len(), 7 ); let e = decoder.decode_fut(&mut bytes).await.unwrap_err(); assert_eq!(e.kind(), io::ErrorKind::UnexpectedEof); } #[cfg(not(miri))] #[tokio::test] async fn test_read_chunked_single_read() { let mut mock_buf = &b"10\r\n1234567890abcdef\r\n0\r\n"[..]; let buf = Decoder::chunked(None, None) .decode_fut(&mut mock_buf) .await .expect("decode") .into_data() .expect("unknown frame type"); assert_eq!(16, buf.len()); let result = String::from_utf8(buf.as_ref().to_vec()).expect("decode String"); assert_eq!("1234567890abcdef", &result); } #[tokio::test] async fn test_read_chunked_with_missing_zero_digit() { // After reading a valid chunk, the ending is missing a zero. let mut mock_buf = &b"1\r\nZ\r\n\r\n\r\n"[..]; let mut decoder = Decoder::chunked(None, None); let buf = decoder .decode_fut(&mut mock_buf) .await .expect("decode") .into_data() .expect("unknown frame type"); assert_eq!("Z", buf); let err = decoder .decode_fut(&mut mock_buf) .await .expect_err("decode 2"); assert_eq!(err.kind(), io::ErrorKind::InvalidInput); } #[tokio::test] async fn test_read_chunked_extensions_over_limit() { // construct a chunked body where each individual chunked extension // is totally fine, but combined is over the limit. let per_chunk = super::CHUNKED_EXTENSIONS_LIMIT * 2 / 3; let mut scratch = vec![]; for _ in 0..2 { scratch.extend(b"1;"); scratch.extend(b"x".repeat(per_chunk as usize)); scratch.extend(b"\r\nA\r\n"); } scratch.extend(b"0\r\n\r\n"); let mut mock_buf = Bytes::from(scratch); let mut decoder = Decoder::chunked(None, None); let buf1 = decoder .decode_fut(&mut mock_buf) .await .expect("decode1") .into_data() .expect("unknown frame type"); assert_eq!(&buf1[..], b"A"); let err = decoder .decode_fut(&mut mock_buf) .await .expect_err("decode2"); assert_eq!(err.kind(), io::ErrorKind::InvalidData); assert_eq!(err.to_string(), "chunk extensions over limit"); } #[cfg(not(miri))] #[tokio::test] async fn test_read_chunked_trailer_with_missing_lf() { let mut mock_buf = &b"10\r\n1234567890abcdef\r\n0\r\nbad\r\r\n"[..]; let mut decoder = Decoder::chunked(None, None); decoder.decode_fut(&mut mock_buf).await.expect("decode"); let e = decoder.decode_fut(&mut mock_buf).await.unwrap_err(); assert_eq!(e.kind(), io::ErrorKind::InvalidInput); } #[cfg(not(miri))] #[tokio::test] async fn test_read_chunked_after_eof() { let mut mock_buf = &b"10\r\n1234567890abcdef\r\n0\r\n\r\n"[..]; let mut decoder = Decoder::chunked(None, None); // normal read let buf = decoder .decode_fut(&mut mock_buf) .await .unwrap() .into_data() .expect("unknown frame type"); assert_eq!(16, buf.len()); let result = String::from_utf8(buf.as_ref().to_vec()).expect("decode String"); assert_eq!("1234567890abcdef", &result); // eof read let buf = decoder .decode_fut(&mut mock_buf) .await .expect("decode") .into_data() .expect("unknown frame type"); assert_eq!(0, buf.len()); // ensure read after eof also returns eof let buf = decoder .decode_fut(&mut mock_buf) .await .expect("decode") .into_data() .expect("unknown frame type"); assert_eq!(0, buf.len()); } // perform an async read using a custom buffer size and causing a blocking // read at the specified byte async fn read_async(mut decoder: Decoder, content: &[u8], block_at: usize) -> String { let mut outs = Vec::new(); let mut ins = crate::common::io::Compat::new(if block_at == 0 { tokio_test::io::Builder::new() .wait(Duration::from_millis(10)) .read(content) .build() } else { tokio_test::io::Builder::new() .read(&content[..block_at]) .wait(Duration::from_millis(10)) .read(&content[block_at..]) .build() }); let mut ins = &mut ins as &mut (dyn Read + Unpin); loop { let buf = decoder .decode_fut(&mut ins) .await .expect("unexpected decode error") .into_data() .expect("unexpected frame type"); if buf.is_empty() { break; // eof } outs.extend(buf.as_ref()); } String::from_utf8(outs).expect("decode String") } // iterate over the different ways that this async read could go. // tests blocking a read at each byte along the content - The shotgun approach async fn all_async_cases(content: &str, expected: &str, decoder: Decoder) { let content_len = content.len(); for block_at in 0..content_len { let actual = read_async(decoder.clone(), content.as_bytes(), block_at).await; assert_eq!(expected, &actual) //, "Failed async. Blocking at {}", block_at); } } #[cfg(not(miri))] #[tokio::test] async fn test_read_length_async() { let content = "foobar"; all_async_cases(content, content, Decoder::length(content.len() as u64)).await; } #[cfg(not(miri))] #[tokio::test] async fn test_read_chunked_async() { let content = "3\r\nfoo\r\n3\r\nbar\r\n0\r\n\r\n"; let expected = "foobar"; all_async_cases(content, expected, Decoder::chunked(None, None)).await; } #[cfg(not(miri))] #[tokio::test] async fn test_read_eof_async() { let content = "foobar"; all_async_cases(content, content, Decoder::eof()).await; } #[cfg(all(feature = "nightly", not(miri)))] #[bench] fn bench_decode_chunked_1kb(b: &mut test::Bencher) { let rt = new_runtime(); const LEN: usize = 1024; let mut vec = Vec::new(); vec.extend(format!("{:x}\r\n", LEN).as_bytes()); vec.extend(&[0; LEN][..]); vec.extend(b"\r\n"); let content = Bytes::from(vec); b.bytes = LEN as u64; b.iter(|| { let mut decoder = Decoder::chunked(None, None); rt.block_on(async { let mut raw = content.clone(); let chunk = decoder .decode_fut(&mut raw) .await .unwrap() .into_data() .unwrap(); assert_eq!(chunk.len(), LEN); }); }); } #[cfg(all(feature = "nightly", not(miri)))] #[bench] fn bench_decode_length_1kb(b: &mut test::Bencher) { let rt = new_runtime(); const LEN: usize = 1024; let content = Bytes::from(&[0; LEN][..]); b.bytes = LEN as u64; b.iter(|| { let mut decoder = Decoder::length(LEN as u64); rt.block_on(async { let mut raw = content.clone(); let chunk = decoder .decode_fut(&mut raw) .await .unwrap() .into_data() .unwrap(); assert_eq!(chunk.len(), LEN); }); }); } #[cfg(feature = "nightly")] fn new_runtime() -> tokio::runtime::Runtime { tokio::runtime::Builder::new_current_thread() .enable_all() .build() .expect("rt build") } #[test] fn test_decode_trailers() { let mut buf = BytesMut::new(); buf.extend_from_slice( b"Expires: Wed, 21 Oct 2015 07:28:00 GMT\r\nX-Stream-Error: failed to decode\r\n\r\n", ); let headers = decode_trailers(&mut buf, 2).expect("decode_trailers"); assert_eq!(headers.len(), 2); assert_eq!( headers.get("Expires").unwrap(), "Wed, 21 Oct 2015 07:28:00 GMT" ); assert_eq!(headers.get("X-Stream-Error").unwrap(), "failed to decode"); } #[tokio::test] async fn test_trailer_max_headers_enforced() { let h1_max_headers = 10; let mut scratch = vec![]; scratch.extend(b"10\r\n1234567890abcdef\r\n0\r\n"); for i in 0..h1_max_headers { scratch.extend(format!("trailer{}: {}\r\n", i, i).as_bytes()); } scratch.extend(b"\r\n"); let mut mock_buf = Bytes::from(scratch); let mut decoder = Decoder::chunked(Some(h1_max_headers), None); // ready chunked body let buf = decoder .decode_fut(&mut mock_buf) .await .unwrap() .into_data() .expect("unknown frame type"); assert_eq!(16, buf.len()); // eof read let err = decoder .decode_fut(&mut mock_buf) .await .expect_err("trailer fields over limit"); assert_eq!(err.kind(), io::ErrorKind::InvalidData); } #[tokio::test] async fn test_trailer_max_header_size_huge_trailer() { let max_header_size = 1024; let mut scratch = vec![]; scratch.extend(b"10\r\n1234567890abcdef\r\n0\r\n"); scratch.extend(format!("huge_trailer: {}\r\n", "x".repeat(max_header_size)).as_bytes()); scratch.extend(b"\r\n"); let mut mock_buf = Bytes::from(scratch); let mut decoder = Decoder::chunked(None, Some(max_header_size)); // ready chunked body let buf = decoder .decode_fut(&mut mock_buf) .await .unwrap() .into_data() .expect("unknown frame type"); assert_eq!(16, buf.len()); // eof read let err = decoder .decode_fut(&mut mock_buf) .await .expect_err("trailers over limit"); assert_eq!(err.kind(), io::ErrorKind::InvalidData); } #[tokio::test] async fn test_trailer_max_header_size_many_small_trailers() { let max_headers = 10; let header_size = 64; let mut scratch = vec![]; scratch.extend(b"10\r\n1234567890abcdef\r\n0\r\n"); for i in 0..max_headers { scratch.extend(format!("trailer{}: {}\r\n", i, "x".repeat(header_size)).as_bytes()); } scratch.extend(b"\r\n"); let mut mock_buf = Bytes::from(scratch); let mut decoder = Decoder::chunked(None, Some(max_headers * header_size)); // ready chunked body let buf = decoder .decode_fut(&mut mock_buf) .await .unwrap() .into_data() .expect("unknown frame type"); assert_eq!(16, buf.len()); // eof read let err = decoder .decode_fut(&mut mock_buf) .await .expect_err("trailers over limit"); assert_eq!(err.kind(), io::ErrorKind::InvalidData); } } hyper-1.5.2/src/proto/h1/dispatch.rs000064400000000000000000000721641046102023000154010ustar 00000000000000use std::{ error::Error as StdError, future::Future, marker::Unpin, pin::Pin, task::{Context, Poll}, }; use crate::rt::{Read, Write}; use bytes::{Buf, Bytes}; use futures_util::ready; use http::Request; use super::{Http1Transaction, Wants}; use crate::body::{Body, DecodedLength, Incoming as IncomingBody}; #[cfg(feature = "client")] use crate::client::dispatch::TrySendError; use crate::common::task; use crate::proto::{BodyLength, Conn, Dispatched, MessageHead, RequestHead}; use crate::upgrade::OnUpgrade; pub(crate) struct Dispatcher { conn: Conn, dispatch: D, body_tx: Option, body_rx: Pin>>, is_closing: bool, } pub(crate) trait Dispatch { type PollItem; type PollBody; type PollError; type RecvItem; fn poll_msg( self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll>>; fn recv_msg(&mut self, msg: crate::Result<(Self::RecvItem, IncomingBody)>) -> crate::Result<()>; fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll>; fn should_poll(&self) -> bool; } cfg_server! { use crate::service::HttpService; pub(crate) struct Server, B> { in_flight: Pin>>, pub(crate) service: S, } } cfg_client! { pin_project_lite::pin_project! { pub(crate) struct Client { callback: Option, http::Response>>, #[pin] rx: ClientRx, rx_closed: bool, } } type ClientRx = crate::client::dispatch::Receiver, http::Response>; } impl Dispatcher where D: Dispatch< PollItem = MessageHead, PollBody = Bs, RecvItem = MessageHead, > + Unpin, D::PollError: Into>, I: Read + Write + Unpin, T: Http1Transaction + Unpin, Bs: Body + 'static, Bs::Error: Into>, { pub(crate) fn new(dispatch: D, conn: Conn) -> Self { Dispatcher { conn, dispatch, body_tx: None, body_rx: Box::pin(None), is_closing: false, } } #[cfg(feature = "server")] pub(crate) fn disable_keep_alive(&mut self) { self.conn.disable_keep_alive(); // If keep alive has been disabled and no read or write has been seen on // the connection yet, we must be in a state where the server is being asked to // shut down before any data has been seen on the connection if self.conn.is_write_closed() || self.conn.has_initial_read_write_state() { self.close(); } } pub(crate) fn into_inner(self) -> (I, Bytes, D) { let (io, buf) = self.conn.into_inner(); (io, buf, self.dispatch) } /// Run this dispatcher until HTTP says this connection is done, /// but don't call `Write::shutdown` on the underlying IO. /// /// This is useful for old-style HTTP upgrades, but ignores /// newer-style upgrade API. pub(crate) fn poll_without_shutdown( &mut self, cx: &mut Context<'_>, ) -> Poll> { Pin::new(self).poll_catch(cx, false).map_ok(|ds| { if let Dispatched::Upgrade(pending) = ds { pending.manual(); } }) } fn poll_catch( &mut self, cx: &mut Context<'_>, should_shutdown: bool, ) -> Poll> { Poll::Ready(ready!(self.poll_inner(cx, should_shutdown)).or_else(|e| { // Be sure to alert a streaming body of the failure. if let Some(mut body) = self.body_tx.take() { body.send_error(crate::Error::new_body("connection error")); } // An error means we're shutting down either way. // We just try to give the error to the user, // and close the connection with an Ok. If we // cannot give it to the user, then return the Err. self.dispatch.recv_msg(Err(e))?; Ok(Dispatched::Shutdown) })) } fn poll_inner( &mut self, cx: &mut Context<'_>, should_shutdown: bool, ) -> Poll> { T::update_date(); ready!(self.poll_loop(cx))?; if self.is_done() { if let Some(pending) = self.conn.pending_upgrade() { self.conn.take_error()?; return Poll::Ready(Ok(Dispatched::Upgrade(pending))); } else if should_shutdown { ready!(self.conn.poll_shutdown(cx)).map_err(crate::Error::new_shutdown)?; } self.conn.take_error()?; Poll::Ready(Ok(Dispatched::Shutdown)) } else { Poll::Pending } } fn poll_loop(&mut self, cx: &mut Context<'_>) -> Poll> { // Limit the looping on this connection, in case it is ready far too // often, so that other futures don't starve. // // 16 was chosen arbitrarily, as that is number of pipelined requests // benchmarks often use. Perhaps it should be a config option instead. for _ in 0..16 { let _ = self.poll_read(cx)?; let _ = self.poll_write(cx)?; let _ = self.poll_flush(cx)?; // This could happen if reading paused before blocking on IO, // such as getting to the end of a framed message, but then // writing/flushing set the state back to Init. In that case, // if the read buffer still had bytes, we'd want to try poll_read // again, or else we wouldn't ever be woken up again. // // Using this instead of task::current() and notify() inside // the Conn is noticeably faster in pipelined benchmarks. if !self.conn.wants_read_again() { //break; return Poll::Ready(Ok(())); } } trace!("poll_loop yielding (self = {:p})", self); task::yield_now(cx).map(|never| match never {}) } fn poll_read(&mut self, cx: &mut Context<'_>) -> Poll> { loop { if self.is_closing { return Poll::Ready(Ok(())); } else if self.conn.can_read_head() { ready!(self.poll_read_head(cx))?; } else if let Some(mut body) = self.body_tx.take() { if self.conn.can_read_body() { match body.poll_ready(cx) { Poll::Ready(Ok(())) => (), Poll::Pending => { self.body_tx = Some(body); return Poll::Pending; } Poll::Ready(Err(_canceled)) => { // user doesn't care about the body // so we should stop reading trace!("body receiver dropped before eof, draining or closing"); self.conn.poll_drain_or_close_read(cx); continue; } } match self.conn.poll_read_body(cx) { Poll::Ready(Some(Ok(frame))) => { if frame.is_data() { let chunk = frame.into_data().unwrap_or_else(|_| unreachable!()); match body.try_send_data(chunk) { Ok(()) => { self.body_tx = Some(body); } Err(_canceled) => { if self.conn.can_read_body() { trace!("body receiver dropped before eof, closing"); self.conn.close_read(); } } } } else if frame.is_trailers() { let trailers = frame.into_trailers().unwrap_or_else(|_| unreachable!()); match body.try_send_trailers(trailers) { Ok(()) => { self.body_tx = Some(body); } Err(_canceled) => { if self.conn.can_read_body() { trace!("body receiver dropped before eof, closing"); self.conn.close_read(); } } } } else { // we should have dropped all unknown frames in poll_read_body error!("unexpected frame"); } } Poll::Ready(None) => { // just drop, the body will close automatically } Poll::Pending => { self.body_tx = Some(body); return Poll::Pending; } Poll::Ready(Some(Err(e))) => { body.send_error(crate::Error::new_body(e)); } } } else { // just drop, the body will close automatically } } else { return self.conn.poll_read_keep_alive(cx); } } } fn poll_read_head(&mut self, cx: &mut Context<'_>) -> Poll> { // can dispatch receive, or does it still care about other incoming message? match ready!(self.dispatch.poll_ready(cx)) { Ok(()) => (), Err(()) => { trace!("dispatch no longer receiving messages"); self.close(); return Poll::Ready(Ok(())); } } // dispatch is ready for a message, try to read one match ready!(self.conn.poll_read_head(cx)) { Some(Ok((mut head, body_len, wants))) => { let body = match body_len { DecodedLength::ZERO => IncomingBody::empty(), other => { let (tx, rx) = IncomingBody::new_channel(other, wants.contains(Wants::EXPECT)); self.body_tx = Some(tx); rx } }; if wants.contains(Wants::UPGRADE) { let upgrade = self.conn.on_upgrade(); debug_assert!(!upgrade.is_none(), "empty upgrade"); debug_assert!( head.extensions.get::().is_none(), "OnUpgrade already set" ); head.extensions.insert(upgrade); } self.dispatch.recv_msg(Ok((head, body)))?; Poll::Ready(Ok(())) } Some(Err(err)) => { debug!("read_head error: {}", err); self.dispatch.recv_msg(Err(err))?; // if here, the dispatcher gave the user the error // somewhere else. we still need to shutdown, but // not as a second error. self.close(); Poll::Ready(Ok(())) } None => { // read eof, the write side will have been closed too unless // allow_read_close was set to true, in which case just do // nothing... debug_assert!(self.conn.is_read_closed()); if self.conn.is_write_closed() { self.close(); } Poll::Ready(Ok(())) } } } fn poll_write(&mut self, cx: &mut Context<'_>) -> Poll> { loop { if self.is_closing { return Poll::Ready(Ok(())); } else if self.body_rx.is_none() && self.conn.can_write_head() && self.dispatch.should_poll() { if let Some(msg) = ready!(Pin::new(&mut self.dispatch).poll_msg(cx)) { let (head, body) = msg.map_err(crate::Error::new_user_service)?; let body_type = if body.is_end_stream() { self.body_rx.set(None); None } else { let btype = body .size_hint() .exact() .map(BodyLength::Known) .or(Some(BodyLength::Unknown)); self.body_rx.set(Some(body)); btype }; self.conn.write_head(head, body_type); } else { self.close(); return Poll::Ready(Ok(())); } } else if !self.conn.can_buffer_body() { ready!(self.poll_flush(cx))?; } else { // A new scope is needed :( if let (Some(mut body), clear_body) = OptGuard::new(self.body_rx.as_mut()).guard_mut() { debug_assert!(!*clear_body, "opt guard defaults to keeping body"); if !self.conn.can_write_body() { trace!( "no more write body allowed, user body is_end_stream = {}", body.is_end_stream(), ); *clear_body = true; continue; } let item = ready!(body.as_mut().poll_frame(cx)); if let Some(item) = item { let frame = item.map_err(|e| { *clear_body = true; crate::Error::new_user_body(e) })?; if frame.is_data() { let chunk = frame.into_data().unwrap_or_else(|_| unreachable!()); let eos = body.is_end_stream(); if eos { *clear_body = true; if chunk.remaining() == 0 { trace!("discarding empty chunk"); self.conn.end_body()?; } else { self.conn.write_body_and_end(chunk); } } else { if chunk.remaining() == 0 { trace!("discarding empty chunk"); continue; } self.conn.write_body(chunk); } } else if frame.is_trailers() { *clear_body = true; self.conn.write_trailers( frame.into_trailers().unwrap_or_else(|_| unreachable!()), ); } else { trace!("discarding unknown frame"); continue; } } else { *clear_body = true; self.conn.end_body()?; } } else { // If there's no body_rx, end the body if self.conn.can_write_body() { self.conn.end_body()?; } else { return Poll::Pending; } } } } } fn poll_flush(&mut self, cx: &mut Context<'_>) -> Poll> { self.conn.poll_flush(cx).map_err(|err| { debug!("error writing: {}", err); crate::Error::new_body_write(err) }) } fn close(&mut self) { self.is_closing = true; self.conn.close_read(); self.conn.close_write(); } fn is_done(&self) -> bool { if self.is_closing { return true; } let read_done = self.conn.is_read_closed(); if !T::should_read_first() && read_done { // a client that cannot read may was well be done. true } else { let write_done = self.conn.is_write_closed() || (!self.dispatch.should_poll() && self.body_rx.is_none()); read_done && write_done } } } impl Future for Dispatcher where D: Dispatch< PollItem = MessageHead, PollBody = Bs, RecvItem = MessageHead, > + Unpin, D::PollError: Into>, I: Read + Write + Unpin, T: Http1Transaction + Unpin, Bs: Body + 'static, Bs::Error: Into>, { type Output = crate::Result; #[inline] fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { self.poll_catch(cx, true) } } // ===== impl OptGuard ===== /// A drop guard to allow a mutable borrow of an Option while being able to /// set whether the `Option` should be cleared on drop. struct OptGuard<'a, T>(Pin<&'a mut Option>, bool); impl<'a, T> OptGuard<'a, T> { fn new(pin: Pin<&'a mut Option>) -> Self { OptGuard(pin, false) } fn guard_mut(&mut self) -> (Option>, &mut bool) { (self.0.as_mut().as_pin_mut(), &mut self.1) } } impl Drop for OptGuard<'_, T> { fn drop(&mut self) { if self.1 { self.0.set(None); } } } // ===== impl Server ===== cfg_server! { impl Server where S: HttpService, { pub(crate) fn new(service: S) -> Server { Server { in_flight: Box::pin(None), service, } } pub(crate) fn into_service(self) -> S { self.service } } // Service is never pinned impl, B> Unpin for Server {} impl Dispatch for Server where S: HttpService, S::Error: Into>, Bs: Body, { type PollItem = MessageHead; type PollBody = Bs; type PollError = S::Error; type RecvItem = RequestHead; fn poll_msg( mut self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll>> { let mut this = self.as_mut(); let ret = if let Some(ref mut fut) = this.in_flight.as_mut().as_pin_mut() { let resp = ready!(fut.as_mut().poll(cx)?); let (parts, body) = resp.into_parts(); let head = MessageHead { version: parts.version, subject: parts.status, headers: parts.headers, extensions: parts.extensions, }; Poll::Ready(Some(Ok((head, body)))) } else { unreachable!("poll_msg shouldn't be called if no inflight"); }; // Since in_flight finished, remove it this.in_flight.set(None); ret } fn recv_msg(&mut self, msg: crate::Result<(Self::RecvItem, IncomingBody)>) -> crate::Result<()> { let (msg, body) = msg?; let mut req = Request::new(body); *req.method_mut() = msg.subject.0; *req.uri_mut() = msg.subject.1; *req.headers_mut() = msg.headers; *req.version_mut() = msg.version; *req.extensions_mut() = msg.extensions; let fut = self.service.call(req); self.in_flight.set(Some(fut)); Ok(()) } fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll> { if self.in_flight.is_some() { Poll::Pending } else { Poll::Ready(Ok(())) } } fn should_poll(&self) -> bool { self.in_flight.is_some() } } } // ===== impl Client ===== cfg_client! { use std::convert::Infallible; impl Client { pub(crate) fn new(rx: ClientRx) -> Client { Client { callback: None, rx, rx_closed: false, } } } impl Dispatch for Client where B: Body, { type PollItem = RequestHead; type PollBody = B; type PollError = Infallible; type RecvItem = crate::proto::ResponseHead; fn poll_msg( mut self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll>> { let mut this = self.as_mut(); debug_assert!(!this.rx_closed); match this.rx.poll_recv(cx) { Poll::Ready(Some((req, mut cb))) => { // check that future hasn't been canceled already match cb.poll_canceled(cx) { Poll::Ready(()) => { trace!("request canceled"); Poll::Ready(None) } Poll::Pending => { let (parts, body) = req.into_parts(); let head = RequestHead { version: parts.version, subject: crate::proto::RequestLine(parts.method, parts.uri), headers: parts.headers, extensions: parts.extensions, }; this.callback = Some(cb); Poll::Ready(Some(Ok((head, body)))) } } } Poll::Ready(None) => { // user has dropped sender handle trace!("client tx closed"); this.rx_closed = true; Poll::Ready(None) } Poll::Pending => Poll::Pending, } } fn recv_msg(&mut self, msg: crate::Result<(Self::RecvItem, IncomingBody)>) -> crate::Result<()> { match msg { Ok((msg, body)) => { if let Some(cb) = self.callback.take() { let res = msg.into_response(body); cb.send(Ok(res)); Ok(()) } else { // Getting here is likely a bug! An error should have happened // in Conn::require_empty_read() before ever parsing a // full message! Err(crate::Error::new_unexpected_message()) } } Err(err) => { if let Some(cb) = self.callback.take() { cb.send(Err(TrySendError { error: err, message: None, })); Ok(()) } else if !self.rx_closed { self.rx.close(); if let Some((req, cb)) = self.rx.try_recv() { trace!("canceling queued request with connection error: {}", err); // in this case, the message was never even started, so it's safe to tell // the user that the request was completely canceled cb.send(Err(TrySendError { error: crate::Error::new_canceled().with(err), message: Some(req), })); Ok(()) } else { Err(err) } } else { Err(err) } } } } fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { match self.callback { Some(ref mut cb) => match cb.poll_canceled(cx) { Poll::Ready(()) => { trace!("callback receiver has dropped"); Poll::Ready(Err(())) } Poll::Pending => Poll::Ready(Ok(())), }, None => Poll::Ready(Err(())), } } fn should_poll(&self) -> bool { self.callback.is_none() } } } #[cfg(test)] mod tests { use super::*; use crate::common::io::Compat; use crate::proto::h1::ClientTransaction; use std::time::Duration; #[test] fn client_read_bytes_before_writing_request() { let _ = pretty_env_logger::try_init(); tokio_test::task::spawn(()).enter(|cx, _| { let (io, mut handle) = tokio_test::io::Builder::new().build_with_handle(); // Block at 0 for now, but we will release this response before // the request is ready to write later... let (mut tx, rx) = crate::client::dispatch::channel(); let conn = Conn::<_, bytes::Bytes, ClientTransaction>::new(Compat::new(io)); let mut dispatcher = Dispatcher::new(Client::new(rx), conn); // First poll is needed to allow tx to send... assert!(Pin::new(&mut dispatcher).poll(cx).is_pending()); // Unblock our IO, which has a response before we've sent request! // handle.read(b"HTTP/1.1 200 OK\r\n\r\n"); let mut res_rx = tx .try_send(crate::Request::new(IncomingBody::empty())) .unwrap(); tokio_test::assert_ready_ok!(Pin::new(&mut dispatcher).poll(cx)); let err = tokio_test::assert_ready_ok!(Pin::new(&mut res_rx).poll(cx)) .expect_err("callback should send error"); match (err.error.is_canceled(), err.message.as_ref()) { (true, Some(_)) => (), _ => panic!("expected Canceled, got {:?}", err), } }); } #[cfg(not(miri))] #[tokio::test] async fn client_flushing_is_not_ready_for_next_request() { let _ = pretty_env_logger::try_init(); let (io, _handle) = tokio_test::io::Builder::new() .write(b"POST / HTTP/1.1\r\ncontent-length: 4\r\n\r\n") .read(b"HTTP/1.1 200 OK\r\ncontent-length: 0\r\n\r\n") .wait(std::time::Duration::from_secs(2)) .build_with_handle(); let (mut tx, rx) = crate::client::dispatch::channel(); let mut conn = Conn::<_, bytes::Bytes, ClientTransaction>::new(Compat::new(io)); conn.set_write_strategy_queue(); let dispatcher = Dispatcher::new(Client::new(rx), conn); let _dispatcher = tokio::spawn(async move { dispatcher.await }); let body = { let (mut tx, body) = IncomingBody::new_channel(DecodedLength::new(4), false); tx.try_send_data("reee".into()).unwrap(); body }; let req = crate::Request::builder().method("POST").body(body).unwrap(); let res = tx.try_send(req).unwrap().await.expect("response"); drop(res); assert!(!tx.is_ready()); } #[cfg(not(miri))] #[tokio::test] async fn body_empty_chunks_ignored() { let _ = pretty_env_logger::try_init(); let io = tokio_test::io::Builder::new() // no reading or writing, just be blocked for the test... .wait(Duration::from_secs(5)) .build(); let (mut tx, rx) = crate::client::dispatch::channel(); let conn = Conn::<_, bytes::Bytes, ClientTransaction>::new(Compat::new(io)); let mut dispatcher = tokio_test::task::spawn(Dispatcher::new(Client::new(rx), conn)); // First poll is needed to allow tx to send... assert!(dispatcher.poll().is_pending()); let body = { let (mut tx, body) = IncomingBody::channel(); tx.try_send_data("".into()).unwrap(); body }; let _res_rx = tx.try_send(crate::Request::new(body)).unwrap(); // Ensure conn.write_body wasn't called with the empty chunk. // If it is, it will trigger an assertion. assert!(dispatcher.poll().is_pending()); } } hyper-1.5.2/src/proto/h1/encode.rs000064400000000000000000000467221046102023000150400ustar 00000000000000use std::collections::HashMap; use std::fmt; use std::io::IoSlice; use bytes::buf::{Chain, Take}; use bytes::{Buf, Bytes}; use http::{ header::{ AUTHORIZATION, CACHE_CONTROL, CONTENT_ENCODING, CONTENT_LENGTH, CONTENT_RANGE, CONTENT_TYPE, HOST, MAX_FORWARDS, SET_COOKIE, TE, TRAILER, TRANSFER_ENCODING, }, HeaderMap, HeaderName, HeaderValue, }; use super::io::WriteBuf; use super::role::{write_headers, write_headers_title_case}; type StaticBuf = &'static [u8]; /// Encoders to handle different Transfer-Encodings. #[derive(Debug, Clone, PartialEq)] pub(crate) struct Encoder { kind: Kind, is_last: bool, } #[derive(Debug)] pub(crate) struct EncodedBuf { kind: BufKind, } #[derive(Debug)] pub(crate) struct NotEof(u64); #[derive(Debug, PartialEq, Clone)] enum Kind { /// An Encoder for when Transfer-Encoding includes `chunked`. Chunked(Option>), /// An Encoder for when Content-Length is set. /// /// Enforces that the body is not longer than the Content-Length header. Length(u64), /// An Encoder for when neither Content-Length nor Chunked encoding is set. /// /// This is mostly only used with HTTP/1.0 with a length. This kind requires /// the connection to be closed when the body is finished. #[cfg(feature = "server")] CloseDelimited, } #[derive(Debug)] enum BufKind { Exact(B), Limited(Take), Chunked(Chain, StaticBuf>), ChunkedEnd(StaticBuf), Trailers(Chain, StaticBuf>), } impl Encoder { fn new(kind: Kind) -> Encoder { Encoder { kind, is_last: false, } } pub(crate) fn chunked() -> Encoder { Encoder::new(Kind::Chunked(None)) } pub(crate) fn length(len: u64) -> Encoder { Encoder::new(Kind::Length(len)) } #[cfg(feature = "server")] pub(crate) fn close_delimited() -> Encoder { Encoder::new(Kind::CloseDelimited) } pub(crate) fn into_chunked_with_trailing_fields(self, trailers: Vec) -> Encoder { match self.kind { Kind::Chunked(_) => Encoder { kind: Kind::Chunked(Some(trailers)), is_last: self.is_last, }, _ => self, } } pub(crate) fn is_eof(&self) -> bool { matches!(self.kind, Kind::Length(0)) } #[cfg(feature = "server")] pub(crate) fn set_last(mut self, is_last: bool) -> Self { self.is_last = is_last; self } pub(crate) fn is_last(&self) -> bool { self.is_last } pub(crate) fn is_close_delimited(&self) -> bool { match self.kind { #[cfg(feature = "server")] Kind::CloseDelimited => true, _ => false, } } pub(crate) fn is_chunked(&self) -> bool { matches!(self.kind, Kind::Chunked(_)) } pub(crate) fn end(&self) -> Result>, NotEof> { match self.kind { Kind::Length(0) => Ok(None), Kind::Chunked(_) => Ok(Some(EncodedBuf { kind: BufKind::ChunkedEnd(b"0\r\n\r\n"), })), #[cfg(feature = "server")] Kind::CloseDelimited => Ok(None), Kind::Length(n) => Err(NotEof(n)), } } pub(crate) fn encode(&mut self, msg: B) -> EncodedBuf where B: Buf, { let len = msg.remaining(); debug_assert!(len > 0, "encode() called with empty buf"); let kind = match self.kind { Kind::Chunked(_) => { trace!("encoding chunked {}B", len); let buf = ChunkSize::new(len) .chain(msg) .chain(b"\r\n" as &'static [u8]); BufKind::Chunked(buf) } Kind::Length(ref mut remaining) => { trace!("sized write, len = {}", len); if len as u64 > *remaining { let limit = *remaining as usize; *remaining = 0; BufKind::Limited(msg.take(limit)) } else { *remaining -= len as u64; BufKind::Exact(msg) } } #[cfg(feature = "server")] Kind::CloseDelimited => { trace!("close delimited write {}B", len); BufKind::Exact(msg) } }; EncodedBuf { kind } } pub(crate) fn encode_trailers( &self, trailers: HeaderMap, title_case_headers: bool, ) -> Option> { trace!("encoding trailers"); match &self.kind { Kind::Chunked(Some(allowed_trailer_fields)) => { let allowed_trailer_field_map = allowed_trailer_field_map(allowed_trailer_fields); let mut cur_name = None; let mut allowed_trailers = HeaderMap::new(); for (opt_name, value) in trailers { if let Some(n) = opt_name { cur_name = Some(n); } let name = cur_name.as_ref().expect("current header name"); if allowed_trailer_field_map.contains_key(name.as_str()) { if is_valid_trailer_field(name) { allowed_trailers.insert(name, value); } else { debug!("trailer field is not valid: {}", &name); } } else { debug!("trailer header name not found in trailer header: {}", &name); } } let mut buf = Vec::new(); if title_case_headers { write_headers_title_case(&allowed_trailers, &mut buf); } else { write_headers(&allowed_trailers, &mut buf); } if buf.is_empty() { return None; } Some(EncodedBuf { kind: BufKind::Trailers(b"0\r\n".chain(Bytes::from(buf)).chain(b"\r\n")), }) } Kind::Chunked(None) => { debug!("attempted to encode trailers, but the trailer header is not set"); None } _ => { debug!("attempted to encode trailers for non-chunked response"); None } } } pub(super) fn encode_and_end(&self, msg: B, dst: &mut WriteBuf>) -> bool where B: Buf, { let len = msg.remaining(); debug_assert!(len > 0, "encode() called with empty buf"); match self.kind { Kind::Chunked(_) => { trace!("encoding chunked {}B", len); let buf = ChunkSize::new(len) .chain(msg) .chain(b"\r\n0\r\n\r\n" as &'static [u8]); dst.buffer(buf); !self.is_last } Kind::Length(remaining) => { use std::cmp::Ordering; trace!("sized write, len = {}", len); match (len as u64).cmp(&remaining) { Ordering::Equal => { dst.buffer(msg); !self.is_last } Ordering::Greater => { dst.buffer(msg.take(remaining as usize)); !self.is_last } Ordering::Less => { dst.buffer(msg); false } } } #[cfg(feature = "server")] Kind::CloseDelimited => { trace!("close delimited write {}B", len); dst.buffer(msg); false } } } } fn is_valid_trailer_field(name: &HeaderName) -> bool { !matches!( *name, AUTHORIZATION | CACHE_CONTROL | CONTENT_ENCODING | CONTENT_LENGTH | CONTENT_RANGE | CONTENT_TYPE | HOST | MAX_FORWARDS | SET_COOKIE | TRAILER | TRANSFER_ENCODING | TE ) } fn allowed_trailer_field_map(allowed_trailer_fields: &Vec) -> HashMap { let mut trailer_map = HashMap::new(); for header_value in allowed_trailer_fields { if let Ok(header_str) = header_value.to_str() { let items: Vec<&str> = header_str.split(',').map(|item| item.trim()).collect(); for item in items { trailer_map.entry(item.to_string()).or_insert(()); } } } trailer_map } impl Buf for EncodedBuf where B: Buf, { #[inline] fn remaining(&self) -> usize { match self.kind { BufKind::Exact(ref b) => b.remaining(), BufKind::Limited(ref b) => b.remaining(), BufKind::Chunked(ref b) => b.remaining(), BufKind::ChunkedEnd(ref b) => b.remaining(), BufKind::Trailers(ref b) => b.remaining(), } } #[inline] fn chunk(&self) -> &[u8] { match self.kind { BufKind::Exact(ref b) => b.chunk(), BufKind::Limited(ref b) => b.chunk(), BufKind::Chunked(ref b) => b.chunk(), BufKind::ChunkedEnd(ref b) => b.chunk(), BufKind::Trailers(ref b) => b.chunk(), } } #[inline] fn advance(&mut self, cnt: usize) { match self.kind { BufKind::Exact(ref mut b) => b.advance(cnt), BufKind::Limited(ref mut b) => b.advance(cnt), BufKind::Chunked(ref mut b) => b.advance(cnt), BufKind::ChunkedEnd(ref mut b) => b.advance(cnt), BufKind::Trailers(ref mut b) => b.advance(cnt), } } #[inline] fn chunks_vectored<'t>(&'t self, dst: &mut [IoSlice<'t>]) -> usize { match self.kind { BufKind::Exact(ref b) => b.chunks_vectored(dst), BufKind::Limited(ref b) => b.chunks_vectored(dst), BufKind::Chunked(ref b) => b.chunks_vectored(dst), BufKind::ChunkedEnd(ref b) => b.chunks_vectored(dst), BufKind::Trailers(ref b) => b.chunks_vectored(dst), } } } #[cfg(target_pointer_width = "32")] const USIZE_BYTES: usize = 4; #[cfg(target_pointer_width = "64")] const USIZE_BYTES: usize = 8; // each byte will become 2 hex const CHUNK_SIZE_MAX_BYTES: usize = USIZE_BYTES * 2; #[derive(Clone, Copy)] struct ChunkSize { bytes: [u8; CHUNK_SIZE_MAX_BYTES + 2], pos: u8, len: u8, } impl ChunkSize { fn new(len: usize) -> ChunkSize { use std::fmt::Write; let mut size = ChunkSize { bytes: [0; CHUNK_SIZE_MAX_BYTES + 2], pos: 0, len: 0, }; write!(&mut size, "{:X}\r\n", len).expect("CHUNK_SIZE_MAX_BYTES should fit any usize"); size } } impl Buf for ChunkSize { #[inline] fn remaining(&self) -> usize { (self.len - self.pos).into() } #[inline] fn chunk(&self) -> &[u8] { &self.bytes[self.pos.into()..self.len.into()] } #[inline] fn advance(&mut self, cnt: usize) { assert!(cnt <= self.remaining()); self.pos += cnt as u8; // just asserted cnt fits in u8 } } impl fmt::Debug for ChunkSize { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("ChunkSize") .field("bytes", &&self.bytes[..self.len.into()]) .field("pos", &self.pos) .finish() } } impl fmt::Write for ChunkSize { fn write_str(&mut self, num: &str) -> fmt::Result { use std::io::Write; (&mut self.bytes[self.len.into()..]) .write_all(num.as_bytes()) .expect("&mut [u8].write() cannot error"); self.len += num.len() as u8; // safe because bytes is never bigger than 256 Ok(()) } } impl From for EncodedBuf { fn from(buf: B) -> Self { EncodedBuf { kind: BufKind::Exact(buf), } } } impl From> for EncodedBuf { fn from(buf: Take) -> Self { EncodedBuf { kind: BufKind::Limited(buf), } } } impl From, StaticBuf>> for EncodedBuf { fn from(buf: Chain, StaticBuf>) -> Self { EncodedBuf { kind: BufKind::Chunked(buf), } } } impl fmt::Display for NotEof { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "early end, expected {} more bytes", self.0) } } impl std::error::Error for NotEof {} #[cfg(test)] mod tests { use bytes::BufMut; use http::{ header::{ AUTHORIZATION, CACHE_CONTROL, CONTENT_ENCODING, CONTENT_LENGTH, CONTENT_RANGE, CONTENT_TYPE, HOST, MAX_FORWARDS, SET_COOKIE, TE, TRAILER, TRANSFER_ENCODING, }, HeaderMap, HeaderName, HeaderValue, }; use super::super::io::Cursor; use super::Encoder; #[test] fn chunked() { let mut encoder = Encoder::chunked(); let mut dst = Vec::new(); let msg1 = b"foo bar".as_ref(); let buf1 = encoder.encode(msg1); dst.put(buf1); assert_eq!(dst, b"7\r\nfoo bar\r\n"); let msg2 = b"baz quux herp".as_ref(); let buf2 = encoder.encode(msg2); dst.put(buf2); assert_eq!(dst, b"7\r\nfoo bar\r\nD\r\nbaz quux herp\r\n"); let end = encoder.end::>>().unwrap().unwrap(); dst.put(end); assert_eq!( dst, b"7\r\nfoo bar\r\nD\r\nbaz quux herp\r\n0\r\n\r\n".as_ref() ); } #[test] fn length() { let max_len = 8; let mut encoder = Encoder::length(max_len as u64); let mut dst = Vec::new(); let msg1 = b"foo bar".as_ref(); let buf1 = encoder.encode(msg1); dst.put(buf1); assert_eq!(dst, b"foo bar"); assert!(!encoder.is_eof()); encoder.end::<()>().unwrap_err(); let msg2 = b"baz".as_ref(); let buf2 = encoder.encode(msg2); dst.put(buf2); assert_eq!(dst.len(), max_len); assert_eq!(dst, b"foo barb"); assert!(encoder.is_eof()); assert!(encoder.end::<()>().unwrap().is_none()); } #[cfg(feature = "server")] #[test] fn eof() { let mut encoder = Encoder::close_delimited(); let mut dst = Vec::new(); let msg1 = b"foo bar".as_ref(); let buf1 = encoder.encode(msg1); dst.put(buf1); assert_eq!(dst, b"foo bar"); assert!(!encoder.is_eof()); encoder.end::<()>().unwrap(); let msg2 = b"baz".as_ref(); let buf2 = encoder.encode(msg2); dst.put(buf2); assert_eq!(dst, b"foo barbaz"); assert!(!encoder.is_eof()); encoder.end::<()>().unwrap(); } #[test] fn chunked_with_valid_trailers() { let encoder = Encoder::chunked(); let trailers = vec![HeaderValue::from_static("chunky-trailer")]; let encoder = encoder.into_chunked_with_trailing_fields(trailers); let headers = HeaderMap::from_iter(vec![ ( HeaderName::from_static("chunky-trailer"), HeaderValue::from_static("header data"), ), ( HeaderName::from_static("should-not-be-included"), HeaderValue::from_static("oops"), ), ]); let buf1 = encoder.encode_trailers::<&[u8]>(headers, false).unwrap(); let mut dst = Vec::new(); dst.put(buf1); assert_eq!(dst, b"0\r\nchunky-trailer: header data\r\n\r\n"); } #[test] fn chunked_with_multiple_trailer_headers() { let encoder = Encoder::chunked(); let trailers = vec![ HeaderValue::from_static("chunky-trailer"), HeaderValue::from_static("chunky-trailer-2"), ]; let encoder = encoder.into_chunked_with_trailing_fields(trailers); let headers = HeaderMap::from_iter(vec![ ( HeaderName::from_static("chunky-trailer"), HeaderValue::from_static("header data"), ), ( HeaderName::from_static("chunky-trailer-2"), HeaderValue::from_static("more header data"), ), ]); let buf1 = encoder.encode_trailers::<&[u8]>(headers, false).unwrap(); let mut dst = Vec::new(); dst.put(buf1); assert_eq!( dst, b"0\r\nchunky-trailer: header data\r\nchunky-trailer-2: more header data\r\n\r\n" ); } #[test] fn chunked_with_no_trailer_header() { let encoder = Encoder::chunked(); let headers = HeaderMap::from_iter(vec![( HeaderName::from_static("chunky-trailer"), HeaderValue::from_static("header data"), )]); assert!(encoder .encode_trailers::<&[u8]>(headers.clone(), false) .is_none()); let trailers = vec![]; let encoder = encoder.into_chunked_with_trailing_fields(trailers); assert!(encoder.encode_trailers::<&[u8]>(headers, false).is_none()); } #[test] fn chunked_with_invalid_trailers() { let encoder = Encoder::chunked(); let trailers = format!( "{},{},{},{},{},{},{},{},{},{},{},{}", AUTHORIZATION, CACHE_CONTROL, CONTENT_ENCODING, CONTENT_LENGTH, CONTENT_RANGE, CONTENT_TYPE, HOST, MAX_FORWARDS, SET_COOKIE, TRAILER, TRANSFER_ENCODING, TE, ); let trailers = vec![HeaderValue::from_str(&trailers).unwrap()]; let encoder = encoder.into_chunked_with_trailing_fields(trailers); let mut headers = HeaderMap::new(); headers.insert(AUTHORIZATION, HeaderValue::from_static("header data")); headers.insert(CACHE_CONTROL, HeaderValue::from_static("header data")); headers.insert(CONTENT_ENCODING, HeaderValue::from_static("header data")); headers.insert(CONTENT_LENGTH, HeaderValue::from_static("header data")); headers.insert(CONTENT_RANGE, HeaderValue::from_static("header data")); headers.insert(CONTENT_TYPE, HeaderValue::from_static("header data")); headers.insert(HOST, HeaderValue::from_static("header data")); headers.insert(MAX_FORWARDS, HeaderValue::from_static("header data")); headers.insert(SET_COOKIE, HeaderValue::from_static("header data")); headers.insert(TRAILER, HeaderValue::from_static("header data")); headers.insert(TRANSFER_ENCODING, HeaderValue::from_static("header data")); headers.insert(TE, HeaderValue::from_static("header data")); assert!(encoder.encode_trailers::<&[u8]>(headers, true).is_none()); } #[test] fn chunked_with_title_case_headers() { let encoder = Encoder::chunked(); let trailers = vec![HeaderValue::from_static("chunky-trailer")]; let encoder = encoder.into_chunked_with_trailing_fields(trailers); let headers = HeaderMap::from_iter(vec![( HeaderName::from_static("chunky-trailer"), HeaderValue::from_static("header data"), )]); let buf1 = encoder.encode_trailers::<&[u8]>(headers, true).unwrap(); let mut dst = Vec::new(); dst.put(buf1); assert_eq!(dst, b"0\r\nChunky-Trailer: header data\r\n\r\n"); } } hyper-1.5.2/src/proto/h1/io.rs000064400000000000000000000744151046102023000142120ustar 00000000000000use std::cmp; use std::fmt; use std::io::{self, IoSlice}; use std::pin::Pin; use std::task::{Context, Poll}; use crate::rt::{Read, ReadBuf, Write}; use bytes::{Buf, BufMut, Bytes, BytesMut}; use futures_util::ready; use super::{Http1Transaction, ParseContext, ParsedMessage}; use crate::common::buf::BufList; /// The initial buffer size allocated before trying to read from IO. pub(crate) const INIT_BUFFER_SIZE: usize = 8192; /// The minimum value that can be set to max buffer size. pub(crate) const MINIMUM_MAX_BUFFER_SIZE: usize = INIT_BUFFER_SIZE; /// The default maximum read buffer size. If the buffer gets this big and /// a message is still not complete, a `TooLarge` error is triggered. // Note: if this changes, update server::conn::Http::max_buf_size docs. pub(crate) const DEFAULT_MAX_BUFFER_SIZE: usize = 8192 + 4096 * 100; /// The maximum number of distinct `Buf`s to hold in a list before requiring /// a flush. Only affects when the buffer strategy is to queue buffers. /// /// Note that a flush can happen before reaching the maximum. This simply /// forces a flush if the queue gets this big. const MAX_BUF_LIST_BUFFERS: usize = 16; pub(crate) struct Buffered { flush_pipeline: bool, io: T, partial_len: Option, read_blocked: bool, read_buf: BytesMut, read_buf_strategy: ReadStrategy, write_buf: WriteBuf, } impl fmt::Debug for Buffered where B: Buf, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("Buffered") .field("read_buf", &self.read_buf) .field("write_buf", &self.write_buf) .finish() } } impl Buffered where T: Read + Write + Unpin, B: Buf, { pub(crate) fn new(io: T) -> Buffered { let strategy = if io.is_write_vectored() { WriteStrategy::Queue } else { WriteStrategy::Flatten }; let write_buf = WriteBuf::new(strategy); Buffered { flush_pipeline: false, io, partial_len: None, read_blocked: false, read_buf: BytesMut::with_capacity(0), read_buf_strategy: ReadStrategy::default(), write_buf, } } #[cfg(feature = "server")] pub(crate) fn set_flush_pipeline(&mut self, enabled: bool) { debug_assert!(!self.write_buf.has_remaining()); self.flush_pipeline = enabled; if enabled { self.set_write_strategy_flatten(); } } pub(crate) fn set_max_buf_size(&mut self, max: usize) { assert!( max >= MINIMUM_MAX_BUFFER_SIZE, "The max_buf_size cannot be smaller than {}.", MINIMUM_MAX_BUFFER_SIZE, ); self.read_buf_strategy = ReadStrategy::with_max(max); self.write_buf.max_buf_size = max; } #[cfg(feature = "client")] pub(crate) fn set_read_buf_exact_size(&mut self, sz: usize) { self.read_buf_strategy = ReadStrategy::Exact(sz); } pub(crate) fn set_write_strategy_flatten(&mut self) { // this should always be called only at construction time, // so this assert is here to catch myself debug_assert!(self.write_buf.queue.bufs_cnt() == 0); self.write_buf.set_strategy(WriteStrategy::Flatten); } pub(crate) fn set_write_strategy_queue(&mut self) { // this should always be called only at construction time, // so this assert is here to catch myself debug_assert!(self.write_buf.queue.bufs_cnt() == 0); self.write_buf.set_strategy(WriteStrategy::Queue); } pub(crate) fn read_buf(&self) -> &[u8] { self.read_buf.as_ref() } #[cfg(test)] #[cfg(feature = "nightly")] pub(super) fn read_buf_mut(&mut self) -> &mut BytesMut { &mut self.read_buf } /// Return the "allocated" available space, not the potential space /// that could be allocated in the future. fn read_buf_remaining_mut(&self) -> usize { self.read_buf.capacity() - self.read_buf.len() } /// Return whether we can append to the headers buffer. /// /// Reasons we can't: /// - The write buf is in queue mode, and some of the past body is still /// needing to be flushed. pub(crate) fn can_headers_buf(&self) -> bool { !self.write_buf.queue.has_remaining() } pub(crate) fn headers_buf(&mut self) -> &mut Vec { let buf = self.write_buf.headers_mut(); &mut buf.bytes } pub(super) fn write_buf(&mut self) -> &mut WriteBuf { &mut self.write_buf } pub(crate) fn buffer>(&mut self, buf: BB) { self.write_buf.buffer(buf) } pub(crate) fn can_buffer(&self) -> bool { self.flush_pipeline || self.write_buf.can_buffer() } pub(crate) fn consume_leading_lines(&mut self) { if !self.read_buf.is_empty() { let mut i = 0; while i < self.read_buf.len() { match self.read_buf[i] { b'\r' | b'\n' => i += 1, _ => break, } } self.read_buf.advance(i); } } pub(super) fn parse( &mut self, cx: &mut Context<'_>, parse_ctx: ParseContext<'_>, ) -> Poll>> where S: Http1Transaction, { loop { match super::role::parse_headers::( &mut self.read_buf, self.partial_len, ParseContext { cached_headers: parse_ctx.cached_headers, req_method: parse_ctx.req_method, h1_parser_config: parse_ctx.h1_parser_config.clone(), h1_max_headers: parse_ctx.h1_max_headers, preserve_header_case: parse_ctx.preserve_header_case, #[cfg(feature = "ffi")] preserve_header_order: parse_ctx.preserve_header_order, h09_responses: parse_ctx.h09_responses, #[cfg(feature = "ffi")] on_informational: parse_ctx.on_informational, }, )? { Some(msg) => { debug!("parsed {} headers", msg.head.headers.len()); self.partial_len = None; return Poll::Ready(Ok(msg)); } None => { let max = self.read_buf_strategy.max(); let curr_len = self.read_buf.len(); if curr_len >= max { debug!("max_buf_size ({}) reached, closing", max); return Poll::Ready(Err(crate::Error::new_too_large())); } if curr_len > 0 { trace!("partial headers; {} bytes so far", curr_len); self.partial_len = Some(curr_len); } else { // 1xx gobled some bytes self.partial_len = None; } } } if ready!(self.poll_read_from_io(cx)).map_err(crate::Error::new_io)? == 0 { trace!("parse eof"); return Poll::Ready(Err(crate::Error::new_incomplete())); } } } pub(crate) fn poll_read_from_io(&mut self, cx: &mut Context<'_>) -> Poll> { self.read_blocked = false; let next = self.read_buf_strategy.next(); if self.read_buf_remaining_mut() < next { self.read_buf.reserve(next); } // SAFETY: ReadBuf and poll_read promise not to set any uninitialized // bytes onto `dst`. let dst = unsafe { self.read_buf.chunk_mut().as_uninit_slice_mut() }; let mut buf = ReadBuf::uninit(dst); match Pin::new(&mut self.io).poll_read(cx, buf.unfilled()) { Poll::Ready(Ok(_)) => { let n = buf.filled().len(); trace!("received {} bytes", n); unsafe { // Safety: we just read that many bytes into the // uninitialized part of the buffer, so this is okay. // @tokio pls give me back `poll_read_buf` thanks self.read_buf.advance_mut(n); } self.read_buf_strategy.record(n); Poll::Ready(Ok(n)) } Poll::Pending => { self.read_blocked = true; Poll::Pending } Poll::Ready(Err(e)) => Poll::Ready(Err(e)), } } pub(crate) fn into_inner(self) -> (T, Bytes) { (self.io, self.read_buf.freeze()) } pub(crate) fn io_mut(&mut self) -> &mut T { &mut self.io } pub(crate) fn is_read_blocked(&self) -> bool { self.read_blocked } pub(crate) fn poll_flush(&mut self, cx: &mut Context<'_>) -> Poll> { if self.flush_pipeline && !self.read_buf.is_empty() { Poll::Ready(Ok(())) } else if self.write_buf.remaining() == 0 { Pin::new(&mut self.io).poll_flush(cx) } else { if let WriteStrategy::Flatten = self.write_buf.strategy { return self.poll_flush_flattened(cx); } const MAX_WRITEV_BUFS: usize = 64; loop { let n = { let mut iovs = [IoSlice::new(&[]); MAX_WRITEV_BUFS]; let len = self.write_buf.chunks_vectored(&mut iovs); ready!(Pin::new(&mut self.io).poll_write_vectored(cx, &iovs[..len]))? }; // TODO(eliza): we have to do this manually because // `poll_write_buf` doesn't exist in Tokio 0.3 yet...when // `poll_write_buf` comes back, the manual advance will need to leave! self.write_buf.advance(n); debug!("flushed {} bytes", n); if self.write_buf.remaining() == 0 { break; } else if n == 0 { trace!( "write returned zero, but {} bytes remaining", self.write_buf.remaining() ); return Poll::Ready(Err(io::ErrorKind::WriteZero.into())); } } Pin::new(&mut self.io).poll_flush(cx) } } /// Specialized version of `flush` when strategy is Flatten. /// /// Since all buffered bytes are flattened into the single headers buffer, /// that skips some bookkeeping around using multiple buffers. fn poll_flush_flattened(&mut self, cx: &mut Context<'_>) -> Poll> { loop { let n = ready!(Pin::new(&mut self.io).poll_write(cx, self.write_buf.headers.chunk()))?; debug!("flushed {} bytes", n); self.write_buf.headers.advance(n); if self.write_buf.headers.remaining() == 0 { self.write_buf.headers.reset(); break; } else if n == 0 { trace!( "write returned zero, but {} bytes remaining", self.write_buf.remaining() ); return Poll::Ready(Err(io::ErrorKind::WriteZero.into())); } } Pin::new(&mut self.io).poll_flush(cx) } #[cfg(test)] fn flush(&mut self) -> impl std::future::Future> + '_ { futures_util::future::poll_fn(move |cx| self.poll_flush(cx)) } } // The `B` is a `Buf`, we never project a pin to it impl Unpin for Buffered {} // TODO: This trait is old... at least rename to PollBytes or something... pub(crate) trait MemRead { fn read_mem(&mut self, cx: &mut Context<'_>, len: usize) -> Poll>; } impl MemRead for Buffered where T: Read + Write + Unpin, B: Buf, { fn read_mem(&mut self, cx: &mut Context<'_>, len: usize) -> Poll> { if !self.read_buf.is_empty() { let n = std::cmp::min(len, self.read_buf.len()); Poll::Ready(Ok(self.read_buf.split_to(n).freeze())) } else { let n = ready!(self.poll_read_from_io(cx))?; Poll::Ready(Ok(self.read_buf.split_to(::std::cmp::min(len, n)).freeze())) } } } #[derive(Clone, Copy, Debug)] enum ReadStrategy { Adaptive { decrease_now: bool, next: usize, max: usize, }, #[cfg(feature = "client")] Exact(usize), } impl ReadStrategy { fn with_max(max: usize) -> ReadStrategy { ReadStrategy::Adaptive { decrease_now: false, next: INIT_BUFFER_SIZE, max, } } fn next(&self) -> usize { match *self { ReadStrategy::Adaptive { next, .. } => next, #[cfg(feature = "client")] ReadStrategy::Exact(exact) => exact, } } fn max(&self) -> usize { match *self { ReadStrategy::Adaptive { max, .. } => max, #[cfg(feature = "client")] ReadStrategy::Exact(exact) => exact, } } fn record(&mut self, bytes_read: usize) { match *self { ReadStrategy::Adaptive { ref mut decrease_now, ref mut next, max, .. } => { if bytes_read >= *next { *next = cmp::min(incr_power_of_two(*next), max); *decrease_now = false; } else { let decr_to = prev_power_of_two(*next); if bytes_read < decr_to { if *decrease_now { *next = cmp::max(decr_to, INIT_BUFFER_SIZE); *decrease_now = false; } else { // Decreasing is a two "record" process. *decrease_now = true; } } else { // A read within the current range should cancel // a potential decrease, since we just saw proof // that we still need this size. *decrease_now = false; } } } #[cfg(feature = "client")] ReadStrategy::Exact(_) => (), } } } fn incr_power_of_two(n: usize) -> usize { n.saturating_mul(2) } fn prev_power_of_two(n: usize) -> usize { // Only way this shift can underflow is if n is less than 4. // (Which would means `usize::MAX >> 64` and underflowed!) debug_assert!(n >= 4); (usize::MAX >> (n.leading_zeros() + 2)) + 1 } impl Default for ReadStrategy { fn default() -> ReadStrategy { ReadStrategy::with_max(DEFAULT_MAX_BUFFER_SIZE) } } #[derive(Clone)] pub(crate) struct Cursor { bytes: T, pos: usize, } impl> Cursor { #[inline] pub(crate) fn new(bytes: T) -> Cursor { Cursor { bytes, pos: 0 } } } impl Cursor> { /// If we've advanced the position a bit in this cursor, and wish to /// extend the underlying vector, we may wish to unshift the "read" bytes /// off, and move everything else over. fn maybe_unshift(&mut self, additional: usize) { if self.pos == 0 { // nothing to do return; } if self.bytes.capacity() - self.bytes.len() >= additional { // there's room! return; } self.bytes.drain(0..self.pos); self.pos = 0; } fn reset(&mut self) { self.pos = 0; self.bytes.clear(); } } impl> fmt::Debug for Cursor { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("Cursor") .field("pos", &self.pos) .field("len", &self.bytes.as_ref().len()) .finish() } } impl> Buf for Cursor { #[inline] fn remaining(&self) -> usize { self.bytes.as_ref().len() - self.pos } #[inline] fn chunk(&self) -> &[u8] { &self.bytes.as_ref()[self.pos..] } #[inline] fn advance(&mut self, cnt: usize) { debug_assert!(self.pos + cnt <= self.bytes.as_ref().len()); self.pos += cnt; } } // an internal buffer to collect writes before flushes pub(super) struct WriteBuf { /// Re-usable buffer that holds message headers headers: Cursor>, max_buf_size: usize, /// Deque of user buffers if strategy is Queue queue: BufList, strategy: WriteStrategy, } impl WriteBuf { fn new(strategy: WriteStrategy) -> WriteBuf { WriteBuf { headers: Cursor::new(Vec::with_capacity(INIT_BUFFER_SIZE)), max_buf_size: DEFAULT_MAX_BUFFER_SIZE, queue: BufList::new(), strategy, } } } impl WriteBuf where B: Buf, { fn set_strategy(&mut self, strategy: WriteStrategy) { self.strategy = strategy; } pub(super) fn buffer>(&mut self, mut buf: BB) { debug_assert!(buf.has_remaining()); match self.strategy { WriteStrategy::Flatten => { let head = self.headers_mut(); head.maybe_unshift(buf.remaining()); trace!( self.len = head.remaining(), buf.len = buf.remaining(), "buffer.flatten" ); //perf: This is a little faster than >::put, //but accomplishes the same result. loop { let adv = { let slice = buf.chunk(); if slice.is_empty() { return; } head.bytes.extend_from_slice(slice); slice.len() }; buf.advance(adv); } } WriteStrategy::Queue => { trace!( self.len = self.remaining(), buf.len = buf.remaining(), "buffer.queue" ); self.queue.push(buf.into()); } } } fn can_buffer(&self) -> bool { match self.strategy { WriteStrategy::Flatten => self.remaining() < self.max_buf_size, WriteStrategy::Queue => { self.queue.bufs_cnt() < MAX_BUF_LIST_BUFFERS && self.remaining() < self.max_buf_size } } } fn headers_mut(&mut self) -> &mut Cursor> { debug_assert!(!self.queue.has_remaining()); &mut self.headers } } impl fmt::Debug for WriteBuf { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("WriteBuf") .field("remaining", &self.remaining()) .field("strategy", &self.strategy) .finish() } } impl Buf for WriteBuf { #[inline] fn remaining(&self) -> usize { self.headers.remaining() + self.queue.remaining() } #[inline] fn chunk(&self) -> &[u8] { let headers = self.headers.chunk(); if !headers.is_empty() { headers } else { self.queue.chunk() } } #[inline] fn advance(&mut self, cnt: usize) { let hrem = self.headers.remaining(); match hrem.cmp(&cnt) { cmp::Ordering::Equal => self.headers.reset(), cmp::Ordering::Greater => self.headers.advance(cnt), cmp::Ordering::Less => { let qcnt = cnt - hrem; self.headers.reset(); self.queue.advance(qcnt); } } } #[inline] fn chunks_vectored<'t>(&'t self, dst: &mut [IoSlice<'t>]) -> usize { let n = self.headers.chunks_vectored(dst); self.queue.chunks_vectored(&mut dst[n..]) + n } } #[derive(Debug)] enum WriteStrategy { Flatten, Queue, } #[cfg(test)] mod tests { use super::*; use crate::common::io::Compat; use std::time::Duration; use tokio_test::io::Builder as Mock; // #[cfg(feature = "nightly")] // use test::Bencher; /* impl MemRead for AsyncIo { fn read_mem(&mut self, len: usize) -> Poll { let mut v = vec![0; len]; let n = try_nb!(self.read(v.as_mut_slice())); Ok(Async::Ready(BytesMut::from(&v[..n]).freeze())) } } */ #[tokio::test] #[ignore] async fn iobuf_write_empty_slice() { // TODO(eliza): can i have writev back pls T_T // // First, let's just check that the Mock would normally return an // // error on an unexpected write, even if the buffer is empty... // let mut mock = Mock::new().build(); // futures_util::future::poll_fn(|cx| { // Pin::new(&mut mock).poll_write_buf(cx, &mut Cursor::new(&[])) // }) // .await // .expect_err("should be a broken pipe"); // // underlying io will return the logic error upon write, // // so we are testing that the io_buf does not trigger a write // // when there is nothing to flush // let mock = Mock::new().build(); // let mut io_buf = Buffered::<_, Cursor>>::new(mock); // io_buf.flush().await.expect("should short-circuit flush"); } #[cfg(not(miri))] #[tokio::test] async fn parse_reads_until_blocked() { use crate::proto::h1::ClientTransaction; let _ = pretty_env_logger::try_init(); let mock = Mock::new() // Split over multiple reads will read all of it .read(b"HTTP/1.1 200 OK\r\n") .read(b"Server: hyper\r\n") // missing last line ending .wait(Duration::from_secs(1)) .build(); let mut buffered = Buffered::<_, Cursor>>::new(Compat::new(mock)); // We expect a `parse` to be not ready, and so can't await it directly. // Rather, this `poll_fn` will wrap the `Poll` result. futures_util::future::poll_fn(|cx| { let parse_ctx = ParseContext { cached_headers: &mut None, req_method: &mut None, h1_parser_config: Default::default(), h1_max_headers: None, preserve_header_case: false, #[cfg(feature = "ffi")] preserve_header_order: false, h09_responses: false, #[cfg(feature = "ffi")] on_informational: &mut None, }; assert!(buffered .parse::(cx, parse_ctx) .is_pending()); Poll::Ready(()) }) .await; assert_eq!( buffered.read_buf, b"HTTP/1.1 200 OK\r\nServer: hyper\r\n"[..] ); } #[test] fn read_strategy_adaptive_increments() { let mut strategy = ReadStrategy::default(); assert_eq!(strategy.next(), 8192); // Grows if record == next strategy.record(8192); assert_eq!(strategy.next(), 16384); strategy.record(16384); assert_eq!(strategy.next(), 32768); // Enormous records still increment at same rate strategy.record(usize::MAX); assert_eq!(strategy.next(), 65536); let max = strategy.max(); while strategy.next() < max { strategy.record(max); } assert_eq!(strategy.next(), max, "never goes over max"); strategy.record(max + 1); assert_eq!(strategy.next(), max, "never goes over max"); } #[test] fn read_strategy_adaptive_decrements() { let mut strategy = ReadStrategy::default(); strategy.record(8192); assert_eq!(strategy.next(), 16384); strategy.record(1); assert_eq!( strategy.next(), 16384, "first smaller record doesn't decrement yet" ); strategy.record(8192); assert_eq!(strategy.next(), 16384, "record was with range"); strategy.record(1); assert_eq!( strategy.next(), 16384, "in-range record should make this the 'first' again" ); strategy.record(1); assert_eq!(strategy.next(), 8192, "second smaller record decrements"); strategy.record(1); assert_eq!(strategy.next(), 8192, "first doesn't decrement"); strategy.record(1); assert_eq!(strategy.next(), 8192, "doesn't decrement under minimum"); } #[test] fn read_strategy_adaptive_stays_the_same() { let mut strategy = ReadStrategy::default(); strategy.record(8192); assert_eq!(strategy.next(), 16384); strategy.record(8193); assert_eq!( strategy.next(), 16384, "first smaller record doesn't decrement yet" ); strategy.record(8193); assert_eq!( strategy.next(), 16384, "with current step does not decrement" ); } #[test] fn read_strategy_adaptive_max_fuzz() { fn fuzz(max: usize) { let mut strategy = ReadStrategy::with_max(max); while strategy.next() < max { strategy.record(usize::MAX); } let mut next = strategy.next(); while next > 8192 { strategy.record(1); strategy.record(1); next = strategy.next(); assert!( next.is_power_of_two(), "decrement should be powers of two: {} (max = {})", next, max, ); } } let mut max = 8192; while max < std::usize::MAX { fuzz(max); max = (max / 2).saturating_mul(3); } fuzz(usize::MAX); } #[test] #[should_panic] #[cfg(debug_assertions)] // needs to trigger a debug_assert fn write_buf_requires_non_empty_bufs() { let mock = Mock::new().build(); let mut buffered = Buffered::<_, Cursor>>::new(Compat::new(mock)); buffered.buffer(Cursor::new(Vec::new())); } /* TODO: needs tokio_test::io to allow configure write_buf calls #[test] fn write_buf_queue() { let _ = pretty_env_logger::try_init(); let mock = AsyncIo::new_buf(vec![], 1024); let mut buffered = Buffered::<_, Cursor>>::new(mock); buffered.headers_buf().extend(b"hello "); buffered.buffer(Cursor::new(b"world, ".to_vec())); buffered.buffer(Cursor::new(b"it's ".to_vec())); buffered.buffer(Cursor::new(b"hyper!".to_vec())); assert_eq!(buffered.write_buf.queue.bufs_cnt(), 3); buffered.flush().unwrap(); assert_eq!(buffered.io, b"hello world, it's hyper!"); assert_eq!(buffered.io.num_writes(), 1); assert_eq!(buffered.write_buf.queue.bufs_cnt(), 0); } */ #[cfg(not(miri))] #[tokio::test] async fn write_buf_flatten() { let _ = pretty_env_logger::try_init(); let mock = Mock::new().write(b"hello world, it's hyper!").build(); let mut buffered = Buffered::<_, Cursor>>::new(Compat::new(mock)); buffered.write_buf.set_strategy(WriteStrategy::Flatten); buffered.headers_buf().extend(b"hello "); buffered.buffer(Cursor::new(b"world, ".to_vec())); buffered.buffer(Cursor::new(b"it's ".to_vec())); buffered.buffer(Cursor::new(b"hyper!".to_vec())); assert_eq!(buffered.write_buf.queue.bufs_cnt(), 0); buffered.flush().await.expect("flush"); } #[test] fn write_buf_flatten_partially_flushed() { let _ = pretty_env_logger::try_init(); let b = |s: &str| Cursor::new(s.as_bytes().to_vec()); let mut write_buf = WriteBuf::>>::new(WriteStrategy::Flatten); write_buf.buffer(b("hello ")); write_buf.buffer(b("world, ")); assert_eq!(write_buf.chunk(), b"hello world, "); // advance most of the way, but not all write_buf.advance(11); assert_eq!(write_buf.chunk(), b", "); assert_eq!(write_buf.headers.pos, 11); assert_eq!(write_buf.headers.bytes.capacity(), INIT_BUFFER_SIZE); // there's still room in the headers buffer, so just push on the end write_buf.buffer(b("it's hyper!")); assert_eq!(write_buf.chunk(), b", it's hyper!"); assert_eq!(write_buf.headers.pos, 11); let rem1 = write_buf.remaining(); let cap = write_buf.headers.bytes.capacity(); // but when this would go over capacity, don't copy the old bytes write_buf.buffer(Cursor::new(vec![b'X'; cap])); assert_eq!(write_buf.remaining(), cap + rem1); assert_eq!(write_buf.headers.pos, 0); } #[cfg(not(miri))] #[tokio::test] async fn write_buf_queue_disable_auto() { let _ = pretty_env_logger::try_init(); let mock = Mock::new() .write(b"hello ") .write(b"world, ") .write(b"it's ") .write(b"hyper!") .build(); let mut buffered = Buffered::<_, Cursor>>::new(Compat::new(mock)); buffered.write_buf.set_strategy(WriteStrategy::Queue); // we have 4 buffers, and vec IO disabled, but explicitly said // don't try to auto detect (via setting strategy above) buffered.headers_buf().extend(b"hello "); buffered.buffer(Cursor::new(b"world, ".to_vec())); buffered.buffer(Cursor::new(b"it's ".to_vec())); buffered.buffer(Cursor::new(b"hyper!".to_vec())); assert_eq!(buffered.write_buf.queue.bufs_cnt(), 3); buffered.flush().await.expect("flush"); assert_eq!(buffered.write_buf.queue.bufs_cnt(), 0); } // #[cfg(feature = "nightly")] // #[bench] // fn bench_write_buf_flatten_buffer_chunk(b: &mut Bencher) { // let s = "Hello, World!"; // b.bytes = s.len() as u64; // let mut write_buf = WriteBuf::::new(); // write_buf.set_strategy(WriteStrategy::Flatten); // b.iter(|| { // let chunk = bytes::Bytes::from(s); // write_buf.buffer(chunk); // ::test::black_box(&write_buf); // write_buf.headers.bytes.clear(); // }) // } } hyper-1.5.2/src/proto/h1/mod.rs000064400000000000000000000054061046102023000143540ustar 00000000000000use bytes::BytesMut; use http::{HeaderMap, Method}; use httparse::ParserConfig; use crate::body::DecodedLength; use crate::proto::{BodyLength, MessageHead}; pub(crate) use self::conn::Conn; pub(crate) use self::decode::Decoder; pub(crate) use self::dispatch::Dispatcher; pub(crate) use self::encode::{EncodedBuf, Encoder}; //TODO: move out of h1::io pub(crate) use self::io::MINIMUM_MAX_BUFFER_SIZE; mod conn; mod decode; pub(crate) mod dispatch; mod encode; mod io; mod role; cfg_client! { pub(crate) type ClientTransaction = role::Client; } cfg_server! { pub(crate) type ServerTransaction = role::Server; } pub(crate) trait Http1Transaction { type Incoming; type Outgoing: Default; #[cfg(feature = "tracing")] const LOG: &'static str; fn parse(bytes: &mut BytesMut, ctx: ParseContext<'_>) -> ParseResult; fn encode(enc: Encode<'_, Self::Outgoing>, dst: &mut Vec) -> crate::Result; fn on_error(err: &crate::Error) -> Option>; fn is_client() -> bool { !Self::is_server() } fn is_server() -> bool { !Self::is_client() } fn should_error_on_parse_eof() -> bool { Self::is_client() } fn should_read_first() -> bool { Self::is_server() } fn update_date() {} } /// Result newtype for Http1Transaction::parse. pub(crate) type ParseResult = Result>, crate::error::Parse>; #[derive(Debug)] pub(crate) struct ParsedMessage { head: MessageHead, decode: DecodedLength, expect_continue: bool, keep_alive: bool, wants_upgrade: bool, } pub(crate) struct ParseContext<'a> { cached_headers: &'a mut Option, req_method: &'a mut Option, h1_parser_config: ParserConfig, h1_max_headers: Option, preserve_header_case: bool, #[cfg(feature = "ffi")] preserve_header_order: bool, h09_responses: bool, #[cfg(feature = "ffi")] on_informational: &'a mut Option, } /// Passed to Http1Transaction::encode pub(crate) struct Encode<'a, T> { head: &'a mut MessageHead, body: Option, #[cfg(feature = "server")] keep_alive: bool, req_method: &'a mut Option, title_case_headers: bool, #[cfg(feature = "server")] date_header: bool, } /// Extra flags that a request "wants", like expect-continue or upgrades. #[derive(Clone, Copy, Debug)] struct Wants(u8); impl Wants { const EMPTY: Wants = Wants(0b00); const EXPECT: Wants = Wants(0b01); const UPGRADE: Wants = Wants(0b10); #[must_use] fn add(self, other: Wants) -> Wants { Wants(self.0 | other.0) } fn contains(&self, other: Wants) -> bool { (self.0 & other.0) == other.0 } } hyper-1.5.2/src/proto/h1/role.rs000064400000000000000000003205441046102023000145410ustar 00000000000000use std::mem::MaybeUninit; #[cfg(feature = "client")] use std::fmt::{self, Write as _}; use bytes::Bytes; use bytes::BytesMut; #[cfg(feature = "client")] use http::header::Entry; #[cfg(feature = "server")] use http::header::ValueIter; use http::header::{self, HeaderMap, HeaderName, HeaderValue}; use http::{Method, StatusCode, Version}; use smallvec::{smallvec, smallvec_inline, SmallVec}; use crate::body::DecodedLength; #[cfg(feature = "server")] use crate::common::date; use crate::error::Parse; use crate::ext::HeaderCaseMap; #[cfg(feature = "ffi")] use crate::ext::OriginalHeaderOrder; use crate::headers; use crate::proto::h1::{ Encode, Encoder, Http1Transaction, ParseContext, ParseResult, ParsedMessage, }; #[cfg(feature = "client")] use crate::proto::RequestHead; use crate::proto::{BodyLength, MessageHead, RequestLine}; pub(crate) const DEFAULT_MAX_HEADERS: usize = 100; const AVERAGE_HEADER_SIZE: usize = 30; // totally scientific #[cfg(feature = "server")] const MAX_URI_LEN: usize = (u16::MAX - 1) as usize; macro_rules! header_name { ($bytes:expr) => {{ { match HeaderName::from_bytes($bytes) { Ok(name) => name, Err(e) => maybe_panic!(e), } } }}; } macro_rules! header_value { ($bytes:expr) => {{ { unsafe { HeaderValue::from_maybe_shared_unchecked($bytes) } } }}; } macro_rules! maybe_panic { ($($arg:tt)*) => ({ let _err = ($($arg)*); if cfg!(debug_assertions) { panic!("{:?}", _err); } else { error!("Internal Hyper error, please report {:?}", _err); return Err(Parse::Internal) } }) } pub(super) fn parse_headers( bytes: &mut BytesMut, prev_len: Option, ctx: ParseContext<'_>, ) -> ParseResult where T: Http1Transaction, { // If the buffer is empty, don't bother entering the span, it's just noise. if bytes.is_empty() { return Ok(None); } let _entered = trace_span!("parse_headers"); if let Some(prev_len) = prev_len { if !is_complete_fast(bytes, prev_len) { return Ok(None); } } T::parse(bytes, ctx) } /// A fast scan for the end of a message. /// Used when there was a partial read, to skip full parsing on a /// a slow connection. fn is_complete_fast(bytes: &[u8], prev_len: usize) -> bool { let start = if prev_len < 3 { 0 } else { prev_len - 3 }; let bytes = &bytes[start..]; for (i, b) in bytes.iter().copied().enumerate() { if b == b'\r' { if bytes[i + 1..].chunks(3).next() == Some(&b"\n\r\n"[..]) { return true; } } else if b == b'\n' && bytes.get(i + 1) == Some(&b'\n') { return true; } } false } pub(super) fn encode_headers( enc: Encode<'_, T::Outgoing>, dst: &mut Vec, ) -> crate::Result where T: Http1Transaction, { let _entered = trace_span!("encode_headers"); T::encode(enc, dst) } // There are 2 main roles, Client and Server. #[cfg(feature = "client")] pub(crate) enum Client {} #[cfg(feature = "server")] pub(crate) enum Server {} #[cfg(feature = "server")] impl Http1Transaction for Server { type Incoming = RequestLine; type Outgoing = StatusCode; #[cfg(feature = "tracing")] const LOG: &'static str = "{role=server}"; fn parse(buf: &mut BytesMut, ctx: ParseContext<'_>) -> ParseResult { debug_assert!(!buf.is_empty(), "parse called with empty buf"); let mut keep_alive; let is_http_11; let subject; let version; let len; let headers_len; let method; let path_range; // Both headers_indices and headers are using uninitialized memory, // but we *never* read any of it until after httparse has assigned // values into it. By not zeroing out the stack memory, this saves // a good ~5% on pipeline benchmarks. let mut headers_indices: SmallVec<[MaybeUninit; DEFAULT_MAX_HEADERS]> = match ctx.h1_max_headers { Some(cap) => smallvec![MaybeUninit::uninit(); cap], None => smallvec_inline![MaybeUninit::uninit(); DEFAULT_MAX_HEADERS], }; { let mut headers: SmallVec<[MaybeUninit>; DEFAULT_MAX_HEADERS]> = match ctx.h1_max_headers { Some(cap) => smallvec![MaybeUninit::uninit(); cap], None => smallvec_inline![MaybeUninit::uninit(); DEFAULT_MAX_HEADERS], }; trace!(bytes = buf.len(), "Request.parse"); let mut req = httparse::Request::new(&mut []); let bytes = buf.as_ref(); match req.parse_with_uninit_headers(bytes, &mut headers) { Ok(httparse::Status::Complete(parsed_len)) => { trace!("Request.parse Complete({})", parsed_len); len = parsed_len; let uri = req.path.unwrap(); if uri.len() > MAX_URI_LEN { return Err(Parse::UriTooLong); } method = Method::from_bytes(req.method.unwrap().as_bytes())?; path_range = Server::record_path_range(bytes, uri); version = if req.version.unwrap() == 1 { keep_alive = true; is_http_11 = true; Version::HTTP_11 } else { keep_alive = false; is_http_11 = false; Version::HTTP_10 }; record_header_indices(bytes, req.headers, &mut headers_indices)?; headers_len = req.headers.len(); } Ok(httparse::Status::Partial) => return Ok(None), Err(err) => { return Err(match err { // if invalid Token, try to determine if for method or path httparse::Error::Token => { if req.method.is_none() { Parse::Method } else { debug_assert!(req.path.is_none()); Parse::Uri } } other => other.into(), }); } } }; let slice = buf.split_to(len).freeze(); let uri = { let uri_bytes = slice.slice_ref(&slice[path_range]); // TODO(lucab): switch to `Uri::from_shared()` once public. http::Uri::from_maybe_shared(uri_bytes)? }; subject = RequestLine(method, uri); // According to https://tools.ietf.org/html/rfc7230#section-3.3.3 // 1. (irrelevant to Request) // 2. (irrelevant to Request) // 3. Transfer-Encoding: chunked has a chunked body. // 4. If multiple differing Content-Length headers or invalid, close connection. // 5. Content-Length header has a sized body. // 6. Length 0. // 7. (irrelevant to Request) let mut decoder = DecodedLength::ZERO; let mut expect_continue = false; let mut con_len = None; let mut is_te = false; let mut is_te_chunked = false; let mut wants_upgrade = subject.0 == Method::CONNECT; let mut header_case_map = if ctx.preserve_header_case { Some(HeaderCaseMap::default()) } else { None }; #[cfg(feature = "ffi")] let mut header_order = if ctx.preserve_header_order { Some(OriginalHeaderOrder::default()) } else { None }; let mut headers = ctx.cached_headers.take().unwrap_or_default(); headers.reserve(headers_len); for header in &headers_indices[..headers_len] { // SAFETY: array is valid up to `headers_len` let header = unsafe { header.assume_init_ref() }; let name = header_name!(&slice[header.name.0..header.name.1]); let value = header_value!(slice.slice(header.value.0..header.value.1)); match name { header::TRANSFER_ENCODING => { // https://tools.ietf.org/html/rfc7230#section-3.3.3 // If Transfer-Encoding header is present, and 'chunked' is // not the final encoding, and this is a Request, then it is // malformed. A server should respond with 400 Bad Request. if !is_http_11 { debug!("HTTP/1.0 cannot have Transfer-Encoding header"); return Err(Parse::transfer_encoding_unexpected()); } is_te = true; if headers::is_chunked_(&value) { is_te_chunked = true; decoder = DecodedLength::CHUNKED; } else { is_te_chunked = false; } } header::CONTENT_LENGTH => { if is_te { continue; } let len = headers::content_length_parse(&value) .ok_or_else(Parse::content_length_invalid)?; if let Some(prev) = con_len { if prev != len { debug!( "multiple Content-Length headers with different values: [{}, {}]", prev, len, ); return Err(Parse::content_length_invalid()); } // we don't need to append this secondary length continue; } decoder = DecodedLength::checked_new(len)?; con_len = Some(len); } header::CONNECTION => { // keep_alive was previously set to default for Version if keep_alive { // HTTP/1.1 keep_alive = !headers::connection_close(&value); } else { // HTTP/1.0 keep_alive = headers::connection_keep_alive(&value); } } header::EXPECT => { // According to https://datatracker.ietf.org/doc/html/rfc2616#section-14.20 // Comparison of expectation values is case-insensitive for unquoted tokens // (including the 100-continue token) expect_continue = value.as_bytes().eq_ignore_ascii_case(b"100-continue"); } header::UPGRADE => { // Upgrades are only allowed with HTTP/1.1 wants_upgrade = is_http_11; } _ => (), } if let Some(ref mut header_case_map) = header_case_map { header_case_map.append(&name, slice.slice(header.name.0..header.name.1)); } #[cfg(feature = "ffi")] if let Some(ref mut header_order) = header_order { header_order.append(&name); } headers.append(name, value); } if is_te && !is_te_chunked { debug!("request with transfer-encoding header, but not chunked, bad request"); return Err(Parse::transfer_encoding_invalid()); } let mut extensions = http::Extensions::default(); if let Some(header_case_map) = header_case_map { extensions.insert(header_case_map); } #[cfg(feature = "ffi")] if let Some(header_order) = header_order { extensions.insert(header_order); } *ctx.req_method = Some(subject.0.clone()); Ok(Some(ParsedMessage { head: MessageHead { version, subject, headers, extensions, }, decode: decoder, expect_continue, keep_alive, wants_upgrade, })) } fn encode(mut msg: Encode<'_, Self::Outgoing>, dst: &mut Vec) -> crate::Result { trace!( "Server::encode status={:?}, body={:?}, req_method={:?}", msg.head.subject, msg.body, msg.req_method ); let mut wrote_len = false; // hyper currently doesn't support returning 1xx status codes as a Response // This is because Service only allows returning a single Response, and // so if you try to reply with a e.g. 100 Continue, you have no way of // replying with the latter status code response. let (ret, is_last) = if msg.head.subject == StatusCode::SWITCHING_PROTOCOLS { (Ok(()), true) } else if msg.req_method == &Some(Method::CONNECT) && msg.head.subject.is_success() { // Sending content-length or transfer-encoding header on 2xx response // to CONNECT is forbidden in RFC 7231. wrote_len = true; (Ok(()), true) } else if msg.head.subject.is_informational() { warn!("response with 1xx status code not supported"); *msg.head = MessageHead::default(); msg.head.subject = StatusCode::INTERNAL_SERVER_ERROR; msg.body = None; (Err(crate::Error::new_user_unsupported_status_code()), true) } else { (Ok(()), !msg.keep_alive) }; // In some error cases, we don't know about the invalid message until already // pushing some bytes onto the `dst`. In those cases, we don't want to send // the half-pushed message, so rewind to before. let orig_len = dst.len(); let init_cap = 30 + msg.head.headers.len() * AVERAGE_HEADER_SIZE; dst.reserve(init_cap); let custom_reason_phrase = msg.head.extensions.get::(); if msg.head.version == Version::HTTP_11 && msg.head.subject == StatusCode::OK && custom_reason_phrase.is_none() { extend(dst, b"HTTP/1.1 200 OK\r\n"); } else { match msg.head.version { Version::HTTP_10 => extend(dst, b"HTTP/1.0 "), Version::HTTP_11 => extend(dst, b"HTTP/1.1 "), Version::HTTP_2 => { debug!("response with HTTP2 version coerced to HTTP/1.1"); extend(dst, b"HTTP/1.1 "); } other => panic!("unexpected response version: {:?}", other), } extend(dst, msg.head.subject.as_str().as_bytes()); extend(dst, b" "); if let Some(reason) = custom_reason_phrase { extend(dst, reason.as_bytes()); } else { // a reason MUST be written, as many parsers will expect it. extend( dst, msg.head .subject .canonical_reason() .unwrap_or("") .as_bytes(), ); } extend(dst, b"\r\n"); } let orig_headers; let extensions = std::mem::take(&mut msg.head.extensions); let orig_headers = match extensions.get::() { None if msg.title_case_headers => { orig_headers = HeaderCaseMap::default(); Some(&orig_headers) } orig_headers => orig_headers, }; let encoder = if let Some(orig_headers) = orig_headers { Self::encode_headers_with_original_case( msg, dst, is_last, orig_len, wrote_len, orig_headers, )? } else { Self::encode_headers_with_lower_case(msg, dst, is_last, orig_len, wrote_len)? }; ret.map(|()| encoder) } fn on_error(err: &crate::Error) -> Option> { use crate::error::Kind; let status = match *err.kind() { Kind::Parse(Parse::Method) | Kind::Parse(Parse::Header(_)) | Kind::Parse(Parse::Uri) | Kind::Parse(Parse::Version) => StatusCode::BAD_REQUEST, Kind::Parse(Parse::TooLarge) => StatusCode::REQUEST_HEADER_FIELDS_TOO_LARGE, Kind::Parse(Parse::UriTooLong) => StatusCode::URI_TOO_LONG, _ => return None, }; debug!("sending automatic response ({}) for parse error", status); let msg = MessageHead { subject: status, ..Default::default() }; Some(msg) } fn is_server() -> bool { true } fn update_date() { date::update(); } } #[cfg(feature = "server")] impl Server { fn can_have_body(method: &Option, status: StatusCode) -> bool { Server::can_chunked(method, status) } fn can_chunked(method: &Option, status: StatusCode) -> bool { if method == &Some(Method::HEAD) || method == &Some(Method::CONNECT) && status.is_success() || status.is_informational() { false } else { !matches!(status, StatusCode::NO_CONTENT | StatusCode::NOT_MODIFIED) } } fn can_have_content_length(method: &Option, status: StatusCode) -> bool { if status.is_informational() || method == &Some(Method::CONNECT) && status.is_success() { false } else { !matches!(status, StatusCode::NO_CONTENT | StatusCode::NOT_MODIFIED) } } fn can_have_implicit_zero_content_length(method: &Option, status: StatusCode) -> bool { Server::can_have_content_length(method, status) && method != &Some(Method::HEAD) } fn encode_headers_with_lower_case( msg: Encode<'_, StatusCode>, dst: &mut Vec, is_last: bool, orig_len: usize, wrote_len: bool, ) -> crate::Result { struct LowercaseWriter; impl HeaderNameWriter for LowercaseWriter { #[inline] fn write_full_header_line( &mut self, dst: &mut Vec, line: &str, _: (HeaderName, &str), ) { extend(dst, line.as_bytes()) } #[inline] fn write_header_name_with_colon( &mut self, dst: &mut Vec, name_with_colon: &str, _: HeaderName, ) { extend(dst, name_with_colon.as_bytes()) } #[inline] fn write_header_name(&mut self, dst: &mut Vec, name: &HeaderName) { extend(dst, name.as_str().as_bytes()) } } Self::encode_headers(msg, dst, is_last, orig_len, wrote_len, LowercaseWriter) } #[cold] #[inline(never)] fn encode_headers_with_original_case( msg: Encode<'_, StatusCode>, dst: &mut Vec, is_last: bool, orig_len: usize, wrote_len: bool, orig_headers: &HeaderCaseMap, ) -> crate::Result { struct OrigCaseWriter<'map> { map: &'map HeaderCaseMap, current: Option<(HeaderName, ValueIter<'map, Bytes>)>, title_case_headers: bool, } impl HeaderNameWriter for OrigCaseWriter<'_> { #[inline] fn write_full_header_line( &mut self, dst: &mut Vec, _: &str, (name, rest): (HeaderName, &str), ) { self.write_header_name(dst, &name); extend(dst, rest.as_bytes()); } #[inline] fn write_header_name_with_colon( &mut self, dst: &mut Vec, _: &str, name: HeaderName, ) { self.write_header_name(dst, &name); extend(dst, b": "); } #[inline] fn write_header_name(&mut self, dst: &mut Vec, name: &HeaderName) { let Self { map, ref mut current, title_case_headers, } = *self; if current.as_ref().map_or(true, |(last, _)| last != name) { *current = None; } let (_, values) = current.get_or_insert_with(|| (name.clone(), map.get_all_internal(name))); if let Some(orig_name) = values.next() { extend(dst, orig_name); } else if title_case_headers { title_case(dst, name.as_str().as_bytes()); } else { extend(dst, name.as_str().as_bytes()); } } } let header_name_writer = OrigCaseWriter { map: orig_headers, current: None, title_case_headers: msg.title_case_headers, }; Self::encode_headers(msg, dst, is_last, orig_len, wrote_len, header_name_writer) } #[inline] fn encode_headers( msg: Encode<'_, StatusCode>, dst: &mut Vec, mut is_last: bool, orig_len: usize, mut wrote_len: bool, mut header_name_writer: W, ) -> crate::Result where W: HeaderNameWriter, { // In some error cases, we don't know about the invalid message until already // pushing some bytes onto the `dst`. In those cases, we don't want to send // the half-pushed message, so rewind to before. let rewind = |dst: &mut Vec| { dst.truncate(orig_len); }; let mut encoder = Encoder::length(0); let mut allowed_trailer_fields: Option> = None; let mut wrote_date = false; let mut cur_name = None; let mut is_name_written = false; let mut must_write_chunked = false; let mut prev_con_len = None; macro_rules! handle_is_name_written { () => {{ if is_name_written { // we need to clean up and write the newline debug_assert_ne!( &dst[dst.len() - 2..], b"\r\n", "previous header wrote newline but set is_name_written" ); if must_write_chunked { extend(dst, b", chunked\r\n"); } else { extend(dst, b"\r\n"); } } }}; } 'headers: for (opt_name, value) in msg.head.headers.drain() { if let Some(n) = opt_name { cur_name = Some(n); handle_is_name_written!(); is_name_written = false; } let name = cur_name.as_ref().expect("current header name"); match *name { header::CONTENT_LENGTH => { if wrote_len && !is_name_written { warn!("unexpected content-length found, canceling"); rewind(dst); return Err(crate::Error::new_user_header()); } match msg.body { Some(BodyLength::Known(known_len)) => { // The Body claims to know a length, and // the headers are already set. For performance // reasons, we are just going to trust that // the values match. // // In debug builds, we'll assert they are the // same to help developers find bugs. #[cfg(debug_assertions)] { if let Some(len) = headers::content_length_parse(&value) { if msg.req_method != &Some(Method::HEAD) || known_len != 0 { assert!( len == known_len, "payload claims content-length of {}, custom content-length header claims {}", known_len, len, ); } } } if !is_name_written { encoder = Encoder::length(known_len); header_name_writer.write_header_name_with_colon( dst, "content-length: ", header::CONTENT_LENGTH, ); extend(dst, value.as_bytes()); wrote_len = true; is_name_written = true; } continue 'headers; } Some(BodyLength::Unknown) => { // The Body impl didn't know how long the // body is, but a length header was included. // We have to parse the value to return our // Encoder... if let Some(len) = headers::content_length_parse(&value) { if let Some(prev) = prev_con_len { if prev != len { warn!( "multiple Content-Length values found: [{}, {}]", prev, len ); rewind(dst); return Err(crate::Error::new_user_header()); } debug_assert!(is_name_written); continue 'headers; } else { // we haven't written content-length yet! encoder = Encoder::length(len); header_name_writer.write_header_name_with_colon( dst, "content-length: ", header::CONTENT_LENGTH, ); extend(dst, value.as_bytes()); wrote_len = true; is_name_written = true; prev_con_len = Some(len); continue 'headers; } } else { warn!("illegal Content-Length value: {:?}", value); rewind(dst); return Err(crate::Error::new_user_header()); } } None => { // We have no body to actually send, // but the headers claim a content-length. // There's only 2 ways this makes sense: // // - The header says the length is `0`. // - This is a response to a `HEAD` request. if msg.req_method == &Some(Method::HEAD) { debug_assert_eq!(encoder, Encoder::length(0)); } else { if value.as_bytes() != b"0" { warn!( "content-length value found, but empty body provided: {:?}", value ); } continue 'headers; } } } wrote_len = true; } header::TRANSFER_ENCODING => { if wrote_len && !is_name_written { warn!("unexpected transfer-encoding found, canceling"); rewind(dst); return Err(crate::Error::new_user_header()); } // check that we actually can send a chunked body... if msg.head.version == Version::HTTP_10 || !Server::can_chunked(msg.req_method, msg.head.subject) { continue; } wrote_len = true; // Must check each value, because `chunked` needs to be the // last encoding, or else we add it. must_write_chunked = !headers::is_chunked_(&value); if !is_name_written { encoder = Encoder::chunked(); is_name_written = true; header_name_writer.write_header_name_with_colon( dst, "transfer-encoding: ", header::TRANSFER_ENCODING, ); extend(dst, value.as_bytes()); } else { extend(dst, b", "); extend(dst, value.as_bytes()); } continue 'headers; } header::CONNECTION => { if !is_last && headers::connection_close(&value) { is_last = true; } if !is_name_written { is_name_written = true; header_name_writer.write_header_name_with_colon( dst, "connection: ", header::CONNECTION, ); extend(dst, value.as_bytes()); } else { extend(dst, b", "); extend(dst, value.as_bytes()); } continue 'headers; } header::DATE => { wrote_date = true; } header::TRAILER => { // check that we actually can send a chunked body... if msg.head.version == Version::HTTP_10 || !Server::can_chunked(msg.req_method, msg.head.subject) { continue; } if !is_name_written { is_name_written = true; header_name_writer.write_header_name_with_colon( dst, "trailer: ", header::TRAILER, ); extend(dst, value.as_bytes()); } else { extend(dst, b", "); extend(dst, value.as_bytes()); } match allowed_trailer_fields { Some(ref mut allowed_trailer_fields) => { allowed_trailer_fields.push(value); } None => { allowed_trailer_fields = Some(vec![value]); } } continue 'headers; } _ => (), } //TODO: this should perhaps instead combine them into //single lines, as RFC7230 suggests is preferable. // non-special write Name and Value debug_assert!( !is_name_written, "{:?} set is_name_written and didn't continue loop", name, ); header_name_writer.write_header_name(dst, name); extend(dst, b": "); extend(dst, value.as_bytes()); extend(dst, b"\r\n"); } handle_is_name_written!(); if !wrote_len { encoder = match msg.body { Some(BodyLength::Unknown) => { if msg.head.version == Version::HTTP_10 || !Server::can_chunked(msg.req_method, msg.head.subject) { Encoder::close_delimited() } else { header_name_writer.write_full_header_line( dst, "transfer-encoding: chunked\r\n", (header::TRANSFER_ENCODING, ": chunked\r\n"), ); Encoder::chunked() } } None | Some(BodyLength::Known(0)) => { if Server::can_have_implicit_zero_content_length( msg.req_method, msg.head.subject, ) { header_name_writer.write_full_header_line( dst, "content-length: 0\r\n", (header::CONTENT_LENGTH, ": 0\r\n"), ) } Encoder::length(0) } Some(BodyLength::Known(len)) => { if !Server::can_have_content_length(msg.req_method, msg.head.subject) { Encoder::length(0) } else { header_name_writer.write_header_name_with_colon( dst, "content-length: ", header::CONTENT_LENGTH, ); extend(dst, ::itoa::Buffer::new().format(len).as_bytes()); extend(dst, b"\r\n"); Encoder::length(len) } } }; } if !Server::can_have_body(msg.req_method, msg.head.subject) { trace!( "server body forced to 0; method={:?}, status={:?}", msg.req_method, msg.head.subject ); encoder = Encoder::length(0); } // cached date is much faster than formatting every request // don't force the write if disabled if !wrote_date && msg.date_header { dst.reserve(date::DATE_VALUE_LENGTH + 8); header_name_writer.write_header_name_with_colon(dst, "date: ", header::DATE); date::extend(dst); extend(dst, b"\r\n\r\n"); } else { extend(dst, b"\r\n"); } if encoder.is_chunked() { if let Some(allowed_trailer_fields) = allowed_trailer_fields { encoder = encoder.into_chunked_with_trailing_fields(allowed_trailer_fields); } } Ok(encoder.set_last(is_last)) } /// Helper for zero-copy parsing of request path URI. #[inline] fn record_path_range(bytes: &[u8], req_path: &str) -> std::ops::Range { let bytes_ptr = bytes.as_ptr() as usize; let start = req_path.as_ptr() as usize - bytes_ptr; let end = start + req_path.len(); std::ops::Range { start, end } } } #[cfg(feature = "server")] trait HeaderNameWriter { fn write_full_header_line( &mut self, dst: &mut Vec, line: &str, name_value_pair: (HeaderName, &str), ); fn write_header_name_with_colon( &mut self, dst: &mut Vec, name_with_colon: &str, name: HeaderName, ); fn write_header_name(&mut self, dst: &mut Vec, name: &HeaderName); } #[cfg(feature = "client")] impl Http1Transaction for Client { type Incoming = StatusCode; type Outgoing = RequestLine; #[cfg(feature = "tracing")] const LOG: &'static str = "{role=client}"; fn parse(buf: &mut BytesMut, ctx: ParseContext<'_>) -> ParseResult { debug_assert!(!buf.is_empty(), "parse called with empty buf"); // Loop to skip information status code headers (100 Continue, etc). loop { let mut headers_indices: SmallVec<[MaybeUninit; DEFAULT_MAX_HEADERS]> = match ctx.h1_max_headers { Some(cap) => smallvec![MaybeUninit::uninit(); cap], None => smallvec_inline![MaybeUninit::uninit(); DEFAULT_MAX_HEADERS], }; let (len, status, reason, version, headers_len) = { let mut headers: SmallVec< [MaybeUninit>; DEFAULT_MAX_HEADERS], > = match ctx.h1_max_headers { Some(cap) => smallvec![MaybeUninit::uninit(); cap], None => smallvec_inline![MaybeUninit::uninit(); DEFAULT_MAX_HEADERS], }; trace!(bytes = buf.len(), "Response.parse"); let mut res = httparse::Response::new(&mut []); let bytes = buf.as_ref(); match ctx.h1_parser_config.parse_response_with_uninit_headers( &mut res, bytes, &mut headers, ) { Ok(httparse::Status::Complete(len)) => { trace!("Response.parse Complete({})", len); let status = StatusCode::from_u16(res.code.unwrap())?; let reason = { let reason = res.reason.unwrap(); // Only save the reason phrase if it isn't the canonical reason if Some(reason) != status.canonical_reason() { Some(Bytes::copy_from_slice(reason.as_bytes())) } else { None } }; let version = if res.version.unwrap() == 1 { Version::HTTP_11 } else { Version::HTTP_10 }; record_header_indices(bytes, res.headers, &mut headers_indices)?; let headers_len = res.headers.len(); (len, status, reason, version, headers_len) } Ok(httparse::Status::Partial) => return Ok(None), Err(httparse::Error::Version) if ctx.h09_responses => { trace!("Response.parse accepted HTTP/0.9 response"); (0, StatusCode::OK, None, Version::HTTP_09, 0) } Err(e) => return Err(e.into()), } }; let mut slice = buf.split_to(len); if ctx .h1_parser_config .obsolete_multiline_headers_in_responses_are_allowed() { for header in &mut headers_indices[..headers_len] { // SAFETY: array is valid up to `headers_len` let header = unsafe { header.assume_init_mut() }; Client::obs_fold_line(&mut slice, header); } } let slice = slice.freeze(); let mut headers = ctx.cached_headers.take().unwrap_or_default(); let mut keep_alive = version == Version::HTTP_11; let mut header_case_map = if ctx.preserve_header_case { Some(HeaderCaseMap::default()) } else { None }; #[cfg(feature = "ffi")] let mut header_order = if ctx.preserve_header_order { Some(OriginalHeaderOrder::default()) } else { None }; headers.reserve(headers_len); for header in &headers_indices[..headers_len] { // SAFETY: array is valid up to `headers_len` let header = unsafe { header.assume_init_ref() }; let name = header_name!(&slice[header.name.0..header.name.1]); let value = header_value!(slice.slice(header.value.0..header.value.1)); if let header::CONNECTION = name { // keep_alive was previously set to default for Version if keep_alive { // HTTP/1.1 keep_alive = !headers::connection_close(&value); } else { // HTTP/1.0 keep_alive = headers::connection_keep_alive(&value); } } if let Some(ref mut header_case_map) = header_case_map { header_case_map.append(&name, slice.slice(header.name.0..header.name.1)); } #[cfg(feature = "ffi")] if let Some(ref mut header_order) = header_order { header_order.append(&name); } headers.append(name, value); } let mut extensions = http::Extensions::default(); if let Some(header_case_map) = header_case_map { extensions.insert(header_case_map); } #[cfg(feature = "ffi")] if let Some(header_order) = header_order { extensions.insert(header_order); } if let Some(reason) = reason { // Safety: httparse ensures that only valid reason phrase bytes are present in this // field. let reason = crate::ext::ReasonPhrase::from_bytes_unchecked(reason); extensions.insert(reason); } let head = MessageHead { version, subject: status, headers, extensions, }; if let Some((decode, is_upgrade)) = Client::decoder(&head, ctx.req_method)? { return Ok(Some(ParsedMessage { head, decode, expect_continue: false, // a client upgrade means the connection can't be used // again, as it is definitely upgrading. keep_alive: keep_alive && !is_upgrade, wants_upgrade: is_upgrade, })); } #[cfg(feature = "ffi")] if head.subject.is_informational() { if let Some(callback) = ctx.on_informational { callback.call(head.into_response(crate::body::Incoming::empty())); } } // Parsing a 1xx response could have consumed the buffer, check if // it is empty now... if buf.is_empty() { return Ok(None); } } } fn encode(msg: Encode<'_, Self::Outgoing>, dst: &mut Vec) -> crate::Result { trace!( "Client::encode method={:?}, body={:?}", msg.head.subject.0, msg.body ); *msg.req_method = Some(msg.head.subject.0.clone()); let body = Client::set_length(msg.head, msg.body); let init_cap = 30 + msg.head.headers.len() * AVERAGE_HEADER_SIZE; dst.reserve(init_cap); extend(dst, msg.head.subject.0.as_str().as_bytes()); extend(dst, b" "); //TODO: add API to http::Uri to encode without std::fmt let _ = write!(FastWrite(dst), "{} ", msg.head.subject.1); match msg.head.version { Version::HTTP_10 => extend(dst, b"HTTP/1.0"), Version::HTTP_11 => extend(dst, b"HTTP/1.1"), Version::HTTP_2 => { debug!("request with HTTP2 version coerced to HTTP/1.1"); extend(dst, b"HTTP/1.1"); } other => panic!("unexpected request version: {:?}", other), } extend(dst, b"\r\n"); if let Some(orig_headers) = msg.head.extensions.get::() { write_headers_original_case( &msg.head.headers, orig_headers, dst, msg.title_case_headers, ); } else if msg.title_case_headers { write_headers_title_case(&msg.head.headers, dst); } else { write_headers(&msg.head.headers, dst); } extend(dst, b"\r\n"); msg.head.headers.clear(); //TODO: remove when switching to drain() Ok(body) } fn on_error(_err: &crate::Error) -> Option> { // we can't tell the server about any errors it creates None } fn is_client() -> bool { true } } #[cfg(feature = "client")] impl Client { /// Returns Some(length, wants_upgrade) if successful. /// /// Returns None if this message head should be skipped (like a 100 status). fn decoder( inc: &MessageHead, method: &mut Option, ) -> Result, Parse> { // According to https://tools.ietf.org/html/rfc7230#section-3.3.3 // 1. HEAD responses, and Status 1xx, 204, and 304 cannot have a body. // 2. Status 2xx to a CONNECT cannot have a body. // 3. Transfer-Encoding: chunked has a chunked body. // 4. If multiple differing Content-Length headers or invalid, close connection. // 5. Content-Length header has a sized body. // 6. (irrelevant to Response) // 7. Read till EOF. match inc.subject.as_u16() { 101 => { return Ok(Some((DecodedLength::ZERO, true))); } 100 | 102..=199 => { trace!("ignoring informational response: {}", inc.subject.as_u16()); return Ok(None); } 204 | 304 => return Ok(Some((DecodedLength::ZERO, false))), _ => (), } match *method { Some(Method::HEAD) => { return Ok(Some((DecodedLength::ZERO, false))); } Some(Method::CONNECT) => { if let 200..=299 = inc.subject.as_u16() { return Ok(Some((DecodedLength::ZERO, true))); } } Some(_) => {} None => { trace!("Client::decoder is missing the Method"); } } if inc.headers.contains_key(header::TRANSFER_ENCODING) { // https://tools.ietf.org/html/rfc7230#section-3.3.3 // If Transfer-Encoding header is present, and 'chunked' is // not the final encoding, and this is a Request, then it is // malformed. A server should respond with 400 Bad Request. if inc.version == Version::HTTP_10 { debug!("HTTP/1.0 cannot have Transfer-Encoding header"); Err(Parse::transfer_encoding_unexpected()) } else if headers::transfer_encoding_is_chunked(&inc.headers) { Ok(Some((DecodedLength::CHUNKED, false))) } else { trace!("not chunked, read till eof"); Ok(Some((DecodedLength::CLOSE_DELIMITED, false))) } } else if let Some(len) = headers::content_length_parse_all(&inc.headers) { Ok(Some((DecodedLength::checked_new(len)?, false))) } else if inc.headers.contains_key(header::CONTENT_LENGTH) { debug!("illegal Content-Length header"); Err(Parse::content_length_invalid()) } else { trace!("neither Transfer-Encoding nor Content-Length"); Ok(Some((DecodedLength::CLOSE_DELIMITED, false))) } } fn set_length(head: &mut RequestHead, body: Option) -> Encoder { let body = if let Some(body) = body { body } else { head.headers.remove(header::TRANSFER_ENCODING); return Encoder::length(0); }; // HTTP/1.0 doesn't know about chunked let can_chunked = head.version == Version::HTTP_11; let headers = &mut head.headers; // If the user already set specific headers, we should respect them, regardless // of what the Body knows about itself. They set them for a reason. // Because of the borrow checker, we can't check the for an existing // Content-Length header while holding an `Entry` for the Transfer-Encoding // header, so unfortunately, we must do the check here, first. let existing_con_len = headers::content_length_parse_all(headers); let mut should_remove_con_len = false; if !can_chunked { // Chunked isn't legal, so if it is set, we need to remove it. if headers.remove(header::TRANSFER_ENCODING).is_some() { trace!("removing illegal transfer-encoding header"); } return if let Some(len) = existing_con_len { Encoder::length(len) } else if let BodyLength::Known(len) = body { set_content_length(headers, len) } else { // HTTP/1.0 client requests without a content-length // cannot have any body at all. Encoder::length(0) }; } // If the user set a transfer-encoding, respect that. Let's just // make sure `chunked` is the final encoding. let encoder = match headers.entry(header::TRANSFER_ENCODING) { Entry::Occupied(te) => { should_remove_con_len = true; if headers::is_chunked(te.iter()) { Some(Encoder::chunked()) } else { warn!("user provided transfer-encoding does not end in 'chunked'"); // There's a Transfer-Encoding, but it doesn't end in 'chunked'! // An example that could trigger this: // // Transfer-Encoding: gzip // // This can be bad, depending on if this is a request or a // response. // // - A request is illegal if there is a `Transfer-Encoding` // but it doesn't end in `chunked`. // - A response that has `Transfer-Encoding` but doesn't // end in `chunked` isn't illegal, it just forces this // to be close-delimited. // // We can try to repair this, by adding `chunked` ourselves. headers::add_chunked(te); Some(Encoder::chunked()) } } Entry::Vacant(te) => { if let Some(len) = existing_con_len { Some(Encoder::length(len)) } else if let BodyLength::Unknown = body { // GET, HEAD, and CONNECT almost never have bodies. // // So instead of sending a "chunked" body with a 0-chunk, // assume no body here. If you *must* send a body, // set the headers explicitly. match head.subject.0 { Method::GET | Method::HEAD | Method::CONNECT => Some(Encoder::length(0)), _ => { te.insert(HeaderValue::from_static("chunked")); Some(Encoder::chunked()) } } } else { None } } }; let encoder = encoder.map(|enc| { if enc.is_chunked() { let allowed_trailer_fields: Vec = headers.get_all(header::TRAILER).iter().cloned().collect(); if !allowed_trailer_fields.is_empty() { return enc.into_chunked_with_trailing_fields(allowed_trailer_fields); } } enc }); // This is because we need a second mutable borrow to remove // content-length header. if let Some(encoder) = encoder { if should_remove_con_len && existing_con_len.is_some() { headers.remove(header::CONTENT_LENGTH); } return encoder; } // User didn't set transfer-encoding, AND we know body length, // so we can just set the Content-Length automatically. let len = if let BodyLength::Known(len) = body { len } else { unreachable!("BodyLength::Unknown would set chunked"); }; set_content_length(headers, len) } fn obs_fold_line(all: &mut [u8], idx: &mut HeaderIndices) { // If the value has obs-folded text, then in-place shift the bytes out // of here. // // https://httpwg.org/specs/rfc9112.html#line.folding // // > A user agent that receives an obs-fold MUST replace each received // > obs-fold with one or more SP octets prior to interpreting the // > field value. // // This means strings like "\r\n\t foo" must replace the "\r\n\t " with // a single space. let buf = &mut all[idx.value.0..idx.value.1]; // look for a newline, otherwise bail out let first_nl = match buf.iter().position(|b| *b == b'\n') { Some(i) => i, None => return, }; // not on standard slices because whatever, sigh fn trim_start(mut s: &[u8]) -> &[u8] { while let [first, rest @ ..] = s { if first.is_ascii_whitespace() { s = rest; } else { break; } } s } fn trim_end(mut s: &[u8]) -> &[u8] { while let [rest @ .., last] = s { if last.is_ascii_whitespace() { s = rest; } else { break; } } s } fn trim(s: &[u8]) -> &[u8] { trim_start(trim_end(s)) } // TODO(perf): we could do the moves in-place, but this is so uncommon // that it shouldn't matter. let mut unfolded = trim_end(&buf[..first_nl]).to_vec(); for line in buf[first_nl + 1..].split(|b| *b == b'\n') { unfolded.push(b' '); unfolded.extend_from_slice(trim(line)); } buf[..unfolded.len()].copy_from_slice(&unfolded); idx.value.1 = idx.value.0 + unfolded.len(); } } #[cfg(feature = "client")] fn set_content_length(headers: &mut HeaderMap, len: u64) -> Encoder { // At this point, there should not be a valid Content-Length // header. However, since we'll be indexing in anyways, we can // warn the user if there was an existing illegal header. // // Or at least, we can in theory. It's actually a little bit slower, // so perhaps only do that while the user is developing/testing. if cfg!(debug_assertions) { match headers.entry(header::CONTENT_LENGTH) { Entry::Occupied(mut cl) => { // Internal sanity check, we should have already determined // that the header was illegal before calling this function. debug_assert!(headers::content_length_parse_all_values(cl.iter()).is_none()); // Uh oh, the user set `Content-Length` headers, but set bad ones. // This would be an illegal message anyways, so let's try to repair // with our known good length. error!("user provided content-length header was invalid"); cl.insert(HeaderValue::from(len)); Encoder::length(len) } Entry::Vacant(cl) => { cl.insert(HeaderValue::from(len)); Encoder::length(len) } } } else { headers.insert(header::CONTENT_LENGTH, HeaderValue::from(len)); Encoder::length(len) } } #[derive(Clone, Copy)] struct HeaderIndices { name: (usize, usize), value: (usize, usize), } fn record_header_indices( bytes: &[u8], headers: &[httparse::Header<'_>], indices: &mut [MaybeUninit], ) -> Result<(), crate::error::Parse> { let bytes_ptr = bytes.as_ptr() as usize; for (header, indices) in headers.iter().zip(indices.iter_mut()) { if header.name.len() >= (1 << 16) { debug!("header name larger than 64kb: {:?}", header.name); return Err(crate::error::Parse::TooLarge); } let name_start = header.name.as_ptr() as usize - bytes_ptr; let name_end = name_start + header.name.len(); let value_start = header.value.as_ptr() as usize - bytes_ptr; let value_end = value_start + header.value.len(); indices.write(HeaderIndices { name: (name_start, name_end), value: (value_start, value_end), }); } Ok(()) } // Write header names as title case. The header name is assumed to be ASCII. fn title_case(dst: &mut Vec, name: &[u8]) { dst.reserve(name.len()); // Ensure first character is uppercased let mut prev = b'-'; for &(mut c) in name { if prev == b'-' { c.make_ascii_uppercase(); } dst.push(c); prev = c; } } pub(crate) fn write_headers_title_case(headers: &HeaderMap, dst: &mut Vec) { for (name, value) in headers { title_case(dst, name.as_str().as_bytes()); extend(dst, b": "); extend(dst, value.as_bytes()); extend(dst, b"\r\n"); } } pub(crate) fn write_headers(headers: &HeaderMap, dst: &mut Vec) { for (name, value) in headers { extend(dst, name.as_str().as_bytes()); extend(dst, b": "); extend(dst, value.as_bytes()); extend(dst, b"\r\n"); } } #[cold] #[cfg(feature = "client")] fn write_headers_original_case( headers: &HeaderMap, orig_case: &HeaderCaseMap, dst: &mut Vec, title_case_headers: bool, ) { // For each header name/value pair, there may be a value in the casemap // that corresponds to the HeaderValue. So, we iterator all the keys, // and for each one, try to pair the originally cased name with the value. // // TODO: consider adding http::HeaderMap::entries() iterator for name in headers.keys() { let mut names = orig_case.get_all(name); for value in headers.get_all(name) { if let Some(orig_name) = names.next() { extend(dst, orig_name.as_ref()); } else if title_case_headers { title_case(dst, name.as_str().as_bytes()); } else { extend(dst, name.as_str().as_bytes()); } // Wanted for curl test cases that send `X-Custom-Header:\r\n` if value.is_empty() { extend(dst, b":\r\n"); } else { extend(dst, b": "); extend(dst, value.as_bytes()); extend(dst, b"\r\n"); } } } } #[cfg(feature = "client")] struct FastWrite<'a>(&'a mut Vec); #[cfg(feature = "client")] impl fmt::Write for FastWrite<'_> { #[inline] fn write_str(&mut self, s: &str) -> fmt::Result { extend(self.0, s.as_bytes()); Ok(()) } #[inline] fn write_fmt(&mut self, args: fmt::Arguments<'_>) -> fmt::Result { fmt::write(self, args) } } #[inline] fn extend(dst: &mut Vec, data: &[u8]) { dst.extend_from_slice(data); } #[cfg(test)] mod tests { use bytes::BytesMut; use super::*; #[cfg(feature = "server")] #[test] fn test_parse_request() { let _ = pretty_env_logger::try_init(); let mut raw = BytesMut::from("GET /echo HTTP/1.1\r\nHost: hyper.rs\r\n\r\n"); let mut method = None; let msg = Server::parse( &mut raw, ParseContext { cached_headers: &mut None, req_method: &mut method, h1_parser_config: Default::default(), h1_max_headers: None, preserve_header_case: false, #[cfg(feature = "ffi")] preserve_header_order: false, h09_responses: false, #[cfg(feature = "ffi")] on_informational: &mut None, }, ) .unwrap() .unwrap(); assert_eq!(raw.len(), 0); assert_eq!(msg.head.subject.0, crate::Method::GET); assert_eq!(msg.head.subject.1, "/echo"); assert_eq!(msg.head.version, crate::Version::HTTP_11); assert_eq!(msg.head.headers.len(), 1); assert_eq!(msg.head.headers["Host"], "hyper.rs"); assert_eq!(method, Some(crate::Method::GET)); } #[test] fn test_parse_response() { let _ = pretty_env_logger::try_init(); let mut raw = BytesMut::from("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"); let ctx = ParseContext { cached_headers: &mut None, req_method: &mut Some(crate::Method::GET), h1_parser_config: Default::default(), h1_max_headers: None, preserve_header_case: false, #[cfg(feature = "ffi")] preserve_header_order: false, h09_responses: false, #[cfg(feature = "ffi")] on_informational: &mut None, }; let msg = Client::parse(&mut raw, ctx).unwrap().unwrap(); assert_eq!(raw.len(), 0); assert_eq!(msg.head.subject, crate::StatusCode::OK); assert_eq!(msg.head.version, crate::Version::HTTP_11); assert_eq!(msg.head.headers.len(), 1); assert_eq!(msg.head.headers["Content-Length"], "0"); } #[cfg(feature = "server")] #[test] fn test_parse_request_errors() { let mut raw = BytesMut::from("GET htt:p// HTTP/1.1\r\nHost: hyper.rs\r\n\r\n"); let ctx = ParseContext { cached_headers: &mut None, req_method: &mut None, h1_parser_config: Default::default(), h1_max_headers: None, preserve_header_case: false, #[cfg(feature = "ffi")] preserve_header_order: false, h09_responses: false, #[cfg(feature = "ffi")] on_informational: &mut None, }; Server::parse(&mut raw, ctx).unwrap_err(); } const H09_RESPONSE: &str = "Baguettes are super delicious, don't you agree?"; #[test] fn test_parse_response_h09_allowed() { let _ = pretty_env_logger::try_init(); let mut raw = BytesMut::from(H09_RESPONSE); let ctx = ParseContext { cached_headers: &mut None, req_method: &mut Some(crate::Method::GET), h1_parser_config: Default::default(), h1_max_headers: None, preserve_header_case: false, #[cfg(feature = "ffi")] preserve_header_order: false, h09_responses: true, #[cfg(feature = "ffi")] on_informational: &mut None, }; let msg = Client::parse(&mut raw, ctx).unwrap().unwrap(); assert_eq!(raw, H09_RESPONSE); assert_eq!(msg.head.subject, crate::StatusCode::OK); assert_eq!(msg.head.version, crate::Version::HTTP_09); assert_eq!(msg.head.headers.len(), 0); } #[test] fn test_parse_response_h09_rejected() { let _ = pretty_env_logger::try_init(); let mut raw = BytesMut::from(H09_RESPONSE); let ctx = ParseContext { cached_headers: &mut None, req_method: &mut Some(crate::Method::GET), h1_parser_config: Default::default(), h1_max_headers: None, preserve_header_case: false, #[cfg(feature = "ffi")] preserve_header_order: false, h09_responses: false, #[cfg(feature = "ffi")] on_informational: &mut None, }; Client::parse(&mut raw, ctx).unwrap_err(); assert_eq!(raw, H09_RESPONSE); } const RESPONSE_WITH_WHITESPACE_BETWEEN_HEADER_NAME_AND_COLON: &str = "HTTP/1.1 200 OK\r\nAccess-Control-Allow-Credentials : true\r\n\r\n"; #[test] fn test_parse_allow_response_with_spaces_before_colons() { use httparse::ParserConfig; let _ = pretty_env_logger::try_init(); let mut raw = BytesMut::from(RESPONSE_WITH_WHITESPACE_BETWEEN_HEADER_NAME_AND_COLON); let mut h1_parser_config = ParserConfig::default(); h1_parser_config.allow_spaces_after_header_name_in_responses(true); let ctx = ParseContext { cached_headers: &mut None, req_method: &mut Some(crate::Method::GET), h1_parser_config, h1_max_headers: None, preserve_header_case: false, #[cfg(feature = "ffi")] preserve_header_order: false, h09_responses: false, #[cfg(feature = "ffi")] on_informational: &mut None, }; let msg = Client::parse(&mut raw, ctx).unwrap().unwrap(); assert_eq!(raw.len(), 0); assert_eq!(msg.head.subject, crate::StatusCode::OK); assert_eq!(msg.head.version, crate::Version::HTTP_11); assert_eq!(msg.head.headers.len(), 1); assert_eq!(msg.head.headers["Access-Control-Allow-Credentials"], "true"); } #[test] fn test_parse_reject_response_with_spaces_before_colons() { let _ = pretty_env_logger::try_init(); let mut raw = BytesMut::from(RESPONSE_WITH_WHITESPACE_BETWEEN_HEADER_NAME_AND_COLON); let ctx = ParseContext { cached_headers: &mut None, req_method: &mut Some(crate::Method::GET), h1_parser_config: Default::default(), h1_max_headers: None, preserve_header_case: false, #[cfg(feature = "ffi")] preserve_header_order: false, h09_responses: false, #[cfg(feature = "ffi")] on_informational: &mut None, }; Client::parse(&mut raw, ctx).unwrap_err(); } #[cfg(feature = "server")] #[test] fn test_parse_preserve_header_case_in_request() { let mut raw = BytesMut::from("GET / HTTP/1.1\r\nHost: hyper.rs\r\nX-BREAD: baguette\r\n\r\n"); let ctx = ParseContext { cached_headers: &mut None, req_method: &mut None, h1_parser_config: Default::default(), h1_max_headers: None, preserve_header_case: true, #[cfg(feature = "ffi")] preserve_header_order: false, h09_responses: false, #[cfg(feature = "ffi")] on_informational: &mut None, }; let parsed_message = Server::parse(&mut raw, ctx).unwrap().unwrap(); let orig_headers = parsed_message .head .extensions .get::() .unwrap(); assert_eq!( orig_headers .get_all_internal(&HeaderName::from_static("host")) .collect::>(), vec![&Bytes::from("Host")] ); assert_eq!( orig_headers .get_all_internal(&HeaderName::from_static("x-bread")) .collect::>(), vec![&Bytes::from("X-BREAD")] ); } #[cfg(feature = "server")] #[test] fn test_decoder_request() { fn parse(s: &str) -> ParsedMessage { let mut bytes = BytesMut::from(s); Server::parse( &mut bytes, ParseContext { cached_headers: &mut None, req_method: &mut None, h1_parser_config: Default::default(), h1_max_headers: None, preserve_header_case: false, #[cfg(feature = "ffi")] preserve_header_order: false, h09_responses: false, #[cfg(feature = "ffi")] on_informational: &mut None, }, ) .expect("parse ok") .expect("parse complete") } fn parse_err(s: &str, comment: &str) -> crate::error::Parse { let mut bytes = BytesMut::from(s); Server::parse( &mut bytes, ParseContext { cached_headers: &mut None, req_method: &mut None, h1_parser_config: Default::default(), h1_max_headers: None, preserve_header_case: false, #[cfg(feature = "ffi")] preserve_header_order: false, h09_responses: false, #[cfg(feature = "ffi")] on_informational: &mut None, }, ) .expect_err(comment) } // no length or transfer-encoding means 0-length body assert_eq!( parse( "\ GET / HTTP/1.1\r\n\ \r\n\ " ) .decode, DecodedLength::ZERO ); assert_eq!( parse( "\ POST / HTTP/1.1\r\n\ \r\n\ " ) .decode, DecodedLength::ZERO ); // transfer-encoding: chunked assert_eq!( parse( "\ POST / HTTP/1.1\r\n\ transfer-encoding: chunked\r\n\ \r\n\ " ) .decode, DecodedLength::CHUNKED ); assert_eq!( parse( "\ POST / HTTP/1.1\r\n\ transfer-encoding: gzip, chunked\r\n\ \r\n\ " ) .decode, DecodedLength::CHUNKED ); assert_eq!( parse( "\ POST / HTTP/1.1\r\n\ transfer-encoding: gzip\r\n\ transfer-encoding: chunked\r\n\ \r\n\ " ) .decode, DecodedLength::CHUNKED ); // content-length assert_eq!( parse( "\ POST / HTTP/1.1\r\n\ content-length: 10\r\n\ \r\n\ " ) .decode, DecodedLength::new(10) ); // transfer-encoding and content-length = chunked assert_eq!( parse( "\ POST / HTTP/1.1\r\n\ content-length: 10\r\n\ transfer-encoding: chunked\r\n\ \r\n\ " ) .decode, DecodedLength::CHUNKED ); assert_eq!( parse( "\ POST / HTTP/1.1\r\n\ transfer-encoding: chunked\r\n\ content-length: 10\r\n\ \r\n\ " ) .decode, DecodedLength::CHUNKED ); assert_eq!( parse( "\ POST / HTTP/1.1\r\n\ transfer-encoding: gzip\r\n\ content-length: 10\r\n\ transfer-encoding: chunked\r\n\ \r\n\ " ) .decode, DecodedLength::CHUNKED ); // multiple content-lengths of same value are fine assert_eq!( parse( "\ POST / HTTP/1.1\r\n\ content-length: 10\r\n\ content-length: 10\r\n\ \r\n\ " ) .decode, DecodedLength::new(10) ); // multiple content-lengths with different values is an error parse_err( "\ POST / HTTP/1.1\r\n\ content-length: 10\r\n\ content-length: 11\r\n\ \r\n\ ", "multiple content-lengths", ); // content-length with prefix is not allowed parse_err( "\ POST / HTTP/1.1\r\n\ content-length: +10\r\n\ \r\n\ ", "prefixed content-length", ); // transfer-encoding that isn't chunked is an error parse_err( "\ POST / HTTP/1.1\r\n\ transfer-encoding: gzip\r\n\ \r\n\ ", "transfer-encoding but not chunked", ); parse_err( "\ POST / HTTP/1.1\r\n\ transfer-encoding: chunked, gzip\r\n\ \r\n\ ", "transfer-encoding doesn't end in chunked", ); parse_err( "\ POST / HTTP/1.1\r\n\ transfer-encoding: chunked\r\n\ transfer-encoding: afterlol\r\n\ \r\n\ ", "transfer-encoding multiple lines doesn't end in chunked", ); // http/1.0 assert_eq!( parse( "\ POST / HTTP/1.0\r\n\ content-length: 10\r\n\ \r\n\ " ) .decode, DecodedLength::new(10) ); // 1.0 doesn't understand chunked, so its an error parse_err( "\ POST / HTTP/1.0\r\n\ transfer-encoding: chunked\r\n\ \r\n\ ", "1.0 chunked", ); } #[test] fn test_decoder_response() { fn parse(s: &str) -> ParsedMessage { parse_with_method(s, Method::GET) } fn parse_ignores(s: &str) { let mut bytes = BytesMut::from(s); assert!(Client::parse( &mut bytes, ParseContext { cached_headers: &mut None, req_method: &mut Some(Method::GET), h1_parser_config: Default::default(), h1_max_headers: None, preserve_header_case: false, #[cfg(feature = "ffi")] preserve_header_order: false, h09_responses: false, #[cfg(feature = "ffi")] on_informational: &mut None, } ) .expect("parse ok") .is_none()) } fn parse_with_method(s: &str, m: Method) -> ParsedMessage { let mut bytes = BytesMut::from(s); Client::parse( &mut bytes, ParseContext { cached_headers: &mut None, req_method: &mut Some(m), h1_parser_config: Default::default(), h1_max_headers: None, preserve_header_case: false, #[cfg(feature = "ffi")] preserve_header_order: false, h09_responses: false, #[cfg(feature = "ffi")] on_informational: &mut None, }, ) .expect("parse ok") .expect("parse complete") } fn parse_err(s: &str) -> crate::error::Parse { let mut bytes = BytesMut::from(s); Client::parse( &mut bytes, ParseContext { cached_headers: &mut None, req_method: &mut Some(Method::GET), h1_parser_config: Default::default(), h1_max_headers: None, preserve_header_case: false, #[cfg(feature = "ffi")] preserve_header_order: false, h09_responses: false, #[cfg(feature = "ffi")] on_informational: &mut None, }, ) .expect_err("parse should err") } // no content-length or transfer-encoding means close-delimited assert_eq!( parse( "\ HTTP/1.1 200 OK\r\n\ \r\n\ " ) .decode, DecodedLength::CLOSE_DELIMITED ); // 204 and 304 never have a body assert_eq!( parse( "\ HTTP/1.1 204 No Content\r\n\ \r\n\ " ) .decode, DecodedLength::ZERO ); assert_eq!( parse( "\ HTTP/1.1 304 Not Modified\r\n\ \r\n\ " ) .decode, DecodedLength::ZERO ); // content-length assert_eq!( parse( "\ HTTP/1.1 200 OK\r\n\ content-length: 8\r\n\ \r\n\ " ) .decode, DecodedLength::new(8) ); assert_eq!( parse( "\ HTTP/1.1 200 OK\r\n\ content-length: 8\r\n\ content-length: 8\r\n\ \r\n\ " ) .decode, DecodedLength::new(8) ); parse_err( "\ HTTP/1.1 200 OK\r\n\ content-length: 8\r\n\ content-length: 9\r\n\ \r\n\ ", ); parse_err( "\ HTTP/1.1 200 OK\r\n\ content-length: +8\r\n\ \r\n\ ", ); // transfer-encoding: chunked assert_eq!( parse( "\ HTTP/1.1 200 OK\r\n\ transfer-encoding: chunked\r\n\ \r\n\ " ) .decode, DecodedLength::CHUNKED ); // transfer-encoding not-chunked is close-delimited assert_eq!( parse( "\ HTTP/1.1 200 OK\r\n\ transfer-encoding: yolo\r\n\ \r\n\ " ) .decode, DecodedLength::CLOSE_DELIMITED ); // transfer-encoding and content-length = chunked assert_eq!( parse( "\ HTTP/1.1 200 OK\r\n\ content-length: 10\r\n\ transfer-encoding: chunked\r\n\ \r\n\ " ) .decode, DecodedLength::CHUNKED ); // HEAD can have content-length, but not body assert_eq!( parse_with_method( "\ HTTP/1.1 200 OK\r\n\ content-length: 8\r\n\ \r\n\ ", Method::HEAD ) .decode, DecodedLength::ZERO ); // CONNECT with 200 never has body { let msg = parse_with_method( "\ HTTP/1.1 200 OK\r\n\ \r\n\ ", Method::CONNECT, ); assert_eq!(msg.decode, DecodedLength::ZERO); assert!(!msg.keep_alive, "should be upgrade"); assert!(msg.wants_upgrade, "should be upgrade"); } // CONNECT receiving non 200 can have a body assert_eq!( parse_with_method( "\ HTTP/1.1 400 Bad Request\r\n\ \r\n\ ", Method::CONNECT ) .decode, DecodedLength::CLOSE_DELIMITED ); // 1xx status codes parse_ignores( "\ HTTP/1.1 100 Continue\r\n\ \r\n\ ", ); parse_ignores( "\ HTTP/1.1 103 Early Hints\r\n\ \r\n\ ", ); // 101 upgrade not supported yet { let msg = parse( "\ HTTP/1.1 101 Switching Protocols\r\n\ \r\n\ ", ); assert_eq!(msg.decode, DecodedLength::ZERO); assert!(!msg.keep_alive, "should be last"); assert!(msg.wants_upgrade, "should be upgrade"); } // http/1.0 assert_eq!( parse( "\ HTTP/1.0 200 OK\r\n\ \r\n\ " ) .decode, DecodedLength::CLOSE_DELIMITED ); // 1.0 doesn't understand chunked parse_err( "\ HTTP/1.0 200 OK\r\n\ transfer-encoding: chunked\r\n\ \r\n\ ", ); // keep-alive assert!( parse( "\ HTTP/1.1 200 OK\r\n\ content-length: 0\r\n\ \r\n\ " ) .keep_alive, "HTTP/1.1 keep-alive is default" ); assert!( !parse( "\ HTTP/1.1 200 OK\r\n\ content-length: 0\r\n\ connection: foo, close, bar\r\n\ \r\n\ " ) .keep_alive, "connection close is always close" ); assert!( !parse( "\ HTTP/1.0 200 OK\r\n\ content-length: 0\r\n\ \r\n\ " ) .keep_alive, "HTTP/1.0 close is default" ); assert!( parse( "\ HTTP/1.0 200 OK\r\n\ content-length: 0\r\n\ connection: foo, keep-alive, bar\r\n\ \r\n\ " ) .keep_alive, "connection keep-alive is always keep-alive" ); } #[cfg(feature = "client")] #[test] fn test_client_obs_fold_line() { fn unfold(src: &str) -> String { let mut buf = src.as_bytes().to_vec(); let mut idx = HeaderIndices { name: (0, 0), value: (0, buf.len()), }; Client::obs_fold_line(&mut buf, &mut idx); String::from_utf8(buf[idx.value.0..idx.value.1].to_vec()).unwrap() } assert_eq!(unfold("a normal line"), "a normal line",); assert_eq!(unfold("obs\r\n fold\r\n\t line"), "obs fold line",); } #[test] fn test_client_request_encode_title_case() { use crate::proto::BodyLength; use http::header::HeaderValue; let mut head = MessageHead::default(); head.headers .insert("content-length", HeaderValue::from_static("10")); head.headers .insert("content-type", HeaderValue::from_static("application/json")); head.headers.insert("*-*", HeaderValue::from_static("o_o")); let mut vec = Vec::new(); Client::encode( Encode { head: &mut head, body: Some(BodyLength::Known(10)), #[cfg(feature = "server")] keep_alive: true, req_method: &mut None, title_case_headers: true, #[cfg(feature = "server")] date_header: true, }, &mut vec, ) .unwrap(); assert_eq!(vec, b"GET / HTTP/1.1\r\nContent-Length: 10\r\nContent-Type: application/json\r\n*-*: o_o\r\n\r\n".to_vec()); } #[test] fn test_client_request_encode_orig_case() { use crate::proto::BodyLength; use http::header::{HeaderValue, CONTENT_LENGTH}; let mut head = MessageHead::default(); head.headers .insert("content-length", HeaderValue::from_static("10")); head.headers .insert("content-type", HeaderValue::from_static("application/json")); let mut orig_headers = HeaderCaseMap::default(); orig_headers.insert(CONTENT_LENGTH, "CONTENT-LENGTH".into()); head.extensions.insert(orig_headers); let mut vec = Vec::new(); Client::encode( Encode { head: &mut head, body: Some(BodyLength::Known(10)), #[cfg(feature = "server")] keep_alive: true, req_method: &mut None, title_case_headers: false, #[cfg(feature = "server")] date_header: true, }, &mut vec, ) .unwrap(); assert_eq!( &*vec, b"GET / HTTP/1.1\r\nCONTENT-LENGTH: 10\r\ncontent-type: application/json\r\n\r\n" .as_ref(), ); } #[test] fn test_client_request_encode_orig_and_title_case() { use crate::proto::BodyLength; use http::header::{HeaderValue, CONTENT_LENGTH}; let mut head = MessageHead::default(); head.headers .insert("content-length", HeaderValue::from_static("10")); head.headers .insert("content-type", HeaderValue::from_static("application/json")); let mut orig_headers = HeaderCaseMap::default(); orig_headers.insert(CONTENT_LENGTH, "CONTENT-LENGTH".into()); head.extensions.insert(orig_headers); let mut vec = Vec::new(); Client::encode( Encode { head: &mut head, body: Some(BodyLength::Known(10)), #[cfg(feature = "server")] keep_alive: true, req_method: &mut None, title_case_headers: true, #[cfg(feature = "server")] date_header: true, }, &mut vec, ) .unwrap(); assert_eq!( &*vec, b"GET / HTTP/1.1\r\nCONTENT-LENGTH: 10\r\nContent-Type: application/json\r\n\r\n" .as_ref(), ); } #[cfg(feature = "server")] #[test] fn test_server_encode_connect_method() { let mut head = MessageHead::default(); let mut vec = Vec::new(); let encoder = Server::encode( Encode { head: &mut head, body: None, keep_alive: true, req_method: &mut Some(Method::CONNECT), title_case_headers: false, date_header: true, }, &mut vec, ) .unwrap(); assert!(encoder.is_last()); } #[cfg(feature = "server")] #[test] fn test_server_response_encode_title_case() { use crate::proto::BodyLength; use http::header::HeaderValue; let mut head = MessageHead::default(); head.headers .insert("content-length", HeaderValue::from_static("10")); head.headers .insert("content-type", HeaderValue::from_static("application/json")); head.headers .insert("weird--header", HeaderValue::from_static("")); let mut vec = Vec::new(); Server::encode( Encode { head: &mut head, body: Some(BodyLength::Known(10)), keep_alive: true, req_method: &mut None, title_case_headers: true, date_header: true, }, &mut vec, ) .unwrap(); let expected_response = b"HTTP/1.1 200 OK\r\nContent-Length: 10\r\nContent-Type: application/json\r\nWeird--Header: \r\n"; assert_eq!(&vec[..expected_response.len()], &expected_response[..]); } #[cfg(feature = "server")] #[test] fn test_server_response_encode_orig_case() { use crate::proto::BodyLength; use http::header::{HeaderValue, CONTENT_LENGTH}; let mut head = MessageHead::default(); head.headers .insert("content-length", HeaderValue::from_static("10")); head.headers .insert("content-type", HeaderValue::from_static("application/json")); let mut orig_headers = HeaderCaseMap::default(); orig_headers.insert(CONTENT_LENGTH, "CONTENT-LENGTH".into()); head.extensions.insert(orig_headers); let mut vec = Vec::new(); Server::encode( Encode { head: &mut head, body: Some(BodyLength::Known(10)), keep_alive: true, req_method: &mut None, title_case_headers: false, date_header: true, }, &mut vec, ) .unwrap(); let expected_response = b"HTTP/1.1 200 OK\r\nCONTENT-LENGTH: 10\r\ncontent-type: application/json\r\ndate: "; assert_eq!(&vec[..expected_response.len()], &expected_response[..]); } #[cfg(feature = "server")] #[test] fn test_server_response_encode_orig_and_title_case() { use crate::proto::BodyLength; use http::header::{HeaderValue, CONTENT_LENGTH}; let mut head = MessageHead::default(); head.headers .insert("content-length", HeaderValue::from_static("10")); head.headers .insert("content-type", HeaderValue::from_static("application/json")); let mut orig_headers = HeaderCaseMap::default(); orig_headers.insert(CONTENT_LENGTH, "CONTENT-LENGTH".into()); head.extensions.insert(orig_headers); let mut vec = Vec::new(); Server::encode( Encode { head: &mut head, body: Some(BodyLength::Known(10)), keep_alive: true, req_method: &mut None, title_case_headers: true, date_header: true, }, &mut vec, ) .unwrap(); // this will also test that the date does exist let expected_response = b"HTTP/1.1 200 OK\r\nCONTENT-LENGTH: 10\r\nContent-Type: application/json\r\nDate: "; assert_eq!(&vec[..expected_response.len()], &expected_response[..]); } #[cfg(feature = "server")] #[test] fn test_disabled_date_header() { use crate::proto::BodyLength; use http::header::{HeaderValue, CONTENT_LENGTH}; let mut head = MessageHead::default(); head.headers .insert("content-length", HeaderValue::from_static("10")); head.headers .insert("content-type", HeaderValue::from_static("application/json")); let mut orig_headers = HeaderCaseMap::default(); orig_headers.insert(CONTENT_LENGTH, "CONTENT-LENGTH".into()); head.extensions.insert(orig_headers); let mut vec = Vec::new(); Server::encode( Encode { head: &mut head, body: Some(BodyLength::Known(10)), keep_alive: true, req_method: &mut None, title_case_headers: true, date_header: false, }, &mut vec, ) .unwrap(); let expected_response = b"HTTP/1.1 200 OK\r\nCONTENT-LENGTH: 10\r\nContent-Type: application/json\r\n\r\n"; assert_eq!(&vec, &expected_response); } #[test] fn parse_header_htabs() { let mut bytes = BytesMut::from("HTTP/1.1 200 OK\r\nserver: hello\tworld\r\n\r\n"); let parsed = Client::parse( &mut bytes, ParseContext { cached_headers: &mut None, req_method: &mut Some(Method::GET), h1_parser_config: Default::default(), h1_max_headers: None, preserve_header_case: false, #[cfg(feature = "ffi")] preserve_header_order: false, h09_responses: false, #[cfg(feature = "ffi")] on_informational: &mut None, }, ) .expect("parse ok") .expect("parse complete"); assert_eq!(parsed.head.headers["server"], "hello\tworld"); } #[cfg(feature = "server")] #[test] fn parse_too_large_headers() { fn gen_req_with_headers(num: usize) -> String { let mut req = String::from("GET / HTTP/1.1\r\n"); for i in 0..num { req.push_str(&format!("key{i}: val{i}\r\n")); } req.push_str("\r\n"); req } fn gen_resp_with_headers(num: usize) -> String { let mut req = String::from("HTTP/1.1 200 OK\r\n"); for i in 0..num { req.push_str(&format!("key{i}: val{i}\r\n")); } req.push_str("\r\n"); req } fn parse(max_headers: Option, gen_size: usize, should_success: bool) { { // server side let mut bytes = BytesMut::from(gen_req_with_headers(gen_size).as_str()); let result = Server::parse( &mut bytes, ParseContext { cached_headers: &mut None, req_method: &mut None, h1_parser_config: Default::default(), h1_max_headers: max_headers, preserve_header_case: false, #[cfg(feature = "ffi")] preserve_header_order: false, h09_responses: false, #[cfg(feature = "ffi")] on_informational: &mut None, }, ); if should_success { result.expect("parse ok").expect("parse complete"); } else { result.expect_err("parse should err"); } } { // client side let mut bytes = BytesMut::from(gen_resp_with_headers(gen_size).as_str()); let result = Client::parse( &mut bytes, ParseContext { cached_headers: &mut None, req_method: &mut None, h1_parser_config: Default::default(), h1_max_headers: max_headers, preserve_header_case: false, #[cfg(feature = "ffi")] preserve_header_order: false, h09_responses: false, #[cfg(feature = "ffi")] on_informational: &mut None, }, ); if should_success { result.expect("parse ok").expect("parse complete"); } else { result.expect_err("parse should err"); } } } // check generator assert_eq!( gen_req_with_headers(0), String::from("GET / HTTP/1.1\r\n\r\n") ); assert_eq!( gen_req_with_headers(1), String::from("GET / HTTP/1.1\r\nkey0: val0\r\n\r\n") ); assert_eq!( gen_req_with_headers(2), String::from("GET / HTTP/1.1\r\nkey0: val0\r\nkey1: val1\r\n\r\n") ); assert_eq!( gen_req_with_headers(3), String::from("GET / HTTP/1.1\r\nkey0: val0\r\nkey1: val1\r\nkey2: val2\r\n\r\n") ); // default max_headers is 100, so // // - less than or equal to 100, accepted // parse(None, 0, true); parse(None, 1, true); parse(None, 50, true); parse(None, 99, true); parse(None, 100, true); // // - more than 100, rejected // parse(None, 101, false); parse(None, 102, false); parse(None, 200, false); // max_headers is 0, parser will reject any headers // // - without header, accepted // parse(Some(0), 0, true); // // - with header(s), rejected // parse(Some(0), 1, false); parse(Some(0), 100, false); // max_headers is 200 // // - less than or equal to 200, accepted // parse(Some(200), 0, true); parse(Some(200), 1, true); parse(Some(200), 100, true); parse(Some(200), 200, true); // // - more than 200, rejected // parse(Some(200), 201, false); parse(Some(200), 210, false); } #[test] fn test_is_complete_fast() { let s = b"GET / HTTP/1.1\r\na: b\r\n\r\n"; for n in 0..s.len() { assert!(is_complete_fast(s, n), "{:?}; {}", s, n); } let s = b"GET / HTTP/1.1\na: b\n\n"; for n in 0..s.len() { assert!(is_complete_fast(s, n)); } // Not let s = b"GET / HTTP/1.1\r\na: b\r\n\r"; for n in 0..s.len() { assert!(!is_complete_fast(s, n)); } let s = b"GET / HTTP/1.1\na: b\n"; for n in 0..s.len() { assert!(!is_complete_fast(s, n)); } } #[test] fn test_write_headers_orig_case_empty_value() { let mut headers = HeaderMap::new(); let name = http::header::HeaderName::from_static("x-empty"); headers.insert(&name, "".parse().expect("parse empty")); let mut orig_cases = HeaderCaseMap::default(); orig_cases.insert(name, Bytes::from_static(b"X-EmptY")); let mut dst = Vec::new(); super::write_headers_original_case(&headers, &orig_cases, &mut dst, false); assert_eq!( dst, b"X-EmptY:\r\n", "there should be no space between the colon and CRLF" ); } #[test] fn test_write_headers_orig_case_multiple_entries() { let mut headers = HeaderMap::new(); let name = http::header::HeaderName::from_static("x-empty"); headers.insert(&name, "a".parse().unwrap()); headers.append(&name, "b".parse().unwrap()); let mut orig_cases = HeaderCaseMap::default(); orig_cases.insert(name.clone(), Bytes::from_static(b"X-Empty")); orig_cases.append(name, Bytes::from_static(b"X-EMPTY")); let mut dst = Vec::new(); super::write_headers_original_case(&headers, &orig_cases, &mut dst, false); assert_eq!(dst, b"X-Empty: a\r\nX-EMPTY: b\r\n"); } #[cfg(feature = "nightly")] use test::Bencher; #[cfg(feature = "nightly")] #[bench] fn bench_parse_incoming(b: &mut Bencher) { let mut raw = BytesMut::from( &b"GET /super_long_uri/and_whatever?what_should_we_talk_about/\ I_wonder/Hard_to_write_in_an_uri_after_all/you_have_to_make\ _up_the_punctuation_yourself/how_fun_is_that?test=foo&test1=\ foo1&test2=foo2&test3=foo3&test4=foo4 HTTP/1.1\r\nHost: \ hyper.rs\r\nAccept: a lot of things\r\nAccept-Charset: \ utf8\r\nAccept-Encoding: *\r\nAccess-Control-Allow-\ Credentials: None\r\nAccess-Control-Allow-Origin: None\r\n\ Access-Control-Allow-Methods: None\r\nAccess-Control-Allow-\ Headers: None\r\nContent-Encoding: utf8\r\nContent-Security-\ Policy: None\r\nContent-Type: text/html\r\nOrigin: hyper\ \r\nSec-Websocket-Extensions: It looks super important!\r\n\ Sec-Websocket-Origin: hyper\r\nSec-Websocket-Version: 4.3\r\ \nStrict-Transport-Security: None\r\nUser-Agent: hyper\r\n\ X-Content-Duration: None\r\nX-Content-Security-Policy: None\ \r\nX-DNSPrefetch-Control: None\r\nX-Frame-Options: \ Something important obviously\r\nX-Requested-With: Nothing\ \r\n\r\n"[..], ); let len = raw.len(); let mut headers = Some(HeaderMap::new()); b.bytes = len as u64; b.iter(|| { let mut msg = Server::parse( &mut raw, ParseContext { cached_headers: &mut headers, req_method: &mut None, h1_parser_config: Default::default(), h1_max_headers: None, preserve_header_case: false, #[cfg(feature = "ffi")] preserve_header_order: false, h09_responses: false, #[cfg(feature = "ffi")] on_informational: &mut None, }, ) .unwrap() .unwrap(); ::test::black_box(&msg); // Remove all references pointing into BytesMut. msg.head.headers.clear(); headers = Some(msg.head.headers); std::mem::take(&mut msg.head.subject); restart(&mut raw, len); }); fn restart(b: &mut BytesMut, len: usize) { b.reserve(1); unsafe { b.set_len(len); } } } #[cfg(feature = "nightly")] #[bench] fn bench_parse_short(b: &mut Bencher) { let s = &b"GET / HTTP/1.1\r\nHost: localhost:8080\r\n\r\n"[..]; let mut raw = BytesMut::from(s); let len = raw.len(); let mut headers = Some(HeaderMap::new()); b.bytes = len as u64; b.iter(|| { let mut msg = Server::parse( &mut raw, ParseContext { cached_headers: &mut headers, req_method: &mut None, h1_parser_config: Default::default(), h1_max_headers: None, preserve_header_case: false, #[cfg(feature = "ffi")] preserve_header_order: false, h09_responses: false, #[cfg(feature = "ffi")] on_informational: &mut None, }, ) .unwrap() .unwrap(); ::test::black_box(&msg); msg.head.headers.clear(); headers = Some(msg.head.headers); restart(&mut raw, len); }); fn restart(b: &mut BytesMut, len: usize) { b.reserve(1); unsafe { b.set_len(len); } } } #[cfg(feature = "nightly")] #[bench] fn bench_server_encode_headers_preset(b: &mut Bencher) { use crate::proto::BodyLength; use http::header::HeaderValue; let len = 108; b.bytes = len as u64; let mut head = MessageHead::default(); let mut headers = HeaderMap::new(); headers.insert("content-length", HeaderValue::from_static("10")); headers.insert("content-type", HeaderValue::from_static("application/json")); b.iter(|| { let mut vec = Vec::new(); head.headers = headers.clone(); Server::encode( Encode { head: &mut head, body: Some(BodyLength::Known(10)), keep_alive: true, req_method: &mut Some(Method::GET), title_case_headers: false, date_header: true, }, &mut vec, ) .unwrap(); assert_eq!(vec.len(), len); ::test::black_box(vec); }) } #[cfg(feature = "nightly")] #[bench] fn bench_server_encode_no_headers(b: &mut Bencher) { use crate::proto::BodyLength; let len = 76; b.bytes = len as u64; let mut head = MessageHead::default(); let mut vec = Vec::with_capacity(128); b.iter(|| { Server::encode( Encode { head: &mut head, body: Some(BodyLength::Known(10)), keep_alive: true, req_method: &mut Some(Method::GET), title_case_headers: false, date_header: true, }, &mut vec, ) .unwrap(); assert_eq!(vec.len(), len); ::test::black_box(&vec); vec.clear(); }) } } hyper-1.5.2/src/proto/h2/client.rs000064400000000000000000000567411046102023000150640ustar 00000000000000use std::{ convert::Infallible, future::Future, marker::PhantomData, pin::Pin, task::{Context, Poll}, time::Duration, }; use crate::rt::{Read, Write}; use bytes::Bytes; use futures_channel::mpsc::{Receiver, Sender}; use futures_channel::{mpsc, oneshot}; use futures_util::future::{Either, FusedFuture, FutureExt as _}; use futures_util::ready; use futures_util::stream::{StreamExt as _, StreamFuture}; use h2::client::{Builder, Connection, SendRequest}; use h2::SendStream; use http::{Method, StatusCode}; use pin_project_lite::pin_project; use super::ping::{Ponger, Recorder}; use super::{ping, H2Upgraded, PipeToSendStream, SendBuf}; use crate::body::{Body, Incoming as IncomingBody}; use crate::client::dispatch::{Callback, SendWhen, TrySendError}; use crate::common::io::Compat; use crate::common::time::Time; use crate::ext::Protocol; use crate::headers; use crate::proto::h2::UpgradedSendStream; use crate::proto::Dispatched; use crate::rt::bounds::Http2ClientConnExec; use crate::upgrade::Upgraded; use crate::{Request, Response}; use h2::client::ResponseFuture; type ClientRx = crate::client::dispatch::Receiver, Response>; ///// An mpsc channel is used to help notify the `Connection` task when *all* ///// other handles to it have been dropped, so that it can shutdown. type ConnDropRef = mpsc::Sender; ///// A oneshot channel watches the `Connection` task, and when it completes, ///// the "dispatch" task will be notified and can shutdown sooner. type ConnEof = oneshot::Receiver; // Our defaults are chosen for the "majority" case, which usually are not // resource constrained, and so the spec default of 64kb can be too limiting // for performance. const DEFAULT_CONN_WINDOW: u32 = 1024 * 1024 * 5; // 5mb const DEFAULT_STREAM_WINDOW: u32 = 1024 * 1024 * 2; // 2mb const DEFAULT_MAX_FRAME_SIZE: u32 = 1024 * 16; // 16kb const DEFAULT_MAX_SEND_BUF_SIZE: usize = 1024 * 1024; // 1mb const DEFAULT_MAX_HEADER_LIST_SIZE: u32 = 1024 * 16; // 16kb // The maximum number of concurrent streams that the client is allowed to open // before it receives the initial SETTINGS frame from the server. // This default value is derived from what the HTTP/2 spec recommends as the // minimum value that endpoints advertise to their peers. It means that using // this value will minimize the chance of the failure where the local endpoint // attempts to open too many streams and gets rejected by the remote peer with // the `REFUSED_STREAM` error. const DEFAULT_INITIAL_MAX_SEND_STREAMS: usize = 100; #[derive(Clone, Debug)] pub(crate) struct Config { pub(crate) adaptive_window: bool, pub(crate) initial_conn_window_size: u32, pub(crate) initial_stream_window_size: u32, pub(crate) initial_max_send_streams: usize, pub(crate) max_frame_size: Option, pub(crate) max_header_list_size: u32, pub(crate) keep_alive_interval: Option, pub(crate) keep_alive_timeout: Duration, pub(crate) keep_alive_while_idle: bool, pub(crate) max_concurrent_reset_streams: Option, pub(crate) max_send_buffer_size: usize, pub(crate) max_pending_accept_reset_streams: Option, pub(crate) header_table_size: Option, pub(crate) max_concurrent_streams: Option, } impl Default for Config { fn default() -> Config { Config { adaptive_window: false, initial_conn_window_size: DEFAULT_CONN_WINDOW, initial_stream_window_size: DEFAULT_STREAM_WINDOW, initial_max_send_streams: DEFAULT_INITIAL_MAX_SEND_STREAMS, max_frame_size: Some(DEFAULT_MAX_FRAME_SIZE), max_header_list_size: DEFAULT_MAX_HEADER_LIST_SIZE, keep_alive_interval: None, keep_alive_timeout: Duration::from_secs(20), keep_alive_while_idle: false, max_concurrent_reset_streams: None, max_send_buffer_size: DEFAULT_MAX_SEND_BUF_SIZE, max_pending_accept_reset_streams: None, header_table_size: None, max_concurrent_streams: None, } } } fn new_builder(config: &Config) -> Builder { let mut builder = Builder::default(); builder .initial_max_send_streams(config.initial_max_send_streams) .initial_window_size(config.initial_stream_window_size) .initial_connection_window_size(config.initial_conn_window_size) .max_header_list_size(config.max_header_list_size) .max_send_buffer_size(config.max_send_buffer_size) .enable_push(false); if let Some(max) = config.max_frame_size { builder.max_frame_size(max); } if let Some(max) = config.max_concurrent_reset_streams { builder.max_concurrent_reset_streams(max); } if let Some(max) = config.max_pending_accept_reset_streams { builder.max_pending_accept_reset_streams(max); } if let Some(size) = config.header_table_size { builder.header_table_size(size); } if let Some(max) = config.max_concurrent_streams { builder.max_concurrent_streams(max); } builder } fn new_ping_config(config: &Config) -> ping::Config { ping::Config { bdp_initial_window: if config.adaptive_window { Some(config.initial_stream_window_size) } else { None }, keep_alive_interval: config.keep_alive_interval, keep_alive_timeout: config.keep_alive_timeout, keep_alive_while_idle: config.keep_alive_while_idle, } } pub(crate) async fn handshake( io: T, req_rx: ClientRx, config: &Config, mut exec: E, timer: Time, ) -> crate::Result> where T: Read + Write + Unpin, B: Body + 'static, B::Data: Send + 'static, E: Http2ClientConnExec + Unpin, B::Error: Into>, { let (h2_tx, mut conn) = new_builder(config) .handshake::<_, SendBuf>(Compat::new(io)) .await .map_err(crate::Error::new_h2)?; // An mpsc channel is used entirely to detect when the // 'Client' has been dropped. This is to get around a bug // in h2 where dropping all SendRequests won't notify a // parked Connection. let (conn_drop_ref, rx) = mpsc::channel(1); let (cancel_tx, conn_eof) = oneshot::channel(); let conn_drop_rx = rx.into_future(); let ping_config = new_ping_config(config); let (conn, ping) = if ping_config.is_enabled() { let pp = conn.ping_pong().expect("conn.ping_pong"); let (recorder, ponger) = ping::channel(pp, ping_config, timer); let conn: Conn<_, B> = Conn::new(ponger, conn); (Either::Left(conn), recorder) } else { (Either::Right(conn), ping::disabled()) }; let conn: ConnMapErr = ConnMapErr { conn, is_terminated: false, }; exec.execute_h2_future(H2ClientFuture::Task { task: ConnTask::new(conn, conn_drop_rx, cancel_tx), }); Ok(ClientTask { ping, conn_drop_ref, conn_eof, executor: exec, h2_tx, req_rx, fut_ctx: None, marker: PhantomData, }) } pin_project! { struct Conn where B: Body, { #[pin] ponger: Ponger, #[pin] conn: Connection, SendBuf<::Data>>, } } impl Conn where B: Body, T: Read + Write + Unpin, { fn new(ponger: Ponger, conn: Connection, SendBuf<::Data>>) -> Self { Conn { ponger, conn } } } impl Future for Conn where B: Body, T: Read + Write + Unpin, { type Output = Result<(), h2::Error>; fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { let mut this = self.project(); match this.ponger.poll(cx) { Poll::Ready(ping::Ponged::SizeUpdate(wnd)) => { this.conn.set_target_window_size(wnd); this.conn.set_initial_window_size(wnd)?; } Poll::Ready(ping::Ponged::KeepAliveTimedOut) => { debug!("connection keep-alive timed out"); return Poll::Ready(Ok(())); } Poll::Pending => {} } Pin::new(&mut this.conn).poll(cx) } } pin_project! { struct ConnMapErr where B: Body, T: Read, T: Write, T: Unpin, { #[pin] conn: Either, Connection, SendBuf<::Data>>>, #[pin] is_terminated: bool, } } impl Future for ConnMapErr where B: Body, T: Read + Write + Unpin, { type Output = Result<(), ()>; fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { let mut this = self.project(); if *this.is_terminated { return Poll::Pending; } let polled = this.conn.poll(cx); if polled.is_ready() { *this.is_terminated = true; } polled.map_err(|_e| { debug!(error = %_e, "connection error"); }) } } impl FusedFuture for ConnMapErr where B: Body, T: Read + Write + Unpin, { fn is_terminated(&self) -> bool { self.is_terminated } } pin_project! { pub struct ConnTask where B: Body, T: Read, T: Write, T: Unpin, { #[pin] drop_rx: StreamFuture>, #[pin] cancel_tx: Option>, #[pin] conn: ConnMapErr, } } impl ConnTask where B: Body, T: Read + Write + Unpin, { fn new( conn: ConnMapErr, drop_rx: StreamFuture>, cancel_tx: oneshot::Sender, ) -> Self { Self { drop_rx, cancel_tx: Some(cancel_tx), conn, } } } impl Future for ConnTask where B: Body, T: Read + Write + Unpin, { type Output = (); fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { let mut this = self.project(); if !this.conn.is_terminated() && this.conn.poll_unpin(cx).is_ready() { // ok or err, the `conn` has finished. return Poll::Ready(()); } if !this.drop_rx.is_terminated() && this.drop_rx.poll_unpin(cx).is_ready() { // mpsc has been dropped, hopefully polling // the connection some more should start shutdown // and then close. trace!("send_request dropped, starting conn shutdown"); drop(this.cancel_tx.take().expect("ConnTask Future polled twice")); } Poll::Pending } } pin_project! { #[project = H2ClientFutureProject] pub enum H2ClientFuture where B: http_body::Body, B: 'static, B::Error: Into>, T: Read, T: Write, T: Unpin, { Pipe { #[pin] pipe: PipeMap, }, Send { #[pin] send_when: SendWhen, }, Task { #[pin] task: ConnTask, }, } } impl Future for H2ClientFuture where B: http_body::Body + 'static, B::Error: Into>, T: Read + Write + Unpin, { type Output = (); fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> std::task::Poll { let this = self.project(); match this { H2ClientFutureProject::Pipe { pipe } => pipe.poll(cx), H2ClientFutureProject::Send { send_when } => send_when.poll(cx), H2ClientFutureProject::Task { task } => task.poll(cx), } } } struct FutCtx where B: Body, { is_connect: bool, eos: bool, fut: ResponseFuture, body_tx: SendStream>, body: B, cb: Callback, Response>, } impl Unpin for FutCtx {} pub(crate) struct ClientTask where B: Body, E: Unpin, { ping: ping::Recorder, conn_drop_ref: ConnDropRef, conn_eof: ConnEof, executor: E, h2_tx: SendRequest>, req_rx: ClientRx, fut_ctx: Option>, marker: PhantomData, } impl ClientTask where B: Body + 'static, E: Http2ClientConnExec + Unpin, B::Error: Into>, T: Read + Write + Unpin, { pub(crate) fn is_extended_connect_protocol_enabled(&self) -> bool { self.h2_tx.is_extended_connect_protocol_enabled() } } pin_project! { pub struct PipeMap where S: Body, { #[pin] pipe: PipeToSendStream, #[pin] conn_drop_ref: Option>, #[pin] ping: Option, } } impl Future for PipeMap where B: http_body::Body, B::Error: Into>, { type Output = (); fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> std::task::Poll { let mut this = self.project(); match this.pipe.poll_unpin(cx) { Poll::Ready(result) => { if let Err(_e) = result { debug!("client request body error: {}", _e); } drop(this.conn_drop_ref.take().expect("Future polled twice")); drop(this.ping.take().expect("Future polled twice")); return Poll::Ready(()); } Poll::Pending => (), }; Poll::Pending } } impl ClientTask where B: Body + 'static + Unpin, B::Data: Send, E: Http2ClientConnExec + Unpin, B::Error: Into>, T: Read + Write + Unpin, { fn poll_pipe(&mut self, f: FutCtx, cx: &mut Context<'_>) { let ping = self.ping.clone(); let send_stream = if !f.is_connect { if !f.eos { let mut pipe = PipeToSendStream::new(f.body, f.body_tx); // eagerly see if the body pipe is ready and // can thus skip allocating in the executor match Pin::new(&mut pipe).poll(cx) { Poll::Ready(_) => (), Poll::Pending => { let conn_drop_ref = self.conn_drop_ref.clone(); // keep the ping recorder's knowledge of an // "open stream" alive while this body is // still sending... let ping = ping.clone(); let pipe = PipeMap { pipe, conn_drop_ref: Some(conn_drop_ref), ping: Some(ping), }; // Clear send task self.executor .execute_h2_future(H2ClientFuture::Pipe { pipe }); } } } None } else { Some(f.body_tx) }; self.executor.execute_h2_future(H2ClientFuture::Send { send_when: SendWhen { when: ResponseFutMap { fut: f.fut, ping: Some(ping), send_stream: Some(send_stream), }, call_back: Some(f.cb), }, }); } } pin_project! { pub(crate) struct ResponseFutMap where B: Body, B: 'static, { #[pin] fut: ResponseFuture, #[pin] ping: Option, #[pin] send_stream: Option::Data>>>>, } } impl Future for ResponseFutMap where B: Body + 'static, { type Output = Result, (crate::Error, Option>)>; fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { let mut this = self.project(); let result = ready!(this.fut.poll(cx)); let ping = this.ping.take().expect("Future polled twice"); let send_stream = this.send_stream.take().expect("Future polled twice"); match result { Ok(res) => { // record that we got the response headers ping.record_non_data(); let content_length = headers::content_length_parse_all(res.headers()); if let (Some(mut send_stream), StatusCode::OK) = (send_stream, res.status()) { if content_length.map_or(false, |len| len != 0) { warn!("h2 connect response with non-zero body not supported"); send_stream.send_reset(h2::Reason::INTERNAL_ERROR); return Poll::Ready(Err(( crate::Error::new_h2(h2::Reason::INTERNAL_ERROR.into()), None::>, ))); } let (parts, recv_stream) = res.into_parts(); let mut res = Response::from_parts(parts, IncomingBody::empty()); let (pending, on_upgrade) = crate::upgrade::pending(); let io = H2Upgraded { ping, send_stream: unsafe { UpgradedSendStream::new(send_stream) }, recv_stream, buf: Bytes::new(), }; let upgraded = Upgraded::new(io, Bytes::new()); pending.fulfill(upgraded); res.extensions_mut().insert(on_upgrade); Poll::Ready(Ok(res)) } else { let res = res.map(|stream| { let ping = ping.for_stream(&stream); IncomingBody::h2(stream, content_length.into(), ping) }); Poll::Ready(Ok(res)) } } Err(err) => { ping.ensure_not_timed_out().map_err(|e| (e, None))?; debug!("client response error: {}", err); Poll::Ready(Err((crate::Error::new_h2(err), None::>))) } } } } impl Future for ClientTask where B: Body + 'static + Unpin, B::Data: Send, B::Error: Into>, E: Http2ClientConnExec + Unpin, T: Read + Write + Unpin, { type Output = crate::Result; fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { loop { match ready!(self.h2_tx.poll_ready(cx)) { Ok(()) => (), Err(err) => { self.ping.ensure_not_timed_out()?; return if err.reason() == Some(::h2::Reason::NO_ERROR) { trace!("connection gracefully shutdown"); Poll::Ready(Ok(Dispatched::Shutdown)) } else { Poll::Ready(Err(crate::Error::new_h2(err))) }; } }; // If we were waiting on pending open // continue where we left off. if let Some(f) = self.fut_ctx.take() { self.poll_pipe(f, cx); continue; } match self.req_rx.poll_recv(cx) { Poll::Ready(Some((req, cb))) => { // check that future hasn't been canceled already if cb.is_canceled() { trace!("request callback is canceled"); continue; } let (head, body) = req.into_parts(); let mut req = ::http::Request::from_parts(head, ()); super::strip_connection_headers(req.headers_mut(), true); if let Some(len) = body.size_hint().exact() { if len != 0 || headers::method_has_defined_payload_semantics(req.method()) { headers::set_content_length_if_missing(req.headers_mut(), len); } } let is_connect = req.method() == Method::CONNECT; let eos = body.is_end_stream(); if is_connect && headers::content_length_parse_all(req.headers()) .map_or(false, |len| len != 0) { warn!("h2 connect request with non-zero body not supported"); cb.send(Err(TrySendError { error: crate::Error::new_h2(h2::Reason::INTERNAL_ERROR.into()), message: None, })); continue; } if let Some(protocol) = req.extensions_mut().remove::() { req.extensions_mut().insert(protocol.into_inner()); } let (fut, body_tx) = match self.h2_tx.send_request(req, !is_connect && eos) { Ok(ok) => ok, Err(err) => { debug!("client send request error: {}", err); cb.send(Err(TrySendError { error: crate::Error::new_h2(err), message: None, })); continue; } }; let f = FutCtx { is_connect, eos, fut, body_tx, body, cb, }; // Check poll_ready() again. // If the call to send_request() resulted in the new stream being pending open // we have to wait for the open to complete before accepting new requests. match self.h2_tx.poll_ready(cx) { Poll::Pending => { // Save Context self.fut_ctx = Some(f); return Poll::Pending; } Poll::Ready(Ok(())) => (), Poll::Ready(Err(err)) => { f.cb.send(Err(TrySendError { error: crate::Error::new_h2(err), message: None, })); continue; } } self.poll_pipe(f, cx); continue; } Poll::Ready(None) => { trace!("client::dispatch::Sender dropped"); return Poll::Ready(Ok(Dispatched::Shutdown)); } Poll::Pending => match ready!(Pin::new(&mut self.conn_eof).poll(cx)) { // As of Rust 1.82, this pattern is no longer needed, and emits a warning. // But we cannot remove it as long as MSRV is less than that. #[allow(unused)] Ok(never) => match never {}, Err(_conn_is_eof) => { trace!("connection task is closed, closing dispatch task"); return Poll::Ready(Ok(Dispatched::Shutdown)); } }, } } } } hyper-1.5.2/src/proto/h2/mod.rs000064400000000000000000000330531046102023000143540ustar 00000000000000use std::error::Error as StdError; use std::future::Future; use std::io::{Cursor, IoSlice}; use std::mem; use std::pin::Pin; use std::task::{Context, Poll}; use bytes::{Buf, Bytes}; use futures_util::ready; use h2::{Reason, RecvStream, SendStream}; use http::header::{HeaderName, CONNECTION, TE, TRANSFER_ENCODING, UPGRADE}; use http::HeaderMap; use pin_project_lite::pin_project; use crate::body::Body; use crate::proto::h2::ping::Recorder; use crate::rt::{Read, ReadBufCursor, Write}; pub(crate) mod ping; cfg_client! { pub(crate) mod client; pub(crate) use self::client::ClientTask; } cfg_server! { pub(crate) mod server; pub(crate) use self::server::Server; } /// Default initial stream window size defined in HTTP2 spec. pub(crate) const SPEC_WINDOW_SIZE: u32 = 65_535; // List of connection headers from RFC 9110 Section 7.6.1 // // TE headers are allowed in HTTP/2 requests as long as the value is "trailers", so they're // tested separately. static CONNECTION_HEADERS: [HeaderName; 4] = [ HeaderName::from_static("keep-alive"), HeaderName::from_static("proxy-connection"), TRANSFER_ENCODING, UPGRADE, ]; fn strip_connection_headers(headers: &mut HeaderMap, is_request: bool) { for header in &CONNECTION_HEADERS { if headers.remove(header).is_some() { warn!("Connection header illegal in HTTP/2: {}", header.as_str()); } } if is_request { if headers .get(TE) .map_or(false, |te_header| te_header != "trailers") { warn!("TE headers not set to \"trailers\" are illegal in HTTP/2 requests"); headers.remove(TE); } } else if headers.remove(TE).is_some() { warn!("TE headers illegal in HTTP/2 responses"); } if let Some(header) = headers.remove(CONNECTION) { warn!( "Connection header illegal in HTTP/2: {}", CONNECTION.as_str() ); let header_contents = header.to_str().unwrap(); // A `Connection` header may have a comma-separated list of names of other headers that // are meant for only this specific connection. // // Iterate these names and remove them as headers. Connection-specific headers are // forbidden in HTTP2, as that information has been moved into frame types of the h2 // protocol. for name in header_contents.split(',') { let name = name.trim(); headers.remove(name); } } } // body adapters used by both Client and Server pin_project! { pub(crate) struct PipeToSendStream where S: Body, { body_tx: SendStream>, data_done: bool, #[pin] stream: S, } } impl PipeToSendStream where S: Body, { fn new(stream: S, tx: SendStream>) -> PipeToSendStream { PipeToSendStream { body_tx: tx, data_done: false, stream, } } } impl Future for PipeToSendStream where S: Body, S::Error: Into>, { type Output = crate::Result<()>; fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { let mut me = self.project(); loop { // we don't have the next chunk of data yet, so just reserve 1 byte to make // sure there's some capacity available. h2 will handle the capacity management // for the actual body chunk. me.body_tx.reserve_capacity(1); if me.body_tx.capacity() == 0 { loop { match ready!(me.body_tx.poll_capacity(cx)) { Some(Ok(0)) => {} Some(Ok(_)) => break, Some(Err(e)) => return Poll::Ready(Err(crate::Error::new_body_write(e))), None => { // None means the stream is no longer in a // streaming state, we either finished it // somehow, or the remote reset us. return Poll::Ready(Err(crate::Error::new_body_write( "send stream capacity unexpectedly closed", ))); } } } } else if let Poll::Ready(reason) = me .body_tx .poll_reset(cx) .map_err(crate::Error::new_body_write)? { debug!("stream received RST_STREAM: {:?}", reason); return Poll::Ready(Err(crate::Error::new_body_write(::h2::Error::from(reason)))); } match ready!(me.stream.as_mut().poll_frame(cx)) { Some(Ok(frame)) => { if frame.is_data() { let chunk = frame.into_data().unwrap_or_else(|_| unreachable!()); let is_eos = me.stream.is_end_stream(); trace!( "send body chunk: {} bytes, eos={}", chunk.remaining(), is_eos, ); let buf = SendBuf::Buf(chunk); me.body_tx .send_data(buf, is_eos) .map_err(crate::Error::new_body_write)?; if is_eos { return Poll::Ready(Ok(())); } } else if frame.is_trailers() { // no more DATA, so give any capacity back me.body_tx.reserve_capacity(0); me.body_tx .send_trailers(frame.into_trailers().unwrap_or_else(|_| unreachable!())) .map_err(crate::Error::new_body_write)?; return Poll::Ready(Ok(())); } else { trace!("discarding unknown frame"); // loop again } } Some(Err(e)) => return Poll::Ready(Err(me.body_tx.on_user_err(e))), None => { // no more frames means we're done here // but at this point, we haven't sent an EOS DATA, or // any trailers, so send an empty EOS DATA. return Poll::Ready(me.body_tx.send_eos_frame()); } } } } } trait SendStreamExt { fn on_user_err(&mut self, err: E) -> crate::Error where E: Into>; fn send_eos_frame(&mut self) -> crate::Result<()>; } impl SendStreamExt for SendStream> { fn on_user_err(&mut self, err: E) -> crate::Error where E: Into>, { let err = crate::Error::new_user_body(err); debug!("send body user stream error: {}", err); self.send_reset(err.h2_reason()); err } fn send_eos_frame(&mut self) -> crate::Result<()> { trace!("send body eos"); self.send_data(SendBuf::None, true) .map_err(crate::Error::new_body_write) } } #[repr(usize)] enum SendBuf { Buf(B), Cursor(Cursor>), None, } impl Buf for SendBuf { #[inline] fn remaining(&self) -> usize { match *self { Self::Buf(ref b) => b.remaining(), Self::Cursor(ref c) => Buf::remaining(c), Self::None => 0, } } #[inline] fn chunk(&self) -> &[u8] { match *self { Self::Buf(ref b) => b.chunk(), Self::Cursor(ref c) => c.chunk(), Self::None => &[], } } #[inline] fn advance(&mut self, cnt: usize) { match *self { Self::Buf(ref mut b) => b.advance(cnt), Self::Cursor(ref mut c) => c.advance(cnt), Self::None => {} } } fn chunks_vectored<'a>(&'a self, dst: &mut [IoSlice<'a>]) -> usize { match *self { Self::Buf(ref b) => b.chunks_vectored(dst), Self::Cursor(ref c) => c.chunks_vectored(dst), Self::None => 0, } } } struct H2Upgraded where B: Buf, { ping: Recorder, send_stream: UpgradedSendStream, recv_stream: RecvStream, buf: Bytes, } impl Read for H2Upgraded where B: Buf, { fn poll_read( mut self: Pin<&mut Self>, cx: &mut Context<'_>, mut read_buf: ReadBufCursor<'_>, ) -> Poll> { if self.buf.is_empty() { self.buf = loop { match ready!(self.recv_stream.poll_data(cx)) { None => return Poll::Ready(Ok(())), Some(Ok(buf)) if buf.is_empty() && !self.recv_stream.is_end_stream() => { continue } Some(Ok(buf)) => { self.ping.record_data(buf.len()); break buf; } Some(Err(e)) => { return Poll::Ready(match e.reason() { Some(Reason::NO_ERROR) | Some(Reason::CANCEL) => Ok(()), Some(Reason::STREAM_CLOSED) => { Err(std::io::Error::new(std::io::ErrorKind::BrokenPipe, e)) } _ => Err(h2_to_io_error(e)), }) } } }; } let cnt = std::cmp::min(self.buf.len(), read_buf.remaining()); read_buf.put_slice(&self.buf[..cnt]); self.buf.advance(cnt); let _ = self.recv_stream.flow_control().release_capacity(cnt); Poll::Ready(Ok(())) } } impl Write for H2Upgraded where B: Buf, { fn poll_write( mut self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8], ) -> Poll> { if buf.is_empty() { return Poll::Ready(Ok(0)); } self.send_stream.reserve_capacity(buf.len()); // We ignore all errors returned by `poll_capacity` and `write`, as we // will get the correct from `poll_reset` anyway. let cnt = match ready!(self.send_stream.poll_capacity(cx)) { None => Some(0), Some(Ok(cnt)) => self .send_stream .write(&buf[..cnt], false) .ok() .map(|()| cnt), Some(Err(_)) => None, }; if let Some(cnt) = cnt { return Poll::Ready(Ok(cnt)); } Poll::Ready(Err(h2_to_io_error( match ready!(self.send_stream.poll_reset(cx)) { Ok(Reason::NO_ERROR) | Ok(Reason::CANCEL) | Ok(Reason::STREAM_CLOSED) => { return Poll::Ready(Err(std::io::ErrorKind::BrokenPipe.into())) } Ok(reason) => reason.into(), Err(e) => e, }, ))) } fn poll_flush(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll> { Poll::Ready(Ok(())) } fn poll_shutdown( mut self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll> { if self.send_stream.write(&[], true).is_ok() { return Poll::Ready(Ok(())); } Poll::Ready(Err(h2_to_io_error( match ready!(self.send_stream.poll_reset(cx)) { Ok(Reason::NO_ERROR) => return Poll::Ready(Ok(())), Ok(Reason::CANCEL) | Ok(Reason::STREAM_CLOSED) => { return Poll::Ready(Err(std::io::ErrorKind::BrokenPipe.into())) } Ok(reason) => reason.into(), Err(e) => e, }, ))) } } fn h2_to_io_error(e: h2::Error) -> std::io::Error { if e.is_io() { e.into_io().unwrap() } else { std::io::Error::new(std::io::ErrorKind::Other, e) } } struct UpgradedSendStream(SendStream>>); impl UpgradedSendStream where B: Buf, { unsafe fn new(inner: SendStream>) -> Self { assert_eq!(mem::size_of::(), mem::size_of::>()); Self(mem::transmute(inner)) } fn reserve_capacity(&mut self, cnt: usize) { unsafe { self.as_inner_unchecked().reserve_capacity(cnt) } } fn poll_capacity(&mut self, cx: &mut Context<'_>) -> Poll>> { unsafe { self.as_inner_unchecked().poll_capacity(cx) } } fn poll_reset(&mut self, cx: &mut Context<'_>) -> Poll> { unsafe { self.as_inner_unchecked().poll_reset(cx) } } fn write(&mut self, buf: &[u8], end_of_stream: bool) -> Result<(), std::io::Error> { let send_buf = SendBuf::Cursor(Cursor::new(buf.into())); unsafe { self.as_inner_unchecked() .send_data(send_buf, end_of_stream) .map_err(h2_to_io_error) } } unsafe fn as_inner_unchecked(&mut self) -> &mut SendStream> { &mut *(&mut self.0 as *mut _ as *mut _) } } #[repr(transparent)] struct Neutered { _inner: B, impossible: Impossible, } enum Impossible {} unsafe impl Send for Neutered {} impl Buf for Neutered { fn remaining(&self) -> usize { match self.impossible {} } fn chunk(&self) -> &[u8] { match self.impossible {} } fn advance(&mut self, _cnt: usize) { match self.impossible {} } } hyper-1.5.2/src/proto/h2/ping.rs000064400000000000000000000346611046102023000145400ustar 00000000000000/// HTTP2 Ping usage /// /// hyper uses HTTP2 pings for two purposes: /// /// 1. Adaptive flow control using BDP /// 2. Connection keep-alive /// /// Both cases are optional. /// /// # BDP Algorithm /// /// 1. When receiving a DATA frame, if a BDP ping isn't outstanding: /// 1a. Record current time. /// 1b. Send a BDP ping. /// 2. Increment the number of received bytes. /// 3. When the BDP ping ack is received: /// 3a. Record duration from sent time. /// 3b. Merge RTT with a running average. /// 3c. Calculate bdp as bytes/rtt. /// 3d. If bdp is over 2/3 max, set new max to bdp and update windows. use std::fmt; use std::future::Future; use std::pin::Pin; use std::sync::{Arc, Mutex}; use std::task::{self, Poll}; use std::time::{Duration, Instant}; use h2::{Ping, PingPong}; use crate::common::time::Time; use crate::rt::Sleep; type WindowSize = u32; pub(super) fn disabled() -> Recorder { Recorder { shared: None } } pub(super) fn channel(ping_pong: PingPong, config: Config, __timer: Time) -> (Recorder, Ponger) { debug_assert!( config.is_enabled(), "ping channel requires bdp or keep-alive config", ); let bdp = config.bdp_initial_window.map(|wnd| Bdp { bdp: wnd, max_bandwidth: 0.0, rtt: 0.0, ping_delay: Duration::from_millis(100), stable_count: 0, }); let (bytes, next_bdp_at) = if bdp.is_some() { (Some(0), Some(Instant::now())) } else { (None, None) }; let keep_alive = config.keep_alive_interval.map(|interval| KeepAlive { interval, timeout: config.keep_alive_timeout, while_idle: config.keep_alive_while_idle, sleep: __timer.sleep(interval), state: KeepAliveState::Init, timer: __timer, }); let last_read_at = keep_alive.as_ref().map(|_| Instant::now()); let shared = Arc::new(Mutex::new(Shared { bytes, last_read_at, is_keep_alive_timed_out: false, ping_pong, ping_sent_at: None, next_bdp_at, })); ( Recorder { shared: Some(shared.clone()), }, Ponger { bdp, keep_alive, shared, }, ) } #[derive(Clone)] pub(super) struct Config { pub(super) bdp_initial_window: Option, /// If no frames are received in this amount of time, a PING frame is sent. pub(super) keep_alive_interval: Option, /// After sending a keepalive PING, the connection will be closed if /// a pong is not received in this amount of time. pub(super) keep_alive_timeout: Duration, /// If true, sends pings even when there are no active streams. pub(super) keep_alive_while_idle: bool, } #[derive(Clone)] pub(crate) struct Recorder { shared: Option>>, } pub(super) struct Ponger { bdp: Option, keep_alive: Option, shared: Arc>, } struct Shared { ping_pong: PingPong, ping_sent_at: Option, // bdp /// If `Some`, bdp is enabled, and this tracks how many bytes have been /// read during the current sample. bytes: Option, /// We delay a variable amount of time between BDP pings. This allows us /// to send less pings as the bandwidth stabilizes. next_bdp_at: Option, // keep-alive /// If `Some`, keep-alive is enabled, and the Instant is how long ago /// the connection read the last frame. last_read_at: Option, is_keep_alive_timed_out: bool, } struct Bdp { /// Current BDP in bytes bdp: u32, /// Largest bandwidth we've seen so far. max_bandwidth: f64, /// Round trip time in seconds rtt: f64, /// Delay the next ping by this amount. /// /// This will change depending on how stable the current bandwidth is. ping_delay: Duration, /// The count of ping round trips where BDP has stayed the same. stable_count: u32, } struct KeepAlive { /// If no frames are received in this amount of time, a PING frame is sent. interval: Duration, /// After sending a keepalive PING, the connection will be closed if /// a pong is not received in this amount of time. timeout: Duration, /// If true, sends pings even when there are no active streams. while_idle: bool, state: KeepAliveState, sleep: Pin>, timer: Time, } enum KeepAliveState { Init, Scheduled(Instant), PingSent, } pub(super) enum Ponged { SizeUpdate(WindowSize), KeepAliveTimedOut, } #[derive(Debug)] pub(super) struct KeepAliveTimedOut; // ===== impl Config ===== impl Config { pub(super) fn is_enabled(&self) -> bool { self.bdp_initial_window.is_some() || self.keep_alive_interval.is_some() } } // ===== impl Recorder ===== impl Recorder { pub(crate) fn record_data(&self, len: usize) { let shared = if let Some(ref shared) = self.shared { shared } else { return; }; let mut locked = shared.lock().unwrap(); locked.update_last_read_at(); // are we ready to send another bdp ping? // if not, we don't need to record bytes either if let Some(ref next_bdp_at) = locked.next_bdp_at { if Instant::now() < *next_bdp_at { return; } else { locked.next_bdp_at = None; } } if let Some(ref mut bytes) = locked.bytes { *bytes += len; } else { // no need to send bdp ping if bdp is disabled return; } if !locked.is_ping_sent() { locked.send_ping(); } } pub(crate) fn record_non_data(&self) { let shared = if let Some(ref shared) = self.shared { shared } else { return; }; let mut locked = shared.lock().unwrap(); locked.update_last_read_at(); } /// If the incoming stream is already closed, convert self into /// a disabled reporter. #[cfg(feature = "client")] pub(super) fn for_stream(self, stream: &h2::RecvStream) -> Self { if stream.is_end_stream() { disabled() } else { self } } pub(super) fn ensure_not_timed_out(&self) -> crate::Result<()> { if let Some(ref shared) = self.shared { let locked = shared.lock().unwrap(); if locked.is_keep_alive_timed_out { return Err(KeepAliveTimedOut.crate_error()); } } // else Ok(()) } } // ===== impl Ponger ===== impl Ponger { pub(super) fn poll(&mut self, cx: &mut task::Context<'_>) -> Poll { let now = Instant::now(); let mut locked = self.shared.lock().unwrap(); let is_idle = self.is_idle(); if let Some(ref mut ka) = self.keep_alive { ka.maybe_schedule(is_idle, &locked); ka.maybe_ping(cx, is_idle, &mut locked); } if !locked.is_ping_sent() { // XXX: this doesn't register a waker...? return Poll::Pending; } match locked.ping_pong.poll_pong(cx) { Poll::Ready(Ok(_pong)) => { let start = locked .ping_sent_at .expect("pong received implies ping_sent_at"); locked.ping_sent_at = None; let rtt = now - start; trace!("recv pong"); if let Some(ref mut ka) = self.keep_alive { locked.update_last_read_at(); ka.maybe_schedule(is_idle, &locked); ka.maybe_ping(cx, is_idle, &mut locked); } if let Some(ref mut bdp) = self.bdp { let bytes = locked.bytes.expect("bdp enabled implies bytes"); locked.bytes = Some(0); // reset trace!("received BDP ack; bytes = {}, rtt = {:?}", bytes, rtt); let update = bdp.calculate(bytes, rtt); locked.next_bdp_at = Some(now + bdp.ping_delay); if let Some(update) = update { return Poll::Ready(Ponged::SizeUpdate(update)); } } } Poll::Ready(Err(_e)) => { debug!("pong error: {}", _e); } Poll::Pending => { if let Some(ref mut ka) = self.keep_alive { if let Err(KeepAliveTimedOut) = ka.maybe_timeout(cx) { self.keep_alive = None; locked.is_keep_alive_timed_out = true; return Poll::Ready(Ponged::KeepAliveTimedOut); } } } } // XXX: this doesn't register a waker...? Poll::Pending } fn is_idle(&self) -> bool { Arc::strong_count(&self.shared) <= 2 } } // ===== impl Shared ===== impl Shared { fn send_ping(&mut self) { match self.ping_pong.send_ping(Ping::opaque()) { Ok(()) => { self.ping_sent_at = Some(Instant::now()); trace!("sent ping"); } Err(_err) => { debug!("error sending ping: {}", _err); } } } fn is_ping_sent(&self) -> bool { self.ping_sent_at.is_some() } fn update_last_read_at(&mut self) { if self.last_read_at.is_some() { self.last_read_at = Some(Instant::now()); } } fn last_read_at(&self) -> Instant { self.last_read_at.expect("keep_alive expects last_read_at") } } // ===== impl Bdp ===== /// Any higher than this likely will be hitting the TCP flow control. const BDP_LIMIT: usize = 1024 * 1024 * 16; impl Bdp { fn calculate(&mut self, bytes: usize, rtt: Duration) -> Option { // No need to do any math if we're at the limit. if self.bdp as usize == BDP_LIMIT { self.stabilize_delay(); return None; } // average the rtt let rtt = seconds(rtt); if self.rtt == 0.0 { // First sample means rtt is first rtt. self.rtt = rtt; } else { // Weigh this rtt as 1/8 for a moving average. self.rtt += (rtt - self.rtt) * 0.125; } // calculate the current bandwidth let bw = (bytes as f64) / (self.rtt * 1.5); trace!("current bandwidth = {:.1}B/s", bw); if bw < self.max_bandwidth { // not a faster bandwidth, so don't update self.stabilize_delay(); return None; } else { self.max_bandwidth = bw; } // if the current `bytes` sample is at least 2/3 the previous // bdp, increase to double the current sample. if bytes >= self.bdp as usize * 2 / 3 { self.bdp = (bytes * 2).min(BDP_LIMIT) as WindowSize; trace!("BDP increased to {}", self.bdp); self.stable_count = 0; self.ping_delay /= 2; Some(self.bdp) } else { self.stabilize_delay(); None } } fn stabilize_delay(&mut self) { if self.ping_delay < Duration::from_secs(10) { self.stable_count += 1; if self.stable_count >= 2 { self.ping_delay *= 4; self.stable_count = 0; } } } } fn seconds(dur: Duration) -> f64 { const NANOS_PER_SEC: f64 = 1_000_000_000.0; let secs = dur.as_secs() as f64; secs + (dur.subsec_nanos() as f64) / NANOS_PER_SEC } // ===== impl KeepAlive ===== impl KeepAlive { fn maybe_schedule(&mut self, is_idle: bool, shared: &Shared) { match self.state { KeepAliveState::Init => { if !self.while_idle && is_idle { return; } self.schedule(shared); } KeepAliveState::PingSent => { if shared.is_ping_sent() { return; } self.schedule(shared); } KeepAliveState::Scheduled(..) => (), } } fn schedule(&mut self, shared: &Shared) { let interval = shared.last_read_at() + self.interval; self.state = KeepAliveState::Scheduled(interval); self.timer.reset(&mut self.sleep, interval); } fn maybe_ping(&mut self, cx: &mut task::Context<'_>, is_idle: bool, shared: &mut Shared) { match self.state { KeepAliveState::Scheduled(at) => { if Pin::new(&mut self.sleep).poll(cx).is_pending() { return; } // check if we've received a frame while we were scheduled if shared.last_read_at() + self.interval > at { self.state = KeepAliveState::Init; cx.waker().wake_by_ref(); // schedule us again return; } if !self.while_idle && is_idle { trace!("keep-alive no need to ping when idle and while_idle=false"); return; } trace!("keep-alive interval ({:?}) reached", self.interval); shared.send_ping(); self.state = KeepAliveState::PingSent; let timeout = Instant::now() + self.timeout; self.timer.reset(&mut self.sleep, timeout); } KeepAliveState::Init | KeepAliveState::PingSent => (), } } fn maybe_timeout(&mut self, cx: &mut task::Context<'_>) -> Result<(), KeepAliveTimedOut> { match self.state { KeepAliveState::PingSent => { if Pin::new(&mut self.sleep).poll(cx).is_pending() { return Ok(()); } trace!("keep-alive timeout ({:?}) reached", self.timeout); Err(KeepAliveTimedOut) } KeepAliveState::Init | KeepAliveState::Scheduled(..) => Ok(()), } } } // ===== impl KeepAliveTimedOut ===== impl KeepAliveTimedOut { pub(super) fn crate_error(self) -> crate::Error { crate::Error::new(crate::error::Kind::Http2).with(self) } } impl fmt::Display for KeepAliveTimedOut { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str("keep-alive timed out") } } impl std::error::Error for KeepAliveTimedOut { fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { Some(&crate::error::TimedOut) } } hyper-1.5.2/src/proto/h2/server.rs000064400000000000000000000454661046102023000151160ustar 00000000000000use std::error::Error as StdError; use std::future::Future; use std::pin::Pin; use std::task::{Context, Poll}; use std::time::Duration; use bytes::Bytes; use futures_util::ready; use h2::server::{Connection, Handshake, SendResponse}; use h2::{Reason, RecvStream}; use http::{Method, Request}; use pin_project_lite::pin_project; use super::{ping, PipeToSendStream, SendBuf}; use crate::body::{Body, Incoming as IncomingBody}; use crate::common::date; use crate::common::io::Compat; use crate::common::time::Time; use crate::ext::Protocol; use crate::headers; use crate::proto::h2::ping::Recorder; use crate::proto::h2::{H2Upgraded, UpgradedSendStream}; use crate::proto::Dispatched; use crate::rt::bounds::Http2ServerConnExec; use crate::rt::{Read, Write}; use crate::service::HttpService; use crate::upgrade::{OnUpgrade, Pending, Upgraded}; use crate::Response; // Our defaults are chosen for the "majority" case, which usually are not // resource constrained, and so the spec default of 64kb can be too limiting // for performance. // // At the same time, a server more often has multiple clients connected, and // so is more likely to use more resources than a client would. const DEFAULT_CONN_WINDOW: u32 = 1024 * 1024; // 1mb const DEFAULT_STREAM_WINDOW: u32 = 1024 * 1024; // 1mb const DEFAULT_MAX_FRAME_SIZE: u32 = 1024 * 16; // 16kb const DEFAULT_MAX_SEND_BUF_SIZE: usize = 1024 * 400; // 400kb const DEFAULT_SETTINGS_MAX_HEADER_LIST_SIZE: u32 = 1024 * 16; // 16kb const DEFAULT_MAX_LOCAL_ERROR_RESET_STREAMS: usize = 1024; #[derive(Clone, Debug)] pub(crate) struct Config { pub(crate) adaptive_window: bool, pub(crate) initial_conn_window_size: u32, pub(crate) initial_stream_window_size: u32, pub(crate) max_frame_size: u32, pub(crate) enable_connect_protocol: bool, pub(crate) max_concurrent_streams: Option, pub(crate) max_pending_accept_reset_streams: Option, pub(crate) max_local_error_reset_streams: Option, pub(crate) keep_alive_interval: Option, pub(crate) keep_alive_timeout: Duration, pub(crate) max_send_buffer_size: usize, pub(crate) max_header_list_size: u32, pub(crate) date_header: bool, } impl Default for Config { fn default() -> Config { Config { adaptive_window: false, initial_conn_window_size: DEFAULT_CONN_WINDOW, initial_stream_window_size: DEFAULT_STREAM_WINDOW, max_frame_size: DEFAULT_MAX_FRAME_SIZE, enable_connect_protocol: false, max_concurrent_streams: Some(200), max_pending_accept_reset_streams: None, max_local_error_reset_streams: Some(DEFAULT_MAX_LOCAL_ERROR_RESET_STREAMS), keep_alive_interval: None, keep_alive_timeout: Duration::from_secs(20), max_send_buffer_size: DEFAULT_MAX_SEND_BUF_SIZE, max_header_list_size: DEFAULT_SETTINGS_MAX_HEADER_LIST_SIZE, date_header: true, } } } pin_project! { pub(crate) struct Server where S: HttpService, B: Body, { exec: E, timer: Time, service: S, state: State, date_header: bool, close_pending: bool } } enum State where B: Body, { Handshaking { ping_config: ping::Config, hs: Handshake, SendBuf>, }, Serving(Serving), } struct Serving where B: Body, { ping: Option<(ping::Recorder, ping::Ponger)>, conn: Connection, SendBuf>, closing: Option, date_header: bool, } impl Server where T: Read + Write + Unpin, S: HttpService, S::Error: Into>, B: Body + 'static, E: Http2ServerConnExec, { pub(crate) fn new( io: T, service: S, config: &Config, exec: E, timer: Time, ) -> Server { let mut builder = h2::server::Builder::default(); builder .initial_window_size(config.initial_stream_window_size) .initial_connection_window_size(config.initial_conn_window_size) .max_frame_size(config.max_frame_size) .max_header_list_size(config.max_header_list_size) .max_local_error_reset_streams(config.max_local_error_reset_streams) .max_send_buffer_size(config.max_send_buffer_size); if let Some(max) = config.max_concurrent_streams { builder.max_concurrent_streams(max); } if let Some(max) = config.max_pending_accept_reset_streams { builder.max_pending_accept_reset_streams(max); } if config.enable_connect_protocol { builder.enable_connect_protocol(); } let handshake = builder.handshake(Compat::new(io)); let bdp = if config.adaptive_window { Some(config.initial_stream_window_size) } else { None }; let ping_config = ping::Config { bdp_initial_window: bdp, keep_alive_interval: config.keep_alive_interval, keep_alive_timeout: config.keep_alive_timeout, // If keep-alive is enabled for servers, always enabled while // idle, so it can more aggressively close dead connections. keep_alive_while_idle: true, }; Server { exec, timer, state: State::Handshaking { ping_config, hs: handshake, }, service, date_header: config.date_header, close_pending: false, } } pub(crate) fn graceful_shutdown(&mut self) { trace!("graceful_shutdown"); match self.state { State::Handshaking { .. } => { self.close_pending = true; } State::Serving(ref mut srv) => { if srv.closing.is_none() { srv.conn.graceful_shutdown(); } } } } } impl Future for Server where T: Read + Write + Unpin, S: HttpService, S::Error: Into>, B: Body + 'static, E: Http2ServerConnExec, { type Output = crate::Result; fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { let me = &mut *self; loop { let next = match me.state { State::Handshaking { ref mut hs, ref ping_config, } => { let mut conn = ready!(Pin::new(hs).poll(cx).map_err(crate::Error::new_h2))?; let ping = if ping_config.is_enabled() { let pp = conn.ping_pong().expect("conn.ping_pong"); Some(ping::channel(pp, ping_config.clone(), me.timer.clone())) } else { None }; State::Serving(Serving { ping, conn, closing: None, date_header: me.date_header, }) } State::Serving(ref mut srv) => { // graceful_shutdown was called before handshaking finished, if me.close_pending && srv.closing.is_none() { srv.conn.graceful_shutdown(); } ready!(srv.poll_server(cx, &mut me.service, &mut me.exec))?; return Poll::Ready(Ok(Dispatched::Shutdown)); } }; me.state = next; } } } impl Serving where T: Read + Write + Unpin, B: Body + 'static, { fn poll_server( &mut self, cx: &mut Context<'_>, service: &mut S, exec: &mut E, ) -> Poll> where S: HttpService, S::Error: Into>, E: Http2ServerConnExec, { if self.closing.is_none() { loop { self.poll_ping(cx); match ready!(self.conn.poll_accept(cx)) { Some(Ok((req, mut respond))) => { trace!("incoming request"); let content_length = headers::content_length_parse_all(req.headers()); let ping = self .ping .as_ref() .map(|ping| ping.0.clone()) .unwrap_or_else(ping::disabled); // Record the headers received ping.record_non_data(); let is_connect = req.method() == Method::CONNECT; let (mut parts, stream) = req.into_parts(); let (mut req, connect_parts) = if !is_connect { ( Request::from_parts( parts, IncomingBody::h2(stream, content_length.into(), ping), ), None, ) } else { if content_length.map_or(false, |len| len != 0) { warn!("h2 connect request with non-zero body not supported"); respond.send_reset(h2::Reason::INTERNAL_ERROR); return Poll::Ready(Ok(())); } let (pending, upgrade) = crate::upgrade::pending(); debug_assert!(parts.extensions.get::().is_none()); parts.extensions.insert(upgrade); ( Request::from_parts(parts, IncomingBody::empty()), Some(ConnectParts { pending, ping, recv_stream: stream, }), ) }; if let Some(protocol) = req.extensions_mut().remove::() { req.extensions_mut().insert(Protocol::from_inner(protocol)); } let fut = H2Stream::new( service.call(req), connect_parts, respond, self.date_header, ); exec.execute_h2stream(fut); } Some(Err(e)) => { return Poll::Ready(Err(crate::Error::new_h2(e))); } None => { // no more incoming streams... if let Some((ref ping, _)) = self.ping { ping.ensure_not_timed_out()?; } trace!("incoming connection complete"); return Poll::Ready(Ok(())); } } } } debug_assert!( self.closing.is_some(), "poll_server broke loop without closing" ); ready!(self.conn.poll_closed(cx).map_err(crate::Error::new_h2))?; Poll::Ready(Err(self.closing.take().expect("polled after error"))) } fn poll_ping(&mut self, cx: &mut Context<'_>) { if let Some((_, ref mut estimator)) = self.ping { match estimator.poll(cx) { Poll::Ready(ping::Ponged::SizeUpdate(wnd)) => { self.conn.set_target_window_size(wnd); let _ = self.conn.set_initial_window_size(wnd); } Poll::Ready(ping::Ponged::KeepAliveTimedOut) => { debug!("keep-alive timed out, closing connection"); self.conn.abrupt_shutdown(h2::Reason::NO_ERROR); } Poll::Pending => {} } } } } pin_project! { #[allow(missing_debug_implementations)] pub struct H2Stream where B: Body, { reply: SendResponse>, #[pin] state: H2StreamState, date_header: bool, } } pin_project! { #[project = H2StreamStateProj] enum H2StreamState where B: Body, { Service { #[pin] fut: F, connect_parts: Option, }, Body { #[pin] pipe: PipeToSendStream, }, } } struct ConnectParts { pending: Pending, ping: Recorder, recv_stream: RecvStream, } impl H2Stream where B: Body, { fn new( fut: F, connect_parts: Option, respond: SendResponse>, date_header: bool, ) -> H2Stream { H2Stream { reply: respond, state: H2StreamState::Service { fut, connect_parts }, date_header, } } } macro_rules! reply { ($me:expr, $res:expr, $eos:expr) => {{ match $me.reply.send_response($res, $eos) { Ok(tx) => tx, Err(e) => { debug!("send response error: {}", e); $me.reply.send_reset(Reason::INTERNAL_ERROR); return Poll::Ready(Err(crate::Error::new_h2(e))); } } }}; } impl H2Stream where F: Future, E>>, B: Body, B::Data: 'static, B::Error: Into>, E: Into>, { fn poll2(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { let mut me = self.project(); loop { let next = match me.state.as_mut().project() { H2StreamStateProj::Service { fut: h, connect_parts, } => { let res = match h.poll(cx) { Poll::Ready(Ok(r)) => r, Poll::Pending => { // Response is not yet ready, so we want to check if the client has sent a // RST_STREAM frame which would cancel the current request. if let Poll::Ready(reason) = me.reply.poll_reset(cx).map_err(crate::Error::new_h2)? { debug!("stream received RST_STREAM: {:?}", reason); return Poll::Ready(Err(crate::Error::new_h2(reason.into()))); } return Poll::Pending; } Poll::Ready(Err(e)) => { let err = crate::Error::new_user_service(e); warn!("http2 service errored: {}", err); me.reply.send_reset(err.h2_reason()); return Poll::Ready(Err(err)); } }; let (head, body) = res.into_parts(); let mut res = ::http::Response::from_parts(head, ()); super::strip_connection_headers(res.headers_mut(), false); // set Date header if it isn't already set if instructed if *me.date_header { res.headers_mut() .entry(::http::header::DATE) .or_insert_with(date::update_and_header_value); } if let Some(connect_parts) = connect_parts.take() { if res.status().is_success() { if headers::content_length_parse_all(res.headers()) .map_or(false, |len| len != 0) { warn!("h2 successful response to CONNECT request with body not supported"); me.reply.send_reset(h2::Reason::INTERNAL_ERROR); return Poll::Ready(Err(crate::Error::new_user_header())); } if res .headers_mut() .remove(::http::header::CONTENT_LENGTH) .is_some() { warn!("successful response to CONNECT request disallows content-length header"); } let send_stream = reply!(me, res, false); connect_parts.pending.fulfill(Upgraded::new( H2Upgraded { ping: connect_parts.ping, recv_stream: connect_parts.recv_stream, send_stream: unsafe { UpgradedSendStream::new(send_stream) }, buf: Bytes::new(), }, Bytes::new(), )); return Poll::Ready(Ok(())); } } if !body.is_end_stream() { // automatically set Content-Length from body... if let Some(len) = body.size_hint().exact() { headers::set_content_length_if_missing(res.headers_mut(), len); } let body_tx = reply!(me, res, false); H2StreamState::Body { pipe: PipeToSendStream::new(body, body_tx), } } else { reply!(me, res, true); return Poll::Ready(Ok(())); } } H2StreamStateProj::Body { pipe } => { return pipe.poll(cx); } }; me.state.set(next); } } } impl Future for H2Stream where F: Future, E>>, B: Body, B::Data: 'static, B::Error: Into>, E: Into>, { type Output = (); fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { self.poll2(cx).map(|res| { if let Err(_e) = res { debug!("stream error: {}", _e); } }) } } hyper-1.5.2/src/proto/mod.rs000064400000000000000000000040321046102023000140360ustar 00000000000000//! Pieces pertaining to the HTTP message protocol. cfg_feature! { #![feature = "http1"] pub(crate) mod h1; pub(crate) use self::h1::Conn; #[cfg(feature = "client")] pub(crate) use self::h1::dispatch; #[cfg(feature = "server")] pub(crate) use self::h1::ServerTransaction; } #[cfg(feature = "http2")] pub(crate) mod h2; /// An Incoming Message head. Includes request/status line, and headers. #[cfg(feature = "http1")] #[derive(Debug, Default)] pub(crate) struct MessageHead { /// HTTP version of the message. pub(crate) version: http::Version, /// Subject (request line or status line) of Incoming message. pub(crate) subject: S, /// Headers of the Incoming message. pub(crate) headers: http::HeaderMap, /// Extensions. extensions: http::Extensions, } /// An incoming request message. #[cfg(feature = "http1")] pub(crate) type RequestHead = MessageHead; #[derive(Debug, Default, PartialEq)] #[cfg(feature = "http1")] pub(crate) struct RequestLine(pub(crate) http::Method, pub(crate) http::Uri); /// An incoming response message. #[cfg(all(feature = "http1", feature = "client"))] pub(crate) type ResponseHead = MessageHead; #[derive(Debug)] #[cfg(feature = "http1")] pub(crate) enum BodyLength { /// Content-Length Known(u64), /// Transfer-Encoding: chunked (if h1) Unknown, } /// Status of when a Dispatcher future completes. pub(crate) enum Dispatched { /// Dispatcher completely shutdown connection. Shutdown, /// Dispatcher has pending upgrade, and so did not shutdown. #[cfg(feature = "http1")] Upgrade(crate::upgrade::Pending), } #[cfg(all(feature = "client", feature = "http1"))] impl MessageHead { fn into_response(self, body: B) -> http::Response { let mut res = http::Response::new(body); *res.status_mut() = self.subject; *res.headers_mut() = self.headers; *res.version_mut() = self.version; *res.extensions_mut() = self.extensions; res } } hyper-1.5.2/src/rt/bounds.rs000064400000000000000000000063001046102023000140330ustar 00000000000000//! Trait aliases //! //! Traits in this module ease setting bounds and usually automatically //! implemented by implementing another trait. #[cfg(all(feature = "server", feature = "http2"))] pub use self::h2::Http2ServerConnExec; #[cfg(all(feature = "client", feature = "http2"))] pub use self::h2_client::Http2ClientConnExec; #[cfg(all(feature = "client", feature = "http2"))] #[cfg_attr(docsrs, doc(cfg(all(feature = "client", feature = "http2"))))] mod h2_client { use std::{error::Error, future::Future}; use crate::rt::{Read, Write}; use crate::{proto::h2::client::H2ClientFuture, rt::Executor}; /// An executor to spawn http2 futures for the client. /// /// This trait is implemented for any type that implements [`Executor`] /// trait for any future. /// /// This trait is sealed and cannot be implemented for types outside this crate. /// /// [`Executor`]: crate::rt::Executor pub trait Http2ClientConnExec: sealed_client::Sealed<(B, T)> where B: http_body::Body, B::Error: Into>, T: Read + Write + Unpin, { #[doc(hidden)] fn execute_h2_future(&mut self, future: H2ClientFuture); } impl Http2ClientConnExec for E where E: Executor>, B: http_body::Body + 'static, B::Error: Into>, H2ClientFuture: Future, T: Read + Write + Unpin, { fn execute_h2_future(&mut self, future: H2ClientFuture) { self.execute(future) } } impl sealed_client::Sealed<(B, T)> for E where E: Executor>, B: http_body::Body + 'static, B::Error: Into>, H2ClientFuture: Future, T: Read + Write + Unpin, { } mod sealed_client { pub trait Sealed {} } } #[cfg(all(feature = "server", feature = "http2"))] #[cfg_attr(docsrs, doc(cfg(all(feature = "server", feature = "http2"))))] mod h2 { use crate::{proto::h2::server::H2Stream, rt::Executor}; use http_body::Body; use std::future::Future; /// An executor to spawn http2 connections. /// /// This trait is implemented for any type that implements [`Executor`] /// trait for any future. /// /// This trait is sealed and cannot be implemented for types outside this crate. /// /// [`Executor`]: crate::rt::Executor pub trait Http2ServerConnExec: sealed::Sealed<(F, B)> + Clone { #[doc(hidden)] fn execute_h2stream(&mut self, fut: H2Stream); } #[doc(hidden)] impl Http2ServerConnExec for E where E: Executor> + Clone, H2Stream: Future, B: Body, { fn execute_h2stream(&mut self, fut: H2Stream) { self.execute(fut) } } impl sealed::Sealed<(F, B)> for E where E: Executor> + Clone, H2Stream: Future, B: Body, { } mod sealed { pub trait Sealed {} } } hyper-1.5.2/src/rt/io.rs000064400000000000000000000305201046102023000131510ustar 00000000000000use std::fmt; use std::mem::MaybeUninit; use std::ops::DerefMut; use std::pin::Pin; use std::task::{Context, Poll}; // New IO traits? What?! Why, are you bonkers? // // I mean, yes, probably. But, here's the goals: // // 1. Supports poll-based IO operations. // 2. Opt-in vectored IO. // 3. Can use an optional buffer pool. // 4. Able to add completion-based (uring) IO eventually. // // Frankly, the last point is the entire reason we're doing this. We want to // have forwards-compatibility with an eventually stable io-uring runtime. We // don't need that to work right away. But it must be possible to add in here // without breaking hyper 1.0. // // While in here, if there's small tweaks to poll_read or poll_write that would // allow even the "slow" path to be faster, such as if someone didn't remember // to forward along an `is_completion` call. /// Reads bytes from a source. /// /// This trait is similar to `std::io::Read`, but supports asynchronous reads. pub trait Read { /// Attempts to read bytes into the `buf`. /// /// On success, returns `Poll::Ready(Ok(()))` and places data in the /// unfilled portion of `buf`. If no data was read (`buf.remaining()` is /// unchanged), it implies that EOF has been reached. /// /// If no data is available for reading, the method returns `Poll::Pending` /// and arranges for the current task (via `cx.waker()`) to receive a /// notification when the object becomes readable or is closed. fn poll_read( self: Pin<&mut Self>, cx: &mut Context<'_>, buf: ReadBufCursor<'_>, ) -> Poll>; } /// Write bytes asynchronously. /// /// This trait is similar to `std::io::Write`, but for asynchronous writes. pub trait Write { /// Attempt to write bytes from `buf` into the destination. /// /// On success, returns `Poll::Ready(Ok(num_bytes_written)))`. If /// successful, it must be guaranteed that `n <= buf.len()`. A return value /// of `0` means that the underlying object is no longer able to accept /// bytes, or that the provided buffer is empty. /// /// If the object is not ready for writing, the method returns /// `Poll::Pending` and arranges for the current task (via `cx.waker()`) to /// receive a notification when the object becomes writable or is closed. fn poll_write( self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8], ) -> Poll>; /// Attempts to flush the object. /// /// On success, returns `Poll::Ready(Ok(()))`. /// /// If flushing cannot immediately complete, this method returns /// `Poll::Pending` and arranges for the current task (via `cx.waker()`) to /// receive a notification when the object can make progress. fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll>; /// Attempts to shut down this writer. fn poll_shutdown( self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll>; /// Returns whether this writer has an efficient `poll_write_vectored` /// implementation. /// /// The default implementation returns `false`. fn is_write_vectored(&self) -> bool { false } /// Like `poll_write`, except that it writes from a slice of buffers. fn poll_write_vectored( self: Pin<&mut Self>, cx: &mut Context<'_>, bufs: &[std::io::IoSlice<'_>], ) -> Poll> { let buf = bufs .iter() .find(|b| !b.is_empty()) .map_or(&[][..], |b| &**b); self.poll_write(cx, buf) } } /// A wrapper around a byte buffer that is incrementally filled and initialized. /// /// This type is a sort of "double cursor". It tracks three regions in the /// buffer: a region at the beginning of the buffer that has been logically /// filled with data, a region that has been initialized at some point but not /// yet logically filled, and a region at the end that may be uninitialized. /// The filled region is guaranteed to be a subset of the initialized region. /// /// In summary, the contents of the buffer can be visualized as: /// /// ```not_rust /// [ capacity ] /// [ filled | unfilled ] /// [ initialized | uninitialized ] /// ``` /// /// It is undefined behavior to de-initialize any bytes from the uninitialized /// region, since it is merely unknown whether this region is uninitialized or /// not, and if part of it turns out to be initialized, it must stay initialized. pub struct ReadBuf<'a> { raw: &'a mut [MaybeUninit], filled: usize, init: usize, } /// The cursor part of a [`ReadBuf`]. /// /// This is created by calling `ReadBuf::unfilled()`. #[derive(Debug)] pub struct ReadBufCursor<'a> { buf: &'a mut ReadBuf<'a>, } impl<'data> ReadBuf<'data> { /// Create a new `ReadBuf` with a slice of initialized bytes. #[inline] pub fn new(raw: &'data mut [u8]) -> Self { let len = raw.len(); Self { // SAFETY: We never de-init the bytes ourselves. raw: unsafe { &mut *(raw as *mut [u8] as *mut [MaybeUninit]) }, filled: 0, init: len, } } /// Create a new `ReadBuf` with a slice of uninitialized bytes. #[inline] pub fn uninit(raw: &'data mut [MaybeUninit]) -> Self { Self { raw, filled: 0, init: 0, } } /// Get a slice of the buffer that has been filled in with bytes. #[inline] pub fn filled(&self) -> &[u8] { // SAFETY: We only slice the filled part of the buffer, which is always valid unsafe { &*(&self.raw[0..self.filled] as *const [MaybeUninit] as *const [u8]) } } /// Get a cursor to the unfilled portion of the buffer. #[inline] pub fn unfilled<'cursor>(&'cursor mut self) -> ReadBufCursor<'cursor> { ReadBufCursor { // SAFETY: self.buf is never re-assigned, so its safe to narrow // the lifetime. buf: unsafe { std::mem::transmute::<&'cursor mut ReadBuf<'data>, &'cursor mut ReadBuf<'cursor>>( self, ) }, } } #[inline] #[cfg(all(any(feature = "client", feature = "server"), feature = "http2"))] pub(crate) unsafe fn set_init(&mut self, n: usize) { self.init = self.init.max(n); } #[inline] #[cfg(all(any(feature = "client", feature = "server"), feature = "http2"))] pub(crate) unsafe fn set_filled(&mut self, n: usize) { self.filled = self.filled.max(n); } #[inline] #[cfg(all(any(feature = "client", feature = "server"), feature = "http2"))] pub(crate) fn len(&self) -> usize { self.filled } #[inline] #[cfg(all(any(feature = "client", feature = "server"), feature = "http2"))] pub(crate) fn init_len(&self) -> usize { self.init } #[inline] fn remaining(&self) -> usize { self.capacity() - self.filled } #[inline] fn capacity(&self) -> usize { self.raw.len() } } impl fmt::Debug for ReadBuf<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("ReadBuf") .field("filled", &self.filled) .field("init", &self.init) .field("capacity", &self.capacity()) .finish() } } impl ReadBufCursor<'_> { /// Access the unfilled part of the buffer. /// /// # Safety /// /// The caller must not uninitialize any bytes that may have been /// initialized before. #[inline] pub unsafe fn as_mut(&mut self) -> &mut [MaybeUninit] { &mut self.buf.raw[self.buf.filled..] } /// Advance the `filled` cursor by `n` bytes. /// /// # Safety /// /// The caller must take care that `n` more bytes have been initialized. #[inline] pub unsafe fn advance(&mut self, n: usize) { self.buf.filled = self.buf.filled.checked_add(n).expect("overflow"); self.buf.init = self.buf.filled.max(self.buf.init); } /// Returns the number of bytes that can be written from the current /// position until the end of the buffer is reached. /// /// This value is equal to the length of the slice returned by `as_mut()``. #[inline] pub fn remaining(&self) -> usize { self.buf.remaining() } /// Transfer bytes into `self`` from `src` and advance the cursor /// by the number of bytes written. /// /// # Panics /// /// `self` must have enough remaining capacity to contain all of `src`. #[inline] pub fn put_slice(&mut self, src: &[u8]) { assert!( self.buf.remaining() >= src.len(), "src.len() must fit in remaining()" ); let amt = src.len(); // Cannot overflow, asserted above let end = self.buf.filled + amt; // Safety: the length is asserted above unsafe { self.buf.raw[self.buf.filled..end] .as_mut_ptr() .cast::() .copy_from_nonoverlapping(src.as_ptr(), amt); } if self.buf.init < end { self.buf.init = end; } self.buf.filled = end; } } macro_rules! deref_async_read { () => { fn poll_read( mut self: Pin<&mut Self>, cx: &mut Context<'_>, buf: ReadBufCursor<'_>, ) -> Poll> { Pin::new(&mut **self).poll_read(cx, buf) } }; } impl Read for Box { deref_async_read!(); } impl Read for &mut T { deref_async_read!(); } impl

Read for Pin

where P: DerefMut, P::Target: Read, { fn poll_read( self: Pin<&mut Self>, cx: &mut Context<'_>, buf: ReadBufCursor<'_>, ) -> Poll> { pin_as_deref_mut(self).poll_read(cx, buf) } } macro_rules! deref_async_write { () => { fn poll_write( mut self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8], ) -> Poll> { Pin::new(&mut **self).poll_write(cx, buf) } fn poll_write_vectored( mut self: Pin<&mut Self>, cx: &mut Context<'_>, bufs: &[std::io::IoSlice<'_>], ) -> Poll> { Pin::new(&mut **self).poll_write_vectored(cx, bufs) } fn is_write_vectored(&self) -> bool { (**self).is_write_vectored() } fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { Pin::new(&mut **self).poll_flush(cx) } fn poll_shutdown( mut self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll> { Pin::new(&mut **self).poll_shutdown(cx) } }; } impl Write for Box { deref_async_write!(); } impl Write for &mut T { deref_async_write!(); } impl

Write for Pin

where P: DerefMut, P::Target: Write, { fn poll_write( self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8], ) -> Poll> { pin_as_deref_mut(self).poll_write(cx, buf) } fn poll_write_vectored( self: Pin<&mut Self>, cx: &mut Context<'_>, bufs: &[std::io::IoSlice<'_>], ) -> Poll> { pin_as_deref_mut(self).poll_write_vectored(cx, bufs) } fn is_write_vectored(&self) -> bool { (**self).is_write_vectored() } fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { pin_as_deref_mut(self).poll_flush(cx) } fn poll_shutdown(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { pin_as_deref_mut(self).poll_shutdown(cx) } } /// Polyfill for Pin::as_deref_mut() /// TODO: use Pin::as_deref_mut() instead once stabilized fn pin_as_deref_mut(pin: Pin<&mut Pin

>) -> Pin<&mut P::Target> { // SAFETY: we go directly from Pin<&mut Pin

> to Pin<&mut P::Target>, without moving or // giving out the &mut Pin

in the process. See Pin::as_deref_mut() for more detail. unsafe { pin.get_unchecked_mut() }.as_mut() } hyper-1.5.2/src/rt/mod.rs000064400000000000000000000016511046102023000133240ustar 00000000000000//! Runtime components //! //! The traits and types within this module are used to allow plugging in //! runtime types. These include: //! //! - Executors //! - Timers //! - IO transports pub mod bounds; mod io; mod timer; pub use self::io::{Read, ReadBuf, ReadBufCursor, Write}; pub use self::timer::{Sleep, Timer}; /// An executor of futures. /// /// This trait allows Hyper to abstract over async runtimes. Implement this trait for your own type. /// /// # Example /// /// ``` /// # use hyper::rt::Executor; /// # use std::future::Future; /// #[derive(Clone)] /// struct TokioExecutor; /// /// impl Executor for TokioExecutor /// where /// F: Future + Send + 'static, /// F::Output: Send + 'static, /// { /// fn execute(&self, future: F) { /// tokio::spawn(future); /// } /// } /// ``` pub trait Executor { /// Place the future into the executor to be run. fn execute(&self, fut: Fut); } hyper-1.5.2/src/rt/timer.rs000064400000000000000000000065101046102023000136640ustar 00000000000000//! Provides a timer trait with timer-like functions //! //! Example using tokio timer: //! ```rust //! use std::{ //! future::Future, //! pin::Pin, //! task::{Context, Poll}, //! time::{Duration, Instant}, //! }; //! //! use pin_project_lite::pin_project; //! use hyper::rt::{Timer, Sleep}; //! //! #[derive(Clone, Debug)] //! pub struct TokioTimer; //! //! impl Timer for TokioTimer { //! fn sleep(&self, duration: Duration) -> Pin> { //! Box::pin(TokioSleep { //! inner: tokio::time::sleep(duration), //! }) //! } //! //! fn sleep_until(&self, deadline: Instant) -> Pin> { //! Box::pin(TokioSleep { //! inner: tokio::time::sleep_until(deadline.into()), //! }) //! } //! //! fn reset(&self, sleep: &mut Pin>, new_deadline: Instant) { //! if let Some(sleep) = sleep.as_mut().downcast_mut_pin::() { //! sleep.reset(new_deadline.into()) //! } //! } //! } //! //! pin_project! { //! pub(crate) struct TokioSleep { //! #[pin] //! pub(crate) inner: tokio::time::Sleep, //! } //! } //! //! impl Future for TokioSleep { //! type Output = (); //! //! fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { //! self.project().inner.poll(cx) //! } //! } //! //! impl Sleep for TokioSleep {} //! //! impl TokioSleep { //! pub fn reset(self: Pin<&mut Self>, deadline: Instant) { //! self.project().inner.as_mut().reset(deadline.into()); //! } //! } //! ``` use std::{ any::TypeId, future::Future, pin::Pin, time::{Duration, Instant}, }; /// A timer which provides timer-like functions. pub trait Timer { /// Return a future that resolves in `duration` time. fn sleep(&self, duration: Duration) -> Pin>; /// Return a future that resolves at `deadline`. fn sleep_until(&self, deadline: Instant) -> Pin>; /// Reset a future to resolve at `new_deadline` instead. fn reset(&self, sleep: &mut Pin>, new_deadline: Instant) { *sleep = self.sleep_until(new_deadline); } } /// A future returned by a `Timer`. pub trait Sleep: Send + Sync + Future { #[doc(hidden)] /// This method is private and can not be implemented by downstream crate fn __type_id(&self, _: private::Sealed) -> TypeId where Self: 'static, { TypeId::of::() } } impl dyn Sleep { //! This is a re-implementation of downcast methods from std::any::Any /// Check whether the type is the same as `T` pub fn is(&self) -> bool where T: Sleep + 'static, { self.__type_id(private::Sealed {}) == TypeId::of::() } /// Downcast a pinned &mut Sleep object to its original type pub fn downcast_mut_pin(self: Pin<&mut Self>) -> Option> where T: Sleep + 'static, { if self.is::() { unsafe { let inner = Pin::into_inner_unchecked(self); Some(Pin::new_unchecked( &mut *(&mut *inner as *mut dyn Sleep as *mut T), )) } } else { None } } } mod private { #![allow(missing_debug_implementations)] pub struct Sealed {} } hyper-1.5.2/src/server/conn/http1.rs000064400000000000000000000427541046102023000154340ustar 00000000000000//! HTTP/1 Server Connections use std::error::Error as StdError; use std::fmt; use std::future::Future; use std::pin::Pin; use std::sync::Arc; use std::task::{Context, Poll}; use std::time::Duration; use crate::rt::{Read, Write}; use crate::upgrade::Upgraded; use bytes::Bytes; use futures_util::ready; use crate::body::{Body, Incoming as IncomingBody}; use crate::proto; use crate::service::HttpService; use crate::{ common::time::{Dur, Time}, rt::Timer, }; type Http1Dispatcher = proto::h1::Dispatcher< proto::h1::dispatch::Server, B, T, proto::ServerTransaction, >; pin_project_lite::pin_project! { /// A [`Future`](core::future::Future) representing an HTTP/1 connection, bound to a /// [`Service`](crate::service::Service), returned from /// [`Builder::serve_connection`](struct.Builder.html#method.serve_connection). /// /// To drive HTTP on this connection this future **must be polled**, typically with /// `.await`. If it isn't polled, no progress will be made on this connection. #[must_use = "futures do nothing unless polled"] pub struct Connection where S: HttpService, { conn: Http1Dispatcher, } } /// A configuration builder for HTTP/1 server connections. /// /// **Note**: The default values of options are *not considered stable*. They /// are subject to change at any time. /// /// # Example /// /// ``` /// # use std::time::Duration; /// # use hyper::server::conn::http1::Builder; /// # fn main() { /// let mut http = Builder::new(); /// // Set options one at a time /// http.half_close(false); /// /// // Or, chain multiple options /// http.keep_alive(false).title_case_headers(true).max_buf_size(8192); /// /// # } /// ``` /// /// Use [`Builder::serve_connection`](struct.Builder.html#method.serve_connection) /// to bind the built connection to a service. #[derive(Clone, Debug)] pub struct Builder { timer: Time, h1_half_close: bool, h1_keep_alive: bool, h1_title_case_headers: bool, h1_preserve_header_case: bool, h1_max_headers: Option, h1_header_read_timeout: Dur, h1_writev: Option, max_buf_size: Option, pipeline_flush: bool, date_header: bool, } /// Deconstructed parts of a `Connection`. /// /// This allows taking apart a `Connection` at a later time, in order to /// reclaim the IO object, and additional related pieces. #[derive(Debug)] #[non_exhaustive] pub struct Parts { /// The original IO object used in the handshake. pub io: T, /// A buffer of bytes that have been read but not processed as HTTP. /// /// If the client sent additional bytes after its last request, and /// this connection "ended" with an upgrade, the read buffer will contain /// those bytes. /// /// You will want to check for any existing bytes if you plan to continue /// communicating on the IO object. pub read_buf: Bytes, /// The `Service` used to serve this connection. pub service: S, } // ===== impl Connection ===== impl fmt::Debug for Connection where S: HttpService, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("Connection").finish() } } impl Connection where S: HttpService, S::Error: Into>, I: Read + Write + Unpin, B: Body + 'static, B::Error: Into>, { /// Start a graceful shutdown process for this connection. /// /// This `Connection` should continue to be polled until shutdown /// can finish. /// /// # Note /// /// This should only be called while the `Connection` future is still /// pending. If called after `Connection::poll` has resolved, this does /// nothing. pub fn graceful_shutdown(mut self: Pin<&mut Self>) { self.conn.disable_keep_alive(); } /// Return the inner IO object, and additional information. /// /// If the IO object has been "rewound" the io will not contain those bytes rewound. /// This should only be called after `poll_without_shutdown` signals /// that the connection is "done". Otherwise, it may not have finished /// flushing all necessary HTTP bytes. /// /// # Panics /// This method will panic if this connection is using an h2 protocol. pub fn into_parts(self) -> Parts { let (io, read_buf, dispatch) = self.conn.into_inner(); Parts { io, read_buf, service: dispatch.into_service(), } } /// Poll the connection for completion, but without calling `shutdown` /// on the underlying IO. /// /// This is useful to allow running a connection while doing an HTTP /// upgrade. Once the upgrade is completed, the connection would be "done", /// but it is not desired to actually shutdown the IO object. Instead you /// would take it back using `into_parts`. pub fn poll_without_shutdown(&mut self, cx: &mut Context<'_>) -> Poll> where S: Unpin, S::Future: Unpin, { self.conn.poll_without_shutdown(cx) } /// Prevent shutdown of the underlying IO object at the end of service the request, /// instead run `into_parts`. This is a convenience wrapper over `poll_without_shutdown`. /// /// # Error /// /// This errors if the underlying connection protocol is not HTTP/1. pub fn without_shutdown(self) -> impl Future>> { let mut zelf = Some(self); futures_util::future::poll_fn(move |cx| { ready!(zelf.as_mut().unwrap().conn.poll_without_shutdown(cx))?; Poll::Ready(Ok(zelf.take().unwrap().into_parts())) }) } /// Enable this connection to support higher-level HTTP upgrades. /// /// See [the `upgrade` module](crate::upgrade) for more. pub fn with_upgrades(self) -> UpgradeableConnection where I: Send, { UpgradeableConnection { inner: Some(self) } } } impl Future for Connection where S: HttpService, S::Error: Into>, I: Read + Write + Unpin, B: Body + 'static, B::Error: Into>, { type Output = crate::Result<()>; fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { match ready!(Pin::new(&mut self.conn).poll(cx)) { Ok(done) => { match done { proto::Dispatched::Shutdown => {} proto::Dispatched::Upgrade(pending) => { // With no `Send` bound on `I`, we can't try to do // upgrades here. In case a user was trying to use // `Body::on_upgrade` with this API, send a special // error letting them know about that. pending.manual(); } }; Poll::Ready(Ok(())) } Err(e) => Poll::Ready(Err(e)), } } } // ===== impl Builder ===== impl Builder { /// Create a new connection builder. pub fn new() -> Self { Self { timer: Time::Empty, h1_half_close: false, h1_keep_alive: true, h1_title_case_headers: false, h1_preserve_header_case: false, h1_max_headers: None, h1_header_read_timeout: Dur::Default(Some(Duration::from_secs(30))), h1_writev: None, max_buf_size: None, pipeline_flush: false, date_header: true, } } /// Set whether HTTP/1 connections should support half-closures. /// /// Clients can chose to shutdown their write-side while waiting /// for the server to respond. Setting this to `true` will /// prevent closing the connection immediately if `read` /// detects an EOF in the middle of a request. /// /// Default is `false`. pub fn half_close(&mut self, val: bool) -> &mut Self { self.h1_half_close = val; self } /// Enables or disables HTTP/1 keep-alive. /// /// Default is true. pub fn keep_alive(&mut self, val: bool) -> &mut Self { self.h1_keep_alive = val; self } /// Set whether HTTP/1 connections will write header names as title case at /// the socket level. /// /// Default is false. pub fn title_case_headers(&mut self, enabled: bool) -> &mut Self { self.h1_title_case_headers = enabled; self } /// Set whether to support preserving original header cases. /// /// Currently, this will record the original cases received, and store them /// in a private extension on the `Request`. It will also look for and use /// such an extension in any provided `Response`. /// /// Since the relevant extension is still private, there is no way to /// interact with the original cases. The only effect this can have now is /// to forward the cases in a proxy-like fashion. /// /// Default is false. pub fn preserve_header_case(&mut self, enabled: bool) -> &mut Self { self.h1_preserve_header_case = enabled; self } /// Set the maximum number of headers. /// /// When a request is received, the parser will reserve a buffer to store headers for optimal /// performance. /// /// If server receives more headers than the buffer size, it responds to the client with /// "431 Request Header Fields Too Large". /// /// Note that headers is allocated on the stack by default, which has higher performance. After /// setting this value, headers will be allocated in heap memory, that is, heap memory /// allocation will occur for each request, and there will be a performance drop of about 5%. /// /// Default is 100. pub fn max_headers(&mut self, val: usize) -> &mut Self { self.h1_max_headers = Some(val); self } /// Set a timeout for reading client request headers. If a client does not /// transmit the entire header within this time, the connection is closed. /// /// Requires a [`Timer`] set by [`Builder::timer`] to take effect. Panics if `header_read_timeout` is configured /// without a [`Timer`]. /// /// Pass `None` to disable. /// /// Default is 30 seconds. pub fn header_read_timeout(&mut self, read_timeout: impl Into>) -> &mut Self { self.h1_header_read_timeout = Dur::Configured(read_timeout.into()); self } /// Set whether HTTP/1 connections should try to use vectored writes, /// or always flatten into a single buffer. /// /// Note that setting this to false may mean more copies of body data, /// but may also improve performance when an IO transport doesn't /// support vectored writes well, such as most TLS implementations. /// /// Setting this to true will force hyper to use queued strategy /// which may eliminate unnecessary cloning on some TLS backends /// /// Default is `auto`. In this mode hyper will try to guess which /// mode to use pub fn writev(&mut self, val: bool) -> &mut Self { self.h1_writev = Some(val); self } /// Set the maximum buffer size for the connection. /// /// Default is ~400kb. /// /// # Panics /// /// The minimum value allowed is 8192. This method panics if the passed `max` is less than the minimum. pub fn max_buf_size(&mut self, max: usize) -> &mut Self { assert!( max >= proto::h1::MINIMUM_MAX_BUFFER_SIZE, "the max_buf_size cannot be smaller than the minimum that h1 specifies." ); self.max_buf_size = Some(max); self } /// Set whether the `date` header should be included in HTTP responses. /// /// Note that including the `date` header is recommended by RFC 7231. /// /// Default is true. pub fn auto_date_header(&mut self, enabled: bool) -> &mut Self { self.date_header = enabled; self } /// Aggregates flushes to better support pipelined responses. /// /// Experimental, may have bugs. /// /// Default is false. pub fn pipeline_flush(&mut self, enabled: bool) -> &mut Self { self.pipeline_flush = enabled; self } /// Set the timer used in background tasks. pub fn timer(&mut self, timer: M) -> &mut Self where M: Timer + Send + Sync + 'static, { self.timer = Time::Timer(Arc::new(timer)); self } /// Bind a connection together with a [`Service`](crate::service::Service). /// /// This returns a Future that must be polled in order for HTTP to be /// driven on the connection. /// /// # Panics /// /// If a timeout option has been configured, but a `timer` has not been /// provided, calling `serve_connection` will panic. /// /// # Example /// /// ``` /// # use hyper::{body::Incoming, Request, Response}; /// # use hyper::service::Service; /// # use hyper::server::conn::http1::Builder; /// # use hyper::rt::{Read, Write}; /// # async fn run(some_io: I, some_service: S) /// # where /// # I: Read + Write + Unpin + Send + 'static, /// # S: Service, Response=hyper::Response> + Send + 'static, /// # S::Error: Into>, /// # S::Future: Send, /// # { /// let http = Builder::new(); /// let conn = http.serve_connection(some_io, some_service); /// /// if let Err(e) = conn.await { /// eprintln!("server connection error: {}", e); /// } /// # } /// # fn main() {} /// ``` pub fn serve_connection(&self, io: I, service: S) -> Connection where S: HttpService, S::Error: Into>, S::ResBody: 'static, ::Error: Into>, I: Read + Write + Unpin, { let mut conn = proto::Conn::new(io); conn.set_timer(self.timer.clone()); if !self.h1_keep_alive { conn.disable_keep_alive(); } if self.h1_half_close { conn.set_allow_half_close(); } if self.h1_title_case_headers { conn.set_title_case_headers(); } if self.h1_preserve_header_case { conn.set_preserve_header_case(); } if let Some(max_headers) = self.h1_max_headers { conn.set_http1_max_headers(max_headers); } if let Some(dur) = self .timer .check(self.h1_header_read_timeout, "header_read_timeout") { conn.set_http1_header_read_timeout(dur); }; if let Some(writev) = self.h1_writev { if writev { conn.set_write_strategy_queue(); } else { conn.set_write_strategy_flatten(); } } conn.set_flush_pipeline(self.pipeline_flush); if let Some(max) = self.max_buf_size { conn.set_max_buf_size(max); } if !self.date_header { conn.disable_date_header(); } let sd = proto::h1::dispatch::Server::new(service); let proto = proto::h1::Dispatcher::new(sd, conn); Connection { conn: proto } } } /// A future binding a connection with a Service with Upgrade support. #[must_use = "futures do nothing unless polled"] #[allow(missing_debug_implementations)] pub struct UpgradeableConnection where S: HttpService, { pub(super) inner: Option>, } impl UpgradeableConnection where S: HttpService, S::Error: Into>, I: Read + Write + Unpin, B: Body + 'static, B::Error: Into>, { /// Start a graceful shutdown process for this connection. /// /// This `Connection` should continue to be polled until shutdown /// can finish. pub fn graceful_shutdown(mut self: Pin<&mut Self>) { // Connection (`inner`) is `None` if it was upgraded (and `poll` is `Ready`). // In that case, we don't need to call `graceful_shutdown`. if let Some(conn) = self.inner.as_mut() { Pin::new(conn).graceful_shutdown() } } } impl Future for UpgradeableConnection where S: HttpService, S::Error: Into>, I: Read + Write + Unpin + Send + 'static, B: Body + 'static, B::Error: Into>, { type Output = crate::Result<()>; fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { if let Some(conn) = self.inner.as_mut() { match ready!(Pin::new(&mut conn.conn).poll(cx)) { Ok(proto::Dispatched::Shutdown) => Poll::Ready(Ok(())), Ok(proto::Dispatched::Upgrade(pending)) => { let (io, buf, _) = self.inner.take().unwrap().conn.into_inner(); pending.fulfill(Upgraded::new(io, buf)); Poll::Ready(Ok(())) } Err(e) => Poll::Ready(Err(e)), } } else { // inner is `None`, meaning the connection was upgraded, thus it's `Poll::Ready(Ok(()))` Poll::Ready(Ok(())) } } } hyper-1.5.2/src/server/conn/http2.rs000064400000000000000000000243111046102023000154220ustar 00000000000000//! HTTP/2 Server Connections use std::error::Error as StdError; use std::fmt; use std::future::Future; use std::pin::Pin; use std::sync::Arc; use std::task::{Context, Poll}; use std::time::Duration; use crate::rt::{Read, Write}; use futures_util::ready; use pin_project_lite::pin_project; use crate::body::{Body, Incoming as IncomingBody}; use crate::proto; use crate::rt::bounds::Http2ServerConnExec; use crate::service::HttpService; use crate::{common::time::Time, rt::Timer}; pin_project! { /// A [`Future`](core::future::Future) representing an HTTP/2 connection, bound to a /// [`Service`](crate::service::Service), returned from /// [`Builder::serve_connection`](struct.Builder.html#method.serve_connection). /// /// To drive HTTP on this connection this future **must be polled**, typically with /// `.await`. If it isn't polled, no progress will be made on this connection. #[must_use = "futures do nothing unless polled"] pub struct Connection where S: HttpService, { conn: proto::h2::Server, } } /// A configuration builder for HTTP/2 server connections. /// /// **Note**: The default values of options are *not considered stable*. They /// are subject to change at any time. #[derive(Clone, Debug)] pub struct Builder { exec: E, timer: Time, h2_builder: proto::h2::server::Config, } // ===== impl Connection ===== impl fmt::Debug for Connection where S: HttpService, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("Connection").finish() } } impl Connection where S: HttpService, S::Error: Into>, I: Read + Write + Unpin, B: Body + 'static, B::Error: Into>, E: Http2ServerConnExec, { /// Start a graceful shutdown process for this connection. /// /// This `Connection` should continue to be polled until shutdown /// can finish. /// /// # Note /// /// This should only be called while the `Connection` future is still /// pending. If called after `Connection::poll` has resolved, this does /// nothing. pub fn graceful_shutdown(mut self: Pin<&mut Self>) { self.conn.graceful_shutdown(); } } impl Future for Connection where S: HttpService, S::Error: Into>, I: Read + Write + Unpin, B: Body + 'static, B::Error: Into>, E: Http2ServerConnExec, { type Output = crate::Result<()>; fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { match ready!(Pin::new(&mut self.conn).poll(cx)) { Ok(_done) => { //TODO: the proto::h2::Server no longer needs to return //the Dispatched enum Poll::Ready(Ok(())) } Err(e) => Poll::Ready(Err(e)), } } } // ===== impl Builder ===== impl Builder { /// Create a new connection builder. /// /// This starts with the default options, and an executor which is a type /// that implements [`Http2ServerConnExec`] trait. /// /// [`Http2ServerConnExec`]: crate::rt::bounds::Http2ServerConnExec pub fn new(exec: E) -> Self { Self { exec, timer: Time::Empty, h2_builder: Default::default(), } } /// Configures the maximum number of pending reset streams allowed before a GOAWAY will be sent. /// /// This will default to the default value set by the [`h2` crate](https://crates.io/crates/h2). /// As of v0.4.0, it is 20. /// /// See for more information. pub fn max_pending_accept_reset_streams(&mut self, max: impl Into>) -> &mut Self { self.h2_builder.max_pending_accept_reset_streams = max.into(); self } /// Configures the maximum number of local reset streams allowed before a GOAWAY will be sent. /// /// If not set, hyper will use a default, currently of 1024. /// /// If `None` is supplied, hyper will not apply any limit. /// This is not advised, as it can potentially expose servers to DOS vulnerabilities. /// /// See for more information. #[cfg(feature = "http2")] #[cfg_attr(docsrs, doc(cfg(feature = "http2")))] pub fn max_local_error_reset_streams(mut self, max: impl Into>) -> Self { self.h2_builder.max_local_error_reset_streams = max.into(); self } /// Sets the [`SETTINGS_INITIAL_WINDOW_SIZE`][spec] option for HTTP2 /// stream-level flow control. /// /// Passing `None` will do nothing. /// /// If not set, hyper will use a default. /// /// [spec]: https://httpwg.org/specs/rfc9113.html#SETTINGS_INITIAL_WINDOW_SIZE pub fn initial_stream_window_size(&mut self, sz: impl Into>) -> &mut Self { if let Some(sz) = sz.into() { self.h2_builder.adaptive_window = false; self.h2_builder.initial_stream_window_size = sz; } self } /// Sets the max connection-level flow control for HTTP2. /// /// Passing `None` will do nothing. /// /// If not set, hyper will use a default. pub fn initial_connection_window_size(&mut self, sz: impl Into>) -> &mut Self { if let Some(sz) = sz.into() { self.h2_builder.adaptive_window = false; self.h2_builder.initial_conn_window_size = sz; } self } /// Sets whether to use an adaptive flow control. /// /// Enabling this will override the limits set in /// `initial_stream_window_size` and /// `initial_connection_window_size`. pub fn adaptive_window(&mut self, enabled: bool) -> &mut Self { use proto::h2::SPEC_WINDOW_SIZE; self.h2_builder.adaptive_window = enabled; if enabled { self.h2_builder.initial_conn_window_size = SPEC_WINDOW_SIZE; self.h2_builder.initial_stream_window_size = SPEC_WINDOW_SIZE; } self } /// Sets the maximum frame size to use for HTTP2. /// /// Passing `None` will do nothing. /// /// If not set, hyper will use a default. pub fn max_frame_size(&mut self, sz: impl Into>) -> &mut Self { if let Some(sz) = sz.into() { self.h2_builder.max_frame_size = sz; } self } /// Sets the [`SETTINGS_MAX_CONCURRENT_STREAMS`][spec] option for HTTP2 /// connections. /// /// Default is 200, but not part of the stability of hyper. It could change /// in a future release. You are encouraged to set your own limit. /// /// Passing `None` will remove any limit. /// /// [spec]: https://httpwg.org/specs/rfc9113.html#SETTINGS_MAX_CONCURRENT_STREAMS pub fn max_concurrent_streams(&mut self, max: impl Into>) -> &mut Self { self.h2_builder.max_concurrent_streams = max.into(); self } /// Sets an interval for HTTP2 Ping frames should be sent to keep a /// connection alive. /// /// Pass `None` to disable HTTP2 keep-alive. /// /// Default is currently disabled. pub fn keep_alive_interval(&mut self, interval: impl Into>) -> &mut Self { self.h2_builder.keep_alive_interval = interval.into(); self } /// Sets a timeout for receiving an acknowledgement of the keep-alive ping. /// /// If the ping is not acknowledged within the timeout, the connection will /// be closed. Does nothing if `keep_alive_interval` is disabled. /// /// Default is 20 seconds. pub fn keep_alive_timeout(&mut self, timeout: Duration) -> &mut Self { self.h2_builder.keep_alive_timeout = timeout; self } /// Set the maximum write buffer size for each HTTP/2 stream. /// /// Default is currently ~400KB, but may change. /// /// # Panics /// /// The value must be no larger than `u32::MAX`. pub fn max_send_buf_size(&mut self, max: usize) -> &mut Self { assert!(max <= u32::MAX as usize); self.h2_builder.max_send_buffer_size = max; self } /// Enables the [extended CONNECT protocol]. /// /// [extended CONNECT protocol]: https://datatracker.ietf.org/doc/html/rfc8441#section-4 pub fn enable_connect_protocol(&mut self) -> &mut Self { self.h2_builder.enable_connect_protocol = true; self } /// Sets the max size of received header frames. /// /// Default is currently 16KB, but can change. pub fn max_header_list_size(&mut self, max: u32) -> &mut Self { self.h2_builder.max_header_list_size = max; self } /// Set the timer used in background tasks. pub fn timer(&mut self, timer: M) -> &mut Self where M: Timer + Send + Sync + 'static, { self.timer = Time::Timer(Arc::new(timer)); self } /// Set whether the `date` header should be included in HTTP responses. /// /// Note that including the `date` header is recommended by RFC 7231. /// /// Default is true. pub fn auto_date_header(&mut self, enabled: bool) -> &mut Self { self.h2_builder.date_header = enabled; self } /// Bind a connection together with a [`Service`](crate::service::Service). /// /// This returns a Future that must be polled in order for HTTP to be /// driven on the connection. pub fn serve_connection(&self, io: I, service: S) -> Connection where S: HttpService, S::Error: Into>, Bd: Body + 'static, Bd::Error: Into>, I: Read + Write + Unpin, E: Http2ServerConnExec, { let proto = proto::h2::Server::new( io, service, &self.h2_builder, self.exec.clone(), self.timer.clone(), ); Connection { conn: proto } } } hyper-1.5.2/src/server/conn/mod.rs000064400000000000000000000016561046102023000151470ustar 00000000000000//! Server connection API. //! //! The types in this module are to provide a lower-level API based around a //! single connection. Accepting a connection and binding it with a service //! are not handled at this level. This module provides the building blocks to //! customize those things externally. //! //! This module is split by HTTP version, providing a connection builder for //! each. They work similarly, but they each have specific options. //! //! If your server needs to support both versions, an auto-connection builder is //! provided in the [`hyper-util`](https://github.com/hyperium/hyper-util/tree/master) //! crate. This builder wraps the HTTP/1 and HTTP/2 connection builders from this //! module, allowing you to set configuration for both. The builder will then check //! the version of the incoming connection and serve it accordingly. #[cfg(feature = "http1")] pub mod http1; #[cfg(feature = "http2")] pub mod http2; hyper-1.5.2/src/server/mod.rs000064400000000000000000000005711046102023000142050ustar 00000000000000//! HTTP Server //! //! A "server" is usually created by listening on a port for new connections, //! parse HTTP requests, and hand them off to a `Service`. //! //! How exactly you choose to listen for connections is not something hyper //! concerns itself with. After you have a connection, you can handle HTTP over //! it with the types in the [`conn`] module. pub mod conn; hyper-1.5.2/src/service/http.rs000064400000000000000000000025661046102023000145450ustar 00000000000000use std::error::Error as StdError; use std::future::Future; use crate::body::Body; use crate::service::service::Service; use crate::{Request, Response}; /// An asynchronous function from `Request` to `Response`. pub trait HttpService: sealed::Sealed { /// The `Body` body of the `http::Response`. type ResBody: Body; /// The error type that can occur within this `Service`. /// /// Note: Returning an `Error` to a hyper server will cause the connection /// to be abruptly aborted. In most cases, it is better to return a `Response` /// with a 4xx or 5xx status code. type Error: Into>; /// The `Future` returned by this `Service`. type Future: Future, Self::Error>>; #[doc(hidden)] fn call(&mut self, req: Request) -> Self::Future; } impl HttpService for T where T: Service, Response = Response>, B2: Body, T::Error: Into>, { type ResBody = B2; type Error = T::Error; type Future = T::Future; fn call(&mut self, req: Request) -> Self::Future { Service::call(self, req) } } impl sealed::Sealed for T where T: Service, Response = Response>, B2: Body, { } mod sealed { pub trait Sealed {} } hyper-1.5.2/src/service/mod.rs000064400000000000000000000020461046102023000143360ustar 00000000000000//! Asynchronous Services //! //! A [`Service`] is a trait representing an asynchronous //! function of a request to a response. It's similar to //! `async fn(Request) -> Result`. //! //! The argument and return value isn't strictly required to be for HTTP. //! Therefore, hyper uses several "trait aliases" to reduce clutter around //! bounds. These are: //! //! - `HttpService`: This is blanketly implemented for all types that //! implement `Service, Response = http::Response>`. //! //! # HttpService //! //! In hyper, especially in the server setting, a `Service` is usually bound //! to a single connection. It defines how to respond to **all** requests that //! connection will receive. //! //! The helper [`service_fn`] should be sufficient for most cases, but //! if you need to implement `Service` for a type manually, you can follow the example //! in `service_struct_impl.rs`. mod http; mod service; mod util; pub use self::http::HttpService; pub use self::service::Service; pub use self::util::service_fn; hyper-1.5.2/src/service/service.rs000064400000000000000000000066161046102023000152260ustar 00000000000000use std::future::Future; /// An asynchronous function from a `Request` to a `Response`. /// /// The `Service` trait is a simplified interface making it easy to write /// network applications in a modular and reusable way, decoupled from the /// underlying protocol. /// /// # Functional /// /// A `Service` is a function of a `Request`. It immediately returns a /// `Future` representing the eventual completion of processing the /// request. The actual request processing may happen at any time in the /// future, on any thread or executor. The processing may depend on calling /// other services. At some point in the future, the processing will complete, /// and the `Future` will resolve to a response or error. /// /// At a high level, the `Service::call` function represents an RPC request. The /// `Service` value can be a server or a client. pub trait Service { /// Responses given by the service. type Response; /// Errors produced by the service. /// /// Note: Returning an `Error` to a hyper server, the behavior depends on the /// protocol. In most cases, hyper will cause the connection to be abruptly aborted. /// It will abort the request however the protocol allows, either with some sort of RST_STREAM, /// or killing the connection if that doesn't exist. type Error; /// The future response value. type Future: Future>; /// Process the request and return the response asynchronously. /// `call` takes `&self` instead of `mut &self` because: /// - It prepares the way for async fn, /// since then the future only borrows `&self`, and thus a Service can concurrently handle /// multiple outstanding requests at once. /// - It's clearer that Services can likely be cloned /// - To share state across clones, you generally need `Arc>` /// That means you're not really using the `&mut self` and could do with a `&self`. /// The discussion on this is here: fn call(&self, req: Request) -> Self::Future; } impl + ?Sized> Service for &'_ S { type Response = S::Response; type Error = S::Error; type Future = S::Future; #[inline] fn call(&self, req: Request) -> Self::Future { (**self).call(req) } } impl + ?Sized> Service for &'_ mut S { type Response = S::Response; type Error = S::Error; type Future = S::Future; #[inline] fn call(&self, req: Request) -> Self::Future { (**self).call(req) } } impl + ?Sized> Service for Box { type Response = S::Response; type Error = S::Error; type Future = S::Future; #[inline] fn call(&self, req: Request) -> Self::Future { (**self).call(req) } } impl + ?Sized> Service for std::rc::Rc { type Response = S::Response; type Error = S::Error; type Future = S::Future; #[inline] fn call(&self, req: Request) -> Self::Future { (**self).call(req) } } impl + ?Sized> Service for std::sync::Arc { type Response = S::Response; type Error = S::Error; type Future = S::Future; #[inline] fn call(&self, req: Request) -> Self::Future { (**self).call(req) } } hyper-1.5.2/src/service/util.rs000064400000000000000000000036411046102023000145360ustar 00000000000000use std::error::Error as StdError; use std::fmt; use std::future::Future; use std::marker::PhantomData; use crate::body::Body; use crate::service::service::Service; use crate::{Request, Response}; /// Create a `Service` from a function. /// /// # Example /// /// ``` /// use bytes::Bytes; /// use hyper::{body, Request, Response, Version}; /// use http_body_util::Full; /// use hyper::service::service_fn; /// /// let service = service_fn(|req: Request| async move { /// if req.version() == Version::HTTP_11 { /// Ok(Response::new(Full::::from("Hello World"))) /// } else { /// // Note: it's usually better to return a Response /// // with an appropriate StatusCode instead of an Err. /// Err("not HTTP/1.1, abort connection") /// } /// }); /// ``` pub fn service_fn(f: F) -> ServiceFn where F: Fn(Request) -> S, S: Future, { ServiceFn { f, _req: PhantomData, } } /// Service returned by [`service_fn`] pub struct ServiceFn { f: F, _req: PhantomData, } impl Service> for ServiceFn where F: Fn(Request) -> Ret, ReqBody: Body, Ret: Future, E>>, E: Into>, ResBody: Body, { type Response = crate::Response; type Error = E; type Future = Ret; fn call(&self, req: Request) -> Self::Future { (self.f)(req) } } impl fmt::Debug for ServiceFn { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("impl Service").finish() } } impl Clone for ServiceFn where F: Clone, { fn clone(&self) -> Self { ServiceFn { f: self.f.clone(), _req: PhantomData, } } } impl Copy for ServiceFn where F: Copy {} hyper-1.5.2/src/trace.rs000064400000000000000000000047671046102023000132310ustar 00000000000000// For completeness, wrappers around all of tracing's public logging and span macros are provided, // even if they are not used at the present time. #![allow(unused_macros)] #[cfg(all(not(hyper_unstable_tracing), feature = "tracing"))] compile_error!( "\ The `tracing` feature is unstable, and requires the \ `RUSTFLAGS='--cfg hyper_unstable_tracing'` environment variable to be set.\ " ); macro_rules! debug { ($($arg:tt)+) => { #[cfg(feature = "tracing")] { tracing::debug!($($arg)+); } } } macro_rules! debug_span { ($($arg:tt)*) => { { #[cfg(feature = "tracing")] { let _span = tracing::debug_span!($($arg)+); _span.entered() } } } } macro_rules! error { ($($arg:tt)*) => { #[cfg(feature = "tracing")] { tracing::error!($($arg)+); } } } macro_rules! error_span { ($($arg:tt)*) => { { #[cfg(feature = "tracing")] { let _span = tracing::error_span!($($arg)+); _span.entered() } } } } macro_rules! info { ($($arg:tt)*) => { #[cfg(feature = "tracing")] { tracing::info!($($arg)+); } } } macro_rules! info_span { ($($arg:tt)*) => { { #[cfg(feature = "tracing")] { let _span = tracing::info_span!($($arg)+); _span.entered() } } } } macro_rules! trace { ($($arg:tt)*) => { #[cfg(feature = "tracing")] { tracing::trace!($($arg)+); } } } macro_rules! trace_span { ($($arg:tt)*) => { { #[cfg(feature = "tracing")] { let _span = tracing::trace_span!($($arg)+); _span.entered() } } } } macro_rules! span { ($($arg:tt)*) => { { #[cfg(feature = "tracing")] { let _span = tracing::span!($($arg)+); _span.entered() } } } } macro_rules! warn { ($($arg:tt)*) => { #[cfg(feature = "tracing")] { tracing::warn!($($arg)+); } } } macro_rules! warn_span { ($($arg:tt)*) => { { #[cfg(feature = "tracing")] { let _span = tracing::warn_span!($($arg)+); _span.entered() } } } } hyper-1.5.2/src/upgrade.rs000064400000000000000000000270171046102023000135530ustar 00000000000000//! HTTP Upgrades //! //! This module deals with managing [HTTP Upgrades][mdn] in hyper. Since //! several concepts in HTTP allow for first talking HTTP, and then converting //! to a different protocol, this module conflates them into a single API. //! Those include: //! //! - HTTP/1.1 Upgrades //! - HTTP `CONNECT` //! //! You are responsible for any other pre-requisites to establish an upgrade, //! such as sending the appropriate headers, methods, and status codes. You can //! then use [`on`][] to grab a `Future` which will resolve to the upgraded //! connection object, or an error if the upgrade fails. //! //! [mdn]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Protocol_upgrade_mechanism //! //! # Client //! //! Sending an HTTP upgrade from the [`client`](super::client) involves setting //! either the appropriate method, if wanting to `CONNECT`, or headers such as //! `Upgrade` and `Connection`, on the `http::Request`. Once receiving the //! `http::Response` back, you must check for the specific information that the //! upgrade is agreed upon by the server (such as a `101` status code), and then //! get the `Future` from the `Response`. //! //! # Server //! //! Receiving upgrade requests in a server requires you to check the relevant //! headers in a `Request`, and if an upgrade should be done, you then send the //! corresponding headers in a response. To then wait for hyper to finish the //! upgrade, you call `on()` with the `Request`, and then can spawn a task //! awaiting it. //! //! # Example //! //! See [this example][example] showing how upgrades work with both //! Clients and Servers. //! //! [example]: https://github.com/hyperium/hyper/blob/master/examples/upgrades.rs use std::any::TypeId; use std::error::Error as StdError; use std::fmt; use std::future::Future; use std::io; use std::pin::Pin; use std::sync::{Arc, Mutex}; use std::task::{Context, Poll}; use crate::rt::{Read, ReadBufCursor, Write}; use bytes::Bytes; use tokio::sync::oneshot; use crate::common::io::Rewind; /// An upgraded HTTP connection. /// /// This type holds a trait object internally of the original IO that /// was used to speak HTTP before the upgrade. It can be used directly /// as a [`Read`] or [`Write`] for convenience. /// /// Alternatively, if the exact type is known, this can be deconstructed /// into its parts. pub struct Upgraded { io: Rewind>, } /// A future for a possible HTTP upgrade. /// /// If no upgrade was available, or it doesn't succeed, yields an `Error`. #[derive(Clone)] pub struct OnUpgrade { rx: Option>>>>, } /// The deconstructed parts of an [`Upgraded`] type. /// /// Includes the original IO type, and a read buffer of bytes that the /// HTTP state machine may have already read before completing an upgrade. #[derive(Debug)] #[non_exhaustive] pub struct Parts { /// The original IO object used before the upgrade. pub io: T, /// A buffer of bytes that have been read but not processed as HTTP. /// /// For instance, if the `Connection` is used for an HTTP upgrade request, /// it is possible the server sent back the first bytes of the new protocol /// along with the response upgrade. /// /// You will want to check for any existing bytes if you plan to continue /// communicating on the IO object. pub read_buf: Bytes, } /// Gets a pending HTTP upgrade from this message. /// /// This can be called on the following types: /// /// - `http::Request` /// - `http::Response` /// - `&mut http::Request` /// - `&mut http::Response` pub fn on(msg: T) -> OnUpgrade { msg.on_upgrade() } #[cfg(all( any(feature = "client", feature = "server"), any(feature = "http1", feature = "http2"), ))] pub(super) struct Pending { tx: oneshot::Sender>, } #[cfg(all( any(feature = "client", feature = "server"), any(feature = "http1", feature = "http2"), ))] pub(super) fn pending() -> (Pending, OnUpgrade) { let (tx, rx) = oneshot::channel(); ( Pending { tx }, OnUpgrade { rx: Some(Arc::new(Mutex::new(rx))), }, ) } // ===== impl Upgraded ===== impl Upgraded { #[cfg(all( any(feature = "client", feature = "server"), any(feature = "http1", feature = "http2") ))] pub(super) fn new(io: T, read_buf: Bytes) -> Self where T: Read + Write + Unpin + Send + 'static, { Upgraded { io: Rewind::new_buffered(Box::new(io), read_buf), } } /// Tries to downcast the internal trait object to the type passed. /// /// On success, returns the downcasted parts. On error, returns the /// `Upgraded` back. pub fn downcast(self) -> Result, Self> { let (io, buf) = self.io.into_inner(); match io.__hyper_downcast() { Ok(t) => Ok(Parts { io: *t, read_buf: buf, }), Err(io) => Err(Upgraded { io: Rewind::new_buffered(io, buf), }), } } } impl Read for Upgraded { fn poll_read( mut self: Pin<&mut Self>, cx: &mut Context<'_>, buf: ReadBufCursor<'_>, ) -> Poll> { Pin::new(&mut self.io).poll_read(cx, buf) } } impl Write for Upgraded { fn poll_write( mut self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8], ) -> Poll> { Pin::new(&mut self.io).poll_write(cx, buf) } fn poll_write_vectored( mut self: Pin<&mut Self>, cx: &mut Context<'_>, bufs: &[io::IoSlice<'_>], ) -> Poll> { Pin::new(&mut self.io).poll_write_vectored(cx, bufs) } fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { Pin::new(&mut self.io).poll_flush(cx) } fn poll_shutdown(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { Pin::new(&mut self.io).poll_shutdown(cx) } fn is_write_vectored(&self) -> bool { self.io.is_write_vectored() } } impl fmt::Debug for Upgraded { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("Upgraded").finish() } } // ===== impl OnUpgrade ===== impl OnUpgrade { pub(super) fn none() -> Self { OnUpgrade { rx: None } } #[cfg(all(any(feature = "client", feature = "server"), feature = "http1"))] pub(super) fn is_none(&self) -> bool { self.rx.is_none() } } impl Future for OnUpgrade { type Output = Result; fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { match self.rx { Some(ref rx) => Pin::new(&mut *rx.lock().unwrap()) .poll(cx) .map(|res| match res { Ok(Ok(upgraded)) => Ok(upgraded), Ok(Err(err)) => Err(err), Err(_oneshot_canceled) => { Err(crate::Error::new_canceled().with(UpgradeExpected)) } }), None => Poll::Ready(Err(crate::Error::new_user_no_upgrade())), } } } impl fmt::Debug for OnUpgrade { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("OnUpgrade").finish() } } // ===== impl Pending ===== #[cfg(all( any(feature = "client", feature = "server"), any(feature = "http1", feature = "http2") ))] impl Pending { pub(super) fn fulfill(self, upgraded: Upgraded) { trace!("pending upgrade fulfill"); let _ = self.tx.send(Ok(upgraded)); } #[cfg(feature = "http1")] /// Don't fulfill the pending Upgrade, but instead signal that /// upgrades are handled manually. pub(super) fn manual(self) { #[cfg(any(feature = "http1", feature = "http2"))] trace!("pending upgrade handled manually"); let _ = self.tx.send(Err(crate::Error::new_user_manual_upgrade())); } } // ===== impl UpgradeExpected ===== /// Error cause returned when an upgrade was expected but canceled /// for whatever reason. /// /// This likely means the actual `Conn` future wasn't polled and upgraded. #[derive(Debug)] struct UpgradeExpected; impl fmt::Display for UpgradeExpected { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str("upgrade expected but not completed") } } impl StdError for UpgradeExpected {} // ===== impl Io ===== pub(super) trait Io: Read + Write + Unpin + 'static { fn __hyper_type_id(&self) -> TypeId { TypeId::of::() } } impl Io for T {} impl dyn Io + Send { fn __hyper_is(&self) -> bool { let t = TypeId::of::(); self.__hyper_type_id() == t } fn __hyper_downcast(self: Box) -> Result, Box> { if self.__hyper_is::() { // Taken from `std::error::Error::downcast()`. unsafe { let raw: *mut dyn Io = Box::into_raw(self); Ok(Box::from_raw(raw as *mut T)) } } else { Err(self) } } } mod sealed { use super::OnUpgrade; pub trait CanUpgrade { fn on_upgrade(self) -> OnUpgrade; } impl CanUpgrade for http::Request { fn on_upgrade(mut self) -> OnUpgrade { self.extensions_mut() .remove::() .unwrap_or_else(OnUpgrade::none) } } impl CanUpgrade for &'_ mut http::Request { fn on_upgrade(self) -> OnUpgrade { self.extensions_mut() .remove::() .unwrap_or_else(OnUpgrade::none) } } impl CanUpgrade for http::Response { fn on_upgrade(mut self) -> OnUpgrade { self.extensions_mut() .remove::() .unwrap_or_else(OnUpgrade::none) } } impl CanUpgrade for &'_ mut http::Response { fn on_upgrade(self) -> OnUpgrade { self.extensions_mut() .remove::() .unwrap_or_else(OnUpgrade::none) } } } #[cfg(all( any(feature = "client", feature = "server"), any(feature = "http1", feature = "http2"), ))] #[cfg(test)] mod tests { use super::*; #[test] fn upgraded_downcast() { let upgraded = Upgraded::new(Mock, Bytes::new()); let upgraded = upgraded .downcast::>>>() .unwrap_err(); upgraded.downcast::().unwrap(); } // TODO: replace with tokio_test::io when it can test write_buf struct Mock; impl Read for Mock { fn poll_read( self: Pin<&mut Self>, _cx: &mut Context<'_>, _buf: ReadBufCursor<'_>, ) -> Poll> { unreachable!("Mock::poll_read") } } impl Write for Mock { fn poll_write( self: Pin<&mut Self>, _: &mut Context<'_>, buf: &[u8], ) -> Poll> { // panic!("poll_write shouldn't be called"); Poll::Ready(Ok(buf.len())) } fn poll_flush(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll> { unreachable!("Mock::poll_flush") } fn poll_shutdown(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll> { unreachable!("Mock::poll_shutdown") } } }