Skip to content

Commit

Permalink
docs(lib): define the VISION and TENETS
Browse files Browse the repository at this point in the history
  • Loading branch information
seanmonstar committed Apr 12, 2022
1 parent 1d895b8 commit 311ba2b
Show file tree
Hide file tree
Showing 3 changed files with 334 additions and 0 deletions.
100 changes: 100 additions & 0 deletions docs/TENETS.md
@@ -0,0 +1,100 @@
# Charter

> hyper is a protective and efficient HTTP library for all.
# Tenets

Tenets are guiding principles. They guide how decisions are made for the whole
project. Ideally, we do all of them all the time. In some cases, though, we may
be forced to decide between slightly penalizing one goal or another. In that
case, we tend to support those goals that come earlier in the list over those
that come later (but every case is different).

## 0. Open

hyper is open source, always. The success of hyper depends on the health of the
community building and using it. All contributions are in the open. We don't
maintain private versions, and don't include features that aren't useful to
others.

[We prioritize kindness][CONDUCT], compassion and empathy towards all
contributors. Technical skill is not a substitute for human decency.

[CONDUCT]: https://github.com/hyperium/hyper/blob/master/docs/CODE_OF_CONDUCT.md

### Examples

It's not usually hard for an open source library to stay open and also meet its
other priorities. Here's some instances where being **Open** would be more
important than **Correct** or **Fast**:

- Say an individual were to bring forward a contribution that makes hyper more
correct, or faster, perhaps fixing some serious bug. But in doing so, they
also insulted people, harrassed other contributors or users, or shamed
everyone for the previous code. They felt their contribution was "invaluable".
We would not accept such a contribution, instead banning the user and
rewriting the code amongst the kind collaborators of the project.

- Say someone brings a contribution that adds a new feature useful for
performance or correctness, but their work accomplishes this by integrating
hyper with a proprietary library. We would not accept such a contribution,
because we don't want such a feature limited only to those users willing to
compromise openness, and we don't want to bifurcate the ecosystem between those
who make that compromise and those who don't.

## 1. Correct

hyper is a memory safe and precise implementation of the HTTP specification.
Memory safety is vital in a core Internet technology. Following the HTTP
specifications correctly protects users. It makes the software durable to the
“real world”. Where feasible, hyper enforces correct usage.

This is more than just "don't write bugs". hyper actively protects the user.

### Examples

- Even though we follow the **HTTP/\*** specs, hyper doesn't blindly implement
everything without considering if it is safe to do so.

## 2. Fast

A fast experience delights users. A faster network library means a faster
application, resulting in delighting our users’ users. Whether with one request,
or millions.

Being _fast_ means we improve throughput, drive down CPU usage, and improve
sustainability.

Fast _enough_. We don't sacrifice sanity for speed.

## 3. HTTP/*

hyper is specifically focused on HTTP. Supporting new HTTP versions is in scope,
but supporting separate protocols is not.

This also defines what the abstraction layer is: the API is designed around
sending and receiving HTTP messages.

## 4. Flexible

hyper enables as many usecases as possible. It has no opinion on application
structure, and makes few assumptions about its environment. This includes being
portable to different operating systems.

### Examples

- While we choose safer defaults to be **Correct**, hyper includes options to
_allow_ different behavior, when the user requires them.
- Providing choice usually makes things more complex, so being **Flexible** does
mean it's less _easy_. That can sometimes conflict with simplest way of making
hyper **Understandable**.

## 5. Understandable

