diff --git a/CHANGELOG.md b/CHANGELOG.md index 727797f..b683a10 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,30 +1,60 @@ # Unreleased (Yet) +# v1.10.0 (2023-12-11) + +## ttfb lib + +- **BREAKING** Signature of `InvalidUrlError::WrongScheme` changed to `WrongScheme(String)`. +- removed dependency to `regex` + +## ttfb binary + +- reduced binary size from 4.5MB to 3.5MB (release) + + # v1.9.1 (2023-11-30) -- Improved `--help`. +## ttfb lib + +## ttfb binary + +- Improved `--help` output. + # v1.9.0 (2023-11-30) +## ttfb lib + - **BREAKING** The MSRV of the library is `1.65.0` stable. - The dependency requirements are now less strict. +## ttfb binary + + # v1.8.0 (2023-11-14) + +## ttfb lib + - `ttfb` can no longer panic when `resolv.conf` cannot be found: Huge thanks to _Firaenix_: https://github.com/phip1611/ttfb/pull/26 - **BREAKING** `TtfbError::CantConnectTls`'s inner type has switched from `native_tls::Error` to `rustls_connector::HandshakeError` - **MAYBE BREAKING** Introduced new `TtfbError::CantConfigureDNSError` variant +- The lib no longer depends on `openssl` but only on `rustls` + +## ttfb binary + - The binary is now smaller; it is stripped and uses LTO. This shrinks the size from roughly 14MiB to 4MiB (release build). -- The binary no longer depends on `openssl` but only on `rustls` + # v1.7.0 (2023-09-22) + - **BREAKING** The MSRV of the library is `1.64.0` stable. - **BREAKING** The MSRV of the binary is `1.70.0` stable. - introduced new `DurationPair` struct - **BREAKING** replaced several getters - - replaced `TtfbOutcome::dns_duration_rel` and `TtfbOutcome::dns_duration_abs` +- - replaced `TtfbOutcome::dns_duration_rel` and `TtfbOutcome::dns_duration_abs` with `TtfbOutcome::dns_lookup_duration` which returns a `DurationPair` - replaced `TtfbOutcome::tcp_connect_duration_rel` and `TtfbOutcome::tcp_connect_duration_abs` with `TtfbOutcome::tcp_connect_duration` which returns a `DurationPair` @@ -37,35 +67,49 @@ - dependencies updated - added `TtfbError::NoHttpResponse` + # v1.6.0 (2023-01-26) + - MSRV of the binary is now 1.64.0 - MSRV of the library is 1.57.0 + # v1.5.1 (2022-12-01) + - minor internal improvement + # v1.5.0 (2022-12-01) + - updated dependencies - the MSRV is 1.60.0 for the CLI utility (binary) but still 1.56.1 if you use this crate as library. + # v1.4.0 (2022-06-09) + - small **breaking** change: import paths of `ttfb::outcome::TtfbOutcome` and `ttfb::error::TtfbError` were flattened to `ttfb::{TtfbError, TtfbOutcome}` - small internal code and documentation improvements + # v1.3.1 (2022-03-22) + - bugfix, also allow https for IP-Addresses (`$ ttfb https://1.1.1.1` is valid) - updated dependencies + # v1.3.0 (2022-01-19) + - improved code quality - improved doc - updated dependencies - Rust edition 2021 - MSRV is 1.56.1 stable + # v1.2.0 (2021-07-16) + - added `-k/--insecure` to CLI - added `allow_insecure_certificates` as second parameter to library function @@ -78,21 +122,31 @@ You can also type `$ ttfb --help` now. CLI parsing is backed up by the crate `clap` now. + # v1.1.2 (2021-07-13) + - Typo in README + # v1.1.1 (2021-07-12) + - better error handling - call flush to make sure all the streams are actually committed + # v1.1.0 (2021-07-10) + - better output of CLI - removed Display-trait for struct `TtfbOutcome` - all times are given relative and total + # v1.0.1 (2021-07-09) + - removed "termion" dependency -- cross platform now (Linux, Mac, Windows) +- cross-platform now (Linux, Mac, Windows) + # v1.0.0 (2021-07-09) + - initial release diff --git a/Cargo.lock b/Cargo.lock index 795760d..2610689 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17,20 +17,11 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" -[[package]] -name = "aho-corasick" -version = "1.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" -dependencies = [ - "memchr", -] - [[package]] name = "anstream" -version = "0.6.4" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ab91ebe16eb252986481c5b62f6098f3b698a45e34b5b98200cf20dd2484a44" +checksum = "d664a92ecae85fd0a7392615844904654d1d5f5514837f471ddef4a057aba1b6" dependencies = [ "anstyle", "anstyle-parse", @@ -48,30 +39,30 @@ checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87" [[package]] name = "anstyle-parse" -version = "0.2.2" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "317b9a89c1868f5ea6ff1d9539a69f45dffc21ce321ac1fd1160dfa48c8e2140" +checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" -version = "1.0.0" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" +checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648" dependencies = [ - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] name = "anstyle-wincon" -version = "3.0.1" +version = "3.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0699d10d2f4d628a98ee7b57b289abbc98ff3bad977cb3152709d4bf2330628" +checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7" dependencies = [ "anstyle", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -82,7 +73,7 @@ checksum = "a66537f1bb974b254c98ed142ff995236e81b9d0fe4db0575f46612cb15eb0f9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.40", ] [[package]] @@ -147,9 +138,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "clap" -version = "4.4.10" +version = "4.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41fffed7514f420abec6d183b1d3acfd9099c79c3a10a06ade4f8203f1411272" +checksum = "bfaff671f6b22ca62406885ece523383b9b64022e341e53e009a62ebc47a45f2" dependencies = [ "clap_builder", "clap_derive", @@ -157,9 +148,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.4.9" +version = "4.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63361bae7eef3771745f02d8d892bec2fee5f6e34af316ba556e7f97a7069ff1" +checksum = "a216b506622bb1d316cd51328dce24e07bdff4a6128a47c7e7fad11878d5adbb" dependencies = [ "anstream", "anstyle", @@ -179,7 +170,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.40", ] [[package]] @@ -196,9 +187,9 @@ checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" [[package]] name = "core-foundation" -version = "0.9.3" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" dependencies = [ "core-foundation-sys", "libc", @@ -206,9 +197,9 @@ dependencies = [ [[package]] name = "core-foundation-sys" -version = "0.8.4" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" +checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" [[package]] name = "crossterm" @@ -261,7 +252,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.40", ] [[package]] @@ -403,9 +394,9 @@ checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" [[package]] name = "libc" -version = "0.2.150" +version = "0.2.151" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c" +checksum = "302d7ab3130588088d277783b1e2d2e10c9e9e4a16dd9050e6ec93fb3e7048f4" [[package]] name = "linked-hash-map" @@ -415,9 +406,9 @@ checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" [[package]] name = "linux-raw-sys" -version = "0.4.11" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "969488b55f8ac402214f3f5fd243ebb7206cf82de60d3172994707a4bcc2b829" +checksum = "c4cd1a83af159aa67994778be9070f0ae1bd732942279cabb14f86f986a21456" [[package]] name = "lock_api" @@ -467,9 +458,9 @@ dependencies = [ [[package]] name = "mio" -version = "0.8.9" +version = "0.8.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3dce281c5e46beae905d4de1870d8b1509a9142b62eedf18b443b011ca8343d0" +checksum = "8f3d0b296e374a4e6f3c7b0a1f5a51d748a0d34c85e7dc48fc3fa9a87657fe09" dependencies = [ "libc", "log", @@ -498,9 +489,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.18.0" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] name = "openssl-probe" @@ -618,35 +609,6 @@ dependencies = [ "bitflags 1.3.2", ] -[[package]] -name = "regex" -version = "1.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343" -dependencies = [ - "aho-corasick", - "memchr", - "regex-automata", - "regex-syntax", -] - -[[package]] -name = "regex-automata" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f" -dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax", -] - -[[package]] -name = "regex-syntax" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" - [[package]] name = "resolv-conf" version = "0.7.0" @@ -659,9 +621,9 @@ dependencies = [ [[package]] name = "ring" -version = "0.17.6" +version = "0.17.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "684d5e6e18f669ccebf64a92236bb7db9a34f07be010e3627368182027180866" +checksum = "688c63d65483050968b2a8937f7995f443e27041a0f7700aa59b0822aedebb74" dependencies = [ "cc", "getrandom", @@ -679,50 +641,66 @@ checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" [[package]] name = "rustix" -version = "0.38.25" +version = "0.38.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc99bc2d4f1fed22595588a013687477aedf3cdcfb26558c559edb67b4d9b22e" +checksum = "72e572a5e8ca657d7366229cdde4bd14c4eb5499a9573d4d366fe1b599daa316" dependencies = [ "bitflags 2.4.1", "errno", "libc", "linux-raw-sys", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] name = "rustls" -version = "0.21.9" +version = "0.21.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "629648aced5775d558af50b2b4c7b02983a04b312126d45eeead26e7caa498b9" +checksum = "f9d5a6813c0759e4609cd494e8e725babae6a2ca7b62a5536a13daaec6fcb7ba" dependencies = [ "log", "ring", - "rustls-webpki", + "rustls-webpki 0.101.7", "sct", ] +[[package]] +name = "rustls" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe6b63262c9fcac8659abfaa96cac103d28166d3ff3eaf8f412e19f3ae9e5a48" +dependencies = [ + "log", + "ring", + "rustls-pki-types", + "rustls-webpki 0.102.0", + "subtle", + "zeroize", +] + [[package]] name = "rustls-connector" -version = "0.18.5" +version = "0.19.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25da151615461c7347114b1ad1a7458b4cdebc69cb220cd140cd5cb324b1dd37" +checksum = "b5bd40675c79c896f46d0031bf64c448b35e583dd2bc949751ddd800351e453a" dependencies = [ "log", - "rustls", + "rustls 0.22.1", "rustls-native-certs", - "rustls-webpki", - "webpki-roots", + "rustls-pki-types", + "rustls-webpki 0.102.0", + "webpki-roots 0.26.0", ] [[package]] name = "rustls-native-certs" -version = "0.6.3" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9aace74cb666635c918e9c12bc0d348266037aa8eb599b5cba565709a8dff00" +checksum = "8f1fb85efa936c42c6d5fc28d2629bb51e4b2f4b8a5211e297d599cc5a093792" dependencies = [ "openssl-probe", - "rustls-pemfile", + "rustls-pemfile 2.0.0", + "rustls-pki-types", "schannel", "security-framework", ] @@ -736,6 +714,22 @@ dependencies = [ "base64", ] +[[package]] +name = "rustls-pemfile" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35e4980fa29e4c4b212ffb3db068a564cbf560e51d3944b7c88bd8bf5bec64f4" +dependencies = [ + "base64", + "rustls-pki-types", +] + +[[package]] +name = "rustls-pki-types" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7673e0aa20ee4937c6aacfc12bb8341cfbf054cdd21df6bec5fd0629fe9339b" + [[package]] name = "rustls-webpki" version = "0.101.7" @@ -746,6 +740,17 @@ dependencies = [ "untrusted", ] +[[package]] +name = "rustls-webpki" +version = "0.102.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de2635c8bc2b88d367767c5de8ea1d8db9af3f6219eba28442242d9ab81d1b89" +dependencies = [ + "ring", + "rustls-pki-types", + "untrusted", +] + [[package]] name = "schannel" version = "0.1.22" @@ -861,6 +866,12 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" +[[package]] +name = "subtle" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" + [[package]] name = "syn" version = "1.0.109" @@ -874,9 +885,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.39" +version = "2.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a" +checksum = "13fa70a4ee923979ffb522cacce59d34421ebdea5625e1073c4326ef9d2dd42e" dependencies = [ "proc-macro2", "quote", @@ -910,7 +921,7 @@ checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.40", ] [[package]] @@ -930,9 +941,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.34.0" +version = "1.35.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0c014766411e834f7af5b8f4cf46257aab4036ca95e9d2c144a10f59ad6f5b9" +checksum = "841d45b238a16291a4e1584e61820b8ae57d696cc5015c459c229ccc6990cc1c" dependencies = [ "backtrace", "bytes", @@ -950,7 +961,7 @@ version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" dependencies = [ - "rustls", + "rustls 0.21.10", "tokio", ] @@ -973,7 +984,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.40", ] [[package]] @@ -1002,9 +1013,9 @@ dependencies = [ "ipnet", "once_cell", "rand", - "rustls", - "rustls-pemfile", - "rustls-webpki", + "rustls 0.21.10", + "rustls-pemfile 1.0.4", + "rustls-webpki 0.101.7", "smallvec", "thiserror", "tinyvec", @@ -1028,25 +1039,24 @@ dependencies = [ "parking_lot", "rand", "resolv-conf", - "rustls", + "rustls 0.21.10", "smallvec", "thiserror", "tokio", "tokio-rustls", "tracing", "trust-dns-proto", - "webpki-roots", + "webpki-roots 0.25.3", ] [[package]] name = "ttfb" -version = "1.9.1" +version = "1.10.0" dependencies = [ "clap", "crossterm", "derive_more", - "regex", - "rustls", + "rustls 0.22.1", "rustls-connector", "trust-dns-resolver", "url", @@ -1063,9 +1073,9 @@ dependencies = [ [[package]] name = "unicode-bidi" -version = "0.3.13" +version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" +checksum = "6f2528f27a9eb2b21e69c95319b30bd0efd85d09c379741b0f78ea1d86be2416" [[package]] name = "unicode-ident" @@ -1129,6 +1139,15 @@ version = "0.25.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1778a42e8b3b90bff8d0f5032bf22250792889a5cdc752aa0020c84abe3aaf10" +[[package]] +name = "webpki-roots" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0de2cfda980f21be5a7ed2eadb3e6fe074d56022bea2cdeb1a62eb220fc04188" +dependencies = [ + "rustls-pki-types", +] + [[package]] name = "widestring" version = "1.0.2" @@ -1298,3 +1317,9 @@ dependencies = [ "cfg-if", "windows-sys 0.48.0", ] + +[[package]] +name = "zeroize" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d" diff --git a/Cargo.toml b/Cargo.toml index 4f7993f..d14f9bc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,9 +5,9 @@ Library + CLI utility to measure the TTFB (time to first byte) of HTTP(S) requests. This includes data of intermediate steps, such as the relative and absolute timings of DNS lookup, TCP connect, and TLS handshake. """ -version = "1.9.1" +version = "1.10.0" edition = "2021" -rust-version = "1.65" # MSRV of the library +rust-version = "1.65" # MSRV of the library (the MSRV that is smaller) keywords = ["ttfb", "http", "timings", "web"] categories = ["network-programming", "command-line-utilities"] readme = "README.md" @@ -40,8 +40,8 @@ bin = ["clap", "crossterm"] # DNS over systems default DNS resolver trust-dns-resolver = { version = "0.23.0", features = ["dns-over-rustls"] } # TLS handshake -rustls = { version = "0.21.0", features = ["dangerous_configuration"] } -rustls-connector = { version = "0.18.0", features = [ +rustls = { version = "0.22.0", features = [] } +rustls-connector = { version = "0.19.0", features = [ "rustls-native-certs", "webpki-roots", "webpki-roots-certs", @@ -52,7 +52,6 @@ derive_more = { version = "0.99.0", default-features = false, features = [ ] } # nice abstraction of URL url = "2.5.0" -regex = "1.10.0" # +++ BINARY +++ # used for the binary, not the lib diff --git a/src/error.rs b/src/error.rs index b064402..7ce4eca 100644 --- a/src/error.rs +++ b/src/error.rs @@ -61,9 +61,9 @@ pub enum InvalidUrlError { /// The URL is illegal. #[display(fmt = "The URL is illegal because: {}", _0)] WrongFormat(String), - /// This tools only supports http and https. - #[display(fmt = "This tools only supports http and https.")] - WrongScheme, + /// Wrong scheme. Only supports http and https. + #[display(fmt = "Wrong scheme '{}://': Only supports http and https.", _0)] + WrongScheme(String), /// Other unknown error. #[display(fmt = "Other unknown error.")] Other, diff --git a/src/lib.rs b/src/lib.rs index 2a71f60..c42daa6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -57,9 +57,9 @@ SOFTWARE. pub use error::{InvalidUrlError, ResolveDnsError, TtfbError}; pub use outcome::{DurationPair, TtfbOutcome}; -use regex::Regex; -use rustls::client::{ServerCertVerified, ServerCertVerifier}; -use rustls::{Certificate, ClientConfig}; +use rustls::client::danger::{HandshakeSignatureValid, ServerCertVerified, ServerCertVerifier}; +use rustls::pki_types::{CertificateDer, ServerName, UnixTime}; +use rustls::{ClientConfig, DigitallySignedStruct, Error, SignatureScheme}; use rustls_connector::RustlsConnector; use std::io::{Read as IoRead, Write as IoWrite}; use std::net::{IpAddr, TcpStream}; @@ -82,12 +82,12 @@ trait IoReadAndWrite: IoWrite + IoRead {} impl IoReadAndWrite for T {} -/// Takes a URL and connects to it via http/1.1. Measures time for -/// DNS lookup, TCP connection start, TLS handshake, and TTFB (Time to First Byte) -/// of HTML content. +/// Takes a URL and connects to it via http/1.1. Measures time for DNS lookup, +/// TCP connection start, TLS handshake, and TTFB (Time to First Byte) of HTML +/// content. /// /// ## Parameters -/// - `input`: Url. Can be one of +/// - `input`: URL pointing to a HTTP server. Can be one of /// - `phip1611.de` (defaults to `http://`) /// - `http://phip1611.de` /// - `https://phip1611.de` @@ -97,18 +97,25 @@ impl IoReadAndWrite for T {} /// - `https://1.1.1.1` /// - `12.34.56.78/foobar` (defaults to `http://`) /// - `12.34.56.78` (defaults to `http://`) -/// - `allow_insecure_certificates`: if illegal certificates (untrusted, expired) should be accepted -/// when https is used. Similar to `-k/--insecure` in `curl`. +/// - `allow_insecure_certificates`: if illegal certificates (untrusted, +/// expired) should be accepted when https is used. Similar to +/// `-k/--insecure` in `curl`. /// /// ## Return value /// [`TtfbOutcome`] or [`TtfbError`]. -pub fn ttfb(input: String, allow_insecure_certificates: bool) -> Result { +pub fn ttfb( + input: impl AsRef, + allow_insecure_certificates: bool, +) -> Result { + let input = input.as_ref(); if input.is_empty() { return Err(TtfbError::InvalidUrl(InvalidUrlError::MissingInput)); } + let input = input.to_string(); let input = prepend_default_scheme_if_necessary(input); let url = parse_input_as_url(&input)?; - assert_scheme_is_allowed(&url)?; + // println!("final url: {}", url); + check_scheme_is_allowed(&url)?; let (addr, dns_duration) = resolve_dns_if_necessary(&url)?; let port = url.port_or_known_default().unwrap(); @@ -154,7 +161,7 @@ fn tls_handshake_if_necessary( if url.scheme() == "https" { let connector: RustlsConnector = if allow_insecure_certificates { ClientConfig::builder() - .with_safe_defaults() + .dangerous() .with_custom_certificate_verifier(Arc::new(AllowInvalidCertsVerifier)) .with_no_client_auth() .into() @@ -186,15 +193,51 @@ pub struct AllowInvalidCertsVerifier; impl ServerCertVerifier for AllowInvalidCertsVerifier { fn verify_server_cert( &self, - _end_entity: &Certificate, - _intermediates: &[Certificate], - _server_name: &rustls::ServerName, - _scts: &mut dyn Iterator, + _end_entity: &CertificateDer<'_>, + _intermediates: &[CertificateDer<'_>], + _server_name: &ServerName<'_>, _ocsp_response: &[u8], - _now: std::time::SystemTime, - ) -> Result { + _now: UnixTime, + ) -> Result { Ok(ServerCertVerified::assertion()) } + + fn verify_tls12_signature( + &self, + _message: &[u8], + _cert: &CertificateDer<'_>, + _dss: &DigitallySignedStruct, + ) -> Result { + Ok(HandshakeSignatureValid::assertion()) + } + + fn verify_tls13_signature( + &self, + _message: &[u8], + _cert: &CertificateDer<'_>, + _dss: &DigitallySignedStruct, + ) -> Result { + Ok(HandshakeSignatureValid::assertion()) + } + + fn supported_verify_schemes(&self) -> Vec { + // Return a list of all. + vec![ + SignatureScheme::RSA_PKCS1_SHA1, + SignatureScheme::ECDSA_SHA1_Legacy, + SignatureScheme::RSA_PKCS1_SHA256, + SignatureScheme::ECDSA_NISTP256_SHA256, + SignatureScheme::RSA_PKCS1_SHA384, + SignatureScheme::ECDSA_NISTP384_SHA384, + SignatureScheme::RSA_PKCS1_SHA512, + SignatureScheme::ECDSA_NISTP521_SHA512, + SignatureScheme::RSA_PSS_SHA256, + SignatureScheme::RSA_PSS_SHA384, + SignatureScheme::RSA_PSS_SHA512, + SignatureScheme::ED25519, + SignatureScheme::ED448, + ] + } } /// Executes the HTTP/1.1 GET-Request on the given socket. This works with TCP or `TLS`. @@ -251,27 +294,27 @@ fn parse_input_as_url(input: &str) -> Result { .map_err(|e| TtfbError::InvalidUrl(InvalidUrlError::WrongFormat(e.to_string()))) } -/// Prepends the default scheme "http://" is necessary. Without a scheme, [`parse_input_as_url`] -/// will fail. +/// Prepends the default scheme `http://` is necessary to the user input. fn prepend_default_scheme_if_necessary(url: String) -> String { - let regex = Regex::new("^(?P.*://)?").unwrap(); - let captures = regex.captures(&url); - if let Some(captures) = captures { - if captures.name("scheme").is_some() { - return url; - } - } + const SCHEME_SEPARATOR: &str = "://"; + const DEFAULT_SCHEME: &str = "http"; - format!("http://{}", url) + (!url.contains(SCHEME_SEPARATOR)) + .then(|| format!("{DEFAULT_SCHEME}://{url}")) + .unwrap_or(url) } -/// Assert the scheme is on the allow list. Currently, we only allow "http" and "https". -fn assert_scheme_is_allowed(url: &Url) -> Result<(), TtfbError> { - let allowed_scheme = url.scheme() == "http" || url.scheme() == "https"; +/// Checks the scheme is on the allow list. Currently, we only allow "http" +/// and "https". +fn check_scheme_is_allowed(url: &Url) -> Result<(), TtfbError> { + let actual_scheme = url.scheme(); + let allowed_scheme = actual_scheme == "http" || actual_scheme == "https"; if allowed_scheme { Ok(()) } else { - Err(TtfbError::InvalidUrl(InvalidUrlError::WrongScheme)) + Err(TtfbError::InvalidUrl(InvalidUrlError::WrongScheme( + actual_scheme.to_string(), + ))) } } @@ -379,21 +422,21 @@ mod tests { #[test] fn test_check_scheme() { - assert_scheme_is_allowed( + check_scheme_is_allowed( &Url::from_str(&prepend_default_scheme_if_necessary( "phip1611.de".to_owned(), )) .unwrap(), ) .expect("must accept http"); - assert_scheme_is_allowed( + check_scheme_is_allowed( &Url::from_str(&prepend_default_scheme_if_necessary( "https://phip1611.de".to_owned(), )) .unwrap(), ) .expect("must accept http"); - assert_scheme_is_allowed( + check_scheme_is_allowed( &Url::from_str(&prepend_default_scheme_if_necessary( "ftp://phip1611.de".to_owned(), ))