LICENSE Documentation

Official TypeScript developer resources and documentation for hyperium/LICENSE. Verified technical context for AI.

0 stars TypeScript TypeScript 1 file · ~56,401 tokens
RAW Doc

1. Project Overview & Quickstart (hyperium/LICENSE)

LICENSE

Open-source repository hyperium/LICENSE

Repository Details

Note: High-volume repository documentation is actively indexed and synchronized by YakaAI.

2. Official Technical Reference & Guides (hyperium/hyperium.github.io)

File: _config.yml

Site settings

title: hyper - fast and safe HTTP for the Rust language
baseurl: "/" # the subpath of your site, e.g. /blog/
url: "https://hyper.rs" # the base hostname & protocol for your site

defaults:

text
scope:
  path: "_legacy/"
  type: "guides"
values:
  layout: "guide"
  • scope:
    path: "_stable/"
    type: "guides"
    values:
    layout: "guide"
  • scope:
    path: "_contrib/"
    type: "guides"
    values:
    layout: "guide"

collections:
legacy:
permalink: /guides/0.14/:path/
output: true
stable:
permalink: /guides/1/:path/
output: true
posts:
permalink: /blog/:year/:month/:day/:title/
output: true
contrib:
permalink: /contrib/:path/
output: true

Build settings

markdown: kramdown
highlighter: rouge

kramdown:
input: GFM
syntax_highlighter: rouge

relative_links:
enabled: true
collections: true

plugins:

  • jekyll-redirect-from

hyper_docs_url: https://docs.rs/hyper/latest
examples_url: https://github.com/hyperium/hyper/tree/master/examples
http_body_util_url: https://docs.rs/http-body-util/latest
hyper_tls_url: https://docs.rs/hyper-tls/latest
hyper_util_url: https://docs.rs/hyper-util/latest
http_docs_url: https://docs.rs/http/latest

futures_url: https://docs.rs/futures/0.3.*
legacy_docs_url: https://docs.rs/hyper/0.14.*
legacy_examples_url: https://github.com/hyperium/hyper/tree/0.14.x/examples


File: blog.md


text
{% for post in site.posts %}
    <li>
        <h2 class="post-title">
            <a class="post-link" href="{{ post.url | prepend: site.baseurl }}">{{ post.title }}</a>
        </h2>
    </li>
{% endfor %}

File: index.md


layout: home


File: _contrib/code-of-conduct.md


title: >- Code of Conduct layout: guide hyper_path: docs/CODE_OF_CONDUCT.md

Be Kind

  • Don't be mean.
  • Insulting anyone is prohibited.
  • Harassment of any kind is prohibited.
  • If another person feels uncomfortable with your remarks, stop it.
  • If a moderator deems your comment or conduct as inappropriate, stop it.
  • Disagreeing is fine, but keep it to technical arguments. Never attack the person.
  • Give the benefit of the doubt. Assume good intentions.
  • Show empathy. There are 3 interpretations to any message: what we thought, what we said, and what they understand.
  • This does mean we exclude people who are not kind. We are happy to make that sacrifice.

Or Else

  • Violations of the Code of Conduct will result in 1 warning.
  • If the violation is major, a moderator may just ban immediately.
  • If a warning has already been given, a moderator will ban the offender.
  • There is no process for appealing a ban.
  • Any violations can be reported to [email protected].

File: _contrib/code-style.md


title: >- Code Style layout: guide hyper_path: docs/CODE_STYLE.md

hyper uses the default configuration of rustfmt.

cargo fmt

cargo fmt --all does not work in hyper. Please use the following commands:

txt
# Mac or Linux
rustfmt --check --edition 2021 $(git ls-files '*.rs')

# Powershell
Get-ChildItem . -Filter "*.rs" -Recurse | foreach { rustfmt --check --edition 2021 $_.FullName }

NOTE: If you are using rust-analyzer, you can add the following two lines in your settings.json to make sure the features get taken into account when checking the project:

json
"rust-analyzer.cargo.features": ["full"],
 "rust-analyzer.check.features": ["full"],

File: _contrib/collaborators.md


title: >- hyper Collaborator Guide layout: guide hyper_path: docs/COLLABORATORS.md

This document describes how collaborators manage hyper.

  • First off, a collaborator doesn't need to do all of the following perfectly.
    • It takes time to learn.
    • Instead, focus on growth.
    • Get in the habit of checking this guide with some frequency, and improve rather than stagnate.

Exemplify the Code of Conduct

  • Our code of conduct is not elaborate. It doesn't require much study, nor explication.
  • To be a good collaborator, it does require internalizing it.
  • The code of conduct is proactive, positive.
  • A collaborator must be a good example of being kind.
    • It should not be possible to find significant fault in a collaborator against the simple principles in the code of conduct.

Internalize the Vision

  • Understand and use the project's vision when helping guide decisions.

Review Pull Requests

  • Review pull requests promptly and kindly. Approve CI for new contributors.
    • Consider telling a maintainer to add you to the automated review queue.
  • Collaborators should feel comfortable approving and merging straightforward changes. Such as documentation, chores, styling, simple refactors, and basic bug fixes.
  • When reviewing larger changes, keep in mind the points on being API caretakers.
  • When approving a PR, allow time for other collaborators to look before merging.
    • Especially if the PR is newer, or has a more significant change.
  • When merging, prefer squashing (it adds in the PR number). Touch up the commit message to match our commits style.

Provide Feedback on Features and Designs

Collaborators try to participate early and often when new features are proposed. They also actively consider and recommend new features that users may need.

API Caretakers

Collaborators are caretakers of hyper's API. When proposing, discussing, or reviewing changes to hyper, keep the following in mind:

  • Conservative additions
    • Conservative APIs reduce breaking changes
  • Don't expose internal implementation details
  • hyper-util is a place to explore first
  • Unstable features
    • Unstable features are not bound by our stability promise.
    • We don't just name a crate feature with unstable.
      • This has the problem of an intermediary crate enabling it, and someone depending on that crate and not realizing they are accessing unstable hyper features.
    • All unstable features require a conditional config flag passed to the Rust compiler.
      • For example, hyper_unstable_ffi.
  • Breaking changes
    • These would usually be reserved for a new SemVer major release, such as v2.0, and thus would usually be very rare.
    • An exception is fixing a mistake quickly after release of a new feature.
    • All breaking changes require approval by a maintainer.

Welcome New Contributors

Answer Questions

  • Answer questions in issues or chat when you can, or help find someone who can. See the project's help channels.

Mentor Contributors

  • Help contributors grow toward becoming project members. Issue triaging often provides opportunities for mentoring, but the ideas can be applied to reviews, or when answering questions.

File: _contrib/commits.md


title: >- Git Commit Guidelines layout: guide hyper_path: docs/COMMITS.md

We have very precise rules over how our git commit messages can be formatted. This leads to more
readable messages
that are easy to follow when looking through the project history. But also,
we use the git commit messages to generate the change log.

Commit Message Format

Each commit message consists of a header, a body and a footer. The header has a special
format that includes a type, a scope and a subject:

text
<type>(<scope>): <subject>
<BLANK LINE>
<body>
<BLANK LINE>
<footer>

Any line of the commit message cannot be longer 100 characters! This allows the message to be easier
to read on github as well as in various git tools.

Type

Must be one of the following:

  • feat: A new feature
  • fix: A bug fix
  • docs: Documentation only changes
  • style: Changes that do not affect the meaning of the code (white-space, formatting, missing
    semi-colons, etc)
  • refactor: A code change that neither fixes a bug or adds a feature
  • perf: A code change that improves performance
  • test: Adding missing tests
  • chore: Changes to the build process or auxiliary tools and libraries such as documentation
    generation

Scope

The scope should refer to a module in hyper that is being touched. Examples:

  • client
  • server
  • http1
  • http2
  • ffi
  • upgrade
  • examples

Subject

The subject contains succinct description of the change:

  • use the imperative, present tense: "change" not "changed" nor "changes"
  • don't capitalize first letter
  • no dot (.) at the end

Body

Just as in the subject, use the imperative, present tense: "change" not "changed" nor "changes"
The body should include the motivation for the change and contrast this with previous behavior.

The footer should contain any information about Breaking Changes and is also the place to
reference GitHub issues that this commit Closes.

The last line of commits introducing breaking changes should be in the form BREAKING CHANGE: <desc>


File: _contrib/contributing.md


title: >- Contributing to Hyper layout: guide hyper_path: docs/CONTRIBUTING.md

You want to contribute? You're awesome!

Contributions come in all shapes and sizes. Let's take a tour of some of the different ways you could contribute.

[Code of Conduct](code-of-conduct.md)

Firstly, all interactions with the project need to abide by the code of conduct. This makes sure everyone is treated kindly.

[Issues](issues.md)

  • Filing an issue is a contribution. We appreciate you letting us know about bugs you've found, and any information that you can provide that we can use to make hyper better. Without your filing it, we may not be aware of the bug.
  • Triaging issues is a huge help. By your helping make issues better, the reporters can get answers sooner, and others can fix them with less effort. You can also volunteer as a frequent triager.
  • Discuss feature requests (especially those marked request-for-comment).

[Pull Requests](pull-requests.md)

By the way, consider checking the list of easy issues if you want to submit something.

Documentation

Improving hyper's documentation is a huge help for everyone who is trying to use hyper.

  • The API documentation is stored as rustdoc comments directly in the source.
  • The main website has tutorial-style guides. As of v1, they are currently in a revamp, and would greatly benefit from being filled out.

Help

Helping others use hyper in their specific workflows is a very valuable way to contribute.

  • Answer questions asked directly in hyper's Discussions.
  • Join our Discord and help those who show up with questions.
  • Blog about hyper. You writing a blog post about how you use hyper to do something specific, or how you contributed a new feature, or debugged something in it, is a great idea. Not all examples can fit in the hyper repo. Search engines will help people find their use cases on your blog. And you can describe processes in more depth or from a different perspective.

File: _contrib/governance.md


title: >- Governance layout: guide hyper_path: docs/GOVERNANCE.md

Making decisions

There's two main pieces to the way decisions are made in hyper:

  1. A decision-making framework
  2. The people who apply it

The people are described lower down in this document.

Decision-making framework

We start with the users. The project wouldn't exist without them, and it exists
in order to enable users to do amazing things with HTTP. We listen to our
users. Some actively contribute their thoughts, but many others we must seek
out to learn about their usage, joys, and headaches. Those insights allow our
experts to determine the best solutions for the users.

We then define a set of tenets, which are guiding principles
that can be used to measure aspects of individual decisions. It should be
possible to identify one or more tenets that apply to why a decision is made.
And the set helps us balance which priorities are more important for our users.

We combine the usecases with the tenets to come up with a vision
that provides a longer-term plan of what hyper should look like.

Finally, we define a roadmap that describes what the
short-term, tactical changes to bring hyper closer in line with the vision.

Roles

These are the roles people can fill when participating in the project. A list
of the people in each role is available in maintainers.

Contributor

A contributor is anyone who contributes their time to provide value for the
project. This could be in the form of code, documentation, filing issues,
discussing designs, or helping others on the issue tracker or in chat.

All contributors MUST follow the Code of Conduct.

👋 New here? This could be you!

Triager

Triagers assess issues on the issue tracker. They help make sure the work is
well organized, and are critical for making new issue reporters feeling
welcome.

Responsibilities:

Privileges:

  • Can edit, label, and close issues
  • Member of the organization
  • Can be assigned issues and pull requests

How to become:

Collaborator

Collaborators are contributors who have been helping out in a consistent basis.
See the collaborator guide for practical guidance on the role.

Responsibilities:

  • Be exemplars of the Code of Conduct
  • Internalize the vision
  • Reviewing pull requests from other contributors
  • Provide feedback on proposed features and design documents
  • Welcome new contributors
  • Answer questions in issues and/or chat
  • Mentor contributors to become members

Privileges:

  • Can review and merge pull requests
  • Can trigger re-runs of CI, and approve CI for first-time contributors
  • Can assign issues and pull requests to other organization members

How to become:

  • Work at fulfilling the above responsibilities.
  • Any collaborator may nominate a contributor who has been around for some time
    and is already filling the responsibilities.
  • Another collaborator must second the nomination.
  • If there are no objections, a maintainer will welcome the new collaborator.

Don't be afraid to ask a collaborator for what you could work on to reach this
goal!

Advisor

Advisors are contributors with a lot of domain expertise. They offer advice on
proposals, complicated bugs or features, based on their experience elsewhere in
the industry.

Responsibilities:

  • Adhere to the Code of Conduct
  • Try to offer advice when the @hyperium/advisors team is tagged for input.
    (This is not a frequent occurrence, so the time commitment should generally be
    low.)

How to become:

  • Advisors are invited by a maintainer.
  • Sometimes they are experienced Collaborators who must step back from active
    contributing. Sometimes they are experts placed elsewhere who are friendly to
    the project.

Maintainer

Maintainers are the project admins. Besides being a collaborator, they take care
of house-keeping duties, help lead the direction, and have the final authority when
required.


File: _contrib/index.md


This is a set of documents outline how hyper is developed, how to contribute or get involved, various processes we use, and internal design explanations.


File: _contrib/issues.md


title: >- Issues layout: guide hyper_path: docs/ISSUES.md

The issue tracker for hyper is where we track all features, bugs, and discuss proposals.

Triaging

Once an issue has been opened, it is normal for there to be discussion
around it. Some contributors may have differing opinions about the issue,
including whether the behavior being seen is a bug or a feature. This
discussion is part of the process and should be kept focused, helpful, and
professional.

The objective of helping with triaging issues is to help reduce the issue
backlog and keep the issue tracker healthy, while enabling newcomers another
meaningful way to get engaged and contribute.

Acknowledge