hyper is [no more complicated than it has to
be](https://en.wikipedia.org/wiki/Occam%27s_razor). HTTP is not simple. It may
not be as "easy" as 1-line to do everything, but it shouldn't be "hard" to find
the answers.

From logical and misuse-resistant APIs, to stellar documentation, to transparent
metrics.
230 changes: 230 additions & 0 deletions docs/VISION.md
@@ -0,0 +1,230 @@
# hyper Vision

## Purpose

This is an overview of what the shape of hyper looks like, but also somewhat
zoomed out, so that the _vision_ can survive while the exact minute details
might shift and change over time.

### Charter

> hyper is a protective and efficient HTTP library for all.
### Tenets

Tenets are guiding principles. They guide how decisions are made for the whole
project. Ideally, we do all of them all the time. In some cases, though, we may
be forced to decide between slightly penalizing one goal or another. In that
case, we tend to support those goals that come earlier in the list over those
that come later (but every case is different).

0. Open
1. Correct
2. Fast
3. HTTP/\*
4. Flexible
5. Understandable

There's a lot more detail about each in [TENETS](./TENETS.md).

## Use Cases

Who are the *users* of hyper? How would they use hyper?

### Low-Level Client Library (curl, reqwest, aws-sdk)

These client libraries care that hyper is **Flexible**, since they are
expressing their own opinion on how a more-featured HTTP client should act.
This includes opinions on connection establishment, management, pooling, HTTP
version options, and even runtimes.

curl's main reason for using hyper is that it is **Safe**.

### Web Server Frameworks (deno, axum)

These are using hyper's server feature to expose a different, higher-level API
to users. Besides the obvious requirements, these require that hyper is
**Fast**. Servers are costly, handling more requests faster is important to
them.

That hyper is **Flexible** is also important, in that it needs to be flexible
enough for them to build a server framework, and allow them to express their
own opinions about API to their users.

### Services and Proxies (linkerd, cloudflare, fastly)

These are using hyper directly, likely both the client and server, in order to
build efficient and powerful services, applications, and tools for their end
users. They care greatly that hyper is **Correct**, since web traffic can
stretch the limits of what is valid HTTP, and exercise less-common parts of the
specifications.

They also require hyper to be **Fast**, for similar reasons that the web server
frameworks do.

### New Rust Web Developers

These are developers who are either new to Rust, or new to web servers, and
have reached for hyper to start with.

It's likely that these users don't have strong opinions about how an HTTP
server or client should work, just that it _should_ handle all the things they
normally assume it would. For these users, it would be best to quickly help
them compare their own expectations with hyper's capabitilities, and may
suggest reaching for higher-level, _easier_ libraries instead.

Those that stick around after that recommendation are users that wish both to
learn at a lower level, and to pick and choose what batteries they plug in to
hyper as they move along. While they do care about the other tenets, that hyper
is **Understandable** is of extra importance to them.

## The Library

So with all that context in mind, what does hyper, the library, actually look
like? This doesn't highlight what _is_ and _isn't_ present. What currently
needs to change to reach this vision is left to individual version roadmaps.

### Layers

In all cases, a user brings their own runtime and IO to work with hyper. The IO
is provided to hyper, and hyper acts on top of it. hyper returns `Future`s that
the user then decides how to poll, likely involving their runtime options.

![architecture diagram](./vision-arch.svg)


#### Protocol Codecs

hyper has dedicated codecs for the major HTTP versions. Each is internally
designed to be **Correct** and **Fast** when it comes to encoding and decoding.

The individual codecs may be implemented as sub-crates, with a less-stable
promise, to support the **Flexible** needs of some users who wish to build
their own connection management, or customize encoding and decoding beyond what
is officially supported.

#### Connection State Management

A **Correct** implementation includes more than just enforcing certain
characters when encoding and decoding. Order of frames, and flags in certain
frames can affect the state of the connection. Some examples of things enforced
at this layer:

- If a message has a `content-length`, enforce only that many bytes are read or
written.
- Reading a `Response` before a `Request` is even written implies a mismatched
reply that should be interpreted as an error.
- The presence of some headers, such as `Connection: close`, or the absence of
others, such as `content-length` and `transfer-encoding`, can mean that the
connection should terminate after the current message.
- HTTP/2 and HTTP/3 may send connection-level frames that don't pertain to any
specific transaction, and must be read and handled regardless of if a user is
currently checking for a message.

#### HTTP Role and Version Abstraction

This is the public API layer. Methods exposed are around sending and receiving
`http::Request`s and `http::Response`s, not around framing specifics of the
different versions. These are built around a client or server `Connection`
interface.

By exposing this layer publicly, we take care of the **Correct** tenet, by not
forcing the user to send the specific frames themselves. The API should be
designed in a way that a user cannot easily (if at all) create an _incorrect_
HTTP connection.

Motivated by the **Flexible** tenet, there _are_ version-specific options that
can be configured at this level, and version-specific functionality can usually
be handled via `http::Extensions`.

### Not quite stable, but utile (useful)

Beyond what is directly in the hyper crate, there are useful (utile) parts that
may not meet hyper's stability promise. Developing, experimenting, and exposing
those parts is the purpose of the `hyper-util` crate. That crate does not have
the same stability level as hyper. However, the goal is that things that other
libraries might want to expose as a public dependency do not live in
`hyper-util` forever, but rather stabilize and get promoted into `hyper`.

Exactly what gets put into `hyper-util` presently is kept in the roadmap
documents.

### Stability Promise

What even is hyper's stability promise? Does it mean we are "done"? No. Will we
ever make breaking changes again? Probably. We'll still follow the [semantic
versioning](https://semver.org).

Prior to 1.0, hyper has already only done breaking changes once a year. So 1
year isn't much of a promise. We'll have significant more use and understanding
after a few years, and that could prompt some redesign.

As of this writing, we'll promise that _major_ versions of hyper are stable for
3 years. New features will come out in _minor_ versions frequently. If it is
determined necessary to make breaking changes to the API, we'll save them for
after the 3 years.

hyper also establishes a Minimum Supported Rust Version (MSRV). hyper will
support Rust versions at least 6 months old. If a new Rust version is released
with a feature hyper wishes to use, we won't do so until at least 6 months
afterwards. hyper will only ever require a new Rust version as a _minor_
release (1.x), not as a patch (1.x.y).

## Security

The security of hyper is a large part of what makes hyper _protective_. We make
hyper secure via the combined efforts of being **Correct**, focusing on
**HTTP/\***, and making it all **Understandable**.

### Memory Safety

Being **Correct** requires that hyper be memory-safe. Using the Rust language
gets us most of the way there. But there is the ability to write `unsafe`
Rust. Does being **Correct** mean that we can _never_ write `unsafe` code
anywhere? Even if it helps make hyper **Fast**? We can, carefully.

How do we balance the two, so that hyper is secure?

hyper prefers not to have large modules of intertwined `unsafe` code. hyper
does allow small `unsafe` blocks, no more than a few lines, where it's easier
to verify that the `unsafe` code was written **Correctly**.

### Meticulous Testing

hyper's test suite grows and grows. There's a lot that needs to be right.
Parsers, encoders, state machines. When easily isolated, those pieces have
internal unit tests. But hyper also keeps a large list of growing integration
tests that make sure all the parts are **Correct**.

Making writing new tests easy is a high priority. Investing in the testing
infrastructure is a proven way to make sure hyper stays **Correct** and secure.

### Constant Fuzzing

One thing is to know specific cases to test for. But we can't know all the
inputs or states that *might* cause a bug. That's why hyper has rounds of
fuzzing built into its CI. It's also why hyper signs up for and uses resources
to provide *constant*, around-the-clock fuzzing, always looking for something
that hyper should be hardened against.

### Security Process

hyper has an outlined
[SECURITY](https://github.com/hyperium/hyper/blob/master/SECURITY.md) process,
so we can safely report and fix issues.

## Non-goals

After writing this up, it is easier to articulate what sorts of things many
might associate with an HTTP library, but which are explicitly *not* for hyper.
These are all things that definitely **out of scope**.

- TLS: We learned early that bundling TLS directly in hyper [has
problems](https://github.com/hyperium/hyper/issues/985). People also have
very strong opinions about which TLS implementation to use. The design of
hyper allows users to bring their own TLS.
- Routing
- Cookies
- Not-HTTP: WebSockets, or other protocols that are built next to HTTP. It
should be possible to _use_ hyper to upgrade, but the actual next-protocol
should be handled by a different library.

0 comments on commit 311ba2b

Please sign in to comment.