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 xitca-io to quinn 0.11 #1031

Merged
merged 4 commits into from
May 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 2 additions & 2 deletions .cargo/config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ ci-check-client = "hack check --package xitca-client --each-feature --no-dev-dep
ci-check-other-exclude-io-uring = "hack check --workspace --exclude xitca-http --exclude xitca-client --exclude xitca-web --feature-powerset --exclude-features=io-uring,tokio-uring,runtime-uring,rustls-uring,rustls-uring-no-crypto"
ci-check-other = "hack check --workspace --exclude xitca-http --exclude xitca-client --exclude xitca-web --feature-powerset"

ci-test-exclude-io-uring = "hack test --workspace --feature-powerset --exclude-features=io-uring --no-fail-fast -- --nocapture"
ci-test = "test --workspace --all-features --no-fail-fast -- --nocapture"
ci-test-other = "test --workspace --exclude xitca-client --exclude xitca-test --all-features --no-fail-fast -- --nocapture"
ci-test-test = "test --package xitca-test --all-features --no-fail-fast -- --nocapture"

ci-check-examples-exclude-io-uring = "hack check --workspace --exclude xitca-web-wasi --exclude xitca-web-io-uring --feature-powerset"
ci-check-examples = "hack check --workspace --exclude xitca-web-wasi --feature-powerset"
Expand Down
9 changes: 7 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -240,10 +240,15 @@ jobs:
with:
tool: cargo-hack

- name: tests-linux
- name: [test][linux][other]
if: matrix.target.os == 'ubuntu-latest'
run: |
sudo bash -c "ulimit -Sl 512 && ulimit -Hl 512 && PATH=$PATH:/usr/share/rust/.cargo/bin && RUSTUP_TOOLCHAIN=${{ matrix.version }} cargo ci-test"
sudo bash -c "ulimit -Sl 512 && ulimit -Hl 512 && PATH=$PATH:/usr/share/rust/.cargo/bin && RUSTUP_TOOLCHAIN=${{ matrix.version }} cargo ci-test-other"

- name: [test][linux][xitca_test]
if: matrix.target.os == 'ubuntu-latest'
run: |
sudo bash -c "ulimit -Sl 512 && ulimit -Hl 512 && PATH=$PATH:/usr/share/rust/.cargo/bin && RUSTUP_TOOLCHAIN=${{ matrix.version }} cargo ci-test-test"

- name: Clear the cargo caches
run: |
Expand Down
14 changes: 5 additions & 9 deletions client/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ http1 = ["httparse", "xitca-http/http1"]
# http/2 client(tls enabled by default. see `dangerous` feature for clear text http/2)
http2 = ["h2", "itoa", "xitca-http/http2"]
# http/3 client(tls always enabled with rustls)
http3 = ["h3", "h3-quinn", "quinn/tls-rustls", "itoa", "async-stream", "rustls_0dot21", "webpki_roots_0dot25"]
http3 = ["h3", "h3-quinn", "quinn", "itoa", "async-stream", "rustls-ring-crypto"]
# openssl as http/1 and http/2 tls handler
openssl = ["xitca-tls/openssl"]
# rustls as http/1 and http/2 tls handler
Expand All @@ -27,7 +27,7 @@ websocket = ["http-ws"]
# feature for trusted local network:
# - http/2 clear text over plain tcp connection
# - http/3 connection to server with self signed certificates
dangerous = ["rustls_0dot21/dangerous_configuration"]
dangerous = []

[dependencies]
xitca-http = { version = "0.5", default-features = false, features = ["runtime"] }
Expand All @@ -47,9 +47,9 @@ httparse = { version = "1.8.0", optional = true }
h2 = { version = "0.4", optional = true }

# http/3
h3 = { version = "0.0.4", optional = true }
h3-quinn = { version = "0.0.5", optional = true }
quinn = { version = "0.10", optional = true }
h3 = { version = "0.0.5", optional = true }
h3-quinn = { version = "0.0.6", optional = true }
quinn = { version = "0.11", features = ["ring"], optional = true }
async-stream = { version = "0.3", optional = true }

# http/2 and http/3 shared
Expand All @@ -61,10 +61,6 @@ xitca-tls = { version = "0.3.0", optional = true }
# rustls, http3 and dangerous features shared
webpki-roots = { version = "0.26", optional = true }

# http3 temporary exclusive
rustls_0dot21 = { package = "rustls", version = "0.21", optional = true }
webpki_roots_0dot25 = { package = "webpki-roots", version = "0.25", optional = true }

# compression
http-encoding = { version = "0.2", features = ["br", "gz", "de"], optional = true }