Acknowledge the human. This is meant actively, such as giving a welcome, or
thanks for a detailed report, or any other greeting that makes the person feel
that their contribution (issues are contributions!) is valued. It also is meant
to be internalized, and be sure to always treat the person kindly
throughout the rest of the steps of triaging.

Ask for more info

Frequently, we need more information than was originally provided to fully
evaluate an issue.

If it is a bug report, ask follow up questions that help us get a minimum
reproducible example
. This may take several round-trip questions. Once
all the details are gathered, it may be helpful to edit the original issue text
to include them all.

Categorize

Once enough information has been gathered, the issue should be categorized
with labels. Ideally, most issues should be labelled with an area,
effort, and severity. An issue can have multiple areas, pick what fits. There
should be only one severity, and the descriptions of each should help to pick
the right one. The hardest label to select is "effort". If after reading the
descriptions of each effort level, you're still unsure, you can ping a
maintainer to pick one.

Adjust the title

An optional step when triaging is to adjust the title once more information is
known. Sometimes an issue starts as a question, and through discussion, it
turns out to be a feature request, or a bug report. In those cases, the title
should be changed from a question, and the title should be a succinct action to
be taken. For example, a question about an non-existent configuration option
may be reworded to "Add option to Client to do Zed".

Mentoring

The last part of triaging is to try to make the issue a learning experience.
After a discussion with the reporter, it would be good to ask if they are now
interested in submitting the change described in the issue.

Otherwise, it would be best to leave the issue with a series of steps for
anyone else to try to write the change. That could be pointing out that a
design proposal is needed, addressing certain points. Or, if the required
changes are mostly know, a list of links to modules and functions where code
needs to be changed, and to what. That way we mentor newcomers to become
successful contributors of new pull requests.

Labels

Issues are organized with a set of labels. Most labels follow a system of being prefixed by a "type".

Area

The area labels describe what part of hyper is relevant.

  • A-body: streaming request and response bodies.
  • A-client: the HTTP client.
  • A-dependencies: library dependencies.
  • A-docs: documentation.
  • A-error: error reporting and types.
  • A-ffi: the C API.
  • A-http1: the HTTP/1 specifics.
  • A-http2: the HTTP/2 specifics.
  • A-server: the HTTP server.
  • A-tests: the unit and integration tests.

Blocked

These labels indicate an issue is "blocked" for some reason.

  • B-breaking-change: a breaking change that is waiting for the next semver-compatible release.
  • B-rfc: request for comments. More discussion is needed to explore the design.
  • B-upstream: waiting on something in a dependency or the compiler.

Category

The category marks what kind of issue this is.

  • C-bug: something is wrong, this is bad!
  • C-feature: this is a new feature request, adding something new.
  • C-performance: make existing working code go faster.
  • C-refactor: improve internal code to help readability and maintenance.
  • C-chore: a general chore or maintenance task.

Effort

The effort labels are a best-guess at roughly how much effort and knowledge of hyper is needed to accomplish the task.

  • E-easy: a great starting point for a new contributor.
  • E-medium: some knowledge of how hyper internals work would be useful.
  • E-hard: likely requires a deeper understanding of how hyper internals work.

File: _contrib/maintainers.md


title: >- The People layout: guide hyper_path: docs/MAINTAINERS.md

To see what these roles do, and how to become one, look at governance.

Triagers

  • Eray Karatay (@programatik29)
  • Oddbjørn Grødem (@oddgrd)

Collaborators

  • Sean McArthur (@seanmonstar)
  • Steven Fackler (@sfackler)
  • dswij (@dswij)
  • tottoto (@tottoto)
  • katelyn martin (@cratelyn)
  • 0x676e67 (@0x676e67)

Collaborator emeriti

  • Jonathan Reem (@reem)
  • Carl Lerche (@carllerche)
  • Oliver Gould (@olix0r)
  • Eliza Weisman (@hawkw)
  • Lucio Franco (@LucioFranco)
  • Adam Foltzer (@acfoltzer)
  • David Pedersen (@davidpdrsn)
  • Anthony Ramine (@nox)
  • Noah Kennedy (@Noah-Kennedy)

Advisors

  • Oliver Gould (@olix0r)
  • Adam Foltzer (@acfoltzer)

Maintainers

  • Sean McArthur (@seanmonstar)

File: _contrib/msrv.md


title: >- Minimum Supported Rust Version (MSRV) layout: guide hyper_path: docs/MSRV.md

hyper's current policy is to always support a Rust version at least 6 months
old. That is, a compiler version released within the last 6 months can compile
hyper. It is possible that an older compiler can work, but that is not
guaranteed. We try to increase the MSRV responsibly, only when a significant
new feature is needed.

The current MSRV is: 1.63.


File: _contrib/pull-requests.md


title: >- Pull Requests layout: guide hyper_path: docs/PULL_REQUESTS.md

Pull requests are the way to submit changes to the hyper repository.

Submitting a Pull Request

In most cases, it a good idea to discuss a potential change in an
issue. This will allow other contributors to provide guidance and
feedback before significant code work is done, and can increase the
likelihood of getting the pull request merged.

Tests

If the change being proposed alters code (as opposed to only documentation for
example), it is either adding new functionality to hyper or it is fixing
existing, broken functionality. In both of these cases, the pull request should
include one or more tests to ensure that hyper does not regress in the future.

Commits

Once code, tests, and documentation have been written, a commit needs to be
made. Following the commit guidelines will help with the review
process by making your change easier to understand, and makes it easier for
hyper to produce a valuable changelog with each release.

However, if your message doesn't perfectly match the guidelines, do not
worry!
The person that eventually merges can easily fixup the message at that
time.

Opening the Pull Request

From within GitHub, open a new pull request from your personal branch.

Once opened, pull requests are usually reviewed within a few days.

Discuss and Update

You will probably get feedback or requests for changes to your Pull Request.
This is a big part of the submission process so don't be discouraged! Some
contributors may sign off on the Pull Request right away, others may have more
detailed comments or feedback. This is a necessary part of the process in order
to evaluate whether the changes are correct and necessary.

Any community member can review a PR and you might get conflicting feedback.
Keep an eye out for comments from code owners to provide guidance on
conflicting feedback.

You don't need to close the PR and create a new one to address feedback. You
may simply push new commits to the existing branch.


File: _contrib/roadmap-1.0.md


title: >- hyper 1.0 Roadmap layout: guide hyper_path: docs/ROADMAP-1.0.md

This was the roadmap to arrive at hyper v1.0. It is kept for historical purposes. See roadmap for the latest.

Goal

Align current hyper to the hyper vision.

The vision outlines a decision-making framework, use-cases, and general shape
of hyper. This roadmap describes the currently known problems with hyper, and
then shows what changes are needed to make hyper 1.0 look more like what is in
the vision.

Known Issues

Note: These known issues are as of hyper v0.14.x. After v1.0 is released,
ideally these issues will have been solved. Keeping this history may be helpful
to Future Us, though.

Higher-level Client and Server problems

Both the higher-level Client and Server types have stability concerns.

For the hyper::Server:

  • The Accept trait is complex, and too easy to get wrong. If used with TLS, a slow TLS handshake
    can affect all other new connections waiting for it to finish.
  • The MakeService<&IO> is confusing. The bounds are an assault on the eyes.
  • The MakeService API doesn't allow to easily annotate the HTTP connection with tracing.
  • Graceful shutdown doesn't give enough control.

It's more common for people to simply use hyper::server::conn at this point,
than to bother with the hyper::Server.

While the hyper::Client is much easier to use, problems still exist:

  • The whole Connect design isn't stable.
    • ALPN and proxies can provide surprising extra configuration of connections.
    • Some Connect implementations may wish to view the path, in addition to the scheme, host, and port.
    • Wants runtime feature
  • The Pool could be made more general or composable. At the same time, more customization is
    desired, and it's not clear
    how to expose it yet.

Runtime woes

hyper has been able to support different runtimes, but it has sometimes awkward
default support for Tokio.

  • The runtime cargo-feature isn't additive
  • Built-in Tokio support can be confusing
  • Executors and Timers
    • The runtime feature currently enables a few options that require a timer, such as timeouts and
      keepalive intervals. It implicitly relies on Tokio's timer context. This can be quite confusing.
  • IO traits
    • Should we publicly depend on Tokio's traits?
    • futures-io?
      • Definitely nope.
      • Not stable. (0.3?)
      • No uninitialized memory.
    • Eventual std traits?
      • They've been in design for years.
      • We cannot base our schedule on them.
      • When they are stable, we can:
        • Provide a bridge in hyper-util.
        • Consider a 2.0 of hyper.
    • Define our own traits, provide util wrappers?

Forwards-compatibility

There's a concern about forwards-compatibility. We want to be able to add
support for new HTTP features without needing a new major version. While most
of http and hyper are prepared for that, there's two potential problems.

  • New frames on an HTTP stream (body)
    • Receiving a new frame type would require a new trait method
      • There's no way to implement a "receive unknown frame" that hyper doesn't know about.
    • Sending an unknown frame type would be even harder.
      • Besides being able to pass an "unknown" type through the trait, the user would need to be
        able to describe how that frame is encoded in HTTP/2/3.
  • New HTTP versions
    • HTTP/3 will require a new transport abstraction. It's not as simple as just using some
      impl AsyncRead + AsyncWrite. While HTTP/2 bundled the concept of stream creation internally,
      and thus could be managed wholly on top of a read-write transport, HTTP/3 is different. Stream
      creation is shifted to the QUIC protocol, and HTTP/3 needs to be able to use that directly.
    • This means the existing Connection types for both client and server will not be able to
      accept a QUIC transport so we can add HTTP/3 support.

Errors

It's not easy to match for specific errors.

The Error::source() can leak an internal dependency. For example, a
hyper::Error may wrap an h2::Error. Users can downcast the source at
runtime, and hyper internally changing the version of its h2 dependency can
cause runtime breakage for users.

Formatting errors is in conflict with the current expected norm. The
fmt::Display implementation for hyper::Error currently prints its own
message, and then prints the message of any wrapped source error. The Errors
Working Group currently recommends that errors only print their own message
(link?). This conflict means that error "reporters", which crawl a source chain
and print each error, has a lot of duplicated information.

text
error fetching website: error trying to connect: tcp connect error: Connection refused (os error 61)
tcp connect error: Connection refused (os error 61)
Connection refused (os error 61)

While there is a good reason for why hyper's Error types do this, at the very
least, it is unfortunate.

You call hyper, or hyper calls you?

Note: this problem space, of who calls whom, will be explored more deeply in
a future article.

At times, it's been wondered whether hyper should call user code, or if user
code should call hyper. For instance, should a Service be called with a
request when the connection receives one, or should the user always poll for
the next request.

There's a similar question around sending a message body. Should hyper ask the
body for more data to write, or should the user call a write method directly?

These both get at a root topic about write
observability
. How do you know
when a response, or when body data, has been written successfully? This is
desirable for metrics, or for triggering other side-effects.

The Service trait also has some other frequently mentioned issues. Does
poll_ready pull its complexity weight for servers? What about returning
errors, what does that mean? Ideally users would turn all errors into
appropriate http::Responses. But in HTTP/2 and beyond, stream errors are
different from HTTP Server Error responses. Could the Service::Error type do
more to encourage best practices?

Design

The goal is to get hyper closer to the vision, using that to determine the
best way to solve the known issues above. The main thrust of the proposed
changes are to make hyper more Flexible and stable.

In order to keep hyper Understandable, however, the proposed changes must
be accompanied by providing utilities that solve the common usage patterns,
documentation explaining how to use the more flexible pieces, and guides on how
to reach for the hyper-utility belt.

The majority of the changes are smaller and can be contained to the Public
API
section, since they usually only apply to a single module or type. But the
biggest changes are explained in detail here.

Split per HTTP version

The existing Connection types, both for the client and server, abstract over
HTTP version by requiring a generic AsyncRead + AsyncWrite transport type.
But as we figure out HTTP/3, that needs to change. So to prepare now, the
Connection types will be split up.

For example, there will now be hyper::server::conn::http1::Connection and
hyper::server::conn::http2::Connection types.

These specific types will still have a very similar looking API that, as the
vision describes, provides Correct connection management as it pertains to
HTTP.

There will be still be a type to wrap the different versions. It will no longer
be generic over the transport type, to prepare for being able to wrap HTTP/3
connections. Exactly how it will wrap, either by using internal trait objects,
or an enum Either style, or using a trait Connection that each type
implements, is something to be determined. It's likely that this "auto" type
will start in hyper-util.

Focus on the `Connection` level

As mentioned in the Known Issues, the higher-level Client and Server have
stability and complexity problems. Therefore, for hyper 1.0, the main API will
focus on the "lower-level" connection types. The Client and Server helpers
will be moved to hyper-util.

Public API

body

The Body struct is removed. Its internal "variants" are separated into
distinct types
, and can start
in either hyper-util or http-body-util.

The exported trait HttpBody is renamed to Body.

A single Body implementation in hyper is the one provided by receiving
client responses and server requests. It has the name Streaming.

Unresolved: Other names can be considered during implementation. Another
option is to not publicly name the implementation, but return Responses.

The Body trait will be experimented on to see about making it possible to
return more frame types beyonds just data and trailers.

Unresolved: What exactly this looks like will only be known after
experimentation.

client

The high-level hyper::Client will be removed, along with the
hyper::client::connect module. They will be explored more in hyper-util.

As described in Design, the client::conn module will gain http1 and
http2 sub-modules, providing per-version SendRequest, Connection, and
Builder structs. An auto version can be explored in hyper-util.

error

The hyper::Error struct remains in place.

All errors returned from Error::source() are made opaque. They are wrapped an
internal Opaque newtype that still allows printing, but prevents downcasting
to the internal dependency.

A new hyper::error::Code struct is defined. It is an opaque struct, with
associated constants defining various code variants.

Alternative: define a non-exhaustive enum. It's not clear that this is
definitely better, though. Keeping it an opaque struct means we can add
secondary parts to the code in the future, or add bit flags, or similar
extensions.

