diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 4fc852db..937b7f49 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -11,6 +11,7 @@ on: env: CARGO_TERM_COLOR: always + RUST_BACKTRACE: 1 jobs: check-style: diff --git a/CHANGELOG.adoc b/CHANGELOG.adoc index 1c1cd048..1091346d 100644 --- a/CHANGELOG.adoc +++ b/CHANGELOG.adoc @@ -15,6 +15,54 @@ https://github.com/oxidecomputer/dropshot/compare/v0.12.0\...HEAD[Full list of commits] +* https://github.com/oxidecomputer/dropshot/pull/1122[#1122] Adds a new `ServerBuilder` as the primary way of constructing a Dropshot server. This replaces `HttpServerStarter::new()` and `HttpServerStarter::new_with_tls()`. These older functions still exist for compatibility. They may be removed in an upcoming release, along with the `HttpServerStarter`. ++ +In this release, using the builder interface is not very different from using these older functions. But as we look at adding new construction-time options (e.g., for API versioning), those will only be added to the builder. ++ +The builder also provides structured errors rather than the `GenericError` provided by these older functions. ++ +Most non-TLS callers were using `HttpServerStarter::new()` and then calling `start()` right away. In that case, you can replace: ++ +```rust +HttpServerStarter::new(&config, api, private, &log).map_err(...)?.start() +``` ++ +with: ++ +```rust +ServerBuilder::new(api, private, log).config(config).start().map_err(...)? +``` ++ +If you were using `HttpServerStarter::new_with_tls()`, you'd similarly replace: ++ +```rust +HttpServerStarter::new_with_tls(&config, api, private, &log, tls).map_err(...)?.start() +``` ++ +with: ++ +```rust +ServerBuilder::new(api, private, log).config(config).tls(tls).start().map_err(...)? +``` ++ +If you were _not_ invoking `start()` immediately before, you can still construct an intermediate starter object with `build_starter()`. If you were doing this: ++ +```rust +let starter = HttpServerStarter::new(&config, api, private, &log).map_err(...)?; +... +starter.start() +``` ++ +Then you can now do: ++ +```rust +let starter = ServerBuilder::new(api, private, log).config(config).build_starter().map_err(...)?; +... +starter.start() +``` ++ +We'd like to remove the `HttpServerStarter` altogether, so let us know if you're still using it for some reason. + == 0.12.0 (released 2024-09-26) https://github.com/oxidecomputer/dropshot/compare/v0.11.0\...v0.12.0[Full list of commits] diff --git a/Cargo.lock b/Cargo.lock index ba5cef1a..50fdf093 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -69,7 +69,7 @@ checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn", ] [[package]] @@ -80,7 +80,7 @@ checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn", ] [[package]] @@ -198,7 +198,7 @@ dependencies = [ "iana-time-zone", "num-traits", "serde", - "windows-targets 0.52.0", + "windows-targets 0.52.6", ] [[package]] @@ -365,7 +365,7 @@ dependencies = [ "http", "http-body-util", "hyper", - "hyper-rustls", + "hyper-rustls 0.26.0", "hyper-staticfile", "hyper-util", "indexmap", @@ -378,7 +378,8 @@ dependencies = [ "pem", "percent-encoding", "rcgen", - "rustls", + "reqwest", + "rustls 0.22.4", "rustls-pemfile", "rustls-pki-types", "schemars", @@ -398,7 +399,7 @@ dependencies = [ "tempfile", "thiserror", "tokio", - "tokio-rustls", + "tokio-rustls 0.25.0", "tokio-tungstenite", "toml", "trybuild", @@ -420,7 +421,7 @@ dependencies = [ "schema", "serde", "serde_tokenstream", - "syn 2.0.79", + "syn", ] [[package]] @@ -515,6 +516,21 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + [[package]] name = "form_urlencoded" version = "1.2.1" @@ -580,7 +596,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn", ] [[package]] @@ -809,14 +825,32 @@ dependencies = [ "hyper", "hyper-util", "log", - "rustls", + "rustls 0.22.4", "rustls-native-certs", "rustls-pki-types", "tokio", - "tokio-rustls", + "tokio-rustls 0.25.0", "tower-service", ] +[[package]] +name = "hyper-rustls" +version = "0.27.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08afdbb5c31130e3034af566421053ab03787c640246a446327f550d11bcb333" +dependencies = [ + "futures-util", + "http", + "hyper", + "hyper-util", + "rustls 0.23.13", + "rustls-pki-types", + "tokio", + "tokio-rustls 0.26.0", + "tower-service", + "webpki-roots", +] + [[package]] name = "hyper-staticfile" version = "0.10.1" @@ -836,6 +870,22 @@ dependencies = [ "winapi", ] +[[package]] +name = "hyper-tls" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" +dependencies = [ + "bytes", + "http-body-util", + "hyper", + "hyper-util", + "native-tls", + "tokio", + "tokio-native-tls", + "tower-service", +] + [[package]] name = "hyper-util" version = "0.1.9" @@ -871,11 +921,10 @@ dependencies = [ [[package]] name = "idna" -version = "0.2.3" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8" +checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" dependencies = [ - "matches", "unicode-bidi", "unicode-normalization", ] @@ -891,6 +940,12 @@ dependencies = [ "serde", ] +[[package]] +name = "ipnet" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "187674a687eed5fe42285b40c6291f9a01517d415fad1c3cbc6a9f778af7fcd4" + [[package]] name = "is-terminal" version = "0.4.12" @@ -959,12 +1014,6 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ffbee8634e0d45d258acb448e7eaab3fce7a0a467395d4d9f228e3c1f01fb2e4" -[[package]] -name = "matches" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f" - [[package]] name = "memchr" version = "2.6.0" @@ -1034,6 +1083,23 @@ dependencies = [ "version_check", ] +[[package]] +name = "native-tls" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8614eb2c83d59d1c8cc974dd3f920198647674a0a035e1af1fa58707e317466" +dependencies = [ + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + [[package]] name = "newline-converter" version = "0.3.0" @@ -1088,9 +1154,12 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.13.1" +version = "1.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "074864da206b4973b84eb91683020dbefd6a8c3f0f38e054d93954e891935e4e" +checksum = "82881c4be219ab5faaf2ad5e5e5ecdff8c66bd7402ca3160975c93b24961afd1" +dependencies = [ + "portable-atomic", +] [[package]] name = "openapiv3" @@ -1103,12 +1172,50 @@ dependencies = [ "serde_json", ] +[[package]] +name = "openssl" +version = "0.10.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9529f4786b70a3e8c61e11179af17ab6188ad8d0ded78c5529441ed39d4bd9c1" +dependencies = [ + "bitflags 2.4.0", + "cfg-if", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "openssl-probe" version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "28988d872ab76095a6e6ac88d99b54fd267702734fd7ffe610ca27f533ddb95a" +[[package]] +name = "openssl-sys" +version = "0.9.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f9e8deee91df40a943c71b917e5874b951d32a802526c85721ce3b776c929d6" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + [[package]] name = "parking" version = "2.2.0" @@ -1191,7 +1298,7 @@ dependencies = [ "pest_meta", "proc-macro2", "quote", - "syn 2.0.79", + "syn", ] [[package]] @@ -1217,12 +1324,24 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +[[package]] +name = "pkg-config" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" + [[package]] name = "plain" version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" +[[package]] +name = "portable-atomic" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc9c68a3f6da06753e9335d63e27f6b9754dd1920d941135b7ea8224f141adb2" + [[package]] name = "powerfmt" version = "0.2.0" @@ -1248,7 +1367,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "479cf940fbbb3426c32c5d5176f62ad57549a0bb84773423ba8be9d089f5faba" dependencies = [ "proc-macro2", - "syn 2.0.79", + "syn", ] [[package]] @@ -1260,6 +1379,54 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "quinn" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2d2fb862b7ba45e615c1429def928f2e15f815bdf933b27a2d3824e224c1f46" +dependencies = [ + "bytes", + "pin-project-lite", + "quinn-proto", + "quinn-udp", + "rustc-hash", + "rustls 0.23.13", + "socket2", + "thiserror", + "tokio", + "tracing", +] + +[[package]] +name = "quinn-proto" +version = "0.11.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba92fb39ec7ad06ca2582c0ca834dfeadcaf06ddfc8e635c80aa7e1c05315fdd" +dependencies = [ + "bytes", + "rand", + "ring", + "rustc-hash", + "rustls 0.23.13", + "slab", + "thiserror", + "tinyvec", + "tracing", +] + +[[package]] +name = "quinn-udp" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fe68c2e9e1a1234e218683dbdf9f9dfcb094113c5ac2b938dfcb9bab4c4140b" +dependencies = [ + "libc", + "once_cell", + "socket2", + "tracing", + "windows-sys 0.59.0", +] + [[package]] name = "quote" version = "1.0.37" @@ -1341,6 +1508,54 @@ dependencies = [ "redox_syscall", ] +[[package]] +name = "reqwest" +version = "0.12.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f713147fbe92361e52392c73b8c9e48c04c6625bce969ef54dc901e58e042a7b" +dependencies = [ + "base64", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-rustls 0.27.3", + "hyper-tls", + "hyper-util", + "ipnet", + "js-sys", + "log", + "mime", + "native-tls", + "once_cell", + "percent-encoding", + "pin-project-lite", + "quinn", + "rustls 0.23.13", + "rustls-pemfile", + "rustls-pki-types", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper", + "system-configuration", + "tokio", + "tokio-native-tls", + "tokio-rustls 0.26.0", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "webpki-roots", + "windows-registry", +] + [[package]] name = "ring" version = "0.17.7" @@ -1361,6 +1576,12 @@ version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" +[[package]] +name = "rustc-hash" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "583034fd73374156e66797ed8e5b0d5690409c9226b22d87cb7f19821c05d152" + [[package]] name = "rustix" version = "0.38.31" @@ -1388,6 +1609,20 @@ dependencies = [ "zeroize", ] +[[package]] +name = "rustls" +version = "0.23.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2dabaac7466917e566adb06783a81ca48944c6898a1b08b9374106dd671f4c8" +dependencies = [ + "once_cell", + "ring", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", +] + [[package]] name = "rustls-native-certs" version = "0.7.0" @@ -1419,9 +1654,9 @@ checksum = "fc0a2ce646f8655401bb81e7927b812614bd5d91dbc968696be50603510fcaf0" [[package]] name = "rustls-webpki" -version = "0.102.1" +version = "0.102.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef4ca26037c909dedb327b48c3327d0ba91d3dd3c4e05dad328f210ffb68e95b" +checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9" dependencies = [ "ring", "rustls-pki-types", @@ -1442,12 +1677,11 @@ checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" [[package]] name = "schannel" -version = "0.1.19" +version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f05ba609c234e60bee0d547fe94a4c7e9da733d1c962cf6e59efa4cd9c8bc75" +checksum = "e9aaafd5a2b6e3d657ff009d82fbd630b6bd54dd4eb06f21693925cdf80f9b8b" dependencies = [ - "lazy_static", - "winapi", + "windows-sys 0.59.0", ] [[package]] @@ -1459,7 +1693,7 @@ dependencies = [ "proc-macro2", "quote", "schema-derive", - "syn 2.0.79", + "syn", ] [[package]] @@ -1471,7 +1705,7 @@ dependencies = [ "paste", "proc-macro2", "quote", - "syn 2.0.79", + "syn", ] [[package]] @@ -1497,7 +1731,7 @@ dependencies = [ "proc-macro2", "quote", "serde_derive_internals", - "syn 2.0.79", + "syn", ] [[package]] @@ -1523,7 +1757,7 @@ checksum = "7f81c2fde025af7e69b1d1420531c8a8811ca898919db177141a85313b1cb932" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn", ] [[package]] @@ -1566,7 +1800,7 @@ checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn", ] [[package]] @@ -1577,7 +1811,7 @@ checksum = "330f01ce65a3a5fe59a60c82f3c9a024b573b8a6e875bd233fe5f934e71d54e3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn", ] [[package]] @@ -1620,7 +1854,7 @@ dependencies = [ "proc-macro2", "quote", "serde", - "syn 2.0.79", + "syn", ] [[package]] @@ -1779,9 +2013,9 @@ checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" [[package]] name = "syn" -version = "1.0.109" +version = "2.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +checksum = "89132cd0bf050864e1d38dc3bbc07a0eb8e7530af26344d3d2bbbef83499f590" dependencies = [ "proc-macro2", "quote", @@ -1789,14 +2023,33 @@ dependencies = [ ] [[package]] -name = "syn" -version = "2.0.79" +name = "sync_wrapper" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89132cd0bf050864e1d38dc3bbc07a0eb8e7530af26344d3d2bbbef83499f590" +checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394" dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", + "futures-core", +] + +[[package]] +name = "system-configuration" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" +dependencies = [ + "bitflags 2.4.0", + "core-foundation", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" +dependencies = [ + "core-foundation-sys", + "libc", ] [[package]] @@ -1854,7 +2107,7 @@ checksum = "08904e7672f5eb876eaaf87e0ce17857500934f4981c4a0ab2b4aa98baac7fc3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn", ] [[package]] @@ -1952,7 +2205,17 @@ checksum = "5f5ae998a069d4b5aba8ee9dad856af7d520c3699e6159b185c2acd48155d39a" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn", +] + +[[package]] +name = "tokio-native-tls" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" +dependencies = [ + "native-tls", + "tokio", ] [[package]] @@ -1961,7 +2224,18 @@ version = "0.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "775e0c0f0adb3a2f22a00c4745d728b479985fc15ee7ca6a2608388c5569860f" dependencies = [ - "rustls", + "rustls 0.22.4", + "rustls-pki-types", + "tokio", +] + +[[package]] +name = "tokio-rustls" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4" +dependencies = [ + "rustls 0.23.13", "rustls-pki-types", "tokio", ] @@ -2112,9 +2386,9 @@ dependencies = [ [[package]] name = "unicode-bidi" -version = "0.3.7" +version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a01404663e3db436ed2746d9fefef640d868edae3cceb81c3b8d5732fda678f" +checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" [[package]] name = "unicode-ident" @@ -2124,9 +2398,9 @@ checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "unicode-normalization" -version = "0.1.19" +version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d54590932941a9e9266f0832deed84ebe1bf2e4c9e4a3554d393d18f5e854bf9" +checksum = "5033c97c4262335cded6d6fc3e5c18ab755e1a3dc96376350f3d8e9f009ad956" dependencies = [ "tinyvec", ] @@ -2151,13 +2425,12 @@ checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] name = "url" -version = "2.2.2" +version = "2.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a507c383b2d33b5fc35d1861e77e6b383d158b2da5e14fe51b83dfedf6fd578c" +checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" dependencies = [ "form_urlencoded", "idna", - "matches", "percent-encoding", ] @@ -2187,7 +2460,7 @@ dependencies = [ "proc-macro2", "quote", "serde_tokenstream", - "syn 2.0.79", + "syn", "usdt-impl", ] @@ -2205,7 +2478,7 @@ dependencies = [ "quote", "serde", "serde_json", - "syn 2.0.79", + "syn", "thiserror", "thread-id", "version_check", @@ -2221,7 +2494,7 @@ dependencies = [ "proc-macro2", "quote", "serde_tokenstream", - "syn 2.0.79", + "syn", "usdt-impl", ] @@ -2241,6 +2514,12 @@ dependencies = [ "serde", ] +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + [[package]] name = "version_check" version = "0.9.5" @@ -2274,34 +2553,47 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.82" +version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc7652e3f6c4706c8d9cd54832c4a4ccb9b5336e2c3bd154d5cccfbf1c1f5f7d" +checksum = "a82edfc16a6c469f5f44dc7b571814045d60404b55a0ee849f9bcfa2e63dd9b5" dependencies = [ "cfg-if", + "once_cell", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" -version = "0.2.82" +version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "662cd44805586bd52971b9586b1df85cdbbd9112e4ef4d8f41559c334dc6ac3f" +checksum = "9de396da306523044d3302746f1208fa71d7532227f15e347e2d93e4145dd77b" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", - "syn 1.0.109", + "syn", "wasm-bindgen-shared", ] +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa76fb221a1f8acddf5b54ace85912606980ad661ac7a503b4570ffd3a624dad" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + [[package]] name = "wasm-bindgen-macro" -version = "0.2.82" +version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b260f13d3012071dfb1512849c033b1925038373aea48ced3012c09df952c602" +checksum = "585c4c91a46b072c92e908d99cb1dcdf95c5218eeb6f3bf1efa991ee7a68cccf" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -2309,22 +2601,41 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.82" +version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5be8e654bdd9b79216c2929ab90721aa82faf65c48cdf08bdc4e7f51357b80da" +checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.82" +version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6598dd0bd3c7d51095ff6531a5b23e02acdc81804e30d8f07afb77b7215a140a" +checksum = "c62a0a307cb4a311d3a07867860911ca130c3494e8c2719593806c08bc5d0484" + +[[package]] +name = "web-sys" +version = "0.3.59" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed055ab27f941423197eb86b2035720b1a3ce40504df082cac2ecc6ed73335a1" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webpki-roots" +version = "0.26.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bd24728e5af82c6c4ec1b66ac4844bdf8156257fccda846ec58b42cd0cdbe6a" +dependencies = [ + "rustls-pki-types", +] [[package]] name = "winapi" @@ -2364,7 +2675,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be" dependencies = [ "windows-core", - "windows-targets 0.52.0", + "windows-targets 0.52.6", ] [[package]] @@ -2373,7 +2684,37 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" dependencies = [ - "windows-targets 0.52.0", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-registry" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e400001bb720a623c1c69032f8e3e4cf09984deec740f007dd2b03ec864804b0" +dependencies = [ + "windows-result", + "windows-strings", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-result" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-strings" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" +dependencies = [ + "windows-result", + "windows-targets 0.52.6", ] [[package]] @@ -2415,7 +2756,16 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.0", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", ] [[package]] @@ -2450,17 +2800,18 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.52.0" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm 0.52.0", - "windows_aarch64_msvc 0.52.0", - "windows_i686_gnu 0.52.0", - "windows_i686_msvc 0.52.0", - "windows_x86_64_gnu 0.52.0", - "windows_x86_64_gnullvm 0.52.0", - "windows_x86_64_msvc 0.52.0", + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", ] [[package]] @@ -2477,9 +2828,9 @@ checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" [[package]] name = "windows_aarch64_gnullvm" -version = "0.52.0" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] name = "windows_aarch64_msvc" @@ -2495,9 +2846,9 @@ checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" [[package]] name = "windows_aarch64_msvc" -version = "0.52.0" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] name = "windows_i686_gnu" @@ -2513,9 +2864,15 @@ checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" [[package]] name = "windows_i686_gnu" -version = "0.52.0" +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 = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] name = "windows_i686_msvc" @@ -2531,9 +2888,9 @@ checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" [[package]] name = "windows_i686_msvc" -version = "0.52.0" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] name = "windows_x86_64_gnu" @@ -2549,9 +2906,9 @@ checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" [[package]] name = "windows_x86_64_gnu" -version = "0.52.0" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[package]] name = "windows_x86_64_gnullvm" @@ -2567,9 +2924,9 @@ checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" [[package]] name = "windows_x86_64_gnullvm" -version = "0.52.0" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] name = "windows_x86_64_msvc" @@ -2585,9 +2942,9 @@ checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" [[package]] name = "windows_x86_64_msvc" -version = "0.52.0" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" @@ -2625,7 +2982,7 @@ checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn", ] [[package]] diff --git a/dropshot/Cargo.toml b/dropshot/Cargo.toml index f41d22a0..238a5166 100644 --- a/dropshot/Cargo.toml +++ b/dropshot/Cargo.toml @@ -105,6 +105,9 @@ trybuild = "1.0.99" # Used by the https examples and tests pem = "3.0" rcgen = "0.13.1" +# Using rustls-tls because it appears the rcgen-generated certificates are not +# supported by the native Windows APIs. +reqwest = { version = "0.12.8", features = ["json", "rustls-tls"] } # Used in a doc-test demonstrating the WebsocketUpgrade extractor. tokio-tungstenite = "0.24.0" diff --git a/dropshot/examples/api-trait-alternate.rs b/dropshot/examples/api-trait-alternate.rs index 733442a8..74cc5598 100644 --- a/dropshot/examples/api-trait-alternate.rs +++ b/dropshot/examples/api-trait-alternate.rs @@ -74,7 +74,7 @@ //! [`AsyncWrite`](tokio::io::AsyncWrite) with a blanket impl. In that case, the //! behavior of methods like `AsyncWriteExt::write_all` cannot be overridden. -use dropshot::{ConfigLogging, ConfigLoggingLevel, HttpServerStarter}; +use dropshot::{ConfigLogging, ConfigLoggingLevel, ServerBuilder}; /// The interface. mod api { @@ -203,9 +203,7 @@ mod imp { #[tokio::main] async fn main() -> Result<(), String> { - let config_dropshot = Default::default(); - // For simplicity, we'll configure an "info"-level logger that writes to - // stderr assuming that it's a terminal. + // See dropshot/examples/basic.rs for more details on most of these pieces. let config_logging = ConfigLogging::StderrTerminal { level: ConfigLoggingLevel::Info }; let log = config_logging @@ -218,14 +216,9 @@ async fn main() -> Result<(), String> { let my_api = api::counter_api_mod::api_description::().unwrap(); - let server = HttpServerStarter::new( - &config_dropshot, - my_api, - imp::AtomicCounter::new(), - &log, - ) - .map_err(|error| format!("failed to create server: {}", error))? - .start(); + let server = ServerBuilder::new(my_api, imp::AtomicCounter::new(), log) + .start() + .map_err(|error| format!("failed to create server: {}", error))?; server.await } diff --git a/dropshot/examples/api-trait-websocket.rs b/dropshot/examples/api-trait-websocket.rs index 9f2d923d..9ef21e6a 100644 --- a/dropshot/examples/api-trait-websocket.rs +++ b/dropshot/examples/api-trait-websocket.rs @@ -2,7 +2,7 @@ //! Example use of `dropshot::api_description` with a WebSocket endpoint. -use dropshot::{ConfigLogging, ConfigLoggingLevel, HttpServerStarter}; +use dropshot::{ConfigLogging, ConfigLoggingLevel, ServerBuilder}; /// The interface. mod api { @@ -133,9 +133,7 @@ mod imp { #[tokio::main] async fn main() -> Result<(), String> { - let config_dropshot = Default::default(); - // For simplicity, we'll configure an "info"-level logger that writes to - // stderr assuming that it's a terminal. + // See dropshot/examples/basic.rs for more details on most of these pieces. let config_logging = ConfigLogging::StderrTerminal { level: ConfigLoggingLevel::Info }; let log = config_logging @@ -148,14 +146,9 @@ async fn main() -> Result<(), String> { let my_server = api::counter_api_mod::api_description::().unwrap(); - let server = HttpServerStarter::new( - &config_dropshot, - my_server, - imp::AtomicCounter::new(), - &log, - ) - .map_err(|error| format!("failed to create server: {}", error))? - .start(); + let server = ServerBuilder::new(my_server, imp::AtomicCounter::new(), log) + .start() + .map_err(|error| format!("failed to create server: {}", error))?; server.await } diff --git a/dropshot/examples/api-trait.rs b/dropshot/examples/api-trait.rs index e68684de..6ab0c4a3 100644 --- a/dropshot/examples/api-trait.rs +++ b/dropshot/examples/api-trait.rs @@ -15,7 +15,7 @@ //! //! This example puts the interface and implementation in separate modules. -use dropshot::{ConfigLogging, ConfigLoggingLevel, HttpServerStarter}; +use dropshot::{ConfigLogging, ConfigLoggingLevel, ServerBuilder}; /// The interface. mod api { @@ -134,9 +134,7 @@ mod imp { #[tokio::main] async fn main() -> Result<(), String> { - let config_dropshot = Default::default(); - // For simplicity, we'll configure an "info"-level logger that writes to - // stderr assuming that it's a terminal. + // See dropshot/examples/basic.rs for more details on most of these pieces. let config_logging = ConfigLogging::StderrTerminal { level: ConfigLoggingLevel::Info }; let log = config_logging @@ -151,14 +149,9 @@ async fn main() -> Result<(), String> { // type parameter. let my_api = api::counter_api_mod::api_description::().unwrap(); - let server = HttpServerStarter::new( - &config_dropshot, - my_api, - imp::AtomicCounter::new(), - &log, - ) - .map_err(|error| format!("failed to create server: {}", error))? - .start(); + let server = ServerBuilder::new(my_api, imp::AtomicCounter::new(), log) + .start() + .map_err(|error| format!("failed to create server: {}", error))?; server.await } diff --git a/dropshot/examples/basic.rs b/dropshot/examples/basic.rs index dbd7ea4d..6dc4a819 100644 --- a/dropshot/examples/basic.rs +++ b/dropshot/examples/basic.rs @@ -3,14 +3,13 @@ use dropshot::endpoint; use dropshot::ApiDescription; -use dropshot::ConfigDropshot; use dropshot::ConfigLogging; use dropshot::ConfigLoggingLevel; use dropshot::HttpError; use dropshot::HttpResponseOk; use dropshot::HttpResponseUpdatedNoContent; -use dropshot::HttpServerStarter; use dropshot::RequestContext; +use dropshot::ServerBuilder; use dropshot::TypedBody; use schemars::JsonSchema; use serde::Deserialize; @@ -20,12 +19,6 @@ use std::sync::atomic::Ordering; #[tokio::main] async fn main() -> Result<(), String> { - // We must specify a configuration with a bind address. We'll use 127.0.0.1 - // since it's available and won't expose this server outside the host. We - // request port 0, which allows the operating system to pick any available - // port. - let config_dropshot: ConfigDropshot = Default::default(); - // For simplicity, we'll configure an "info"-level logger that writes to // stderr assuming that it's a terminal. let config_logging = @@ -43,10 +36,14 @@ async fn main() -> Result<(), String> { let api_context = ExampleContext::new(); // Set up the server. - let server = - HttpServerStarter::new(&config_dropshot, api, api_context, &log) - .map_err(|error| format!("failed to create server: {}", error))? - .start(); + // + // We use the default configuration here, which uses 127.0.0.1 since it's + // always available and won't expose this server outside the host. It also + // uses port 0, which allows the operating system to pick any available + // port. + let server = ServerBuilder::new(api, api_context, log) + .start() + .map_err(|error| format!("failed to create server: {}", error))?; // Wait for the server to stop. Note that there's not any code to shut down // this server, so we should never get past this point. diff --git a/dropshot/examples/file_server.rs b/dropshot/examples/file_server.rs index c9d6fdd9..58ae830d 100644 --- a/dropshot/examples/file_server.rs +++ b/dropshot/examples/file_server.rs @@ -7,8 +7,8 @@ use dropshot::Body; use dropshot::ConfigLogging; use dropshot::ConfigLoggingLevel; use dropshot::HttpError; -use dropshot::HttpServerStarter; use dropshot::RequestContext; +use dropshot::ServerBuilder; use dropshot::{endpoint, Path}; use http::{Response, StatusCode}; use schemars::JsonSchema; @@ -22,34 +22,22 @@ struct FileServerContext { #[tokio::main] async fn main() -> Result<(), String> { - // We must specify a configuration with a bind address. We'll use 127.0.0.1 - // since it's available and won't expose this server outside the host. We - // request port 0, which allows the operating system to pick any available - // port. - let config_dropshot = Default::default(); - - // For simplicity, we'll configure an "info"-level logger that writes to - // stderr assuming that it's a terminal. + // See dropshot/examples/basic.rs for more details on most of these pieces. let config_logging = ConfigLogging::StderrTerminal { level: ConfigLoggingLevel::Info }; let log = config_logging .to_logger("example-basic") .map_err(|error| format!("failed to create logger: {}", error))?; - // Build a description of the API -- in this case it's not much of an API!. let mut api = ApiDescription::new(); api.register(static_content).unwrap(); - // Specify the directory we want to serve. let context = FileServerContext { base: PathBuf::from(".") }; - // Set up the server. - let server = HttpServerStarter::new(&config_dropshot, api, context, &log) - .map_err(|error| format!("failed to create server: {}", error))? - .start(); + let server = ServerBuilder::new(api, context, log) + .start() + .map_err(|error| format!("failed to create server: {}", error))?; - // Wait for the server to stop. Note that there's not any code to shut down - // this server, so we should never get past this point. server.await } diff --git a/dropshot/examples/https.rs b/dropshot/examples/https.rs index 72900c3a..7a17ba1e 100644 --- a/dropshot/examples/https.rs +++ b/dropshot/examples/https.rs @@ -4,15 +4,14 @@ use dropshot::endpoint; use dropshot::ApiDescription; -use dropshot::ConfigDropshot; use dropshot::ConfigLogging; use dropshot::ConfigLoggingLevel; use dropshot::ConfigTls; use dropshot::HttpError; use dropshot::HttpResponseOk; use dropshot::HttpResponseUpdatedNoContent; -use dropshot::HttpServerStarter; use dropshot::RequestContext; +use dropshot::ServerBuilder; use dropshot::TypedBody; use schemars::JsonSchema; use serde::Deserialize; @@ -52,51 +51,33 @@ fn generate_keys() -> Result<(NamedTempFile, NamedTempFile), String> { #[tokio::main] async fn main() -> Result<(), String> { - // Begin by generating TLS certificates and keys. A normal application would - // just pass the paths to these via ConfigDropshot. + // Begin by generating TLS certificates and keys and stuffing them into a + // TLS configuration. let (cert_file, key_file) = generate_keys()?; - - // We must specify a configuration with a bind address. We'll use 127.0.0.1 - // since it's available and won't expose this server outside the host. We - // request port 0, which allows the operating system to pick any available - // port. - // - // In addition, we'll make this an HTTPS server. - let config_dropshot = ConfigDropshot::default(); let config_tls = Some(ConfigTls::AsFile { cert_file: cert_file.path().to_path_buf(), key_file: key_file.path().to_path_buf(), }); - // For simplicity, we'll configure an "info"-level logger that writes to - // stderr assuming that it's a terminal. + // See dropshot/examples/basic.rs for more details on most of these pieces. let config_logging = ConfigLogging::StderrTerminal { level: ConfigLoggingLevel::Info }; let log = config_logging .to_logger("example-basic") .map_err(|error| format!("failed to create logger: {}", error))?; - // Build a description of the API. let mut api = ApiDescription::new(); api.register(example_api_get_counter).unwrap(); api.register(example_api_put_counter).unwrap(); - // The functions that implement our API endpoints will share this context. let api_context = ExampleContext::new(); - // Set up the server. - let server = HttpServerStarter::new_with_tls( - &config_dropshot, - api, - api_context, - &log, - config_tls, - ) - .map_err(|error| format!("failed to create server: {}", error))? - .start(); - - // Wait for the server to stop. Note that there's not any code to shut down - // this server, so we should never get past this point. + let server = ServerBuilder::new(api, api_context, log) + // This differs from the basic example: provide the TLS configuration. + .tls(config_tls) + .start() + .map_err(|error| format!("failed to create server: {}", error))?; + server.await } diff --git a/dropshot/examples/index.rs b/dropshot/examples/index.rs index 1fceee5e..54c9ef3c 100644 --- a/dropshot/examples/index.rs +++ b/dropshot/examples/index.rs @@ -3,12 +3,11 @@ use dropshot::ApiDescription; use dropshot::Body; -use dropshot::ConfigDropshot; use dropshot::ConfigLogging; use dropshot::ConfigLoggingLevel; use dropshot::HttpError; -use dropshot::HttpServerStarter; use dropshot::RequestContext; +use dropshot::ServerBuilder; use dropshot::{endpoint, Path}; use http::{Response, StatusCode}; use schemars::JsonSchema; @@ -16,31 +15,20 @@ use serde::Deserialize; #[tokio::main] async fn main() -> Result<(), String> { - // We must specify a configuration with a bind address. We'll use 127.0.0.1 - // since it's available and won't expose this server outside the host. We - // request port 0, which allows the operating system to pick any available - // port. - let config_dropshot: ConfigDropshot = Default::default(); - - // For simplicity, we'll configure an "info"-level logger that writes to - // stderr assuming that it's a terminal. + // See dropshot/examples/basic.rs for more details on most of these pieces. let config_logging = ConfigLogging::StderrTerminal { level: ConfigLoggingLevel::Info }; let log = config_logging .to_logger("example-basic") .map_err(|error| format!("failed to create logger: {}", error))?; - // Build a description of the API. let mut api = ApiDescription::new(); api.register(index).unwrap(); - // Set up the server. - let server = HttpServerStarter::new(&config_dropshot, api, (), &log) - .map_err(|error| format!("failed to create server: {}", error))? - .start(); + let server = ServerBuilder::new(api, (), log) + .start() + .map_err(|error| format!("failed to create server: {}", error))?; - // Wait for the server to stop. Note that there's not any code to shut down - // this server, so we should never get past this point. server.await } diff --git a/dropshot/examples/module-basic.rs b/dropshot/examples/module-basic.rs index 4b95d6f0..43373347 100644 --- a/dropshot/examples/module-basic.rs +++ b/dropshot/examples/module-basic.rs @@ -4,7 +4,7 @@ use dropshot::ApiDescription; use dropshot::ConfigLogging; use dropshot::ConfigLoggingLevel; -use dropshot::HttpServerStarter; +use dropshot::ServerBuilder; use schemars::JsonSchema; use serde::Deserialize; use serde::Serialize; @@ -12,36 +12,23 @@ use std::sync::atomic::AtomicU64; #[tokio::main] async fn main() -> Result<(), String> { - // We must specify a configuration with a bind address. We'll use 127.0.0.1 - // since it's available and won't expose this server outside the host. We - // request port 0, which allows the operating system to pick any available - // port. - let config_dropshot = Default::default(); - - // For simplicity, we'll configure an "info"-level logger that writes to - // stderr assuming that it's a terminal. + // See dropshot/examples/basic.rs for more details on most of these pieces. let config_logging = ConfigLogging::StderrTerminal { level: ConfigLoggingLevel::Info }; let log = config_logging .to_logger("example-basic") .map_err(|error| format!("failed to create logger: {}", error))?; - // Build a description of the API. let mut api = ApiDescription::new(); api.register(routes::example_api_get_counter).unwrap(); api.register(routes::example_api_put_counter).unwrap(); - // The functions that implement our API endpoints will share this context. let api_context = ExampleContext::new(); - // Set up the server. - let server = - HttpServerStarter::new(&config_dropshot, api, api_context, &log) - .map_err(|error| format!("failed to create server: {}", error))? - .start(); + let server = ServerBuilder::new(api, api_context, log) + .start() + .map_err(|error| format!("failed to create server: {}", error))?; - // Wait for the server to stop. Note that there's not any code to shut down - // this server, so we should never get past this point. server.await } diff --git a/dropshot/examples/module-shared-context.rs b/dropshot/examples/module-shared-context.rs index 3d9ecdf0..b1becf33 100644 --- a/dropshot/examples/module-shared-context.rs +++ b/dropshot/examples/module-shared-context.rs @@ -8,8 +8,8 @@ use dropshot::ConfigLogging; use dropshot::ConfigLoggingLevel; use dropshot::HttpError; use dropshot::HttpResponseOk; -use dropshot::HttpServerStarter; use dropshot::RequestContext; +use dropshot::ServerBuilder; use futures::FutureExt; use schemars::JsonSchema; use serde::Deserialize; @@ -20,36 +20,21 @@ use std::sync::Arc; #[tokio::main] async fn main() -> Result<(), String> { - // We must specify a configuration with a bind address. We'll use 127.0.0.1 - // since it's available and won't expose this server outside the host. We - // request port 0, which allows the operating system to pick any available - // port. - let config_dropshot = Default::default(); - - // For simplicity, we'll configure an "info"-level logger that writes to - // stderr assuming that it's a terminal. + // See dropshot/examples/basic.rs for more details on most of these pieces. let config_logging = ConfigLogging::StderrTerminal { level: ConfigLoggingLevel::Info }; let log = config_logging .to_logger("example-basic") .map_err(|error| format!("failed to create logger: {}", error))?; - // Build a description of the API. let mut api = ApiDescription::new(); api.register(example_api_get_counter).unwrap(); - // The functions that implement our API endpoints will share this context. let api_context = Arc::new(ExampleContext::new()); - // Set up the server. - let server = HttpServerStarter::new( - &config_dropshot, - api, - api_context.clone(), - &log, - ) - .map_err(|error| format!("failed to create server: {}", error))? - .start(); + let server = ServerBuilder::new(api, api_context.clone(), log) + .start() + .map_err(|error| format!("failed to create server: {}", error))?; // Wait for the server to stop. Note that there's not any code to shut down // this server, so we should never get past this point. diff --git a/dropshot/examples/multipart.rs b/dropshot/examples/multipart.rs index b61649db..211d400f 100644 --- a/dropshot/examples/multipart.rs +++ b/dropshot/examples/multipart.rs @@ -4,42 +4,30 @@ use dropshot::endpoint; use dropshot::ApiDescription; use dropshot::Body; -use dropshot::ConfigDropshot; use dropshot::ConfigLogging; use dropshot::ConfigLoggingLevel; use dropshot::HttpError; -use dropshot::HttpServerStarter; use dropshot::MultipartBody; use dropshot::RequestContext; +use dropshot::ServerBuilder; use http::{Response, StatusCode}; #[tokio::main] async fn main() -> Result<(), String> { - // We must specify a configuration with a bind address. We'll use 127.0.0.1 - // since it's available and won't expose this server outside the host. We - // request port 0, which allows the operating system to pick any available - // port. - let config_dropshot: ConfigDropshot = Default::default(); - - // For simplicity, we'll configure an "info"-level logger that writes to - // stderr assuming that it's a terminal. + // See dropshot/examples/basic.rs for more details on most of these pieces. let config_logging = ConfigLogging::StderrTerminal { level: ConfigLoggingLevel::Info }; let log = config_logging .to_logger("example-basic") .map_err(|error| format!("failed to create logger: {}", error))?; - // Build a description of the API. let mut api = ApiDescription::new(); api.register(index).unwrap(); - // Set up the server. - let server = HttpServerStarter::new(&config_dropshot, api, (), &log) - .map_err(|error| format!("failed to create server: {}", error))? - .start(); + let server = ServerBuilder::new(api, (), log) + .start() + .map_err(|error| format!("failed to create server: {}", error))?; - // Wait for the server to stop. Note that there's not any code to shut down - // this server, so we should never get past this point. server.await } diff --git a/dropshot/examples/multiple-servers.rs b/dropshot/examples/multiple-servers.rs index 1f11098c..b3810fec 100644 --- a/dropshot/examples/multiple-servers.rs +++ b/dropshot/examples/multiple-servers.rs @@ -77,9 +77,9 @@ use dropshot::HttpResponseCreated; use dropshot::HttpResponseDeleted; use dropshot::HttpResponseOk; use dropshot::HttpServer; -use dropshot::HttpServerStarter; use dropshot::Path; use dropshot::RequestContext; +use dropshot::ServerBuilder; use dropshot::TypedBody; use futures::future::BoxFuture; use futures::stream::FuturesUnordered; @@ -191,16 +191,13 @@ impl SharedMultiServerContext { Entry::Vacant(slot) => slot, }; - // For simplicity, we'll configure an "info"-level logger that writes to - // stderr assuming that it's a terminal. + // See dropshot/examples/basic.rs for more details on most of these pieces. let config_logging = ConfigLogging::StderrTerminal { level: ConfigLoggingLevel::Info }; let log = config_logging .to_logger(format!("example-multiserver-{name}")) .map_err(|error| format!("failed to create logger: {}", error))?; - // Build a description of the API. - // // TODO: Could `ApiDescription` implement `Clone`, or could we pass an // `Arc` instead? let mut api = ApiDescription::new(); @@ -218,10 +215,10 @@ impl SharedMultiServerContext { name: name.to_string(), log: log.clone(), }; - let server = - HttpServerStarter::new(&config_dropshot, api, context, &log) - .map_err(|error| format!("failed to create server: {}", error))? - .start(); + let server = ServerBuilder::new(api, context, log) + .config(config_dropshot) + .start() + .map_err(|error| format!("failed to create server: {}", error))?; let shutdown_handle = server.wait_for_shutdown(); slot.insert(server); diff --git a/dropshot/examples/pagination-basic.rs b/dropshot/examples/pagination-basic.rs index 38c0344c..395cca31 100644 --- a/dropshot/examples/pagination-basic.rs +++ b/dropshot/examples/pagination-basic.rs @@ -68,11 +68,11 @@ use dropshot::ConfigLoggingLevel; use dropshot::EmptyScanParams; use dropshot::HttpError; use dropshot::HttpResponseOk; -use dropshot::HttpServerStarter; use dropshot::PaginationParams; use dropshot::Query; use dropshot::RequestContext; use dropshot::ResultsPage; +use dropshot::ServerBuilder; use dropshot::WhichPage; use schemars::JsonSchema; use serde::Deserialize; @@ -146,6 +146,7 @@ async fn example_list_projects( #[tokio::main] async fn main() -> Result<(), String> { + // See dropshot/examples/basic.rs for more details on most of these pieces. let port = std::env::args() .nth(1) .map(|p| p.parse::()) @@ -174,8 +175,9 @@ async fn main() -> Result<(), String> { .map_err(|error| format!("failed to create logger: {}", error))?; let mut api = ApiDescription::new(); api.register(example_list_projects).unwrap(); - let server = HttpServerStarter::new(&config_dropshot, api, ctx, &log) - .map_err(|error| format!("failed to create server: {}", error))? - .start(); + let server = ServerBuilder::new(api, ctx, log) + .config(config_dropshot) + .start() + .map_err(|error| format!("failed to create server: {}", error))?; server.await } diff --git a/dropshot/examples/pagination-multiple-resources.rs b/dropshot/examples/pagination-multiple-resources.rs index f4afea27..63775551 100644 --- a/dropshot/examples/pagination-multiple-resources.rs +++ b/dropshot/examples/pagination-multiple-resources.rs @@ -10,7 +10,6 @@ use dropshot::ConfigLogging; use dropshot::ConfigLoggingLevel; use dropshot::HttpError; use dropshot::HttpResponseOk; -use dropshot::HttpServerStarter; use dropshot::PaginationOrder; use dropshot::PaginationOrder::Ascending; use dropshot::PaginationOrder::Descending; @@ -18,6 +17,7 @@ use dropshot::PaginationParams; use dropshot::Query; use dropshot::RequestContext; use dropshot::ResultsPage; +use dropshot::ServerBuilder; use dropshot::WhichPage; use schemars::JsonSchema; use serde::Deserialize; @@ -276,6 +276,7 @@ async fn main() -> Result<(), String> { .unwrap_or(0); // Run the Dropshot server. + // See dropshot/examples/basic.rs for more details on most of these pieces. let ctx = DataCollection::new(); let config_dropshot = ConfigDropshot { bind_address: SocketAddr::from((Ipv4Addr::LOCALHOST, port)), @@ -290,9 +291,10 @@ async fn main() -> Result<(), String> { api.register(example_list_projects).unwrap(); api.register(example_list_disks).unwrap(); api.register(example_list_instances).unwrap(); - let server = HttpServerStarter::new(&config_dropshot, api, ctx, &log) - .map_err(|error| format!("failed to create server: {}", error))? - .start(); + let server = ServerBuilder::new(api, ctx, log) + .config(config_dropshot) + .start() + .map_err(|error| format!("failed to create server: {}", error))?; server.await } diff --git a/dropshot/examples/pagination-multiple-sorts.rs b/dropshot/examples/pagination-multiple-sorts.rs index 7f673f74..133cb0f7 100644 --- a/dropshot/examples/pagination-multiple-sorts.rs +++ b/dropshot/examples/pagination-multiple-sorts.rs @@ -98,7 +98,6 @@ use dropshot::ConfigLogging; use dropshot::ConfigLoggingLevel; use dropshot::HttpError; use dropshot::HttpResponseOk; -use dropshot::HttpServerStarter; use dropshot::PaginationOrder; use dropshot::PaginationOrder::Ascending; use dropshot::PaginationOrder::Descending; @@ -106,6 +105,7 @@ use dropshot::PaginationParams; use dropshot::Query; use dropshot::RequestContext; use dropshot::ResultsPage; +use dropshot::ServerBuilder; use dropshot::WhichPage; use hyper::Uri; use schemars::JsonSchema; @@ -292,6 +292,7 @@ async fn main() -> Result<(), String> { .unwrap_or(0); // Run the Dropshot server. + // See dropshot/examples/basic.rs for more details on most of these pieces. let ctx = ProjectCollection::new(); let config_dropshot = ConfigDropshot { bind_address: SocketAddr::from((Ipv4Addr::LOCALHOST, port)), @@ -304,9 +305,10 @@ async fn main() -> Result<(), String> { .map_err(|error| format!("failed to create logger: {}", error))?; let mut api = ApiDescription::new(); api.register(example_list_projects).unwrap(); - let server = HttpServerStarter::new(&config_dropshot, api, ctx, &log) - .map_err(|error| format!("failed to create server: {}", error))? - .start(); + let server = ServerBuilder::new(api, ctx, log.clone()) + .config(config_dropshot) + .start() + .map_err(|error| format!("failed to create server: {}", error))?; // Print out some example requests to start with. print_example_requests(log, &server.local_addr()); diff --git a/dropshot/examples/request-headers.rs b/dropshot/examples/request-headers.rs index a32916d5..0c09f9d2 100644 --- a/dropshot/examples/request-headers.rs +++ b/dropshot/examples/request-headers.rs @@ -12,17 +12,16 @@ use dropshot::endpoint; use dropshot::ApiDescription; -use dropshot::ConfigDropshot; use dropshot::ConfigLogging; use dropshot::ConfigLoggingLevel; use dropshot::HttpError; use dropshot::HttpResponseOk; -use dropshot::HttpServerStarter; use dropshot::RequestContext; +use dropshot::ServerBuilder; #[tokio::main] async fn main() -> Result<(), String> { - let config_dropshot: ConfigDropshot = Default::default(); + // See dropshot/examples/basic.rs for more details on most of these pieces. let config_logging = ConfigLogging::StderrTerminal { level: ConfigLoggingLevel::Info }; let log = config_logging @@ -32,10 +31,9 @@ async fn main() -> Result<(), String> { api.register(example_api_get_header_generic).unwrap(); let api_context = (); - let server = - HttpServerStarter::new(&config_dropshot, api, api_context, &log) - .map_err(|error| format!("failed to create server: {}", error))? - .start(); + let server = ServerBuilder::new(api, api_context, log) + .start() + .map_err(|error| format!("failed to create server: {}", error))?; server.await } diff --git a/dropshot/examples/self-referential.rs b/dropshot/examples/self-referential.rs index fb7d7c2f..9334f3a2 100644 --- a/dropshot/examples/self-referential.rs +++ b/dropshot/examples/self-referential.rs @@ -4,13 +4,12 @@ use dropshot::endpoint; use dropshot::ApiDescription; -use dropshot::ConfigDropshot; use dropshot::ConfigLogging; use dropshot::ConfigLoggingLevel; use dropshot::HttpError; use dropshot::HttpResponseOk; -use dropshot::HttpServerStarter; use dropshot::RequestContext; +use dropshot::ServerBuilder; use schemars::JsonSchema; use serde::Deserialize; use serde::Serialize; @@ -20,7 +19,7 @@ use std::sync::Arc; #[tokio::main] async fn main() -> Result<(), String> { - let config_dropshot: ConfigDropshot = Default::default(); + // See dropshot/examples/basic.rs for more details on most of these pieces. let config_logging = ConfigLogging::StderrTerminal { level: ConfigLoggingLevel::Info }; let log = config_logging @@ -32,11 +31,9 @@ async fn main() -> Result<(), String> { let api_context = Arc::new(ExampleContext::new()); - // Set up the server. - let server = - HttpServerStarter::new(&config_dropshot, api, api_context, &log) - .map_err(|error| format!("failed to create server: {}", error))? - .start(); + let server = ServerBuilder::new(api, api_context, log) + .start() + .map_err(|error| format!("failed to create server: {}", error))?; let shutdown = server.wait_for_shutdown(); tokio::task::spawn(async move { diff --git a/dropshot/examples/websocket.rs b/dropshot/examples/websocket.rs index 5fa128cd..1bcec4de 100644 --- a/dropshot/examples/websocket.rs +++ b/dropshot/examples/websocket.rs @@ -3,12 +3,11 @@ use dropshot::channel; use dropshot::ApiDescription; -use dropshot::ConfigDropshot; use dropshot::ConfigLogging; use dropshot::ConfigLoggingLevel; -use dropshot::HttpServerStarter; use dropshot::Query; use dropshot::RequestContext; +use dropshot::ServerBuilder; use dropshot::WebsocketConnection; use futures::SinkExt; use schemars::JsonSchema; @@ -18,31 +17,20 @@ use tokio_tungstenite::tungstenite::Message; #[tokio::main] async fn main() -> Result<(), String> { - // We must specify a configuration with a bind address. We'll use 127.0.0.1 - // since it's available and won't expose this server outside the host. We - // request port 0, which allows the operating system to pick any available - // port. - let config_dropshot: ConfigDropshot = Default::default(); - - // For simplicity, we'll configure an "info"-level logger that writes to - // stderr assuming that it's a terminal. + // See dropshot/examples/basic.rs for more details on most of these pieces. let config_logging = ConfigLogging::StderrTerminal { level: ConfigLoggingLevel::Info }; let log = config_logging .to_logger("example-basic") .map_err(|error| format!("failed to create logger: {}", error))?; - // Build a description of the API. let mut api = ApiDescription::new(); api.register(example_api_websocket_counter).unwrap(); - // Set up the server. - let server = HttpServerStarter::new(&config_dropshot, api, (), &log) - .map_err(|error| format!("failed to create server: {}", error))? - .start(); + let server = ServerBuilder::new(api, (), log) + .start() + .map_err(|error| format!("failed to create server: {}", error))?; - // Wait for the server to stop. Note that there's not any code to shut down - // this server, so we should never get past this point. server.await } diff --git a/dropshot/examples/well-tagged.rs b/dropshot/examples/well-tagged.rs index 84c69a94..354aaa69 100644 --- a/dropshot/examples/well-tagged.rs +++ b/dropshot/examples/well-tagged.rs @@ -7,8 +7,8 @@ use dropshot::{ endpoint, ApiDescription, ConfigLogging, ConfigLoggingLevel, - EndpointTagPolicy, HttpError, HttpResponseOk, HttpServerStarter, - RequestContext, TagConfig, TagDetails, TagExternalDocs, + EndpointTagPolicy, HttpError, HttpResponseOk, RequestContext, + ServerBuilder, TagConfig, TagDetails, TagExternalDocs, }; #[endpoint { @@ -46,21 +46,14 @@ async fn get_fryism( #[tokio::main] async fn main() -> Result<(), String> { - // We must specify a configuration with a bind address. We'll use 127.0.0.1 - // since it's available and won't expose this server outside the host. We - // request port 0, which allows the operating system to pick any available - // port. - let config_dropshot = Default::default(); - - // For simplicity, we'll configure an "info"-level logger that writes to - // stderr assuming that it's a terminal. + // See dropshot/examples/basic.rs for more details on most of these pieces. let config_logging = ConfigLogging::StderrTerminal { level: ConfigLoggingLevel::Info }; let log = config_logging .to_logger("example-basic") .map_err(|error| format!("failed to create logger: {}", error))?; - // Build a description of the API -- in this case it's not much of an API!. + // Build a description of the API -- in this case it's not much of an API! let mut api = ApiDescription::new().tag_config(TagConfig { allow_other_tags: false, policy: EndpointTagPolicy::ExactlyOne, @@ -98,12 +91,9 @@ async fn main() -> Result<(), String> { api.register(get_barneyism).unwrap(); api.register(get_fryism).unwrap(); - // Set up the server. - let server = HttpServerStarter::new(&config_dropshot, api, (), &log) - .map_err(|error| format!("failed to create server: {}", error))? - .start(); + let server = ServerBuilder::new(api, (), log) + .start() + .map_err(|error| format!("failed to create server: {}", error))?; - // Wait for the server to stop. Note that there's not any code to shut down - // this server, so we should never get past this point. server.await } diff --git a/dropshot/src/handler.rs b/dropshot/src/handler.rs index b2faba7e..7e17f74a 100644 --- a/dropshot/src/handler.rs +++ b/dropshot/src/handler.rs @@ -567,8 +567,8 @@ impl HttpResponse for Response { } } -/// Wraps a [dropshot::Body] so that it can be used with coded response types such -/// as [HttpResponseOk]. +/// Wraps a [`Body`] so that it can be used with coded response types such as +/// [HttpResponseOk]. pub struct FreeformBody(pub Body); impl From for FreeformBody { diff --git a/dropshot/src/lib.rs b/dropshot/src/lib.rs index 8716777d..10f8d967 100644 --- a/dropshot/src/lib.rs +++ b/dropshot/src/lib.rs @@ -49,7 +49,7 @@ //! use dropshot::ConfigLogging; //! use dropshot::ConfigLoggingLevel; //! use dropshot::HandlerTaskMode; -//! use dropshot::HttpServerStarter; +//! use dropshot::ServerBuilder; //! use std::sync::Arc; //! //! #[tokio::main] @@ -67,20 +67,9 @@ //! // Register API functions -- see detailed example or ApiDescription docs. //! //! // Start the server. -//! let server = -//! HttpServerStarter::new( -//! &ConfigDropshot { -//! bind_address: "127.0.0.1:0".parse().unwrap(), -//! request_body_max_bytes: 1024, -//! default_handler_task_mode: HandlerTaskMode::Detached, -//! log_headers: Default::default(), -//! }, -//! api, -//! Arc::new(()), -//! &log, -//! ) -//! .map_err(|error| format!("failed to start server: {}", error))? -//! .start(); +//! let server = ServerBuilder::new(api, Arc::new(()), log) +//! .start() +//! .map_err(|error| format!("failed to start server: {}", error))?; //! //! server.await //! } @@ -836,6 +825,8 @@ pub use pagination::PaginationOrder; pub use pagination::PaginationParams; pub use pagination::ResultsPage; pub use pagination::WhichPage; +pub use server::BuildError; +pub use server::ServerBuilder; pub use server::ServerContext; pub use server::ShutdownWaitFuture; pub use server::{HttpServer, HttpServerStarter}; diff --git a/dropshot/src/server.rs b/dropshot/src/server.rs index 339c3db3..ab6135bb 100644 --- a/dropshot/src/server.rs +++ b/dropshot/src/server.rs @@ -104,6 +104,10 @@ pub struct ServerConfig { pub log_headers: Vec, } +/// See [`ServerBuilder`] instead. +// It would be nice to remove this structure altogether once we've got +// confidence that no consumers actually need to distinguish between the +// configuration and start steps. pub struct HttpServerStarter { app_state: Arc>, local_addr: SocketAddr, @@ -113,15 +117,23 @@ pub struct HttpServerStarter { } impl HttpServerStarter { + /// Make an `HttpServerStarter` to start an `HttpServer` + /// + /// This function exists for backwards compatibility. You should use + /// [`ServerBuilder`] instead. pub fn new( config: &ConfigDropshot, api: ApiDescription, private: C, log: &Logger, ) -> Result, GenericError> { - Self::new_with_tls(config, api, private, log, None) + HttpServerStarter::new_with_tls(config, api, private, log, None) } + /// Make an `HttpServerStarter` to start an `HttpServer` + /// + /// This function exists for backwards compatibility. You should use + /// [`ServerBuilder`] instead. pub fn new_with_tls( config: &ConfigDropshot, api: ApiDescription, @@ -129,7 +141,10 @@ impl HttpServerStarter { log: &Logger, tls: Option, ) -> Result, GenericError> { - HttpServerStarter::new_internal(config, api, private, log, tls) + ServerBuilder::new(api, private, log.clone()) + .config(config.clone()) + .tls(tls) + .build_starter() .map_err(|e| Box::new(e) as GenericError) } @@ -335,7 +350,8 @@ impl HttpServerStarter { } } -/// Accepts TCP connections like a `TcpListener`, but ignores transient errors rather than propagating them to the caller +/// Accepts TCP connections like a `TcpListener`, but ignores transient errors +/// rather than propagating them to the caller struct HttpAcceptor { tcp: TcpListener, log: slog::Logger, @@ -1049,10 +1065,15 @@ pub enum BuildError { } impl BuildError { + /// Generate an error for failure to bind to `address` fn bind_error(error: std::io::Error, address: SocketAddr) -> BuildError { BuildError::BindError { address, error } } + /// Generate an error for any kind of `std::io::Error` + /// + /// `context` describes more about what we were trying to do that generated + /// the error. fn generic_system>( error: std::io::Error, context: S, @@ -1061,6 +1082,74 @@ impl BuildError { } } +/// Start configuring a Dropshot server +#[derive(Debug)] +pub struct ServerBuilder { + // required caller-provided values + private: C, + log: Logger, + api: DebugIgnore>, + + // optional caller-provided values + config: ConfigDropshot, + tls: Option, +} + +impl ServerBuilder { + /// Start configuring a new Dropshot server + /// + /// * `api`: the API to be hosted on this server + /// * `private`: your private data that will be made available in + /// `RequestContext` + /// * `log`: a slog logger for all server events + pub fn new( + api: ApiDescription, + private: C, + log: Logger, + ) -> ServerBuilder { + ServerBuilder { + private, + log, + api: DebugIgnore(api), + config: Default::default(), + tls: Default::default(), + } + } + + /// Specify the server configuration + pub fn config(mut self, config: ConfigDropshot) -> Self { + self.config = config; + self + } + + /// Specify the TLS configuration, if any + /// + /// `None` (the default) means no TLS. The server will listen for plain + /// HTTP. + pub fn tls(mut self, tls: Option) -> Self { + self.tls = tls; + self + } + + /// Start the server + pub fn start(self) -> Result, BuildError> { + Ok(self.build_starter()?.start()) + } + + /// Build an `HttpServerStarter` that can be used to start the server + /// + /// Most consumers probably want to use `start()` instead. + pub fn build_starter(self) -> Result, BuildError> { + HttpServerStarter::new_internal( + &self.config, + self.api.0, + self.private, + &self.log, + self.tls, + ) + } +} + #[cfg(test)] mod test { use super::*; diff --git a/dropshot/src/test_util.rs b/dropshot/src/test_util.rs index caff1cbc..ce6b23d3 100644 --- a/dropshot/src/test_util.rs +++ b/dropshot/src/test_util.rs @@ -32,7 +32,7 @@ use crate::error::HttpErrorResponseBody; use crate::http_util::CONTENT_TYPE_URL_ENCODED; use crate::logging::ConfigLogging; use crate::pagination::ResultsPage; -use crate::server::{HttpServer, HttpServerStarter, ServerContext}; +use crate::server::{HttpServer, ServerBuilder, ServerContext}; enum AllowedValue<'a> { Any, @@ -490,10 +490,10 @@ impl TestContext { ); // Set up the server itself. - let server = - HttpServerStarter::new(&config_dropshot, api, private, &log) - .unwrap() - .start(); + let server = ServerBuilder::new(api, private, log.clone()) + .config(config_dropshot.clone()) + .start() + .unwrap(); let server_addr = server.local_addr(); let client_log = log.new(o!("http_client" => "dropshot test suite")); diff --git a/dropshot/tests/test_config.rs b/dropshot/tests/test_config.rs index bff8c663..f2b71d7b 100644 --- a/dropshot/tests/test_config.rs +++ b/dropshot/tests/test_config.rs @@ -8,7 +8,7 @@ use dropshot::{ ConfigDropshot, ConfigTls, HandlerTaskMode, HttpError, HttpResponseOk, RequestContext, }; -use dropshot::{HttpServer, HttpServerStarter}; +use dropshot::{HttpServer, ServerBuilder}; use slog::o; use slog::Logger; use std::str::FromStr; @@ -89,14 +89,15 @@ fn make_server( log: &Logger, tls: Option, api_description: Option>, -) -> HttpServerStarter { - HttpServerStarter::new_with_tls( - config, +) -> HttpServer { + ServerBuilder::new( api_description.unwrap_or_else(dropshot::ApiDescription::new), context, - log, - tls, + log.clone(), ) + .config(config.clone()) + .tls(tls) + .start() .unwrap() } @@ -221,7 +222,7 @@ async fn test_config_bind_address_http() { bind_port, HandlerTaskMode::CancelOnDisconnect, ); - make_server(0, &config, &self.log, None, None).start() + make_server(0, &config, &self.log, None, None) } } @@ -293,7 +294,7 @@ async fn test_config_bind_address_https() { bind_port, HandlerTaskMode::CancelOnDisconnect, ); - make_server(0, &config, &self.log, tls, None).start() + make_server(0, &config, &self.log, tls, None) } } @@ -374,7 +375,7 @@ async fn test_config_bind_address_https_buffer() { bind_port, HandlerTaskMode::CancelOnDisconnect, ); - make_server(0, &config, &self.log, tls, None).start() + make_server(0, &config, &self.log, tls, None) } } @@ -503,8 +504,7 @@ impl TestConfigBindServer let mut api = dropshot::ApiDescription::new(); api.register(track_cancel_endpoint).unwrap(); - let server = - make_server(context, &config, &self.log, None, Some(api)).start(); + let server = make_server(context, &config, &self.log, None, Some(api)); self.bound_port.store(server.local_addr().port(), Ordering::SeqCst); diff --git a/dropshot/tests/test_starter.rs b/dropshot/tests/test_starter.rs new file mode 100644 index 00000000..bb90ef52 --- /dev/null +++ b/dropshot/tests/test_starter.rs @@ -0,0 +1,104 @@ +// Copyright 2024 Oxide Computer Company + +//! Quick check that the "legacy" HttpServerStarter::new() and +//! HttpServerStarter::new_with_tls() interfaces work. + +pub mod common; + +use common::create_log_context; +use dropshot::endpoint; +use dropshot::test_util::read_json; +use dropshot::test_util::ClientTestContext; +use dropshot::ApiDescription; +use dropshot::ConfigDropshot; +use dropshot::ConfigTls; +use dropshot::HttpError; +use dropshot::HttpResponseOk; +use dropshot::HttpServerStarter; +use dropshot::RequestContext; + +extern crate slog; + +/// Test starting a server with `HttpServerStarter::new()`. +#[tokio::test] +async fn test_no_tls() { + let api = demo_api(); + let logctx = create_log_context("test_no_tls"); + let starter = + HttpServerStarter::new(&ConfigDropshot::default(), api, 0, &logctx.log) + .unwrap(); + let server = starter.start(); + let server_addr = server.local_addr(); + let client = ClientTestContext::new(server_addr, logctx.log.clone()); + let mut response = client + .make_request_no_body(http::Method::GET, "/demo", http::StatusCode::OK) + .await + .unwrap(); + let json: String = read_json(&mut response).await; + assert_eq!(json, "demo"); + + logctx.cleanup_successful(); +} + +/// Test starting a server with `HttpServerStarter::new_with_tls()`. +#[tokio::test] +async fn test_with_tls() { + let logctx = create_log_context("test_with_tls"); + + // Generate key for the server + let (certs, key) = common::generate_tls_key(); + let (serialized_certs, serialized_key) = + common::tls_key_to_buffer(&certs, &key); + let config_tls = Some(ConfigTls::AsBytes { + certs: serialized_certs.clone(), + key: serialized_key.clone(), + }); + + // Use Rustls-based TLS because the native TLS on Windows does not support + // appear to support adding the rcgen-generated P-256 certificate in this + // way. + let certs = + reqwest::Certificate::from_pem_bundle(&serialized_certs).unwrap(); + let my_ca_root = + certs.into_iter().last().expect("at least one certificate"); + let client = reqwest::Client::builder() + .use_rustls_tls() + .add_root_certificate(my_ca_root) + .build() + .unwrap(); + let api = demo_api(); + let starter = HttpServerStarter::new_with_tls( + &ConfigDropshot::default(), + api, + 0, + &logctx.log, + config_tls, + ) + .unwrap(); + let server = starter.start(); + let server_addr = server.local_addr(); + // It would be nice to just write the whole sockaddr into the URL. But + // for TLS, we're required to use a server name and not an IP address here. + let url = format!("https://localhost:{}/demo", server_addr.port()); + let response = client.get(&url).send().await.unwrap(); + assert_eq!(response.status(), reqwest::StatusCode::OK); + assert_eq!(response.json::().await.unwrap(), "demo"); + + logctx.cleanup_successful(); +} + +#[endpoint { + method = GET, + path = "/demo", +}] +async fn demo_handler( + _rqctx: RequestContext, +) -> Result, HttpError> { + Ok(HttpResponseOk(String::from("demo"))) +} + +fn demo_api() -> ApiDescription { + let mut api = ApiDescription::::new(); + api.register(demo_handler).unwrap(); + api +} diff --git a/dropshot/tests/test_tls.rs b/dropshot/tests/test_tls.rs index 3b6f14d7..9ac700ca 100644 --- a/dropshot/tests/test_tls.rs +++ b/dropshot/tests/test_tls.rs @@ -4,8 +4,8 @@ //! mode, including certificate loading and supported modes. use dropshot::{ - ConfigDropshot, ConfigTls, HandlerTaskMode, HttpResponseOk, - HttpServerStarter, + ConfigDropshot, ConfigTls, HandlerTaskMode, HttpResponseOk, HttpServer, + ServerBuilder, }; use slog::{o, Logger}; use std::convert::TryFrom; @@ -112,10 +112,10 @@ fn make_https_client< } fn make_server( - log: &Logger, + log: Logger, cert_file: &Path, key_file: &Path, -) -> HttpServerStarter { +) -> HttpServer { let config = ConfigDropshot { bind_address: "127.0.0.1:0".parse().unwrap(), request_body_max_bytes: 1024, @@ -126,14 +126,11 @@ fn make_server( cert_file: cert_file.to_path_buf(), key_file: key_file.to_path_buf(), }); - HttpServerStarter::new_with_tls( - &config, - dropshot::ApiDescription::new(), - 0, - log, - config_tls, - ) - .unwrap() + ServerBuilder::new(dropshot::ApiDescription::new(), 0, log) + .config(config) + .tls(config_tls) + .start() + .unwrap() } fn make_pki_verifier( @@ -155,7 +152,7 @@ async fn test_tls_certificate_loading() { let (certs, key) = common::generate_tls_key(); let (cert_file, key_file) = common::tls_key_to_file(&certs, &key); - let server = make_server(&log, cert_file.path(), key_file.path()).start(); + let server = make_server(log, cert_file.path(), key_file.path()); let port = server.local_addr().port(); let uri: hyper::Uri = @@ -210,7 +207,7 @@ async fn test_tls_only() { let (certs, key) = common::generate_tls_key(); let (cert_file, key_file) = common::tls_key_to_file(&certs, &key); - let server = make_server(&log, cert_file.path(), key_file.path()).start(); + let server = make_server(log, cert_file.path(), key_file.path()); let port = server.local_addr().port(); let https_uri: hyper::Uri = @@ -264,7 +261,7 @@ async fn test_tls_refresh_certificates() { let (certs, key) = generate_tls_key(); let (cert_file, key_file) = common::tls_key_to_file(&certs, &key); - let server = make_server(&log, cert_file.path(), key_file.path()).start(); + let server = make_server(log, cert_file.path(), key_file.path()); let port = server.local_addr().port(); let https_uri: hyper::Uri = @@ -356,7 +353,7 @@ async fn test_tls_aborted_negotiation() { let (certs, key) = common::generate_tls_key(); let (cert_file, key_file) = common::tls_key_to_file(&certs, &key); - let server = make_server(&log, cert_file.path(), key_file.path()).start(); + let server = make_server(log, cert_file.path(), key_file.path()); let port = server.local_addr().port(); let uri: hyper::Uri = @@ -445,10 +442,11 @@ async fn test_server_is_https() { }); let mut api = dropshot::ApiDescription::new(); api.register(tls_check_handler).unwrap(); - let server = - HttpServerStarter::new_with_tls(&config, api, 0, &log, config_tls) - .unwrap() - .start(); + let server = ServerBuilder::new(api, 0, log) + .tls(config_tls) + .config(config) + .start() + .unwrap(); let port = server.local_addr().port(); let https_client = make_https_client(make_pki_verifier(&certs)); diff --git a/dropshot_endpoint/src/api_trait.rs b/dropshot_endpoint/src/api_trait.rs index 789aa4a3..2a87e4b7 100644 --- a/dropshot_endpoint/src/api_trait.rs +++ b/dropshot_endpoint/src/api_trait.rs @@ -955,17 +955,13 @@ async fn main() {{ let context = /* some value of type `{trait_ident}Impl::{context_ident}` */; // Create a Dropshot server from the description. - let config = dropshot::ConfigDropshot::default(); let log = /* ... */; - let server = dropshot::HttpServerStarter::new( - &config, - description, - context, - &log, - ).unwrap(); + let server = dropshot::ServerBuilder::new(description, context, log) + .start() + .unwrap(); // Run the server. - server.start().await + server.await }} ``` diff --git a/dropshot_endpoint/tests/output/api_trait_basic.rs b/dropshot_endpoint/tests/output/api_trait_basic.rs index 99eada81..3229ce06 100644 --- a/dropshot_endpoint/tests/output/api_trait_basic.rs +++ b/dropshot_endpoint/tests/output/api_trait_basic.rs @@ -128,17 +128,13 @@ mod my_trait_mod { /// let context = /* some value of type `MyTraitImpl::Context` */; /// /// // Create a Dropshot server from the description. - /// let config = dropshot::ConfigDropshot::default(); /// let log = /* ... */; - /// let server = dropshot::HttpServerStarter::new( - /// &config, - /// description, - /// context, - /// &log, - /// ).unwrap(); + /// let server = dropshot::ServerBuilder::new(description, context, log) + /// .start() + /// .unwrap(); /// /// // Run the server. - /// server.start().await + /// server.await /// } /// ``` /// diff --git a/dropshot_endpoint/tests/output/api_trait_no_endpoints.rs b/dropshot_endpoint/tests/output/api_trait_no_endpoints.rs index 61fd2103..31a007f2 100644 --- a/dropshot_endpoint/tests/output/api_trait_no_endpoints.rs +++ b/dropshot_endpoint/tests/output/api_trait_no_endpoints.rs @@ -78,17 +78,13 @@ pub(crate) mod my_trait_mod { /// let context = /* some value of type `MyTraitImpl::Context` */; /// /// // Create a Dropshot server from the description. - /// let config = dropshot::ConfigDropshot::default(); /// let log = /* ... */; - /// let server = dropshot::HttpServerStarter::new( - /// &config, - /// description, - /// context, - /// &log, - /// ).unwrap(); + /// let server = dropshot::ServerBuilder::new(description, context, log) + /// .start() + /// .unwrap(); /// /// // Run the server. - /// server.start().await + /// server.await /// } /// ``` /// diff --git a/dropshot_endpoint/tests/output/api_trait_with_custom_params.rs b/dropshot_endpoint/tests/output/api_trait_with_custom_params.rs index 1997af94..7781d64d 100644 --- a/dropshot_endpoint/tests/output/api_trait_with_custom_params.rs +++ b/dropshot_endpoint/tests/output/api_trait_with_custom_params.rs @@ -147,17 +147,13 @@ pub mod my_support_module { /// let context = /* some value of type `MyTraitImpl::Situation` */; /// /// // Create a Dropshot server from the description. - /// let config = dropshot::ConfigDropshot::default(); /// let log = /* ... */; - /// let server = dropshot::HttpServerStarter::new( - /// &config, - /// description, - /// context, - /// &log, - /// ).unwrap(); + /// let server = dropshot::ServerBuilder::new(description, context, log) + /// .start() + /// .unwrap(); /// /// // Run the server. - /// server.start().await + /// server.await /// } /// ``` ///