|
| 1 | +# hyper 1.0 Roadmap |
| 2 | + |
| 3 | +## Goal |
| 4 | + |
| 5 | +Align current hyper to the [hyper VISION][VISION]. |
| 6 | + |
| 7 | +The VISION outlines a decision-making framework, use-cases, and general shape |
| 8 | +of hyper. This roadmap describes the currently known problems with hyper, and |
| 9 | +then shows what changes are needed to make hyper 1.0 look more like what is in |
| 10 | +the VISION. |
| 11 | + |
| 12 | +## Known Issues |
| 13 | + |
| 14 | + |
| 15 | +> **Note**: These known issues are as of hyper v0.14.x. After v1.0 is released, |
| 16 | +ideally these issues will have been solved. Keeping this history may be helpful |
| 17 | +to Future Us, though. |
| 18 | + |
| 19 | +### Higher-level Client and Server problems |
| 20 | + |
| 21 | +Both the higher-level `Client` and `Server` types have stability concerns. |
| 22 | + |
| 23 | +For the `hyper::Server`: |
| 24 | + |
| 25 | +- The `Accept` trait is complex, and too easy to get wrong. If used with TLS, a |
| 26 | +slow TLS handshake can affect all other new connections waiting for it to |
| 27 | +finish. - The `MakeService<&IO>` is confusing. The bounds are an assault on |
| 28 | +the eyes. - The `MakeService` API doesn't allow to easily annotate the HTTP |
| 29 | +connection with `tracing`. - Graceful shutdown doesn't give enough control. |
| 30 | + |
| 31 | + |
| 32 | +It's more common for people to simply use `hyper::server::conn` at this point, |
| 33 | +than to bother with the `hyper::Server`. |
| 34 | + |
| 35 | +While the `hyper::Client` is much easier to use, problems still exist: |
| 36 | + |
| 37 | +- The whole `Connect` design isn't stable. |
| 38 | + - ALPN and proxies can provide surprising extra configuration of connections. |
| 39 | + - Some `Connect` implementations may wish to view the path, in addition to the scheme, host, and port. |
| 40 | + - Wants `runtime` feature |
| 41 | +- The Pool could be made more general or composable. At the same time, more customization is |
| 42 | + desired, and it's not clear |
| 43 | +how to expose it yet. |
| 44 | + |
| 45 | + |
| 46 | +### Runtime woes |
| 47 | + |
| 48 | +hyper has been able to support different runtimes, but it has sometimes awkward |
| 49 | +default support for Tokio. |
| 50 | + |
| 51 | +- The `runtime` cargo-feature isn't additive |
| 52 | +- Built-in Tokio support can be confusing |
| 53 | +- Executors and Timers |
| 54 | + - The `runtime` feature currently enables a few options that require a timer, such as timeouts and |
| 55 | + keepalive intervals. It implicitly relies on Tokio's timer context. This can be quite confusing. |
| 56 | +- IO traits |
| 57 | + - Should we publicly depend on Tokio's traits? |
| 58 | + - `futures-io`? |
| 59 | + - Definitely nope. |
| 60 | + - Not stable. (0.3?) |
| 61 | + - No uninitialized memory. |
| 62 | + - Eventual `std` traits? |
| 63 | + - They've been in design for years. |
| 64 | + - We cannot base our schedule on them. |
| 65 | + - When they are stable, we can: |
| 66 | + - Provide a bridge in `hyper-util`. |
| 67 | + - Consider a 2.0 of hyper. |
| 68 | + - Define our own traits, provide util wrappers? |
| 69 | + |
| 70 | +### Forwards-compatibility |
| 71 | + |
| 72 | +There's a concern about forwards-compatibility. We want to be able to add |
| 73 | +support for new HTTP features without needing a new major version. While most |
| 74 | +of `http` and `hyper` are prepared for that, there's two potential problems. |
| 75 | + |
| 76 | +- New frames on an HTTP stream (body) |
| 77 | + - Receiving a new frame type would require a new trait method |
| 78 | + - There's no way to implement a "receive unknown frame" that hyper doesn't know about. |
| 79 | + - Sending an unknown frame type would be even harder. |
| 80 | + - Besides being able to pass an "unknown" type through the trait, the user would need to be |
| 81 | + able to describe how that frame is encoded in HTTP/2/3. |
| 82 | +- New HTTP versions |
| 83 | + - HTTP/3 will require a new transport abstraction. It's not as simple as just using some |
| 84 | + `impl AsyncRead + AsyncWrite`. While HTTP/2 bundled the concept of stream creation internally, |
| 85 | + and thus could be managed wholy on top of a read-write transport, HTTP/3 is different. Stream |
| 86 | + creation is shifted to the QUIC protocol, and HTTP/3 needs to be able to use that directly. |
| 87 | + - This means the existing `Connection` types for both client and server will not be able to |
| 88 | + accept a QUIC transport so we can add HTTP/3 support. |
| 89 | + |
| 90 | +### Errors |
| 91 | + |
| 92 | +It's not easy to match for specific errors. |
| 93 | + |
| 94 | +The `Error::source()` can leak an internal dependency. For example, a |
| 95 | +`hyper::Error` may wrap an `h2::Error`. Users can downcast the source at |
| 96 | +runtime, and hyper internally changing the version of its `h2` dependency can |
| 97 | +cause runtime breakage for users. |
| 98 | + |
| 99 | +Formatting errors is in conflict with the current expected norm. The |
| 100 | +`fmt::Display` implementation for `hyper::Error` currently prints its own |
| 101 | +message, and then prints the message of any wrapped source error. The Errors |
| 102 | +Working Group currently recommends that errors only print their own message |
| 103 | +(link?). This conflict means that error "reporters", which crawl a source chain |
| 104 | +and print each error, has a lot of duplicated information. |
| 105 | + |
| 106 | +``` |
| 107 | +error fetching website: error trying to connect: tcp connect error: Connection refused (os error 61) |
| 108 | +tcp connect error: Connection refused (os error 61) |
| 109 | +Connection refused (os error 61) |
| 110 | +``` |
| 111 | + |
| 112 | +While there is a good reason for why hyper's `Error` types do this, at the very |
| 113 | +least, it _is_ unfortunate. |
| 114 | + |
| 115 | +### You call hyper, or hyper calls you? |
| 116 | + |
| 117 | +> Note: this problem space, of who calls whom, will be explored more deeply in |
| 118 | +> a future article. |
| 119 | +
|
| 120 | +At times, it's been wondered whether hyper should call user code, or if user |
| 121 | +code should call hyper. For instance, should a `Service` be called with a |
| 122 | +request when the connection receives one, or should the user always poll for |
| 123 | +the next request. |
| 124 | + |
| 125 | +There's a similar question around sending a message body. Should hyper ask the |
| 126 | +body for more data to write, or should the user call a `write` method directly? |
| 127 | + |
| 128 | +These both get at a root topic about [write |
| 129 | +observability](https://github.com/hyperium/hyper/issues/2181). How do you know |
| 130 | +when a response, or when body data, has been written successfully? This is |
| 131 | +desirable for metrics, or for triggering other side-effects. |
| 132 | + |
| 133 | +The `Service` trait also has some other frequently mentioned issues. Does |
| 134 | +`poll_ready` pull its complexity weight for servers? What about returning |
| 135 | +errors, what does that mean? Ideally users would turn all errors into |
| 136 | +appropriate `http::Response`s. But in HTTP/2 and beyond, stream errors are |
| 137 | +different from HTTP Server Error responses. Could the `Service::Error` type do |
| 138 | +more to encourage best practices? |
| 139 | + |
| 140 | +## Design |
| 141 | + |
| 142 | +The goal is to get hyper closer to the [VISION][], using that to determine the |
| 143 | +best way to solve the known issues above. The main thrust of the proposed |
| 144 | +changes are to make hyper more **Flexible** and stable. |
| 145 | + |
| 146 | +In order to keep hyper **Understandable**, however, the proposed changes *must* |
| 147 | +be accompanied by providing utilities that solve the common usage patterns, |
| 148 | +documentation explaining how to use the more flexible pieces, and guides on how |
| 149 | +to reach for the `hyper-util`ity belt. |
| 150 | + |
| 151 | +The majority of the changes are smaller and can be contained to the *Public |
| 152 | +API* section, since they usually only apply to a single module or type. But the |
| 153 | +biggest changes are explained in detail here. |
| 154 | + |
| 155 | +### Split per HTTP version |
| 156 | + |
| 157 | +The existing `Connection` types, both for the client and server, abstract over |
| 158 | +HTTP version by requiring a generic `AsyncRead + AsyncWrite` transport type. |
| 159 | +But as we figure out HTTP/3, that needs to change. So to prepare now, the |
| 160 | +`Connection` types will be split up. |
| 161 | + |
| 162 | +For example, there will now be `hyper::server::conn::http1::Connection` and |
| 163 | +`hyper::server::conn::http2::Connection` types. |
| 164 | + |
| 165 | +These specific types will still have a very similar looking API that, as the |
| 166 | +VISION describes, provides **Correct** connection management as it pertains to |
| 167 | +HTTP. |
| 168 | + |
| 169 | +There will be still be a type to wrap the different versions. It will no longer |
| 170 | +be generic over the transport type, to prepare for being able to wrap HTTP/3 |
| 171 | +connections. Exactly how it will wrap, either by using internal trait objects, |
| 172 | +or an `enum Either` style, or using a `trait Connection` that each type |
| 173 | +implements, is something to be determined. It's likely that this "auto" type |
| 174 | +will start in `hyper-util`. |
| 175 | + |
| 176 | +### Focus on the `Connection` level |
| 177 | + |
| 178 | +As mentioned in the *Known Issues*, the higher-level `Client` and `Server` have |
| 179 | +stability and complexity problems. Therefore, for hyper 1.0, the main API will |
| 180 | +focus on the "lower-level" connection types. The `Client` and `Server` helpers |
| 181 | +will be moved to `hyper-util`. |
| 182 | + |
| 183 | +## Public API |
| 184 | + |
| 185 | +### body |
| 186 | + |
| 187 | +The `Body` struct is removed. Its internal "variants" are [separated into |
| 188 | +distinct types](https://github.com/hyperium/hyper/issues/2345), and can start |
| 189 | +in either `hyper-utils` or `http-body-utils`. |
| 190 | + |
| 191 | +The exported trait `HttpBody` is renamed to `Body`. |
| 192 | + |
| 193 | +A single `Body` implementation in `hyper` is the one provided by receiving |
| 194 | +client responses and server requests. It has the name `Streaming`. |
| 195 | + |
| 196 | +> **Unresolved**: Other names can be considered during implementation. Another |
| 197 | +> option is to not publicly name the implementation, but return `Response<impl |
| 198 | +Body>`s. |
| 199 | +
|
| 200 | +The `Body` trait will be experimented on to see about making it possible to |
| 201 | +return more frame types beyonds just data and trailers. |
| 202 | + |
| 203 | +> **Unresolved**: What exactly this looks like will only be known after |
| 204 | +> experimentation. |
| 205 | +
|
| 206 | +### client |
| 207 | + |
| 208 | +The high-level `hyper::Client` will be removed, along with the |
| 209 | +`hyper::client::connect` module. They will be explored more in `hyper-util`. |
| 210 | + |
| 211 | +As described in *Design*, the `client::conn` module will gain `http1` and |
| 212 | +`http2` sub-modules, providing per-version `SendRequest`, `Connection`, and |
| 213 | +`Builder` structs. An `auto` version can be explored in `hyper-util`. |
| 214 | + |
| 215 | +### error |
| 216 | + |
| 217 | +The `hyper::Error` struct remains in place. |
| 218 | + |
| 219 | +All errors returned from `Error::source()` are made opaque. They are wrapped an |
| 220 | +internal `Opaque` newtype that still allows printing, but prevents downcasting |
| 221 | +to the internal dependency. |
| 222 | + |
| 223 | +A new `hyper::error::Code` struct is defined. It is an opaque struct, with |
| 224 | +associated constants defining various code variants. |
| 225 | + |
| 226 | +> Alternative: define a non-exhaustive enum. It's not clear that this is |
| 227 | +> definitely better, though. Keeping it an opaque struct means we can add |
| 228 | +> secondary parts to the code in the future, or add bit flags, or similar |
| 229 | +> extensions. |
| 230 | +
|
| 231 | +The purpose of `Code` is to provide an abstraction over the kind of error that |
| 232 | +is encountered. The `Code` could be some behavior noticed inside hyper, such as |
| 233 | +an incomplete HTTP message. Or it can be "translated" from the underlying |
| 234 | +protocol, if it defines protocol level errors. For example, an |
| 235 | +`h2::Reason::CANCEL`. |
| 236 | + |
| 237 | +### rt |
| 238 | + |
| 239 | +The `Executor` trait stays in here. |
| 240 | + |
| 241 | +Define a new trait `Timer`, which describes a way for users to provide a source |
| 242 | +of sleeping/timeout futures. Similar to `Executor`, a new generic is added to |
| 243 | +connection builders to provide a `Timer`. |
| 244 | + |
| 245 | +### server |
| 246 | + |
| 247 | +The higher-level `hyper::Server` struct, its related `Builder`, and the |
| 248 | +`Accept` trait are all removed. |
| 249 | + |
| 250 | +The `AddrStream` struct will be completely removed, as it provides no value but |
| 251 | +causes binary bloat. |
| 252 | + |
| 253 | +Similar to `client`, and as describe in the *Design*, the `conn` modules will |
| 254 | +be expanded to support `http1` and `http2` submodules. An `auto` version can be |
| 255 | +explored in `hyper-util`. |
| 256 | + |
| 257 | +### service |
| 258 | + |
| 259 | +A vendored and simplified `Service` trait will be explored. |
| 260 | + |
| 261 | +The error type for `Service`s used for a server will explore having the return |
| 262 | +type changed from any error to one that can become a `hyper::error::Code`. |
| 263 | + |
| 264 | +> **Unresolved**: Both of the above points are not set in stone. We will |
| 265 | +> explore and decide if they are the best outcome during development. |
| 266 | +
|
| 267 | +The `MakeService` pieces will be removed. |
| 268 | + |
| 269 | +### Cargo Features |
| 270 | + |
| 271 | +Remove the `stream` feature. The `Stream` trait is not stable, and we cannot |
| 272 | +depend on an unstable API. |
| 273 | + |
| 274 | +Remove the `tcp` and `runtime` features. The automatic executor and timer parts |
| 275 | +are handled by providing implementations of `Executor` and `Timer`. The |
| 276 | +`connect` and `Accept` parts are also moving to `hyper-util`. |
| 277 | + |
| 278 | +### Public Dependencies |
| 279 | + |
| 280 | +- `http` - `http-body`? - `bytes` - `tower-service`? |
| 281 | + |
| 282 | +Cannot be public while "unstable": |
| 283 | + |
| 284 | +- `tracing` |
| 285 | + |
| 286 | +## `hyper-util` |
| 287 | + |
| 288 | + |
| 289 | +### body |
| 290 | + |
| 291 | +A channel implementation of `Body` that has an API to know when the data has |
| 292 | +been successfully written is provided in `hyper_util::body::channel`. |
| 293 | + |
| 294 | +### client |
| 295 | + |
| 296 | +A `Pool` struct that implements `Service` is provided. It fills a similar role |
| 297 | +as the previous `hyper::Client`. |
| 298 | + |
| 299 | +> **Note**: The `Pool` might be something that goes into the `tower` crate |
| 300 | +> instead. Or it might stay here as a slightly more specialized racing-connect |
| 301 | +> pool. We'll find out as we go. |
| 302 | +
|
| 303 | +A `connect` submodule that mostly mirrors the existing `hyper::client::connect` |
| 304 | +module is moved here. Connectors can be used as a source to provide `Service`s |
| 305 | +used by the `Pool`. |
| 306 | + |
| 307 | +### rt |
| 308 | + |
| 309 | +We can provide Tokio-backed implementations of `Executor` and `Timer`. |
| 310 | + |
| 311 | +### server |
| 312 | + |
| 313 | +A `GracefulShutdown` helper is provided, to allow for similar style of graceful |
| 314 | +shutdown as the previous `hyper::Server` did, but with better control. |
| 315 | + |
| 316 | +# Appendix |
| 317 | + |
| 318 | +## Unresolved Questions |
| 319 | + |
| 320 | +There are some parts of the proposal which are not fully resolved. They are |
| 321 | +mentioned in Design and API sections above, but also collected here for easy |
| 322 | +finding. While they all have _plans_, they are more exploratory parts of the |
| 323 | +API, and thus they have a higher possibility of changing as we implement them. |
| 324 | + |
| 325 | +The goal is to have these questions resolved and removed from the document by |
| 326 | +the time there is a [Release Candidate][timeline]. |
| 327 | + |
| 328 | +### Should there be `hyper::io` traits? |
| 329 | + |
| 330 | +### Should returned body types be `impl Body`? |
| 331 | + |
| 332 | +### How could the `Body` trait prepare for unknown frames? |
| 333 | + |
| 334 | +We will experiment with this, and keep track of those experiments in a |
| 335 | +dedicated issue. It might be possible to use something like this: |
| 336 | + |
| 337 | +```rust pub trait Body { type Data; fn poll_frame(..) -> |
| 338 | +Result<Option<Frame<Self::Data>>>; } |
| 339 | + |
| 340 | +pub struct Frame<T>(Kind<T>); |
| 341 | + |
| 342 | +enum Kind<T> { Data(T), Trailers(HeaderMap), Unknown(Box<dyn FrameThingy>) } |
| 343 | +``` |
| 344 | + |
| 345 | +### Should there be a simplified `hyper::Service` trait, or should hyper depend on `tower-service`? |
| 346 | + |
| 347 | +## FAQ |
| 348 | + |
| 349 | +### Why did you pick _that_ name? Why not this other better name? |
| 350 | + |
| 351 | +Naming is hard. We certainly should solve it, but discussion for particular |
| 352 | +names for structs and traits should be scoped to the specific issues. This |
| 353 | +document is to define the shape of the library API. |
| 354 | + |
| 355 | +### Should I publicly depend on `hyper-util`? |
| 356 | + |
| 357 | +The `hyper-util` crate will not reach 1.0 when `hyper` does. Some types and |
| 358 | +traits are being moved to `hyper-util`. As with any pre-1.0 crate, you _can_ |
| 359 | +publicly depend on it, but it is explicitly less stable. |
| 360 | + |
| 361 | +In most cases, it's recommended to not publicly expose your dependency on |
| 362 | +`hyper-util`. If you depend on a trait, such as used by the moved higher-level |
| 363 | +`Client` or `Server`, it may be better for your users to define your own |
| 364 | +abstraction, and then make an internal adapter. |
| 365 | + |
| 366 | +### Isn't this making hyper harder? |
| 367 | + |
| 368 | +We are making hyper more **flexible**. As noted in the [VISION][], most use |
| 369 | +cases of hyper require it to be flexible. That _can_ mean that the exposed API |
| 370 | +is lower level, and that it feels more complicated. It should still be |
| 371 | +**understandable**. |
| 372 | + |
| 373 | +But the hyper 1.0 effort is more than just the single `hyper` crate. Many |
| 374 | +useful helpers will be migrated to a `hyper-util` crate, and likely improved in |
| 375 | +the process. The [timeline][] also points out that we will have a significant |
| 376 | +documentation push. While the flexible pieces will be in hyper to compose how |
| 377 | +they need, we will also write guides for the [hyper.rs][] showing people how to |
| 378 | +accomplish the most common tasks. |
| 379 | + |
| 380 | +[timeline]: https://seanmonstar.com/post/676912131372875776/hyper-10-timeline |
| 381 | +[VISION]: https://github.com/hyperium/hyper/pull/2772 |
| 382 | +[hyper.rs]: https://hyper.rs |
0 commit comments