The purpose of Code is to provide an abstraction over the kind of error that
is encountered. The Code could be some behavior noticed inside hyper, such as
an incomplete HTTP message. Or it can be "translated" from the underlying
protocol, if it defines protocol level errors. For example, an
h2::Reason::CANCEL.

rt

The Executor trait stays in here.

Define a new trait Timer, which describes a way for users to provide a source
of sleeping/timeout futures. Similar to Executor, a new generic is added to
connection builders to provide a Timer.

server

The higher-level hyper::Server struct, its related Builder, and the
Accept trait are all removed.

The AddrStream struct will be completely removed, as it provides no value but
causes binary bloat.

Similar to client, and as describe in the Design, the conn modules will
be expanded to support http1 and http2 submodules. An auto version can be
explored in hyper-util.

service

A vendored and simplified Service trait will be explored.

The error type for Services used for a server will explore having the return
type changed from any error to one that can become a hyper::error::Code.

Unresolved: Both of the above points are not set in stone. We will
explore and decide if they are the best outcome during development.

The MakeService pieces will be removed.

Cargo Features

Remove the stream feature. The Stream trait is not stable, and we cannot
depend on an unstable API.

Remove the tcp and runtime features. The automatic executor and timer parts
are handled by providing implementations of Executor and Timer. The
connect and Accept parts are also moving to hyper-util.

Public Dependencies

  • http
  • http-body
  • bytes

Cannot be public while "unstable":

  • tracing

`hyper-util`

body

A channel implementation of Body that has an API to know when the data has
been successfully written is provided in hyper_util::body::channel.

client

A Pool struct that implements Service is provided. It fills a similar role
as the previous hyper::Client.

Note: The Pool might be something that goes into the tower crate
instead. Or it might stay here as a slightly more specialized racing-connect
pool. We'll find out as we go.

A connect submodule that mostly mirrors the existing hyper::client::connect
module is moved here. Connectors can be used as a source to provide Services
used by the Pool.

rt

We can provide Tokio-backed implementations of Executor and Timer.

server

A GracefulShutdown helper is provided, to allow for similar style of graceful
shutdown as the previous hyper::Server did, but with better control.

Appendix

Unresolved Questions

There are some parts of the proposal which are not fully resolved. They are
mentioned in Design and API sections above, but also collected here for easy
finding. While they all have plans, they are more exploratory parts of the
API, and thus they have a higher possibility of changing as we implement them.

The goal is to have these questions resolved and removed from the document by
the time there is a Release Candidate.

Should there be `hyper::io` traits?

Depending on tokio just for AsyncRead and AsyncWrite is convenient, but
can be confusing for users integrating hyper with other runtimes. It also ties
our version directly to Tokio. We can consider having vendored traits, and
providing Tokio wrappers in hyper-util.

Should returned body types be `impl Body`?

How could the `Body` trait prepare for unknown frames?

We will experiment with this, and keep track of those experiments in a
dedicated issue. It might be possible to use something like this:

rust
pub trait Body {
    type Data;
    fn poll_frame(..) -> Result<Option<Frame<Self::Data>>>;
}

pub struct Frame<T>(Kind<T>);

enum Kind<T> {
   Data(T),
   Trailers(HeaderMap),
   Unknown(Box<dyn FrameThingy>),
}

Should there be a simplified `hyper::Service` trait, or should hyper depend on `tower-service`?

  • There's still a few uncertain decisions around tower, such as if it should be
    changed to async fn call, and if poll_ready is the best way to handle
    backpressure.
  • It's not clear that the backpressure is something needed at the Server
    boundary, thus meaning we should remove poll_ready from hyper.
  • It's not 100% clear if we should keep the service pattern, or use a
    pull-based API. This will be explored in a future blog post.

FAQ

Why did you pick _that_ name? Why not this other better name?

Naming is hard. We certainly should solve it, but discussion for particular
names for structs and traits should be scoped to the specific issues. This
document is to define the shape of the library API.

Should I publicly depend on `hyper-util`?

The hyper-util crate will not reach 1.0 when hyper does. Some types and
traits are being moved to hyper-util. As with any pre-1.0 crate, you can
publicly depend on it, but it is explicitly less stable.

In most cases, it's recommended to not publicly expose your dependency on
hyper-util. If you depend on a trait, such as used by the moved higher-level
Client or Server, it may be better for your users to define your own
abstraction, and then make an internal adapter.

Isn't this making hyper harder?

We are making hyper more flexible. As noted in the vision, most use
cases of hyper require it to be flexible. That can mean that the exposed API
is lower level, and that it feels more complicated. It should still be
understandable.

But the hyper 1.0 effort is more than just the single hyper crate. Many
useful helpers will be migrated to a hyper-util crate, and likely improved in
the process. The timeline also points out that we will have a significant
documentation push. While the flexible pieces will be in hyper to compose how
they need, we will also write guides for the hyper.rs showing people how to
accomplish the most common tasks.


File: _contrib/roadmap.md


title: >- Roadmap layout: guide hyper_path: docs/ROADMAP.md

Goal

Align current hyper to the hyper vision.

The vision outlines a decision-making framework, use-cases, and general shape
of hyper. This roadmap describes the focus areas to continue to improve hyper
to look more like what is in the vision.

Focus Areas

While open source is not a company, open source can be guiding. We can focus
attention to specific areas of improvement, which are based on conversations
with users, and prioritized by frequency and impact.

To that end, the following 4 areas are current focus of the project:

  1. Documentation
  2. hyper-util
  3. HTTP/3
  4. Observability

Each area benefits from having a top level description and goal, a place to
track progress, and a champion (or two) that helps push the effort.

Documentation

hyper has stabilized, so investing in documentation is wise! The way it is used
won't change much, so documentation won't become outdated quickly. A tool that
people don't know how to use isn't helpful at all. This helps hyper be
Understandable.

The documentation focus area includes several different forms:

  • The API docs as a reference.
  • Examples as form of how-to.
  • Website guides as tutorials.

Each of these could benefit from dedicated planning of their overall structure,
editing the content that already exists, and creating the rest that is sorely
missing.

hyper-util

hyper-util serves two main purposes:

  1. Provide useful patterns that build on top of hyper.
  2. Explore, stabilize, and graduate some of those patterns into hyper itself.

To that end, there are several new features that can be worked on and iterated
on in hyper-util right now:

  • New design for a higher-level Client.
  • Breaking apart some patterns from reqwest, such as proxy helpers.
  • Server automatic version detection.
  • Improved builder patterns that make it easier to configure complicated
    options.

HTTP/3

hyper has an HTTP/3 crate, h3, that is generic over any QUIC implementation,
similar to how hyper's HTTP/1 and HTTP/2 can be provided any IO transport. It
supports much of HTTP/3 already, and interoperates with most other
implementations. While some brave users have been trying it out the hard way
(such as reqwest), it's time to bring HTTP/3 to more users.

The aim is to eventually support hyper::client::conn::http3 and
hyper::server::conn::http3.

To do so, work is needed:

  • Harden the h3 crate itself, such as fixing any straggling interop issues,
    and filling out the spec conformance tags we use for accountability.
  • Proposal for (initially unstable) hyper::rt::quic integration, allowing
    people to bring their own QUIC.
  • Write the hyper::proto::http3 glue that translates hyper's connection
    patterns with the h3 crate.

Observability

It's extremely common once operating a service using hyper to want more
visibility in what exactly is happening. It's important to realize that there
are 3 concepts involved that frequently get conflated: events, tracing, and
metrics.

Some existing ways to get some of these:

  • Unstable tracing integraton inside hyper.
  • tower_http::trace which instruments outside of hyper, using Service and
    Body.

However, there are some events and metrics that are only known inside hyper,
and having official, stable support would be very helpful.

Some potential options would be:

  • Stabilizing specific tracing events (blocked on the tracing crate
    stabilizing...)
  • Provide a rudimentary, programmatic way to query metrics without another
    crate.
  • Provide some sort of hyper-metrics helper.

Beyond

The above are focus areas that are the most frequently asked for, and so have
the most attention. That doesn't mean that nothing else can be worked on.

Motivated individuals that want to help make other improvements are certainly
welcome!


File: _contrib/tenets.md


title: >- Charter layout: guide hyper_path: docs/TENETS.md

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, compassion and empathy towards all
contributors. Technical skill is not a substitute for human decency.

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, harassed 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
. 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.


File: _contrib/vision.md


title: >- hyper Vision layout: guide hyper_path: docs/VISION.md

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).

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

There's a lot more detail about each in tenets.

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 capabilities, 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 Futures that
the user then decides how to poll, likely involving their runtime options.

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::Requests and http::Responses, 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
.

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 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 are definitely out of scope.

  • TLS: We learned early that bundling TLS directly in hyper has
    problems
    . 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.

File: _contrib/hips/0000-template.md


title: >- HIP-0000: Template layout: guide hyper_path: docs/hips/0000-template.md

  • Authors:
  • Created: (date)
  • PR: (link to pull request)

Summary

(The tl;dr. Concise statement of the reason for the document and recommendation.)

Tenets

(List of tenets, values, guiding principles that are used to evaluate the proposal.)

Motivation

(Clearly explain the problem.)

Recommendation

User Experience

Implementation

Testing Plan

(Optional: are there any special considerations for testing required?)

Security Considerations

(What security risks might exist for the recommendation? Can we reduce them? What should users consider when using it?)

Alternatives

(List alternatives considered, and especially why they were not recommended. This portion of "documenting the why" helps Future Us to see if anything has changed since last considered.)

Unresolved Questions

References


File: _contrib/hips/0001-hyper-improvement-proposals.md


title: >- HIP-0001: hyper Improvement Proposals layout: guide hyper_path: docs/hips/0001-hyper-improvement-proposals.md

  • Authors: seanmonstar
  • Created: 2026-09-01
  • PR: #4131

Summary

HIPs establish a predictable, written‑first process for proposing and evaluating significant changes to hyper. They provide an authoritative place to articulate the problem, the design rationale, and the tradeoffs so contributors can make informed, durable decisions.

Tenets

  • Open: the decision-making process should be out in the open, for all to see and any to participate.
  • Understandable: Code and changelogs are not the complete theory. Others should be able to understand what we meant.
  • Writing is thinking: while the written artifact is extremely valuable, the process of creating is thinking itself. It refines our thought, sharpens it, makes it better.
  • Document the why: just as important is understanding why we thought that way. This allows evaluating when the decision should be reconsidered.

Motivation

hyper's existing contribution process does not adequately support proposals for large features and design changes.

Much of the shape of hyper can be seen in the vision and 1.0 roadmap. Adding new small features is very easy. However, designing large new systems in hyper must be handled with care. hyper is deployed at massive scale. The stability promise requires we be deliberate with what API to expose.

The current process is to ask people to write up larger designs in an issue. GitHub issues are terrible places to discuss multiple points at once. Or people submit massive pull requests, and the discussion is sprinkled through the code reviews.

It's extremely difficult for reviewers to be sure they understand the design, and to ensure that all critical aspects have been considered. If an implementation is denied for a desirable feature, new pull requests tend to be submitted which don't address the previous concerns. The barrier for someone to contribute goes up due to the lack of structure. And the review process slows down as reviewers struggle to continuously rebuild mental context.

Recommendation

Introduce a structured, written process for proposing, discussing, and deciding large design changes. We call these hyper Improvement Proposals (HIPs). They are similar in spirit to RFCs.

Scope

When should HIPs be used?

HIPs are intended for changes that significantly affect hyper's architecture, public API, performance characteristics, or long‑term maintenance burden. They are useful when a decision is difficult to reverse, when there appear to be several strong options, or as a foundation for future proposals.

When are they not needed?

Small, localized changes that do not alter core design or guarantees generally do not require a HIP.

Content

What should be included in a HIP?

This defines at a high level the purpose of what content is included. The exact sections might change over time, and so are defined in a separate 0000-template.md file.

But in general, HIPs should provide the following information:

  • Objective - the problem, the motivation, the what are we even doing here.
  • Tenets - how to measure solutions
    • A meta point here: spend more time on the objective, tenets, and alternatives. The recommendation will fall into place.
  • A single recommendation
    • The job of a proposal is to propose a solution, not to propose a problem.
    • There will often be several ways to solve anything.
    • The proposal should research and measure the options using the facts and tenets, and decide which is the best.
      • The discussion during the proposal period may change what is the recommended solution.
      • The essence of those discussions should be recaptured into the proposal itself. Both in the recommendation section, and also potentially in an FAQ appendix.
      • Future readers should not need to reconstruct the design from the review comments.
  • The whys (and why nots) of the solution
    • A proposal should always include why the recommendation is the best option, and why the other options were not chosen
  • References
    • Links to all the relevant research that was done
    • Links to previous discussions
  • Appendices
    • FAQ

Do not use AI to write

AI (LLMs) have their benefits, but since a major reason for writing this is the thinking process, do not use AI to write the actual English in a HIP.

What not to include

  • Minor details that do not need to be discussed to accept the proposal.

Lifecycle

  • Start with an issue.
    • (A HIP should almost never be started without a previous discussion)
  • If the feature request seems large enough, a collaborator may ask for a HIP.
  • In a new branch, copy the template file to 0000-your-feature-name.md.
  • Fill out the sections based on the instructions in the template.
    • You don't need to have fully polished prose in every section to submit for discussion.
    • It can even be beneficial to submit initially with bullet points.
    • Balance giving proper thought to the sections, with moving quickly to get appropriate feedback.
  • Consider sharing an early draft with someone else first.
  • Submit a pull request, and include a big reminder to prefer line comments over top-level PR comments.
  • As discussion occurs, revise the sections.
    • Consider adding to an FAQ appendix.
  • Eventually, a maintainer will decide whether the proposal is accepted or rejected.