Expand Down
111 changes: 68 additions & 43 deletions client/src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -330,71 +330,96 @@ impl ClientBuilder {
let h3_client = {
use std::sync::Arc;

use h3_quinn::quinn::{ClientConfig, Endpoint};
use rustls_0dot21 as rustls;

#[cfg(not(feature = "dangerous"))]
let mut crypto = {
use rustls::{OwnedTrustAnchor, RootCertStore};
use webpki_roots_0dot25::TLS_SERVER_ROOTS;

let mut root_certs = RootCertStore::empty();
for cert in TLS_SERVER_ROOTS {
let cert = OwnedTrustAnchor::from_subject_spki_name_constraints(
cert.subject,
cert.spki,
cert.name_constraints,
);
let certs = vec![cert].into_iter();
root_certs.add_trust_anchors(certs);
}
use h3_quinn::quinn::Endpoint;
use webpki_roots::TLS_SERVER_ROOTS;
use xitca_tls::rustls::{ClientConfig, RootCertStore};

rustls::ClientConfig::builder()
.with_safe_defaults()
.with_root_certificates(root_certs)
.with_no_client_auth()
};
let mut root_store = RootCertStore::empty();

root_store.extend(TLS_SERVER_ROOTS.iter().cloned());

let mut cfg = ClientConfig::builder()
.with_root_certificates(root_store)
.with_no_client_auth();

cfg.alpn_protocols = vec![b"h3".to_vec(), b"h32-29".to_vec()];

#[cfg(feature = "dangerous")]
let mut crypto = {
struct SkipServerVerification;
{
use xitca_tls::rustls::{
self,
client::danger::HandshakeSignatureValid,
crypto::{verify_tls12_signature, verify_tls13_signature},
pki_types::{CertificateDer, ServerName, UnixTime},
DigitallySignedStruct,
};

#[derive(Debug)]
pub(crate) struct SkipServerVerification;

impl SkipServerVerification {
fn new() -> Arc<Self> {
Arc::new(Self)
}
}

impl rustls::client::ServerCertVerifier for SkipServerVerification {
impl rustls::client::danger::ServerCertVerifier for SkipServerVerification {
fn verify_server_cert(
&self,
_end_entity: &rustls::Certificate,
_intermediates: &[rustls::Certificate],
_server_name: &rustls::ServerName,
_scts: &mut dyn Iterator<Item = &[u8]>,
_ocsp_response: &[u8],
_now: std::time::SystemTime,
) -> Result<rustls::client::ServerCertVerified, rustls::Error> {
Ok(rustls::client::ServerCertVerified::assertion())
_end_entity: &CertificateDer<'_>,
_intermediates: &[CertificateDer<'_>],
_server_name: &ServerName<'_>,
_ocsp: &[u8],
_now: UnixTime,
) -> Result<rustls::client::danger::ServerCertVerified, rustls::Error> {
Ok(rustls::client::danger::ServerCertVerified::assertion())
}
}

rustls::ClientConfig::builder()
.with_safe_defaults()
.with_custom_certificate_verifier(SkipServerVerification::new())
.with_no_client_auth()
};
fn verify_tls12_signature(
&self,
message: &[u8],
cert: &CertificateDer<'_>,
dss: &DigitallySignedStruct,
) -> Result<HandshakeSignatureValid, rustls::Error> {
verify_tls12_signature(
message,
cert,
dss,
&rustls::crypto::ring::default_provider().signature_verification_algorithms,
)
}

crypto.alpn_protocols = vec![b"h3".to_vec(), b"h32-29".to_vec()];
fn verify_tls13_signature(
&self,
message: &[u8],
cert: &CertificateDer<'_>,
dss: &DigitallySignedStruct,
) -> Result<HandshakeSignatureValid, rustls::Error> {
verify_tls13_signature(
message,
cert,
dss,
&rustls::crypto::ring::default_provider().signature_verification_algorithms,
)
}

fn supported_verify_schemes(&self) -> Vec<rustls::SignatureScheme> {
rustls::crypto::ring::default_provider()
.signature_verification_algorithms
.supported_schemes()
}
}

let config = ClientConfig::new(Arc::new(crypto));
cfg.dangerous().set_certificate_verifier(SkipServerVerification::new());
}

let mut endpoint = match self.local_addr {
Some(addr) => Endpoint::client(addr).unwrap(),
None => Endpoint::client("0.0.0.0:0".parse().unwrap()).unwrap(),
};

endpoint.set_default_client_config(config);
let cfg = quinn::crypto::rustls::QuicClientConfig::try_from(cfg).unwrap();
endpoint.set_default_client_config(quinn::ClientConfig::new(Arc::new(cfg)));

endpoint
};
Expand Down
2 changes: 1 addition & 1 deletion http/CHANGES.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# unreleased
# unreleased 0.5.0
## Change
- update `xitca-io` to `0.3.0`
- update `xitca-tls` to `0.3.0`
Expand Down
4 changes: 2 additions & 2 deletions http/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,8 @@ futures-util = { version = "0.3.17", default-features = false, optional = true }
slab = { version = "0.4", optional = true }

# http/3 support
h3 = { version = "0.0.4", optional = true }
h3-quinn = { version = "0.0.5", optional = true }
h3 = { version = "0.0.5", optional = true }
h3-quinn = { version = "0.0.6", optional = true }

# async runtime support.
tokio = { version = "1.30", features = ["rt", "time"], optional = true }
Expand Down
18 changes: 13 additions & 5 deletions http/src/h3/body.rs
Original file line number Diff line number Diff line change
@@ -1,20 +1,28 @@
use std::{
use core::{
pin::Pin,
task::{Context, Poll},
};

use futures_core::stream::{BoxStream, Stream};
use ::h3::server::RequestStream;
use futures_core::stream::Stream;
use h3_quinn::RecvStream;

use crate::{bytes::Bytes, error::BodyError};
use crate::{
bytes::{Buf, Bytes},
error::BodyError,
};

/// Request body type for Http/3 specifically.
pub struct RequestBody(pub(super) BoxStream<'static, Result<Bytes, h3::Error>>);
pub struct RequestBody(pub(super) RequestStream<RecvStream, Bytes>);

impl Stream for RequestBody {
type Item = Result<Bytes, BodyError>;

fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
self.get_mut().0.as_mut().poll_next(cx).map_err(Into::into)
self.get_mut()
.0
.poll_recv_data(cx)?
.map(|res| res.map(|buf| Ok(Bytes::copy_from_slice(buf.chunk()))))
}
}

Expand Down
94 changes: 3 additions & 91 deletions http/src/h3/proto/dispatcher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@ use core::{
fmt,
future::{poll_fn, Future},
marker::PhantomData,
pin::{pin, Pin},
task::{ready, Context, Poll},
pin::pin,
};

use std::net::SocketAddr;
Expand All @@ -13,13 +12,12 @@ use ::h3::{
server::{self, RequestStream},
};
use futures_core::stream::Stream;
use pin_project_lite::pin_project;
use xitca_io::net::QuicStream;
use xitca_service::Service;
use xitca_unsafe_collection::futures::{Select, SelectOutput};

use crate::{
bytes::{Buf, Bytes},
bytes::Bytes,
error::HttpServiceError,
h3::{body::RequestBody, error::Error},
http::{Extension, Request, RequestExt, Response},
Expand Down Expand Up @@ -69,16 +67,9 @@ where
SelectOutput::A(Ok(Some((req, stream)))) => {
let (tx, rx) = stream.split();

let body = Box::pin(AsyncStream::new(rx, |mut stream| async move {
// What the fuck is this API? We need to find another http3 implementation on quinn
// that actually make sense. This is plain stupid.
let res = stream.recv_data().await?;
Ok(res.map(|bytes| (Bytes::copy_from_slice(bytes.chunk()), stream)))
}));

// Reconstruct Request to attach crate body type.
let req = req.map(|_| {
let body = ReqB::from(RequestBody(body));
let body = ReqB::from(RequestBody(rx));
RequestExt::from_parts(body, Extension::new(self.addr))
});

Expand Down Expand Up @@ -127,82 +118,3 @@ where

Ok(())
}

pin_project! {
struct AsyncStream<F, Arg, Fut>{
callback: F,
#[pin]
inner: _AsyncStream<Arg, Fut>
}
}

pin_project! {
#[project = AsyncStreamProj]
#[project_replace = AsyncStreamReplaceProj]
enum _AsyncStream<Arg, Fut> {
Arg {
arg: Arg
},
Next {
#[pin]
fut: Fut
},
Empty
}
}

impl<F, Arg, Fut> AsyncStream<F, Arg, Fut> {
fn new(arg: Arg, callback: F) -> Self
where
F: Fn(Arg) -> Fut,
{
let fut = callback(arg);

Self {
callback,
inner: _AsyncStream::Next { fut },
}
}
}

impl<F, Arg, Fut, Res, Err> Stream for AsyncStream<F, Arg, Fut>
where
F: Fn(Arg) -> Fut,
Fut: Future<Output = Result<Option<(Res, Arg)>, Err>>,
{
type Item = Result<Res, Err>;

fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
let mut this = self.project();

loop {
match this.inner.as_mut().project() {
AsyncStreamProj::Next { fut } => {
return match ready!(fut.poll(cx)) {
Ok(Some((bytes, arg))) => {
this.inner.set(_AsyncStream::Arg { arg });
Poll::Ready(Some(Ok(bytes)))
}
Ok(None) => {
this.inner.set(_AsyncStream::Empty);
Poll::Ready(None)
}
Err(e) => {
this.inner.set(_AsyncStream::Empty);
Poll::Ready(Some(Err(e)))
}
}
}
AsyncStreamProj::Arg { .. } => match this.inner.as_mut().project_replace(_AsyncStream::Empty) {
AsyncStreamReplaceProj::Arg { arg } => {
this.inner.set(_AsyncStream::Next {
fut: (this.callback)(arg),
});
}
_ => unreachable!("Never gonna happen"),
},
AsyncStreamProj::Empty => unreachable!("StreamRequest polled after finis"),
}
}
}
}
1 change: 1 addition & 0 deletions io/CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
- rename `Udp` prefixed types to `Quic` prefixed
- `AsyncIo::ready` and `AsyncIoDyn::ready` receive `&mut self`. Allowing more arbitrary types implement these traits.
- `AsyncIo::poll_ready` and `AsyncIoDyn::poll_ready` receive `&mut self`. For the same reason as above change.
- update `quinn` to `0.11`

# 0.2.1
## Add
Expand Down
Loading
Loading