Skip to content

Commit ff8bad4

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

File tree

1 file changed

+382
-0
lines changed

1 file changed

+382
-0
lines changed

docs/ROADMAP.md

+382
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,382 @@
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

Comments
 (0)