Once a proposal is accepted, a number will be assigned. The file in the PR can be renamed with the assigned number, and then merged into trunk.

Rejection

If a proposal is rejected or withdrawn, its pull request is closed without assigning a HIP number or merging the document. The pull request remains the record of that discussion. If the discussion reveals a generally applicable constraint or principle, that information should instead be incorporated into the appropriate project documentation.

Mutability and History

HIPs are mutable, even after being accepted.

Significant changes can be added to a ## History section of the document, inserted before any ## References or appendices. Besides explaining what changed, the reasoning for why should be included.

Alternatives

  • Ad-hoc design docs
    • While design documents are an ideal artifact, without defining a process behind them, it's hard for contributors to know when to use one or how.
  • Module documentation
    • A reader of module documentation is not the right audience.
    • The purpose of module documentation is to explain how to use the library.
    • It is not a great place to document the why.
  • Issues and Pull Requests
    • This is the status quo.
    • Discussion in issues is a horrible experience.
    • Spreads out the full design across disconnected areas.
    • Silos the knowledge directly in GitHub.
  • A chat application, such as Zulip
    • Chat-style messages are extremely poor for having thoughtful discussion and decisions.
    • They become littered with messages that have no longer-term value, and it makes it hard to piece back together what a decision is.
    • No contributor should ever have to participate in chat-style messaging.

Unresolved Questions

  • Is there a point when significant changes to an accepted HIP should itself be a new HIP?
    • Possibly, but I'm not sure it's worth deciding yet.

References


File: _contrib/hips/index.md



File: _data/contrib.yml

  • title: Contributing
    path: ""
    docs:
    • title: Getting started
      path: contributing
    • title: Code of conduct
      path: code-of-conduct
    • title: Commits
      path: commits
    • title: Issues
      path: issues
    • title: Pull requests
      path: pull-requests
    • title: HIPs
      path: hips
    • title: MSRV
      path: msrv
    • title: Governance
      path: governance
    • title: Maintainers
      path: maintainers
    • title: Collaborators
      path: collaborators
    • title: Tenets
      path: tenets
    • title: Vision
      path: vision
    • title: Roadmap
      path: roadmap

File: _data/legacy.yml

  • title: Guide
    path: ""
    guides:

    • server
    • client
  • title: Server
    path: "/server"
    guides:

    • hello-world
    • echo
    • graceful-shutdown
  • title: Client
    path: "/client"
    guides:

    • basic
    • advanced
    • configuration

File: _data/stable.yml

  • title: Guide
    path: ""
    guides:

    • init
    • server
    • client
    • upgrading
  • title: Init
    path: "/init"
    guides:

    • setup
    • runtime
  • title: Server
    path: "/server"
    guides:

    • hello-world
    • echo
    • middleware
    • graceful-shutdown
  • title: Client
    path: "/client"
    guides:

    • basic
    • connectors
  • title: Upgrading
    path: "/upgrading"
    anchor: true
    guides: []


File: _legacy/index.md


hyper is an HTTP library for the Rust language.

NOTE: These guides are for the older 0.14.x version of hyper. Did
you want the v1 guides?

You can start using it by first adding it to your Cargo.toml:

toml
[dependencies]
hyper = "0.14"
  • If building a web server, continue with the [Server guide][].
  • If trying to talk to a server, continue with the [Client guide][].

You could also look at the [generated API documentaton][docs].

[docs]: {{ site.legacy_docs_url }}
[Server guide]: server/hello-world.md
[Client guide]: client/basic.md


File: _legacy/client/advanced.md


title: Advanced Client Usage
layout: guide
redirect_from:
- /guides/client/advanced


Once you've done all the setup in the simple guide, you probably
have more advanced requests you need to make. In this guide, we'll
make a POST request, and make multiple requests at the same time.

Making a POST

We can prepare a [Request][Request] before giving it to the client.
Since we want to post some JSON, and not just simply get a resource,
that's what we'll do.

rust
# extern crate hyper;
use hyper::{Body, Method, Request, Uri};
# fn main() {}

After a quick addition to imports, let's prepare a Request:

