Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update to latest versions of hyper and http-body #1882

Merged
merged 60 commits into from
Nov 23, 2023
Merged
Show file tree
Hide file tree
Changes from 54 commits
Commits
Show all changes
60 commits
Select commit Hold shift + click to select a range
807aa59
Start working on v0.7.0
davidpdrsn Nov 25, 2022
8238ca7
Add `axum_core::body::Body` (#1584)
davidpdrsn Nov 27, 2022
280efc4
Change `sse::Event::json_data` to use `axum_core::Error` as its error…
davidpdrsn Feb 16, 2023
f902f74
Fix typo in extract::ws (#1664)
mscofield0 Feb 24, 2023
a4b7d38
Remove `B` type param (#1751)
davidpdrsn Mar 12, 2023
0d50d17
Add `Body::from_stream` (#1848)
davidpdrsn Mar 14, 2023
2ae0cdf
Remove `B` type param: Follow ups (#1789)
davidpdrsn Mar 20, 2023
d153719
Add `serve` function and remove `Server` re-export (#1868)
davidpdrsn Mar 22, 2023
49183e4
Remove `IntoResponse` for http-body types (#1877)
davidpdrsn Mar 22, 2023
44bb38d
update deps
davidpdrsn Mar 23, 2023
c73b6a9
Update to hyper 1.0-rc.3 and http-body-util 0.1.0-rc.2
davidpdrsn Mar 23, 2023
11a543a
use tower-http patch
davidpdrsn Mar 24, 2023
1fd6dbb
fix docs
davidpdrsn Mar 24, 2023
f1c0b11
remove `Limited` from public API
davidpdrsn Mar 24, 2023
b1e9930
serve also requires hyper/http1
davidpdrsn Mar 24, 2023
91bebd8
more cfgs
davidpdrsn Mar 24, 2023
7dece69
handle additional error wrapping
davidpdrsn Mar 24, 2023
69a413c
don't need this import
davidpdrsn Mar 24, 2023
aa6fe6d
tower-http's compression middleware which haven't been upgraded yet
davidpdrsn Mar 24, 2023
f0348be
format
davidpdrsn Mar 24, 2023
82350cc
convert test to use hyper's low level client
davidpdrsn Mar 24, 2023
4079174
Merge branch 'main' into david/hyper-1.0-rc.x
davidpdrsn Aug 6, 2023
a7cbb38
update tower-http
davidpdrsn Nov 10, 2023
d3674df
Merge branch 'main' into david/hyper-1.0-rc.x
davidpdrsn Nov 10, 2023
2873fac
fixes
davidpdrsn Nov 10, 2023
7834089
fixes
davidpdrsn Nov 10, 2023
a5ff5d8
use latest version of tower-http
davidpdrsn Nov 10, 2023
1c2cfe7
fix more things
davidpdrsn Nov 10, 2023
ad8029b
fix docs
davidpdrsn Nov 11, 2023
b598d31
fix macro tests
davidpdrsn Nov 11, 2023
760b460
fix docs link
davidpdrsn Nov 11, 2023
a0d04ea
do we still need this?
davidpdrsn Nov 11, 2023
a663a1d
bump msrv
davidpdrsn Nov 11, 2023
c33b537
remove patches
davidpdrsn Nov 15, 2023
8dcb7bf
update deps
davidpdrsn Nov 15, 2023
dafadf1
start updating to http 1.0
davidpdrsn Nov 16, 2023
2a33bd9
make it build by adding crates-io patches
davidpdrsn Nov 16, 2023
6e2b97d
Update docs for DefaultBodyLimit, it doesn't change the body type
davidpdrsn Nov 16, 2023
50b5ea7
move to published hyper-util
davidpdrsn Nov 18, 2023
c0c12f4
simplify RequestExt::{with_limited_body, into_limited_body}
davidpdrsn Nov 18, 2023
49b88f9
Update axum-core/src/extract/rejection.rs
davidpdrsn Nov 18, 2023
eb1777a
Update axum/CHANGELOG.md
davidpdrsn Nov 18, 2023
4f639a2
clean up imports
davidpdrsn Nov 18, 2023
437e959
Merge branch 'main' into david/hyper-1.0-rc.x
davidpdrsn Nov 18, 2023
90b713d
compression is back
davidpdrsn Nov 19, 2023
ae0b890
fix error I missed before
davidpdrsn Nov 19, 2023
820fb5a
update tower-http patch
davidpdrsn Nov 19, 2023
5b3f2b6
does using `-Z direct-minimal-versions` fix msrv tests?
davidpdrsn Nov 19, 2023
b49ba58
add typos-cli config to allow some false positives
davidpdrsn Nov 19, 2023
6d4fd57
Revert "does using `-Z direct-minimal-versions` fix msrv tests?"
davidpdrsn Nov 19, 2023
770ec7d
fix crc32fast
davidpdrsn Nov 19, 2023
207f346
Add [`Body::into_data_stream`]
davidpdrsn Nov 20, 2023
f93a242
use published tower-http 0.5
davidpdrsn Nov 21, 2023
b9983b6
convert multer's Field headers to http 1.0
davidpdrsn Nov 22, 2023
8879fa4
avoid some clones
davidpdrsn Nov 23, 2023
3c1c453
bring back CompressionLayer in example
davidpdrsn Nov 23, 2023
dee6bff
format imports
davidpdrsn Nov 23, 2023
4a02fba
make `serve` also work with http2 only
davidpdrsn Nov 23, 2023
10bb477
Apply suggestions from code review
davidpdrsn Nov 23, 2023
64b2e39
Update axum/src/lib.rs
davidpdrsn Nov 23, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/CI.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name: CI

env:
CARGO_TERM_COLOR: always
MSRV: '1.65'
MSRV: '1.66'

on:
push:
Expand Down
4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,7 @@ default-members = ["axum", "axum-*"]
# Example has been deleted, but README.md remains
exclude = ["examples/async-graphql"]
resolver = "2"

[patch.crates-io]
# for http 1.0. PR to update is merged but not published
headers = { git = "https://github.com/hyperium/headers", rev = "4400aa90c47a7" }
6 changes: 6 additions & 0 deletions _typos.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[files]
extend-exclude = ["Cargo.toml"]

[default.extend-identifiers]
DefaultOnFailedUpdgrade = "DefaultOnFailedUpdgrade"
OnFailedUpdgrade = "OnFailedUpdgrade"
11 changes: 6 additions & 5 deletions axum-core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,16 +21,17 @@ __private_docs = ["dep:tower-http"]
async-trait = "0.1.67"
bytes = "1.0"
futures-util = { version = "0.3", default-features = false, features = ["alloc"] }
http = "0.2.7"
http-body = "0.4.5"
http = "1.0.0"
http-body = "1.0.0"
http-body-util = "0.1.0"
mime = "0.3.16"
pin-project-lite = "0.2.7"
sync_wrapper = "0.1.1"
tower-layer = "0.3"
tower-service = "0.3"

# optional dependencies
tower-http = { version = "0.4", optional = true, features = ["limit"] }
tower-http = { version = "0.5.0", optional = true, features = ["limit"] }
tracing = { version = "0.1.37", default-features = false, optional = true }

[build-dependencies]
Expand All @@ -40,9 +41,9 @@ rustversion = "1.0.9"
axum = { path = "../axum", version = "0.6.0" }
axum-extra = { path = "../axum-extra", features = ["typed-header"] }
futures-util = { version = "0.3", default-features = false, features = ["alloc"] }
hyper = "0.14.24"
hyper = "1.0.0"
tokio = { version = "1.25.0", features = ["macros"] }
tower-http = { version = "0.4", features = ["limit"] }
tower-http = { version = "0.5.0", features = ["limit"] }

[package.metadata.cargo-public-api-crates]
allowed = [
Expand Down
146 changes: 63 additions & 83 deletions axum-core/src/body.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,16 @@

use crate::{BoxError, Error};
use bytes::Bytes;
use bytes::{Buf, BufMut};
use futures_util::stream::Stream;
use futures_util::TryStream;
use http::HeaderMap;
use http_body::Body as _;
use http_body::{Body as _, Frame};
use http_body_util::BodyExt;
use pin_project_lite::pin_project;
use std::pin::Pin;
use std::task::{Context, Poll};
use sync_wrapper::SyncWrapper;

type BoxBody = http_body::combinators::UnsyncBoxBody<Bytes, Error>;
type BoxBody = http_body_util::combinators::UnsyncBoxBody<Bytes, Error>;

fn boxed<B>(body: B) -> BoxBody
where
Expand All @@ -35,58 +34,6 @@ where
}
}

// copied from hyper under the following license:
// Copyright (c) 2014-2021 Sean McArthur

// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:

// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
pub(crate) async fn to_bytes<T>(body: T) -> Result<Bytes, T::Error>
where
T: http_body::Body,
{
futures_util::pin_mut!(body);

// If there's only 1 chunk, we can just return Buf::to_bytes()
let mut first = if let Some(buf) = body.data().await {
buf?
} else {
return Ok(Bytes::new());
};

let second = if let Some(buf) = body.data().await {
buf?
} else {
return Ok(first.copy_to_bytes(first.remaining()));
};

// With more than 1 buf, we gotta flatten into a Vec first.
let cap = first.remaining() + second.remaining() + body.size_hint().lower() as usize;
let mut vec = Vec::with_capacity(cap);
vec.put(first);
vec.put(second);

while let Some(buf) = body.data().await {
vec.put(buf?);
}

Ok(vec.into())
}

/// The body type used in axum requests and responses.
#[derive(Debug)]
pub struct Body(BoxBody);
Expand All @@ -103,7 +50,7 @@ impl Body {

/// Create an empty body.
pub fn empty() -> Self {
Self::new(http_body::Empty::new())
Self::new(http_body_util::Empty::new())
}

/// Create a new `Body` from a [`Stream`].
Expand All @@ -119,6 +66,16 @@ impl Body {
stream: SyncWrapper::new(stream),
})
}

/// Convert the body into a [`Stream`] of data frames.
///
/// Non-data frames (such as trailers) will be discarded. Use [`http_body_util::BodyStream`] if
/// you need a [`Stream`] of all frame types.
///
/// [`http_body_util::BodyStream`]: https://docs.rs/http-body-util/latest/http_body_util/struct.BodyStream.html
pub fn into_data_stream(self) -> BodyDataStream {
BodyDataStream { inner: self }
}
}

impl Default for Body {
Expand All @@ -131,7 +88,7 @@ macro_rules! body_from_impl {
($ty:ty) => {
impl From<$ty> for Body {
fn from(buf: $ty) -> Self {
Self::new(http_body::Full::from(buf))
Self::new(http_body_util::Full::from(buf))
}
}
};
Expand All @@ -152,19 +109,11 @@ impl http_body::Body for Body {
type Error = Error;

#[inline]
fn poll_data(
mut self: Pin<&mut Self>,
cx: &mut Context<'_>,
) -> std::task::Poll<Option<Result<Self::Data, Self::Error>>> {
Pin::new(&mut self.0).poll_data(cx)
}

#[inline]
fn poll_trailers(
fn poll_frame(
mut self: Pin<&mut Self>,
cx: &mut Context<'_>,
) -> std::task::Poll<Result<Option<HeaderMap>, Self::Error>> {
Pin::new(&mut self.0).poll_trailers(cx)
) -> Poll<Option<Result<Frame<Self::Data>, Self::Error>>> {
Pin::new(&mut self.0).poll_frame(cx)
}

#[inline]
Expand All @@ -178,12 +127,51 @@ impl http_body::Body for Body {
}
}

impl Stream for Body {
/// A stream of data frames.
///
/// Created with [`Body::into_data_stream`].
#[derive(Debug)]
pub struct BodyDataStream {
inner: Body,
}

impl Stream for BodyDataStream {
type Item = Result<Bytes, Error>;

#[inline]
fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
self.poll_data(cx)
fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
loop {
match futures_util::ready!(Pin::new(&mut self.inner).poll_frame(cx)?) {
Some(frame) => match frame.into_data() {
Ok(data) => return Poll::Ready(Some(Ok(data))),
Err(_frame) => {}
davidpdrsn marked this conversation as resolved.
Show resolved Hide resolved
},
None => return Poll::Ready(None),
}
}
}
}

impl http_body::Body for BodyDataStream {
type Data = Bytes;
type Error = Error;

#[inline]
fn poll_frame(
mut self: Pin<&mut Self>,
cx: &mut Context<'_>,
) -> Poll<Option<Result<Frame<Self::Data>, Self::Error>>> {
Pin::new(&mut self.inner).poll_frame(cx)
}

#[inline]
fn is_end_stream(&self) -> bool {
self.inner.is_end_stream()
}

#[inline]
fn size_hint(&self) -> http_body::SizeHint {
self.inner.size_hint()
}
}

Expand All @@ -203,25 +191,17 @@ where
type Data = Bytes;
type Error = Error;

fn poll_data(
fn poll_frame(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
) -> Poll<Option<Result<Self::Data, Self::Error>>> {
) -> Poll<Option<Result<Frame<Self::Data>, Self::Error>>> {
let stream = self.project().stream.get_pin_mut();
match futures_util::ready!(stream.try_poll_next(cx)) {
Some(Ok(chunk)) => Poll::Ready(Some(Ok(chunk.into()))),
Some(Ok(chunk)) => Poll::Ready(Some(Ok(Frame::data(chunk.into())))),
Some(Err(err)) => Poll::Ready(Some(Err(Error::new(err)))),
None => Poll::Ready(None),
}
}

#[inline]
fn poll_trailers(
self: Pin<&mut Self>,
_cx: &mut Context<'_>,
) -> Poll<Result<Option<HeaderMap>, Self::Error>> {
Poll::Ready(Ok(None))
}
}

#[test]
Expand Down
23 changes: 10 additions & 13 deletions axum-core/src/ext_traits/request.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
use crate::body::Body;
use crate::extract::{DefaultBodyLimitKind, FromRequest, FromRequestParts, Request};
use futures_util::future::BoxFuture;
use http_body::Limited;

mod sealed {
pub trait Sealed {}
Expand Down Expand Up @@ -258,13 +257,13 @@ pub trait RequestExt: sealed::Sealed + Sized {

/// Apply the [default body limit](crate::extract::DefaultBodyLimit).
///
/// If it is disabled, return the request as-is in `Err`.
fn with_limited_body(self) -> Result<Request<Limited<Body>>, Request>;
/// If it is disabled, the request is return as-is.
davidpdrsn marked this conversation as resolved.
Show resolved Hide resolved
fn with_limited_body(self) -> Request;

/// Consumes the request, returning the body wrapped in [`Limited`] if a
/// Consumes the request, returning the body wrapped in [`http_body_util::Limited`] if a
/// [default limit](crate::extract::DefaultBodyLimit) is in place, or not wrapped if the
/// default limit is disabled.
fn into_limited_body(self) -> Result<Limited<Body>, Body>;
fn into_limited_body(self) -> Body;
}

impl RequestExt for Request {
Expand Down Expand Up @@ -320,24 +319,22 @@ impl RequestExt for Request {
})
}

fn with_limited_body(self) -> Result<Request<Limited<Body>>, Request> {
fn with_limited_body(self) -> Request {
// update docs in `axum-core/src/extract/default_body_limit.rs` and
// `axum/src/docs/extract.md` if this changes
const DEFAULT_LIMIT: usize = 2_097_152; // 2 mb

match self.extensions().get::<DefaultBodyLimitKind>().copied() {
Some(DefaultBodyLimitKind::Disable) => Err(self),
Some(DefaultBodyLimitKind::Disable) => self,
Some(DefaultBodyLimitKind::Limit(limit)) => {
Ok(self.map(|b| http_body::Limited::new(b, limit)))
self.map(|b| Body::new(http_body_util::Limited::new(b, limit)))
}
None => Ok(self.map(|b| http_body::Limited::new(b, DEFAULT_LIMIT))),
None => self.map(|b| Body::new(http_body_util::Limited::new(b, DEFAULT_LIMIT))),
}
}

fn into_limited_body(self) -> Result<Limited<Body>, Body> {
self.with_limited_body()
.map(Request::into_body)
.map_err(Request::into_body)
fn into_limited_body(self) -> Body {
self.with_limited_body().into_body()
}
}

Expand Down
Loading
Loading