Skip to content

Commit c9f21d1

Browse files
committed
docs(lib): propose 1.0 roadmap
1 parent 53f15e5 commit c9f21d1

File tree

1 file changed

+380
-0
lines changed

1 file changed

+380
-0
lines changed

docs/ROADMAP.md

Lines changed: 380 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,380 @@
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+
``` error fetching website: error trying to connect: tcp connect error:
107+
Connection refused (os error 61) tcp connect error: Connection refused (os
108+
error 61) Connection refused (os error 61) ```
109+
110+
While there is a good reason for why hyper's `Error` types do this, at the very
111+
least, it _is_ unfortunate.
112+
113+
### You call hyper, or hyper calls you?
114+
115+
> Note: this problem space, of who calls whom, will be explored more deeply in
116+
> a future article.
117+
118+
At times, it's been wondered whether hyper should call user code, or if user
119+
code should call hyper. For instance, should a `Service` be called with a
120+
request when the connection receives one, or should the user always poll for
121+
the next request.
122+
123+
There's a similar question around sending a message body. Should hyper ask the
124+
body for more data to write, or should the user call a `write` method directly?
125+
126+
These both get at a root topic about [write
127+
observability](https://github.com/hyperium/hyper/issues/2181). How do you know
128+
when a response, or when body data, has been written successfully? This is
129+
desirable for metrics, or for triggering other side-effects.
130+
131+
The `Service` trait also has some other frequently mentioned issues. Does
132+
`poll_ready` pull its complexity weight for servers? What about returning
133+
errors, what does that mean? Ideally users would turn all errors into
134+
appropriate `http::Response`s. But in HTTP/2 and beyond, stream errors are
135+
different from HTTP Server Error responses. Could the `Service::Error` type do
136+
more to encourage best practices?
137+
138+
## Design
139+
140+
The goal is to get hyper closer to the [VISION][], using that to determine the
141+
best way to solve the known issues above. The main thrust of the proposed
142+
changes are to make hyper more **Flexible** and stable.
143+
144+
In order to keep hyper **Understandable**, however, the proposed changes *must*
145+
be accompanied by providing utilities that solve the common usage patterns,
146+
documentation explaining how to use the more flexible pieces, and guides on how
147+
to reach for the `hyper-util`ity belt.
148+
149+
The majority of the changes are smaller and can be contained to the *Public
150+
API* section, since they usually only apply to a single module or type. But the
151+
biggest changes are explained in detail here.
152+
153+
### Split per HTTP version
154+
155+
The existing `Connection` types, both for the client and server, abstract over
156+
HTTP version by requiring a generic `AsyncRead + AsyncWrite` transport type.
157+
But as we figure out HTTP/3, that needs to change. So to prepare now, the
158+
`Connection` types will be split up.
159+
160+
For example, there will now be `hyper::server::conn::http1::Connection` and
161+
`hyper::server::conn::http2::Connection` types.
162+
163+
These specific types will still have a very similar looking API that, as the
164+
VISION describes, provides **Correct** connection management as it pertains to
165+
HTTP.
166+
167+
There will be still be a type to wrap the different versions. It will no longer
168+
be generic over the transport type, to prepare for being able to wrap HTTP/3
169+
connections. Exactly how it will wrap, either by using internal trait objects,
170+
or an `enum Either` style, or using a `trait Connection` that each type
171+
implements, is something to be determined. It's likely that this "auto" type
172+
will start in `hyper-util`.
173+
174+
### Focus on the `Connection` level
175+
176+
As mentioned in the *Known Issues*, the higher-level `Client` and `Server` have
177+
stability and complexity problems. Therefore, for hyper 1.0, the main API will
178+
focus on the "lower-level" connection types. The `Client` and `Server` helpers
179+
will be moved to `hyper-util`.
180+
181+
## Public API
182+
183+
### body
184+
185+
The `Body` struct is removed. Its internal "variants" are [separated into
186+
distinct types](https://github.com/hyperium/hyper/issues/2345), and can start
187+
in either `hyper-utils` or `http-body-utils`.
188+
189+
The exported trait `HttpBody` is renamed to `Body`.
190+
191+
A single `Body` implementation in `hyper` is the one provided by receiving
192+
client responses and server requests. It has the name `Streaming`.
193+
194+
> **Unresolved**: Other names can be considered during implementation. Another
195+
> option is to not publicly name the implementation, but return `Response<impl
196+
Body>`s.
197+
198+
The `Body` trait will be experimented on to see about making it possible to
199+
return more frame types beyonds just data and trailers.
200+
201+
> **Unresolved**: What exactly this looks like will only be known after
202+
> experimentation.
203+
204+
### client
205+
206+
The high-level `hyper::Client` will be removed, along with the
207+
`hyper::client::connect` module. They will be explored more in `hyper-util`.
208+
209+
As described in *Design*, the `client::conn` module will gain `http1` and
210+
`http2` sub-modules, providing per-version `SendRequest`, `Connection`, and
211+
`Builder` structs. An `auto` version can be explored in `hyper-util`.
212+
213+
### error
214+
215+
The `hyper::Error` struct remains in place.
216+
217+
All errors returned from `Error::source()` are made opaque. They are wrapped an
218+
internal `Opaque` newtype that still allows printing, but prevents downcasting
219+
to the internal dependency.
220+
221+
A new `hyper::error::Code` struct is defined. It is an opaque struct, with
222+
associated constants defining various code variants.
223+
224+
> Alternative: define a non-exhaustive enum. It's not clear that this is
225+
> definitely better, though. Keeping it an opaque struct means we can add
226+
> secondary parts to the code in the future, or add bit flags, or similar
227+
> extensions.
228+
229+
The purpose of `Code` is to provide an abstraction over the kind of error that
230+
is encountered. The `Code` could be some behavior noticed inside hyper, such as
231+
an incomplete HTTP message. Or it can be "translated" from the underlying
232+
protocol, if it defines protocol level errors. For example, an
233+
`h2::Reason::CANCEL`.
234+
235+
### rt
236+
237+
The `Executor` trait stays in here.
238+
239+
Define a new trait `Timer`, which describes a way for users to provide a source
240+
of sleeping/timeout futures. Similar to `Executor`, a new generic is added to
241+
connection builders to provide a `Timer`.
242+
243+
### server
244+
245+
The higher-level `hyper::Server` struct, its related `Builder`, and the
246+
`Accept` trait are all removed.
247+
248+
The `AddrStream` struct will be completely removed, as it provides no value but
249+
causes binary bloat.
250+
251+
Similar to `client`, and as describe in the *Design*, the `conn` modules will
252+
be expanded to support `http1` and `http2` submodules. An `auto` version can be
253+
explored in `hyper-util`.
254+
255+
### service
256+
257+
A vendored and simplified `Service` trait will be explored.
258+
259+
The error type for `Service`s used for a server will explore having the return
260+
type changed from any error to one that can become a `hyper::error::Code`.
261+
262+
> **Unresolved**: Both of the above points are not set in stone. We will
263+
> explore and decide if they are the best outcome during development.
264+
265+
The `MakeService` pieces will be removed.
266+
267+
### Cargo Features
268+
269+
Remove the `stream` feature. The `Stream` trait is not stable, and we cannot
270+
depend on an unstable API.
271+
272+
Remove the `tcp` and `runtime` features. The automatic executor and timer parts
273+
are handled by providing implementations of `Executor` and `Timer`. The
274+
`connect` and `Accept` parts are also moving to `hyper-util`.
275+
276+
### Public Dependencies
277+
278+
- `http` - `http-body`? - `bytes` - `tower-service`?
279+
280+
Cannot be public while "unstable":
281+
282+
- `tracing`
283+
284+
## `hyper-util`
285+
286+
287+
### body
288+
289+
A channel implementation of `Body` that has an API to know when the data has
290+
been successfully written is provided in `hyper_util::body::channel`.
291+
292+
### client
293+
294+
A `Pool` struct that implements `Service` is provided. It fills a similar role
295+
as the previous `hyper::Client`.
296+
297+
> **Note**: The `Pool` might be something that goes into the `tower` crate
298+
> instead. Or it might stay here as a slightly more specialized racing-connect
299+
> pool. We'll find out as we go.
300+
301+
A `connect` submodule that mostly mirrors the existing `hyper::client::connect`
302+
module is moved here. Connectors can be used as a source to provide `Service`s
303+
used by the `Pool`.
304+
305+
### rt
306+
307+
We can provide Tokio-backed implementations of `Executor` and `Timer`.
308+
309+
### server
310+
311+
A `GracefulShutdown` helper is provided, to allow for similar style of graceful
312+
shutdown as the previous `hyper::Server` did, but with better control.
313+
314+
# Appendix
315+
316+
## Unresolved Questions
317+
318+
There are some parts of the proposal which are not fully resolved. They are
319+
mentioned in Design and API sections above, but also collected here for easy
320+
finding. While they all have _plans_, they are more exploratory parts of the
321+
API, and thus they have a higher possibility of changing as we implement them.
322+
323+
The goal is to have these questions resolved and removed from the document by
324+
the time there is a [Release Candidate][timeline].
325+
326+
### Should there be `hyper::io` traits?
327+
328+
### Should returned body types be `impl Body`?
329+
330+
### How could the `Body` trait prepare for unknown frames?
331+
332+
We will experiment with this, and keep track of those experiments in a
333+
dedicated issue. It might be possible to use something like this:
334+
335+
```rust pub trait Body { type Data; fn poll_frame(..) ->
336+
Result<Option<Frame<Self::Data>>>; }
337+
338+
pub struct Frame<T>(Kind<T>);
339+
340+
enum Kind<T> { Data(T), Trailers(HeaderMap), Unknown(Box<dyn FrameThingy>) }
341+
```
342+
343+
### Should there be a simplified `hyper::Service` trait, or should hyper depend on `tower-service`?
344+
345+
## FAQ
346+
347+
### Why did you pick _that_ name? Why not this other better name?
348+
349+
Naming is hard. We certainly should solve it, but discussion for particular
350+
names for structs and traits should be scoped to the specific issues. This
351+
document is to define the shape of the library API.
352+
353+
### Should I publicly depend on `hyper-util`?
354+
355+
The `hyper-util` crate will not reach 1.0 when `hyper` does. Some types and
356+
traits are being moved to `hyper-util`. As with any pre-1.0 crate, you _can_
357+
publicly depend on it, but it is explicitly less stable.
358+
359+
In most cases, it's recommended to not publicly expose your dependency on
360+
`hyper-util`. If you depend on a trait, such as used by the moved higher-level
361+
`Client` or `Server`, it may be better for your users to define your own
362+
abstraction, and then make an internal adapter.
363+
364+
### Isn't this making hyper harder?
365+
366+
We are making hyper more **flexible**. As noted in the [VISION][], most use
367+
cases of hyper require it to be flexible. That _can_ mean that the exposed API
368+
is lower level, and that it feels more complicated. It should still be
369+
**understandable**.
370+
371+
But the hyper 1.0 effort is more than just the single `hyper` crate. Many
372+
useful helpers will be migrated to a `hyper-util` crate, and likely improved in
373+
the process. The [timeline][] also points out that we will have a significant
374+
documentation push. While the flexible pieces will be in hyper to compose how
375+
they need, we will also write guides for the [hyper.rs][] showing people how to
376+
accomplish the most common tasks.
377+
378+
[timeline]: https://seanmonstar.com/post/676912131372875776/hyper-10-timeline
379+
[VISION]: https://github.com/hyperium/hyper/pull/2772
380+
[hyper.rs]: https://hyper.rs

0 commit comments

Comments
 (0)