rust
# extern crate hyper;
# extern crate tokio;
# mod no_run {
# use hyper::{Body, Method, Request};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
    let req = Request::builder()
        .method(Method::POST)
        .uri("http://httpbin.org/post")
        .header("content-type", "application/json")
        .body(Body::from(r#"{"library":"hyper"}"#))?;

    // We'll send it in a second...

    Ok(())
# }
# }
# fn main() {}

Using a convenient request builder, we set the [Method][Method] to POST,
add a URL, and some headers describing our payload. Lastly, a call to body
with our JSON bytes.

Now, we can give that to the client with the request method:

rust
# extern crate hyper;
# use hyper::{Client, Request};
# async fn run() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
# let req = Request::default();
// let req = ...

let client = Client::new();

// POST it...
let resp = client.request(req).await?;

println!("Response: {}", resp.status());
# Ok(())
# }
# fn main() {}

Multiple Requests

While await allows us to write "asynchronous" code in a way that looks
"synchronous", to take full advantage of it, we can make multiple requests
in parallel instead of serially.

We're going to take advantage of "joining" futures, and so need to update our
imports again:

toml
[dependencies]
hyper = { version = "0.14", features = ["full"] }
tokio = { version = "1", features = ["full"] }
futures = "0.3"

Now, we'll create some async blocks to describe each future, but since they
are lazy, we can start them in parallel.

rust
# extern crate hyper;
# extern crate futures;
# use hyper::{Client, Request, Uri};
# async fn run() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let client = Client::new();

let ip_fut = async {
    let resp = client.get(Uri::from_static("http://httpbin.org/ip")).await?;
    hyper::body::to_bytes(resp.into_body()).await
};
let headers_fut = async {
    let resp = client.get(Uri::from_static("http://httpbin.org/headers")).await?;
    hyper::body::to_bytes(resp.into_body()).await
};

// Wait on both them at the same time:
let (ip, headers) = futures::try_join!(ip_fut, headers_fut)?;
#
# Ok(())
# }
# fn main() {}

[Request]: {{ site.legacy_docs_url }}/hyper/struct.Request.html
[Method]: {{ site.legacy_docs_url }}/hyper/struct.Method.html


File: _legacy/client/basic.md


title: Getting Started with a Client
layout: guide
redirect_from:
- /guides/client/basic


To start with, we'll just get a simple GET request to a webpage working,
so we can see all the moving parts. First, we need our dependencies.
Let's tell Cargo about our dependencies by having this in the Cargo.toml.

Dependencies

toml
[dependencies]
hyper = { version = "0.14", features = ["full"] }
tokio = { version = "1", features = ["full"] }

Now, we need to import pieces to use from our dependencies:

rust
# extern crate hyper;
use hyper::Client;
# fn main() {}

Runtime

Now, we'll make a request in the main of our program. This may seem
like a bit of work just to make a simple request, and you'd be correct,
but the point here is just to show all the setup required. Once you have this,
you are set to make thousands of client requests efficiently.

We have to setup some sort of runtime. By default, hyper can make use of the
Tokio runtime. If you've never used futures in Rust
before, you may wish to read through Tokio's guide on Futures.

rust
# extern crate tokio;
# mod no_run {
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
    // This is where we will setup our HTTP client requests.

    Ok(())
}
# }
# fn main() {}

GET

We can now create a hyper Client that will be registered to our runtime.

Calling client.get returns a Future that will eventually be fulfilled with a
[Response][Response].

rust
# extern crate hyper;
# use hyper::Client;
# async fn run() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
// Still inside `async fn main`...
let client = Client::new();

// Parse an `http::Uri`...
let uri = "http://httpbin.org/ip".parse()?;

// Await the response...
let mut resp = client.get(uri).await?;

println!("Response: {}", resp.status());
# Ok(())
# }
# fn main() {}

Response bodies

Bodies in hyper are always streamed asynchronously. But it's easy to await
for each chunk as it comes in! We'll make use of the HttpBody trait.

We'll also simply write the body to stdout. So, some new imports:

rust
# extern crate hyper;
# extern crate tokio;
use hyper::body::HttpBody as _;
use tokio::io::{stdout, AsyncWriteExt as _};
rust
# extern crate hyper;
# extern crate tokio;
# use hyper::Client;
# use hyper::body::HttpBody as _;
# use tokio::io::{stdout, AsyncWriteExt as _};
# async fn run() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
// Previously...
let client = Client::new();
let uri = "http://httpbin.org/ip".parse()?;
let mut resp = client.get(uri).await?;
println!("Response: {}", resp.status());

// And now...
while let Some(chunk) = resp.body_mut().data().await {
    stdout().write_all(&chunk?).await?;
}
#
# Ok(())
# }
# fn main() {}

And that's it! You can see the [full example here][example].

Client: {{ site.legacy_docs_url }}/hyper/client/struct.Client.html
Tokio: https://tokio.rs
Tokio-Futures: https://tokio.rs/tokio/tutorial/async
[StatusCode]: {{ site.legacy_docs_url }}/hyper/struct.StatusCode.html
[Response]: {{ site.legacy_docs_url }}/hyper/struct.Response.html
[example]: {{ site.legacy_examples_url }}/client.rs


File: _legacy/client/configuration.md


title: Client Configuration
layout: guide
redirect_from:
- /guides/client/configuration


Using TLS

By default, a [Client][] can only speak to HTTP addresses. This is because hyper doesn't
ship with a TLS implementation. You may have noticed that Client::new() returns a
Client<HttpConnector>. We can plug in a different connector using the client config.

Since connecting to HTTPS addresses is so common, hyper provides a separate [hyper-tls][]
crate with a pluggable HttpsConnector. Here's how you'd use it.

rust
# extern crate hyper;
# extern crate hyper_tls;
#
use hyper::Client;
use hyper_tls::HttpsConnector;

# fn run() {
let https = HttpsConnector::new();
let client = Client::builder()
    .build::<_, hyper::Body>(https);
# }
# fn main() {}

Connectors

As mentioned in the section about TLS, [Client][] is generic over a connector.

You can plug in any kind of connector you need. This means that you could pick a
different TLS implementation than the one chosen by hyper-tls, such as rustls.
You could also have a completely different way of creating IO objects. Here's some
ideas of things that could be connectors:

  • Unix sockets
  • Proxies
  • In-memory streams (such as for testing)

[Client]: {{ site.legacy_docs_url }}/hyper/client/struct.Client.html
[hyper-tls]: {{ site.hyper_tls_url }}


File: _legacy/client/index.md


title: Client Guides
permalink: /0.14/client
redirect_from:
- /guides/client/


This is just placeholder page. It should probably become a table of
contents for the client guides.


File: _legacy/server/echo.md


title: Echo, echo, echo
layout: guide
redirect_from:
- /guides/server/echo


You already have a Hello World server? Excellent! Usually,
servers do more than just spit out the same body for every request. To
exercise several more parts of hyper, this guide will go through
building an echo server.

An echo server will listen for incoming connections and send back the
request body as the response body on POST requests.

Routing

First thing we will do, beyond renaming our service to echo, is setup some
routing. We want to have a route explaining instructions on how to use
our server, and another for receiving data. Oh, and we should also
handle the case when someone asks for a route we don't know!

We're going to be using more of the [futures][future-crate] crate, so let's add
that as a dependency:

toml
[dependencies]
hyper = { version = "0.14", features = ["full"] }
tokio = { version = "1", features = ["full"] }
futures = "0.3"

Then, we need to add some to our imports:

rust
# extern crate hyper;
# extern crate futures;
use hyper::{Method, StatusCode};
# fn main() {}

And make some changes to your Service, such as returning a Future of a Response,
since we may not have one ready immediately:

rust
# extern crate futures;
# extern crate hyper;
# use futures::future::{self, Future};
# use hyper::{Body, Method, Request, Response, StatusCode};
#
async fn echo(req: Request<Body>) -> Result<Response<Body>, hyper::Error> {
    let mut response = Response::new(Body::empty());

    match (req.method(), req.uri().path()) {
        (&Method::GET, "/") => {
            *response.body_mut() = Body::from("Try POSTing data to /echo");
        },
        (&Method::POST, "/echo") => {
            // we'll be back
        },
        _ => {
            *response.status_mut() = StatusCode::NOT_FOUND;
        },
    };

    Ok(response)
}
# fn main() {}

We built a super simple routing table just by matching on the method
and path of an incoming Request. If someone requests GET /, our
service will let them know they should try our echo powers out. We also
are checking for POST /echo, but currently don't do anything about it.

Our third rule catches any other method and path combination, and
changes the StatusCode of the Response. The default status of a
Response is HTTP's 200 OK (StatusCode::OK), which is correct for
the other routes. But the third case will instead send back 404 Not Found.

Body Streams

Now let's get that echo in place. We'll start with the simplest solution, and
then make alterations exercising more complex things you can do with the
Body streams.

First up, plain echo. Both the Request and the Response have body streams,
and by default, you can easily just pass the Body of the Request into the
Response.

rust
# extern crate hyper;
# use hyper::{Body, Method, Request, Response};
# fn echo(req: Request<Body>) -> Response<Body> {
#     let mut response = Response::default();
#     match (req.method(), req.uri().path()) {
// inside that match from before
(&Method::POST, "/echo") => {
    *response.body_mut() = req.into_body();
},
#         _ => unreachable!(),
#     }
#     response
# }
# fn main() {}

Running our server now will echo any data we POST to /echo. That was easy.
What if we wanted to uppercase all the text? We could use a map on our streams.

Body mapping

We're going to need a couple of extra imports, so let's add those to the top of the file:

rust
# extern crate futures;
# extern crate hyper;
use futures::TryStreamExt as _;
# fn main() {}

A Body implements the Stream trait from futures, producing a bunch of
Bytess, as data comes in. Bytes is just a convenient type from hyper
that represents a bunch of bytes. It can be easily converted into other
typical containers of bytes.

Next, let's add a new /echo/uppercase route mapping the body to uppercase:

rust
# extern crate hyper;
# extern crate futures;
# use futures::TryStreamExt as _;
# use hyper::{Body, Method, Request, Response};
# fn echo(req: Request<Body>) -> Response<Body> {
#     let mut response = Response::default();
#     match (req.method(), req.uri().path()) {
// Yet another route inside our match block...
(&Method::POST, "/echo/uppercase") => {
    // This is actually a new `futures::Stream`...
    let mapping = req
        .into_body()
        .map_ok(|chunk| {
            chunk.iter()
                .map(|byte| byte.to_ascii_uppercase())
                .collect::<Vec<u8>>()
        });

    // Use `Body::wrap_stream` to convert it to a `Body`...
    *response.body_mut() = Body::wrap_stream(mapping);
},
#         _ => unreachable!(),
#     }
#     response
# }
# fn main() {}

And like that, we have two echo routes: /echo which does no transformation,
and /echo/uppercase which returns all bytes after converting them to ASCII
uppercase.

Buffering the Request Body

What if we want our echo service to reverse the data it received and send it
back to us? We can't really stream the data as it comes in, since we need to
find the end before we can respond. To do this, we can explore how to easily
collect the full body.

In this case, we can't really generate a Response immediately. Instead, we
must wait for the full request body to be received.

We want to concatenate the request body, and map the result into our reverse
function, and return the eventual result. We can make use of the
hyper::body::to_bytes utility function to make this easy.

Note: You must always be careful not to buffer without a max bounds. We'll
set a 64kb maximum here.

rust
# extern crate hyper;
# use hyper::{body::HttpBody, Body, Method, Request, Response};
# async fn echo(req: Request<Body>) -> Result<Response<Body>, hyper::Error> {
#     let mut response = Response::default();
#     match (req.method(), req.uri().path()) {
// Yet another route inside our match block...
(&Method::POST, "/echo/reverse") => {
    // Protect our server from massive bodies.
    let upper = req.body().size_hint().upper().unwrap_or(u64::MAX);
    if upper > 1024 * 64 {
        let mut resp = Response::new(Body::from("Body too big"));
        *resp.status_mut() = hyper::StatusCode::PAYLOAD_TOO_LARGE;
        return Ok(resp);
    }

    // Await the full body to be concatenated into a single `Bytes`...
    let full_body = hyper::body::to_bytes(req.into_body()).await?;

    // Iterate the full body in reverse order and collect into a new Vec.
    let reversed = full_body.iter()
        .rev()
        .cloned()
        .collect::<Vec<u8>>();

    *response.body_mut() = reversed.into();
},
#         _ => unreachable!(),
#     }
#     Ok(response)
# }
# fn main() {}

You can see a compiling [example here][example].

[example]: {{ site.legacy_examples_url }}/echo.rs
[future-crate]: https://github.com/rust-lang-nursery/futures-rs


File: _legacy/server/graceful-shutdown.md


title: Gracefully Shutdown a Server layout: guide

hyper Servers have the ability to "gracefully" shutdown. This means stopping to accept new requests, and shutting down once all in-progress requests have completed.

We're going to assume you already have server code working, such as from following the Hello World or Echo guides.

The Server type has a with_graceful_shutdown method that is passed a Future of your choosing. Once that Future completes, the Server will start the shutdown process. It could be any Future, such as a oneshot channel. In this guide, we'll setup a signal handler to listen for the user pressing CTRL+C.

First, let's create a shutdown_signal async function:

rust
# extern crate tokio;
async fn shutdown_signal() {
    // Wait for the CTRL+C signal
    tokio::signal::ctrl_c()
        .await
        .expect("failed to install CTRL+C signal handler");
}
# fn main() {}

Next, we can plug this into an existing server:

rust
# extern crate hyper;
# extern crate tokio;
# mod no_run {
# use std::convert::Infallible;
# use std::net::SocketAddr;
# use hyper::{Body, Request, Response, Server};
# use hyper::service::{make_service_fn, service_fn};
# async fn hello_world(_req: Request<Body>) -> Result<Response<Body>, Infallible> {
#     Ok(Response::new("Hello, World".into()))
# }
# async fn shutdown_signal() {}
#[tokio::main]
async fn main() {
#    let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
#    let make_svc = make_service_fn(|_conn| async {
#        Ok::<_, Infallible>(service_fn(hello_world))
#    });
    // Constructed `addr` and `make_svc` from your app above..

    // And construct the `Server` like normal...
    let server = Server::bind(&addr).serve(make_svc);

    // And now add a graceful shutdown signal...
    let graceful = server.with_graceful_shutdown(shutdown_signal());

    // Run this server for... forever!
    if let Err(e) = graceful.await {
        eprintln!("server error: {}", e);
    }
}
# }
# fn main() {}

File: _legacy/server/hello-world.md


title: Getting Started with a Server
layout: guide
redirect_from:
- /guides/server/hello-world


Let's start by making a "Hello, World!" server, and expand from there.

First, we need our dependencies. Let's tell Cargo about our dependencies by having this in the Cargo.toml.

toml
[dependencies]
hyper = { version = "0.14", features = ["full"] }
tokio = { version = "1", features = ["full"] }

Now lets start on our main.rs, and add some imports:

rust
# extern crate hyper;
use std::convert::Infallible;
use std::net::SocketAddr;
use hyper::{Body, Request, Response, Server};
use hyper::service::{make_service_fn, service_fn};
# fn main() {}

Creating a Service

A [Service][service] lets you define how to respond to incoming requests.
While it is possible to implement the trait directly, there are a few patterns
that are common when using Hyper. We've included some helpers for when these
patterns fit our needs.

In this example, we don't have any state to carry around, so we really just
need a simple async function:

rust
# extern crate hyper;
# use std::convert::Infallible;
# use hyper::{Body, Request, Response};
# fn main() {}
async fn hello_world(_req: Request<Body>) -> Result<Response<Body>, Infallible> {
    Ok(Response::new("Hello, World".into()))
}

As soon as we get a request, nothing is stopping us from knowing the response
immediately! That function will be used when starting our server.

That new Response will by default have a 200 OK status code, and the Body
is able to tell that it is made from a static string, and is able to add a
Content-Length header for us automatically.

Starting the Server

Lastly, we need to hook up our hello_world service into a running hyper
Server.

We'll dive in to the specifics of some of these things in another guide.

rust
# extern crate hyper;
# extern crate tokio;
# mod no_run {
# use std::convert::Infallible;
# use std::net::SocketAddr;
# use hyper::{Body, Request, Response, Server};
# use hyper::service::{make_service_fn, service_fn};
# async fn hello_world(_req: Request<Body>) -> Result<Response<Body>, Infallible> {
#     Ok(Response::new("Hello, World".into()))
# }
#[tokio::main]
async fn main() {
    // We'll bind to 127.0.0.1:3000
    let addr = SocketAddr::from(([127, 0, 0, 1], 3000));

    // A `Service` is needed for every connection, so this
    // creates one from our `hello_world` function.
    let make_svc = make_service_fn(|_conn| async {
        // service_fn converts our function into a `Service`
        Ok::<_, Infallible>(service_fn(hello_world))
    });

    let server = Server::bind(&addr).serve(make_svc);

    // Run this server for... forever!
    if let Err(e) = server.await {
        eprintln!("server error: {}", e);
    }
}
# }
# fn main() {}

To see all the snippets put together, check out the [full example][example]!

[service]: {{ site.legacy_docs_url }}/hyper/service/trait.Service.html
[example]: {{ site.legacy_examples_url }}/hello.rs


File: _legacy/server/index.md


title: Server Guides
permalink: /0.14/server
redirect_from:
- /guides/server/


This is just placeholder page. It should probably become a table of
contents for the server guides.


File: _posts/2021-05-06-welcome-anthony-ramine.md


title: "Welcome Anthony Ramine"

I'm excited to announce that Anthony Ramine (@nox) has joined the team of maintainers for hyper!

  • Implemented CONNECT support over HTTP/2
  • Added HTTP/0.9 optional support
  • Greater support for preserving header name cases
  • Issue triage and PR reviews ❤️

Thank so much for the fantastic support so far!


File: _posts/2022-04-08-welcome-adam-foltzer.md


title: "Welcome Adam Foltzer"

We'd like to welcome Adam Foltzer (@acfoltzer) as a hyper Collaborator. He has done some great work improving edge-case HTTP/1 support, planning, and most importantly, brings a kind attitude. For instance:

Thanks for all you do for us, we're excited to have you aboard!


File: _posts/2024-02-28-welcome-noah-kennedy.md


title: "Welcome Noah Kennedy"

We'd like to welcome Noah Kennedy (@Noah-Kennedy) as a hyper Collaborator. Noah has paid special attention to security issues in hyper, such as:

Thanks for helping keep us all secure!


File: _posts/2024-03-04-welcome-dswij.md


title: "Welcome dswij"

We're excited to announce that @dswij is now a hyper Collaborator. dswij has been helping in a versatile way:

Thanks for raising the bar!


File: _posts/2024-03-08-welcome-tottoto.md


title: "Welcome tottoto"

We're priviledged to share that @tottoto is now a hyper Collaborator. tottoto has been cleaning up and refactoring in a big way:

Thanks for proactively improving hyper all over!


File: _posts/2025-04-21-welcome-katelyn-martin.md


title: "Welcome katelyn martin"

We're thrilled to welcome katelyn martin (@cratelyn) as a hyper Collaborator. katelyn first contributed back in 2020, and more recently has been improving various utilities and documentation. Some highlights:

Thank you for helping the project and ecosystem!


File: _stable/index.md


title: Getting Started
layout: guide
permalink: /guides/1/
redirect_from:
- /guides/


hyper is an HTTP library for the Rust language.

If this is your first time here, take a look at how to
setup
.

  • If building a web server, continue with the [Server guide][].
  • If trying to talk to a server, continue with the [Client guide][].

You could also look at the [generated API documentaton][docs].

[docs]: {{ site.hyper_docs_url }}
[Server guide]: server/hello-world
[Client guide]: client/basic


File: _stable/upgrading.md


This guide is meant to help you upgrade from v0.14 of hyper to v1.

Were you looking for the v0.14 guides?

Prepare with Backports and Deprecations

Before upgrading, you can start preparing your 0.14 code base by enabling the
backports and deprecated features of hyper in your Cargo.toml. Like
this:

toml
[dependencies]
hyper = { version = "0.14", features = ["etc", "backports", "deprecated"] }

The backports feature brings several of the new types from 1.0 to 0.14. If
you enable deprecated feature as well, it will add deprecation warnings to
any of hyper's types that have direct backports available.

NOTE: This won't give you warnings about changes where backports were not
able to be provided.

NOTE: You should upgrade http-body to version 0.4.6 or newer to be
able to access methods in some suggestions.

Read the Changelog

As a general rule, we tried hard to mark every possible breaking change in the
changelog. Read through the "breaking changes" section of the 1.0 releases
(including the RC 1-4), which will provide suggestions on how to overcome each
one.

`Body`

The Body type has changed to be a trait (what used to be HttpBody).

The 0.14 Body could be multiple variants, and in v1 they have been split into
distinct types. You'll benefit from analzying each place you
use hyper::Body to decide which solution to switch to.

  • In general, if you don't need a specific variant, consider making your usage
    generic, accepting an impl Body (or where B: Body).
  • If you want a type that can be any variant, you could use BoxBody.
  • Otherwise, the more specific variants allow for a more
    explicit API in your code.

`Client`

The higher-level pooling Client was removed from hyper 1.0. A similar type
was added to hyper-util, called client::legacy::Client. It's
mostly a drop-in replacement.

`Server`

The v0.14 hyper::Server does not have a drop-in replacement, since it had
problems.

For a server type that can handle both HTTP/1 and HTTP/2 at the same time,
use the server::conn::auto::Builder from hyper-util.

The listening server acceptor can be replaced with a simple loop.

`Service`/`service_fn`

Previously, hyper depended on tower for the Service trait. Because
tower is not yet 1.0, hyper could not publicly depend on it. So, its
Service trait and the helper function service_fn are defined in
hyper::service. Check middleware for more details.


File: _stable/client/basic.md


title: Getting Started with a Client layout: guide

To start with, we'll just get a simple GET request to a webpage working,
so we can see all the moving parts. First, we need our dependencies.
Let's tell Cargo about our dependencies by having this in the Cargo.toml.

Dependencies

toml
[dependencies]
hyper = { version = "1", features = ["full"] }
tokio = { version = "1", features = ["full"] }
http-body-util = "0.1"
hyper-util = { version = "0.1", features = ["full"] }

Now, we need to import pieces to use from our dependencies:

rust
# extern crate http_body_util;
# extern crate hyper;
# extern crate tokio;
# extern crate hyper_util;
use http_body_util::Empty;
use hyper::Request;
use hyper::body::Bytes;
use hyper_util::rt::TokioIo;
use tokio::net::TcpStream;
# fn main() {}

Runtime

Now, we'll make a request in the main of our program. This may seem
like a bit of work just to make a simple request, and you'd be correct,
but the point here is just to show all the setup required. Once you have this,
you are set to make thousands of client requests efficiently.

We have to setup some sort of runtime. You can use whichever async runtime you'd
like, but for this guide we're going to use tokio. If you've never used futures
in Rust before, you may wish to read through Tokio's guide on Futures.

rust
# extern crate tokio;
# mod no_run {
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
    // This is where we will setup our HTTP client requests.

    Ok(())
}
# }
# fn main() {}

Setup

To get started we'll need to get a few things setup. For this guide we're
going to send a GET [Request][Request] to http://httpbin.org/ip,
which will return a 200 OK and the Requester's IP address in the body.

We need to open a TCP connection to the remote host using a hostname and port,
which in this case is httpbin.org and the default port for HTTP: 80. With our
connection opened, we pass it in to the client::conn::http1::handshake function,
performing a handshake to verify the remote is ready to receive our requests.

