1. HTTP3 简介
HTTP3,也被称为HTTP over QUIC,它基于QUIC(Quick UDP Internet Connections)协议,而不是传统的TCP协议。QUIC是由Google开发的一种基于UDP的多路复用传输协议,旨在减少网络延迟并提高数据传输的效率。
2. 相对于HTTP2的主要优点
2.1 真正解决TCP的队头阻塞问题
在HTTP1.x中的流水线技术虽然允许发送多个请求而不必等待响应,但响应仍然需要按照请求的顺序返回,这就可能导致队头阻塞问题:如果一个响应被延迟,那么其后面的所有响应也都会被阻塞。
HTTP2通过多路复用技术解决了这一问题。它允许在单个TCP连接上同时传输多个请求和响应,每个请求或响应被分解成独立的帧,并通过流的方式进行传输。流是独立的、双向的序列,它们在连接中共存,可以各自承载双向的消息交换。多路复用提高了连接效率,减少了延迟。
尽管HTTP2解决了应用层的队头阻塞问题,但在传输层(TCP层)仍然存在队头阻塞问题。TCP协议要求数据严格按照序号顺序传输,如果前面的数据包丢失或延迟,那么后面的数据包即使已经到达接收端,也会被阻塞等待前面的数据包重传。这种队头阻塞问题是由TCP协议本身的特性决定的,HTTP2作为应用层协议,无法直接解决传输层的队头阻塞问题。
为了解决TCP的队头阻塞问题,HTTP3采用了基于UDP的QUIC协议。QUIC协议在UDP的基础上实现了可靠传输、拥塞控制等功能。由于UDP是无连接的协议,不存在TCP那样的队头阻塞问题,因此HTTP/3能够更好地利用网络资源,提高通信效率。
2.2 减少连接建立时间
QUIC协议在初次连接时仅需一次RTT(Round-Trip Time),重用现有连接则无需再执行握手,进一步降低了延迟。 HTTP3的握手过程更加快速,因为它结合了TLS加密和连接建立的过程,减少了往返次数,加快了连接建立的速度。
3. 代码示例
3.1 实现HTTP3请求
下面的代码使用了reqwest实现HTTP3请求,并且支持自定义证书校验
use rustls::client::danger::{HandshakeSignatureValid, ServerCertVerified};
use rustls::crypto::{ring, CryptoProvider};
use rustls::pki_types::{CertificateDer, ServerName, UnixTime};
use rustls::{ClientConfig, DigitallySignedStruct, Error, SignatureScheme};
use std::fmt::{Debug, Formatter};
use std::sync::Arc;
static ALPN: &[u8] = b"h3";
#[tokio::main]
async fn main() -> Result<(), reqwest::Error> {
use http::Version;
use reqwest::{Client, IntoUrl, Response};
CryptoProvider::install_default(ring::default_provider()).expect("install provider failed");
let root_store = rustls::RootCertStore {
roots: webpki_roots::TLS_SERVER_ROOTS.into(),
};
let mut client_config = ClientConfig::builder()
.with_root_certificates(root_store.clone())
.with_no_client_auth();
client_config.alpn_protocols = vec![ALPN.into()];
client_config
.dangerous()
.set_certificate_verifier(Arc::new(NoCertVerifier {}));
let client = Client::builder()
.http3_prior_knowledge()
.use_preconfigured_tls(client_config)
.build()?;
// Some simple CLI args requirements...
let url = match std::env::args().nth(1) {
Some(url) => url,
None => {
println!("No CLI URL provided, using default.");
"https://hyper.rs".into()
}
};
eprintln!("Fetching {url:?}...");
let res = client
.get(url.as_str())
.version(Version::HTTP_3)
.send()
.await?;
eprintln!("Response: {:?} {}", res.version(), res.status());
eprintln!("Headers: {:#?}\n", res.headers());
let body = res.text().await?;
println!("{body}");
Ok(())
}
注意reqwest库要enable http3 feature
reqwest = {version = "0.12.7", default-features = false, features = ["rustls-tls", "http3"]}
另外http3 feature目前还不稳定,编译时加上reqwest_unstable,
RUSTFLAGS='--cfg reqwest_unstable' cargo run
3.2 自定义证书解析
本例中reqwest使用rustls-tls feature,即使用rustls作为tls,此时自定义证书校验代码如下
pub mod custom_verify {
use std::fmt::{Debug, Formatter};
use std::sync::Arc;
use rustls::client::danger::{HandshakeSignatureValid, ServerCertVerified};
use rustls::crypto::{verify_tls12_signature, verify_tls13_signature, CryptoProvider};
use rustls::pki_types::{CertificateDer, ServerName, UnixTime};
use rustls::{DigitallySignedStruct, Error, RootCertStore, SignatureScheme};
use webpki::Error as PkiError;
use crate::asn1_parse;
pub(crate) struct CustomCertVerifier {
roots: Arc<RootCertStore>,
provider: CryptoProvider,
}
impl CustomCertVerifier {
pub fn new(roots: Arc<RootCertStore>, provider: CryptoProvider) -> Self {
CustomCertVerifier { roots, provider }
}
}
impl Debug for CustomCertVerifier {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.write_str("verifier cert")
}
}
impl rustls::client::danger::ServerCertVerifier for CustomCertVerifier {
fn verify_server_cert(
&self,
end_entity: &CertificateDer<'_>,
intermediates: &[CertificateDer<'_>],
server_name: &ServerName<'_>,
ocsp_response: &[u8],
now: UnixTime,
) -> Result<ServerCertVerified, Error> {
let end_cert = webpki::EndEntityCert::try_from(end_entity).unwrap();
let issuer = end_cert.issuer();
let issuer_str = asn1_parse::asn1_parse(issuer);
println!("issuer {issuer_str:?} ");
let dns_name: Vec<&str> = end_cert.valid_dns_names().collect();
println!("dns name {:?}", dns_name);
match server_name {
ServerName::IpAddress(ip) => {
// todo ip check
}
ServerName::DnsName(dnsName) => {
// todo dns name
}
_ => {}
}
match end_cert.verify_for_usage(
&self.provider.signature_verification_algorithms.all,
&self.roots.roots,
intermediates,
now,
webpki::KeyUsage::server_auth(),
None,
None,
) {
Err(e) => match e {
PkiError::BadDerTime => {
// todo
}
PkiError::CertNotValidYet => {
// todo
}
_ => {
// todo
}
},
_ => {}
}
return match end_cert.verify_is_valid_for_subject_name(server_name) {
Ok(_) => Ok(ServerCertVerified::assertion()),
Err(e) => Err(Error::General(e.to_string())),
};
}
fn verify_tls12_signature(
&self,
message: &[u8],
cert: &CertificateDer<'_>,
dss: &DigitallySignedStruct,
) -> Result<HandshakeSignatureValid, Error> {
verify_tls12_signature(
message,
cert,
dss,
&self.provider.signature_verification_algorithms,
)
}
fn verify_tls13_signature(
&self,
message: &[u8],
cert: &CertificateDer<'_>,
dss: &DigitallySignedStruct,
) -> Result<HandshakeSignatureValid, Error> {
verify_tls13_signature(
message,
cert,
dss,
&self.provider.signature_verification_algorithms,
)
}
fn supported_verify_schemes(&self) -> Vec<SignatureScheme> {
self.provider
.signature_verification_algorithms
.supported_schemes()
}
}
}
证书颁发者asn1格式数据解析
use simple_asn1::{from_der, ASN1Block};
pub fn asn1_parse(data: &[u8]) -> Vec<String> {
let mut result = vec![];
match from_der(&data) {
Ok(blocks) => decode_asn1(&blocks, &mut result),
Err(err) => {
println!("Error decoding ASN.1 data: {:?}", err);
}
}
result
}
fn decode_asn1(asn1_blocks: &Vec<ASN1Block>, result: &mut Vec<String>) {
for b in asn1_blocks {
match b {
ASN1Block::Boolean(_, _) => {}
ASN1Block::Integer(_, _) => {}
ASN1Block::BitString(_, _, _) => {}
ASN1Block::OctetString(_, _) => {}
ASN1Block::Null(_) => {}
ASN1Block::ObjectIdentifier(_, _) => {}
ASN1Block::UTF8String(_, _) => {
println!("ASN1Block UTF8String")
}
ASN1Block::PrintableString(_, data) => result.push(data.clone()),
ASN1Block::TeletexString(_, data) => result.push(data.clone()),
ASN1Block::IA5String(_, _) => {}
ASN1Block::UTCTime(_, _) => {}
ASN1Block::GeneralizedTime(_, _) => {}
ASN1Block::UniversalString(_, _) => {}
ASN1Block::BMPString(_, _) => {}
ASN1Block::Sequence(size, seq_blocks) => decode_asn1(seq_blocks, result),
ASN1Block::Set(size, set_datas) => decode_asn1(set_datas, result),
ASN1Block::Explicit(_, _, _, _) => {}
ASN1Block::Unknown(_, _, _, _, _) => {}
}
}
}