A successful handshake will give us a [Connection][Connection] future that processes
all HTTP state, and a [SendRequest][SendRequest] struct that we can use to send our
Requests on the connection.

To start driving the HTTP state we have to poll the Connection, so to finish our
setup we'll spawn a tokio::task and await it.

rust
# extern crate http_body_util;
# extern crate hyper;
# extern crate hyper_util;
# extern crate tokio;
# use http_body_util::Empty;
# use hyper::body::Bytes;
# use hyper::Request;
# use hyper_util::rt::TokioIo;
# use tokio::net::TcpStream;
# async fn run() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
// Parse our URL...
let url = "http://httpbin.org/ip".parse::<hyper::Uri>()?;

// Get the host and the port
let host = url.host().expect("uri has no host");
let port = url.port_u16().unwrap_or(80);

let address = format!("{}:{}", host, port);

// Open a TCP connection to the remote host
let stream = TcpStream::connect(address).await?;

// Use an adapter to access something implementing `tokio::io` traits as if they implement
// `hyper::rt` IO traits.
let io = TokioIo::new(stream);

// Create the Hyper client
let (mut sender, conn) = hyper::client::conn::http1::handshake(io).await?;

// Spawn a task to poll the connection, driving the HTTP state
tokio::task::spawn(async move {
    if let Err(err) = conn.await {
        println!("Connection failed: {:?}", err);
    }
});
# let authority = url.authority().unwrap().clone();
# let req = Request::builder()
#     .uri(url)
#     .header(hyper::header::HOST, authority.as_str())
#     .body(Empty::<Bytes>::new())?;
# let mut res = sender.send_request(req).await?;
# Ok(())
# }
# fn main() {}

GET

Now that we've set up our connection, we're ready to construct and send our first Request!
Since SendRequest doesn't require absolute-form URIs we are required to include a HOST
header in our requests. And while we can send our Request with an empty Body, we need to
explicitly set it, which we'll do with the [Empty][Empty] utility struct.

All we need to do now is pass the Request to SendRequest::send_request, this returns a
future which will resolve to the [Response][Response] from httpbin.org. We'll print the
status of the response to see that it returned the expected 200 OK status.

rust
# extern crate http_body_util;
# extern crate hyper;
# extern crate hyper_util;
# extern crate tokio;
# use http_body_util::Empty;
# use hyper::body::Bytes;
# use hyper::Request;
# use hyper_util::rt::TokioIo;
# use tokio::net::TcpStream;
# async fn run() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
# let url = "http://httpbin.org/ip".parse::<hyper::Uri>()?;
# let host = url.host().expect("uri has no host");
# let port = url.port_u16().unwrap_or(80);
# let addr = format!("{}:{}", host, port);
# let stream = TcpStream::connect(addr).await?;
# let io = TokioIo::new(stream);
# let (mut sender, conn) = hyper::client::conn::http1::handshake(io).await?;
# tokio::task::spawn(async move {
# if let Err(err) = conn.await {
# println!("Connection failed: {:?}", err);
# }
# });
// The authority of our URL will be the hostname of the httpbin remote
let authority = url.authority().unwrap().clone();

// Create an HTTP request with an empty body and a HOST header
let req = Request::builder()
    .uri(url)
    .header(hyper::header::HOST, authority.as_str())
    .body(Empty::<Bytes>::new())?;

// Await the response...
let mut res = sender.send_request(req).await?;

println!("Response status: {}", res.status());
# Ok(())
# }
# fn main() {}

Response bodies

We know that sending a GET Request to httpbin.org/ip will return our IP address in
the Response body. To see the returned body, we'll simply write it to stdout.

Bodies in hyper are asynchronous streams of [Frame][Frame]s, so we don't have to wait for the
whole body to arrive, buffering it into memory, and then writing it out. We can simply
await each Frame and write them directly to stdout as they arrive!

In addition to importing stdout, we'll need to make use of the BodyExt trait:

rust
# extern crate http_body_util;
# extern crate tokio;
use http_body_util::BodyExt;
use tokio::io::{AsyncWriteExt as _, self};
rust
# extern crate http_body_util;
# extern crate hyper;
# extern crate hyper_util;
# extern crate tokio;
# use http_body_util::{BodyExt, Empty};
# use hyper::body::Bytes;
# use hyper::Request;
# use hyper_util::rt::TokioIo;
# use tokio::net::TcpStream;
# use tokio::io::{self, AsyncWriteExt as _};
# async fn run() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
# let url = "http://httpbin.org/ip".parse::<hyper::Uri>()?;
# let host = url.host().expect("uri has no host");
# let port = url.port_u16().unwrap_or(80);
# let addr = format!("{}:{}", host, port);
# let stream = TcpStream::connect(addr).await?;
# let io = TokioIo::new(stream);
# let (mut sender, conn) = hyper::client::conn::http1::handshake(io).await?;
# tokio::task::spawn(async move {
#     if let Err(err) = conn.await {
#         println!("Connection failed: {:?}", err);
#     }
# });
# let authority = url.authority().unwrap().clone();
# let req = Request::builder()
# .uri(url)
# .header(hyper::header::HOST, authority.as_str())
# .body(Empty::<Bytes>::new())?;
# let mut res = sender.send_request(req).await?;
// Stream the body, writing each frame to stdout as it arrives
while let Some(next) = res.frame().await {
    let frame = next?;
    if let Some(chunk) = frame.data_ref() {
        io::stdout().write_all(chunk).await?;
    }
}
# Ok(())
# }
# fn main() {}

And that's it! You can see the [full example here][example].

[StatusCode]: {{ site.hyper_docs_url }}/hyper/struct.StatusCode.html
[Response]: {{ site.http_docs_url }}/http/response/struct.Response.html
[Request]: {{ site.http_docs_url }}/http/request/struct.Request.html
[Connection]: {{ site.hyper_docs_url }}/hyper/client/conn/http1/struct.Connection.html
[SendRequest]: {{ site.hyper_docs_url }}/hyper/client/conn/http1/struct.SendRequest.html
[Frame]: {{ site.hyper_docs_url }}/hyper/body/struct.Frame.html
[Empty]: {{ site.http_body_util_url }}/http_body_util/struct.Empty.html

[example]: {{ site.examples_url }}/client.rs


File: _stable/client/connectors.md


title: Connectors, Pools, and HTTPS layout: guide

Unstable: The code discussed in this guide is in hyper-util,
which is not as stable as that which is in hyper. It is production
ready, but changes may come more frequently.

TODO

What is a connector?

TODO

Connection Pools

TODO

HTTPS

hyper allows you to bring your own IO, so it can work on top of any TLS
implementation. (TODO: link to runtime guide)

There are also crates that provide "connectors" which result in
easy-to-use HTTPS for the legacy client in hyper-util. Each has their
own reason for existing, and pros and cons, but this list is provided to
help you get started[^tls-list]:

[^tls-list]: This isn't an endorsement for any of the crates, and they all are maintained separately from hyper.


File: _stable/client/index.md


This is just placeholder page. It should probably become a table of
contents for the client guides.


File: _stable/client/post.md


title: Post Request Guides layout: guide

POST

Now that we've seen how to make a GET request, let's look at how to make a POST request. This is useful when you need to send data to the server, such as when submitting a form or uploading a file.

To make a POST request, we'll need to change a few things from our GET request:

  1. We'll set the method to POST.
  2. We'll need to provide a request body.
  3. We'll need to specify the type of data in our body by adding a hyper::header::CONTENT_TYPE header.

For the body, we have a couple of options. We can use a simple string, a JSON string, or we can use raw bytes. Let's look at all three:

rust
# extern crate http_body_util;
# extern crate hyper;
# extern crate hyper_util;
# extern crate tokio;
# use http_body_util::{Empty, Full};
# use hyper::body::Bytes;
# use hyper::Request;
# use hyper_util::rt::TokioIo;
# use tokio::net::TcpStream;
# async fn run() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
# let url = "http://httpbin.org/ip".parse::<hyper::Uri>()?;
# let host = url.host().expect("uri has no host");
# let port = url.port_u16().unwrap_or(80);
# let addr = format!("{}:{}", host, port);
# let stream = TcpStream::connect(addr).await?;
# let io = TokioIo::new(stream);
# let (mut sender, conn) = hyper::client::conn::http1::handshake(io).await?;
# tokio::task::spawn(async move {
# if let Err(err) = conn.await {
# println!("Connection failed: {:?}", err);
# }
# });

// The authority of our URL will be the hostname of the httpbin remote
let authority = url.authority().unwrap().clone();

// For plain text
let req_body = Full::<Bytes>::from("Some plain text as a body.");
let req = Request::builder()
    .method(hyper::Method::POST)
    .header(hyper::header::HOST, authority.as_str())
    .header(hyper::header::CONTENT_TYPE, "text/plain")
    .body(req_body)?;

// For JSON data
let json_data = r#"{"key": "value"}"#;
let req_body = Full::<Bytes>::from(json_data);
let req = Request::builder()
    .method(hyper::Method::POST)
    .header(hyper::header::HOST, authority.as_str())
    .header(hyper::header::CONTENT_TYPE, "application/json")
    .body(req_body)?;

// For binary data
let binary_data = vec![0u8; 128]; // Example binary data
let req_body = Full::<Bytes>::from(binary_data);
let req = Request::builder()
    .method(hyper::Method::POST)
    .header(hyper::header::HOST, authority.as_str())
    .header(hyper::header::CONTENT_TYPE, "application/octet-stream")
    .body(req_body)?;


let res = sender.send_request(req).await?;

println!("Response status: {}", res.status());
# Ok(())
# }
# fn main() {}

File: _stable/init/runtime.md


With hyper v1.0 removing tokio as runtime dependency, a new runtime trait hyper::rt is introduced. If you still want to use tokio, a tokio implementation for hyper::rt is provided by hyper-util crate.

Building your own `hyper::rt` implementations with `Tokio`

Let's build a simple hyper::rt implementations with the help of tokio. First, make sure you have tokio as a dependency in your Cargo.toml:

toml
[dependencies]
tokio = { version = "1", features = ["full"] }

Now, let's try to write a simple executor hyper::rt::Executor, we'll call it TokioExecutor:

rust
# extern crate hyper;
/// Future executor that utilises `tokio` threads.
#[non_exhaustive]
#[derive(Default, Debug, Clone)]
pub struct TokioExecutor {}

the trait hyper::rt::Executor expects an execute method that asks the runtime to execute futures. tokio allows this easily with tokio::spawn

rust
# extern crate hyper;
# extern crate tokio;

use std::future::Future;

use hyper::rt::Executor;

# #[non_exhaustive]
# #[derive(Default, Debug, Clone)]
# pub struct TokioExecutor {}

impl<Fut> Executor<Fut> for TokioExecutor
where
    Fut: Future + Send + 'static,
    Fut::Output: Send + 'static,
{
    fn execute(&self, fut: Fut) {
        tokio::spawn(fut);
    }
}

We now have a working hyper::rt::Executor with Tokio, and is ready to be supplied to anything that requires Executor. For example, with the auto connection from hyper-util:

rust
# extern crate hyper;
# extern crate hyper_util;
# extern crate tokio;

use std::future::Future;

use hyper_util::server::conn::auto;
use hyper::rt::Executor;

# #[non_exhaustive]
# #[derive(Default, Debug, Clone)]
# pub struct TokioExecutor {}

# impl<Fut> Executor<Fut> for TokioExecutor
# where
#     Fut: Future + Send + 'static,
#     Fut::Output: Send + 'static,
# {
#     fn execute(&self, fut: Fut) {
#         tokio::spawn(fut);
#     }
# }

impl TokioExecutor {
    pub fn new() -> Self {
        Self {}
    }
}

auto::Builder::new(TokioExecutor::new());

Using `hyper::rt` implementations with tokio in hyper-util

The crate hyper-util provides implementations for hyper::rt traits with Tokio. To use, we'll need to have hyper-util as a dependency.

toml
[dependencies]
hyper-util = { version = "0.1", features = ["full"] }

Then you'll simply need to import and use it as in the example above

rust
# extern crate hyper;
# extern crate hyper_util;

use hyper::rt::Executor;
use hyper_util::rt::TokioExecutor;
use hyper_util::server::conn::auto;

auto::Builder::new(TokioExecutor::new());

There are more implementations in the hyper_util crate. Check out the docs on hyper_util::rt for more details.


File: _stable/init/setup.md


This will help get your initial setup ready to be able to try out the
lessons in the guides.

Dependencies

To make things simple, you can add the follow crates to your
Cargo.toml:

toml
[dependencies]
hyper = { version = "1", features = ["full"] }
tokio = { version = "1", features = ["full"] }
http-body-util = "0.1"
hyper-util = { version = "0.1", features = ["full"] }

And with that, you're good to go! Depending on what you want to
accomplish, you can move on to either the client or server
guides.


File: _stable/server/echo.md


title: Echo, echo, echo layout: guide

You already have a Hello World server? Excellent! Usually,
servers do more than just spit out the same body for every request. To
exercise several more parts of hyper, this guide will go through
building an echo server.

An echo server will listen for incoming connections and send back the
request body as the response body on POST requests.

Routing

First thing we will do, beyond renaming our service to echo, is setup some
routing. We want to have a route explaining instructions on how to use
our server, and another for receiving data. Oh, and we should also
handle the case when someone asks for a route we don't know!

Before we get started we need to add some new imports:

rust
# extern crate hyper;
# extern crate http_body_util;
use hyper::body::Frame;
use hyper::{Method, StatusCode};
use http_body_util::{combinators::BoxBody, BodyExt};
# fn main() {}

Next, we need to make some changes to our Service function, but as you can see
it's still just an async function that takes a Request and returns a Response
future, and you can pass it to your server just like we did for the hello service.

Unlike our hello service where we didn't care about the request body and
we always returned a single chunk of bytes containing our greeting, we're now
going to want a bit more freedom in how we shape our response Body. To achieve
this we will change the type of the Body in our Response to a boxed trait object.
We only care that the response body implements the Body trait, that its data is Bytes and its error is a hyper::Error.

rust
# extern crate hyper;
# extern crate http_body_util;
# use hyper::body::Bytes;
# use http_body_util::{combinators::BoxBody, BodyExt, Empty, Full};
# use hyper::{Method, Request, Response, StatusCode};
async fn echo(
    req: Request<hyper::body::Incoming>,
) -> Result<Response<BoxBody<Bytes, hyper::Error>>, hyper::Error> {
    match (req.method(), req.uri().path()) {
        (&Method::GET, "/") => Ok(Response::new(full(
            "Try POSTing data to /echo",
        ))),
        (&Method::POST, "/echo") => {
            // we'll be back
# Ok(Response::new(req.into_body().boxed()))
        },

        // Return 404 Not Found for other routes.
        _ => {
            let mut not_found = Response::new(empty());
            *not_found.status_mut() = StatusCode::NOT_FOUND;
            Ok(not_found)
        }
    }
}

// We create some utility functions to make Empty and Full bodies
// fit our broadened Response body type.
fn empty() -> BoxBody<Bytes, hyper::Error> {
    Empty::<Bytes>::new()
        .map_err(|never| match never {})
        .boxed()
}
fn full<T: Into<Bytes>>(chunk: T) -> BoxBody<Bytes, hyper::Error> {
    Full::new(chunk.into())
        .map_err(|never| match never {})
        .boxed()
}
# fn main() {}

We built a super simple routing table just by matching on the method and path
of an incoming Request. If someone requests GET /, our service will let them
know they should try our echo powers out. We also check for POST /echo, but
currently don't do anything about it.

Our third rule catches any other method and path combination, and changes the
StatusCode of the Response. The default status of a Response is HTTP’s
200 OK (StatusCode::OK), which is correct for the other routes. But the third
case will instead send back 404 Not Found.

Body Streams

Now let's get that echo in place. An HTTP body is a stream of Frames, each
Frame containing
parts of the Body data or trailers. So rather than reading the entire Body
into a buffer before sending our response, we can stream each frame as it arrives.
We'll start with the simplest solution, and then make alterations exercising more complex
things you can do with the Body streams.

First up, plain echo. Both the Request and the Response have body streams,
and by default, you can easily pass the Body of the Request into a Response.

rust
# extern crate hyper;
# extern crate http_body_util;
# use hyper::body::Bytes;
# use http_body_util::{combinators::BoxBody, BodyExt, Empty, Full};
# use hyper::{Method, Request, Response, StatusCode};
# async fn echo(
#    req: Request<hyper::body::Incoming>,
# ) -> Result<Response<BoxBody<Bytes, hyper::Error>>, hyper::Error> {
#    match (req.method(), req.uri().path()) {
// Inside the match from before
(&Method::POST, "/echo") => Ok(Response::new(req.into_body().boxed())),
#        _ => unreachable!(),
#    }
# }
# fn main() {}

Running our server now will echo any data we POST to /echo. That was easy.
What if we wanted to uppercase all the text? We could use a map on our streams.

Body mapping

Every data Frame of our body stream is a chunk of bytes, which we can conveniently
represent using the Bytes type from hyper. It can be easily converted into other
typical containers of bytes.

Next, let's add a new /echo/uppercase route, mapping each byte in the data Frames
of our request body to uppercase, and returning the stream in our Response:

rust
# extern crate hyper;
# extern crate http_body_util;
# use hyper::body::Bytes;
# use http_body_util::{combinators::BoxBody, BodyExt, Empty, Full};
# use hyper::body::Frame;
# use hyper::{Method, Request, Response, StatusCode};
# async fn echo(
#    req: Request<hyper::body::Incoming>,
# ) -> Result<Response<BoxBody<Bytes, hyper::Error>>, hyper::Error> {
#    match (req.method(), req.uri().path()) {
// Yet another route inside our match block...
(&Method::POST, "/echo/uppercase") => {
    // Map this body's frame to a different type
    let frame_stream = req.into_body().map_frame(|frame| {
        let frame = if let Ok(data) = frame.into_data() {
            // Convert every byte in every Data frame to uppercase
            data.iter()
                .map(|byte| byte.to_ascii_uppercase())
                .collect::<Bytes>()
        } else {
            Bytes::new()
        };

        Frame::data(frame)
    });

    Ok(Response::new(frame_stream.boxed()))
},
#         _ => unreachable!(),
#     }
# }
# fn full<T: Into<Bytes>>(chunk: T) -> BoxBody<Bytes, hyper::Error> {
#     Full::new(chunk.into())
#         .map_err(|never| match never {})
#         .boxed()
# }
# fn main() {}

And like that, we have two echo routes: /echo which does no transformation,
and /echo/uppercase which returns all bytes after converting them to ASCII
uppercase.

Buffering the Request Body

What if we want our echo service to reverse the data it received and send it
back to us? We can't really stream the data as it comes in, since we need to
find the end before we can respond. To do this, we can explore how to easily
collect the full body.

We want to collect the entire request body and map the result into our reverse
function, then return the eventual result. If we import the http_body_util::BodyExt
extension trait, we can call the collect method on our body, which will drive the
stream to completion, collecting all the data and trailer frames into a Collected type.
We can easily turn the Collected body into a single Bytes by calling its into_bytes
method.

Note: You must always be careful not to buffer without a max bounds. We'll
set a 64kb maximum here.

rust
# extern crate hyper;
# extern crate http_body_util;
# use hyper::body::Bytes;
# use http_body_util::{combinators::BoxBody, BodyExt, Empty, Full};
# use hyper::{body::Body, Method, Request, Response, StatusCode};
# async fn echo(
#    req: Request<hyper::body::Incoming>,
# ) -> Result<Response<BoxBody<Bytes, hyper::Error>>, hyper::Error> {
#    match (req.method(), req.uri().path()) {
// Yet another route inside our match block...
(&Method::POST, "/echo/reversed") => {
    // Protect our server from massive bodies.
    let upper = req.body().size_hint().upper().unwrap_or(u64::MAX);
    if upper > 1024 * 64 {
        let mut resp = Response::new(full("Body too big"));
        *resp.status_mut() = hyper::StatusCode::PAYLOAD_TOO_LARGE;
        return Ok(resp);
    }

    // Await the whole body to be collected into a single `Bytes`...
    let whole_body = req.collect().await?.to_bytes();

    // Iterate the whole body in reverse order and collect into a new Vec.
    let reversed_body = whole_body.iter()
        .rev()
        .cloned()
        .collect::<Vec<u8>>();

    Ok(Response::new(full(reversed_body)))
},
#         _ => unreachable!(),
#     }
# }
# fn full<T: Into<Bytes>>(chunk: T) -> BoxBody<Bytes, hyper::Error> {
#     Full::new(chunk.into())
#         .map_err(|never| match never {})
#         .boxed()
# }
# fn main() {}

You can see a compiling [example here][example].

[example]: {{ site.examples_url }}/echo.rs


File: _stable/server/graceful-shutdown.md


title: Gracefully Shutdown a Server
layout: guide
redirect_from:
- /guides/server/graceful-shutdown


hyper's server connections have the ability to initiate a graceful shutdown. A common desire is to coordinate graceful shutdown across all active connections. This is what we'll tackle in this guide.

A graceful shutdown is when a connection stops allowing new requests, while allowing currently in-flight requests to complete.

In order to do, we'll need several pieces:

  1. A signal for when to start the shutdown.
  2. An accept loop handling newly received connections.
  3. A watcher to coordinate the shutdown.

Determine a shutdown signal

You can use any mechanism to signal that graceful shutdown should begin. That could be an process signal handler, a timer, a special HTTP request, or anything else.

We're going to use a CTRL+C signal handler for this guide. Tokio has simple support for making one, let's try:

rust
# extern crate tokio;
async fn shutdown_signal() {
    // Wait for the CTRL+C signal
    tokio::signal::ctrl_c()
        .await
        .expect("failed to install CTRL+C signal handler");
}
# fn main() {}

Modify your server accept loop

Unstable: The code discussed in this guide is in hyper-util,
which is not as stable as that which is in hyper. It is production
ready, but changes may come more frequently.

We're assuming you have an accept loop for your server, similar to what was shown in the Hello World guide. So, we're just going to modify it here:

rust
# extern crate hyper;
# extern crate http_body_util;
# extern crate hyper_util;
# extern crate tokio;
# mod no_run {
# use std::convert::Infallible;
# use std::net::SocketAddr;
#
# use http_body_util::Full;
# use hyper::body::Bytes;
# use hyper::server::conn::http1;
# use hyper::service::service_fn;
# use hyper::{Request, Response};
# use hyper_util::rt::TokioIo;
# use tokio::net::TcpListener;
# async fn shutdown_signal() {}
# async fn hello(
#     _: Request<hyper::body::Incoming>,
# ) -> Result<Response<Full<Bytes>>, Infallible> {
#     Ok(Response::new(Full::new(Bytes::from("Hello World!"))))
# }
# async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
# let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
let listener = TcpListener::bind(addr).await?;
// specify our HTTP settings (http1, http2, auto all work)
let http = http1::Builder::new();
// the graceful watcher
let graceful = hyper_util::server::graceful::GracefulShutdown::new();
// when this signal completes, start shutdown
let mut signal = std::pin::pin!(shutdown_signal());

// Our server accept loop
loop {
    tokio::select! {
        Ok((stream, _addr)) = listener.accept() => {
            let io = TokioIo::new(stream);
            let conn = http.serve_connection(io, service_fn(hello));
            // watch this connection
            let fut = graceful.watch(conn);
            tokio::spawn(async move {
                if let Err(e) = fut.await {
                    eprintln!("Error serving connection: {:?}", e);
                }
            });
        },

        _ = &mut signal => {
            drop(listener);
            eprintln!("graceful shutdown signal received");
            // stop the accept loop
            break;
        }
    }
}

// Now start the shutdown and wait for them to complete
// Optional: start a timeout to limit how long to wait.

tokio::select! {
    _ = graceful.shutdown() => {
        eprintln!("all connections gracefully closed");
    },
    _ = tokio::time::sleep(std::time::Duration::from_secs(10)) => {
        eprintln!("timed out wait for all connections to close");
    }
}
# Ok(())
# }
# }
# fn main() {}

File: _stable/server/hello-world.md


title: Getting Started with a Server layout: guide

Let's start by making a "Hello, World!" server, and expand from there.

First, we need to declare our dependencies, let's add the following to our Cargo.toml:

toml
[dependencies]
hyper = { version = "1", features = ["full"] }
tokio = { version = "1", features = ["full"] }
http-body-util = "0.1"
hyper-util = { version = "0.1", features = ["full"] }

Next, we need to add some imports in our main.rs file:

rust
# extern crate tokio;
# extern crate hyper;
# extern crate http_body_util;
# extern crate hyper_util;
use std::convert::Infallible;
use std::net::SocketAddr;

use http_body_util::Full;
use hyper::body::Bytes;
use hyper::server::conn::http1;
use hyper::service::service_fn;
use hyper::{Request, Response};
use hyper_util::rt::TokioIo;
use tokio::net::TcpListener;
# fn main() {}

Creating a Service

A [Service][service] lets us define how our server will respond to
incoming requests. It represents an async function that takes a
[Request][request] and returns a Future. When the processing of this
future is complete, it will resolve to a [Response][response] or an error.

Hyper provides a utility for creating a Service from a function that should
serve most use cases: [service_fn][service_fn]. We will use this to create
a service from our hello function below when we're ready to start our
server.

rust
# extern crate hyper;
# extern crate http_body_util;
# use std::convert::Infallible;
# use http_body_util::Full;
# use hyper::body::Bytes;
# use hyper::{Request, Response};
# fn main() {}
async fn hello(_: Request<hyper::body::Incoming>) -> Result<Response<Full<Bytes>>, Infallible> {
    Ok(Response::new(Full::new(Bytes::from("Hello, World!"))))
}

Using this function as a service, we tell our server to respond to all requests
with a default 200 OK status. The response Body will contain our friendly
greeting as a single chunk of bytes, and the Content-Length header will be
set automatically.

Starting the Server

Lastly, we need to hook up our hello service into a running Hyper server.

We'll dive in to the specifics of some of these things in another guide.

rust
# extern crate tokio;
# extern crate hyper;
# extern crate http_body_util;
# extern crate hyper_util;
# mod no_run {
# use std::convert::Infallible;
# use std::net::SocketAddr;
# 
# use http_body_util::Full;
# use hyper::body::Bytes;
# use hyper::server::conn::http1;
# use hyper::service::service_fn;
# use hyper::{Request, Response};
# use hyper_util::rt::TokioIo;
# use tokio::net::TcpListener;
# async fn hello(
#     _: Request<hyper::body::Incoming>,
# ) -> Result<Response<Full<Bytes>>, Infallible> {
#     Ok(Response::new(Full::new(Bytes::from("Hello World!"))))
# }
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
    let addr = SocketAddr::from(([127, 0, 0, 1], 3000));

    // We create a TcpListener and bind it to 127.0.0.1:3000
    let listener = TcpListener::bind(addr).await?;

    // We start a loop to continuously accept incoming connections
    loop {
        let (stream, _) = listener.accept().await?;

        // Use an adapter to access something implementing `tokio::io` traits as if they implement
        // `hyper::rt` IO traits.
        let io = TokioIo::new(stream);

        // Spawn a tokio task to serve multiple connections concurrently
        tokio::task::spawn(async move {
            // Finally, we bind the incoming connection to our `hello` service
            if let Err(err) = http1::Builder::new()
                // `service_fn` converts our function in a `Service`
                .serve_connection(io, service_fn(hello))
                .await
            {
                eprintln!("Error serving connection: {:?}", err);
            }
        });
    }
}
# }
# fn main() {}

To see all the snippets put together, check out the [full example][example]!

Also, if service_fn doesn't meet your requirements and you'd like to implement
Service yourself, see this [example][impl service].

HTTP/2

This example uses the http1 module to create a server that speaks HTTP/1.
In case you want to use HTTP/2, you can use the http2 module with a few changes to the
http1 server. The http2 builder requires an executor to run. Executor should implement the hyper::rt::Executor trait.

To implement the Executor, check out the runtime [example][runtime].
To see the full example with HTTP/2, check out the [full example][example_http2]!

[service]: {{ site.hyper_docs_url }}/hyper/service/trait.Service.html
[service_fn]: {{ site.hyper_docs_url }}/hyper/service/fn.service_fn.html
[request]: {{ site.hyper_docs_url }}/hyper/struct.Request.html
[response]: {{ site.hyper_docs_url }}/hyper/struct.Response.html
[parts]: {{ site.http_docs_url }}/http/response/struct.Parts.html
[example]: {{ site.examples_url }}/hello.rs
[example_http2]: {{ site.examples_url }}/hello-http2.rs
[runtime]: ../init/runtime.md
[impl service]: {{ site.examples_url }}/service_struct_impl.rs


File: _stable/server/index.md


This is just placeholder page. It should probably become a table of
contents for the server guides.


File: _stable/server/middleware.md


title: Getting Started with a Server Middleware layout: guide

As [Upgrade][upgrading] mentioned, hyper v1 does not depend on tower for the Service trait. When we want to add tower-like middleware, there are 2 kinds of approach to make it.

Let's create a Logger middleware in [hello-world server][hello-world] for instance:

Add tower dependency first

toml
[dependencies]
hyper = { version = "1", features = ["full"] }
tokio = { version = "1", features = ["full"] }
http-body-util = "0.1"
hyper-util = { version = "0.1", features = ["full"] }
tower = "0.4" # here

Option 1: Use hyper Service trait

Implement hyper Logger middleware

rust
# extern crate hyper;
use hyper::{Request, body::Incoming, service::Service};

#[derive(Debug, Clone)]
pub struct Logger<S> {
    inner: S,
}
impl<S> Logger<S> {
    pub fn new(inner: S) -> Self {
        Logger { inner }
    }
}
type Req = Request<Incoming>;

impl<S> Service<Req> for Logger<S>
where
    S: Service<Req>,
{
    type Response = S::Response;
    type Error = S::Error;
    type Future = S::Future;
    fn call(&self, req: Req) -> Self::Future {
        println!("processing request: {} {}", req.method(), req.uri().path());
        self.inner.call(req)
    }
}
# fn main() {}

Then this can be used in server:

rust
# extern crate tower;
# extern crate hyper;
# extern crate http_body_util;
# extern crate tokio;
# extern crate hyper_util;
# mod no_run {
use std::{convert::Infallible, net::SocketAddr};
use hyper::{
    service::Service,
    body::{Bytes, Incoming},
    server::conn::http1,
    Request, Response,
};
use http_body_util::Full;
use hyper_util::rt::TokioIo;
use tokio::net::TcpListener;
use tower::ServiceBuilder;

# #[derive(Debug, Clone)]
# pub struct Logger<S> {
#     inner: S,
# }
# impl<S> Logger<S> {
#    pub fn new(inner: S) -> Self {
#        Logger { inner }
#    }
# }
# type Req = Request<Incoming>;

# impl<S> Service<Req> for Logger<S>
# where
#     S: Service<Req>,
# {
#     type Response = S::Response;
#     type Error = S::Error;
#     type Future = S::Future;
#     fn call(&self, req: Req) -> Self::Future {
#         println!("processing request: {} {}", req.method(), req.uri().path());
#         self.inner.call(req)
#     }
# }
async fn hello(_: Request<Incoming>) -> Result<Response<Full<Bytes>>, Infallible> {
    Ok(Response::new(Full::new(Bytes::from("Hello, World!"))))
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
    let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
    let listener = TcpListener::bind(addr).await?;
    loop {
        let (stream, _) = listener.accept().await?;
        let io = TokioIo::new(stream);
        tokio::spawn(async move {
            // N.B. should use hyper service_fn here, since it's required to be implemented hyper Service trait!
            let svc = hyper::service::service_fn(hello);
            let svc = ServiceBuilder::new().layer_fn(Logger::new).service(svc);
            if let Err(err) = http1::Builder::new().serve_connection(io, svc).await {
                eprintln!("server error: {}", err);
            }
        });
    }
}
# }
# fn main() {}

Option 2: use hyper TowerToHyperService trait

[hyper_util::service::TowerToHyperService][adapter-trait] trait is an adapter to convert tower Service to hyper Service.

Now implement a tower Logger middleware

rust
# extern crate tower;
# extern crate hyper;
use hyper::{Request, body::Incoming};
use tower::Service;

#[derive(Debug, Clone)]
pub struct Logger<S> {
    inner: S,
}
impl<S> Logger<S> {
    pub fn new(inner: S) -> Self {
        Logger { inner }
    }
}
type Req = Request<Incoming>;
impl<S> Service<Req> for Logger<S>
where
    S: Service<Req> + Clone,
{
    type Response = S::Response;

    type Error = S::Error;

    type Future = S::Future;

    fn poll_ready(
        &mut self,
        cx: &mut std::task::Context<'_>,
    ) -> std::task::Poll<Result<(), Self::Error>> {
        self.inner.poll_ready(cx)
    }

    fn call(&mut self, req: Req) -> Self::Future {
        println!("processing request: {} {}", req.method(), req.uri().path());
        self.inner.call(req)
    }
}
# fn main() {}

Then use it in the server:

rust
# extern crate hyper;
# extern crate http_body_util;
# extern crate hyper_util;
# extern crate tokio;
# extern crate tower;
# mod no_run {
use std::{convert::Infallible, net::SocketAddr};

use hyper::{
    body::{Bytes, Incoming},
    server::conn::http1,
    Request, Response,
};

use http_body_util::Full;
use hyper_util::{rt::TokioIo, service::TowerToHyperService};
use tokio::net::TcpListener;
use tower::{ServiceBuilder, Service};

# #[derive(Debug, Clone)]
# pub struct Logger<S> {
#     inner: S,
# }
# impl<S> Logger<S> {
#     pub fn new(inner: S) -> Self {
#         Logger { inner }
#     }
# }
# type Req = Request<Incoming>;
# impl<S> Service<Req> for Logger<S>
# where
#     S: Service<Req> + Clone,
# {
#     type Response = S::Response;

#     type Error = S::Error;

#     type Future = S::Future;

#     fn poll_ready(
#         &mut self,
#         cx: &mut std::task::Context<'_>,
#     ) -> std::task::Poll<Result<(), Self::Error>> {
#         self.inner.poll_ready(cx)
#     }

#     fn call(&mut self, req: Req) -> Self::Future {
#         println!("processing request: {} {}", req.method(), req.uri().path());
#         self.inner.call(req)
#     }
# }

async fn hello(_: Request<Incoming>) -> Result<Response<Full<Bytes>>, Infallible> {
    Ok(Response::new(Full::new(Bytes::from("Hello, World!"))))
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
    let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
    let listener = TcpListener::bind(addr).await?;
    loop {
        let (stream, _) = listener.accept().await?;
        let io = TokioIo::new(stream);
        tokio::spawn(async move {
            // N.B. should use tower service_fn here, since it's required to be implemented tower Service trait before convert to hyper Service!
            let svc = tower::service_fn(hello);
            let svc = ServiceBuilder::new().layer_fn(Logger::new).service(svc);
            // Convert it to hyper service
            let svc = TowerToHyperService::new(svc);
            if let Err(err) = http1::Builder::new().serve_connection(io, svc).await {
                eprintln!("server error: {}", err);
            }
        });
    }
}
# }
# fn main() {}

[hello-world]: {{ site.url }}/guides/1/server/hello-world/
[upgrading]: {{ site.url }}/guides/1/upgrading/
[adapter-trait]: {{ site.hyper_util_url }}/latest/hyper_util/service/struct.TowerToHyperService.html


File: .github/workflows/CI.yml

name: CI
on:
pull_request:
push:
branches:
- master

env:
RUST_BACKTRACE: 1

permissions:
contents: read # to fetch code (actions/checkout)

jobs:
ci-pass:
name: CI is green
runs-on: ubuntu-latest
needs:
- test
steps:
- run: exit 0

test:
name: Test ${{ matrix.rust }} on ${{ matrix.os }}
strategy:
matrix:
rust:
- stable
- beta

text
os:
      - ubuntu-latest

runs-on: ${{ matrix.os }}

steps:
  - name: Checkout
    uses: actions/checkout@v7

  - name: Install Rust (${{ matrix.rust }})
    uses: dtolnay/rust-toolchain@stable
    with:
      toolchain: ${{ matrix.rust }}

  - name: Test
    run: ./.github/workflows/check_guides.sh

File: .github/workflows/contrib.yml

name: contrib
on:
schedule:
- cron: '15 3 * * *'
workflow_dispatch:

permissions:
contents: write # to update _contrib
pull-requests: write # to send the updated _contrib PRs

jobs:
update-contrib:
name: Update _contrib
runs-on: ubuntu-latest

text
steps:
  - name: Checkout website
    uses: actions/checkout@v7
    with:
      persist-credentials: false

  - name: Checkout hyper
    uses: actions/checkout@v7
    with:
      persist-credentials: false
      path: hyper

  - name: Copy CONTRIBUTING.md into docs
    run: |
      sed -i -e 's|./docs/||g' hyper/CONTRIBUTING.md
      cp -a hyper/CONTRIBUTING.md hyper/docs/CONTRIBUTING.md

  - name: Generate HIP index
    run: |
      index=hyper/docs/hips/index.md
      printf '# hyper Improvement Proposals\n\n' > "$index"
      for f in hyper/docs/hips/[0-9][0-9][0-9][0-9]-*.md; do
        if [[ "$(basename "$f")" == '0000-template.md' ]]; then
          continue
        fi

        title=$(sed -n '1s/^# //p' "$f")
        path=$(basename "$f" .md)
        printf -- '- [%s](./%s/)\n' "$title" "$path" >> "$index"
      done

  # Insert frontmatter borders, replace markdown header with
  # frontmatter title and insert: 
  #   - layout: guide
  #   - hyper_path: {path}
  - name: Convert doc titles to frontmatter 
    run: |
      find hyper/docs -mindepth 1 -maxdepth 2 -type f -name '*.md' -print0 |
      while IFS= read -r -d '' f; do
        sed -i -e '1i ---' \
        -e '1s/^# /title: >-\n  /' \
        -e '2i layout: guide' \
        -e "2i hyper_path: ${f#hyper/}" \
        -e '2i ---' "$f"
      done

  # CONTRIBUTING.md is uniquely copied into the docs folder.
  # This adjustment links the correct source file.
  - name: Retain correct CONTRIBUTING.md path
    run: |
      sed -i -e '4s/docs\///' hyper/docs/CONTRIBUTING.md

  # Use the hyper docs readme as the index page of contrib,
  # and insert permalink: /contrib/ in the frontmatter
  - name: Convert readme to index
    run: |
      sed -i -e '4i permalink: /contrib/' hyper/docs/README.md
      mv hyper/docs/README.md hyper/docs/index.md
      sed -i -e '/^hyper_path:/d' hyper/docs/hips/index.md
      sed -i -e '4i permalink: /contrib/hips/' hyper/docs/hips/index.md


  # Lowercase the internal links and replace underscores with
  # hyphens, so they will point to the correct file.
  - name: Lowercase internal links
    run: |
      mapfile -d '' markdown_files < <(
        find hyper/docs -mindepth 1 -maxdepth 2 -type f -name '*.md' -print0
      )

      for filename in "${markdown_files[@]}"; do
        # since we move some docs from hyper root into the docs folder,
        # fix links to them
        if [[ "$filename" != hyper/docs/hips/* ]]; then
          sed -i -e 's|\.\./|\./|g' "$filename"
        fi

        # cut `.md` from the filename before search and replace
        filename=${filename::-3};

        for file in "${markdown_files[@]}"; do
          # filename without parent path
          filename=${filename##*/}

          # don't lowercase MSRV
          if [[ "${filename}" == 'MSRV' ]]; then
            continue
          fi

          # lowercase filenames outside frontmatter
          sed -i -e "1,/^---$/!s|${filename}|${filename,,}|g" "$file"

          # match on the lowercased filename from here on
          lowercased=${filename,,}

          hyphenated=${lowercased//_/-}

          # replace underscores in internal links with hyphens
          sed -i -e "s|${lowercased}|${hyphenated}|g" "$file"
        done
      done

  - name: Lowercase filenames and replace underscores
    run: |
      find hyper/docs -mindepth 1 -maxdepth 2 -type f -print0 |
      while IFS= read -r -d '' f; do
        directory=$(dirname "$f")
        filename=$(basename "$f" | tr '[:upper:]' '[:lower:]' | tr '_' '-')
        mv -vn "$f" "$directory/$filename" || true
      done

  - name: Copy the hyper docs to contrib
    run: |
      mkdir -p _contrib
      cp -a hyper/docs/. _contrib/

  - uses: gr2m/create-or-update-pull-request-action@v1
    env:
      GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
    with:
      author: github-actions <[email protected]>
      branch: actions/update-contrib
      title: 'doc: update contrib docs'
      body: >
        The _contrib docs are likely out of date. This is an automatically 
        generated PR by the `contrib.yml` GitHub workflow, which clones the docs
        from the main repo, implements the changes in _contrib then submits a new 
        PR or updates an existing PR.
      commit-message: 'doc: update contrib docs'
      path: _contrib/