mirror of
https://github.com/mountain-loop/yaak.git
synced 2026-05-18 13:47:02 +02:00
Increase HTTP/2 response header limit
Set Yaak's reqwest request clients to accept HTTP/2 response header lists up to 1 MiB and wrap configured clients so the sender path cannot accidentally bypass the shared builder. Feedback: https://yaak.app/feedback/posts/when-response-headers-exceed-a-certain-size-hyper-throws-error
This commit is contained in:
@@ -1,11 +1,36 @@
|
|||||||
use crate::dns::LocalhostResolver;
|
use crate::dns::LocalhostResolver;
|
||||||
use crate::error::Result;
|
use crate::error::Result;
|
||||||
use log::{debug, info, warn};
|
use log::{debug, info, warn};
|
||||||
use reqwest::{Client, Proxy, redirect};
|
use reqwest::{Client, ClientBuilder, Proxy, redirect};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use yaak_models::models::DnsOverride;
|
use yaak_models::models::DnsOverride;
|
||||||
use yaak_tls::{ClientCertificateConfig, get_tls_config};
|
use yaak_tls::{ClientCertificateConfig, get_tls_config};
|
||||||
|
|
||||||
|
pub const HTTP2_MAX_RESPONSE_HEADER_LIST_SIZE: u32 = 1024 * 1024;
|
||||||
|
|
||||||
|
fn client_builder() -> ClientBuilder {
|
||||||
|
Client::builder().http2_max_header_list_size(HTTP2_MAX_RESPONSE_HEADER_LIST_SIZE)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct ConfiguredClient {
|
||||||
|
inner: Client,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ConfiguredClient {
|
||||||
|
pub(crate) fn build_default() -> Result<Self> {
|
||||||
|
Ok(Self { inner: client_builder().build()? })
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn from_inner(inner: Client) -> Self {
|
||||||
|
Self { inner }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn inner(&self) -> &Client {
|
||||||
|
&self.inner
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct HttpConnectionProxySettingAuth {
|
pub struct HttpConnectionProxySettingAuth {
|
||||||
pub user: String,
|
pub user: String,
|
||||||
@@ -37,8 +62,8 @@ impl HttpConnectionOptions {
|
|||||||
/// Build a reqwest Client and return it along with the DNS resolver.
|
/// Build a reqwest Client and return it along with the DNS resolver.
|
||||||
/// The resolver is returned separately so it can be configured per-request
|
/// The resolver is returned separately so it can be configured per-request
|
||||||
/// to emit DNS timing events to the appropriate channel.
|
/// to emit DNS timing events to the appropriate channel.
|
||||||
pub(crate) fn build_client(&self) -> Result<(Client, Arc<LocalhostResolver>)> {
|
pub(crate) fn build_client(&self) -> Result<(ConfiguredClient, Arc<LocalhostResolver>)> {
|
||||||
let mut client = Client::builder()
|
let mut client = client_builder()
|
||||||
.connection_verbose(true)
|
.connection_verbose(true)
|
||||||
.redirect(redirect::Policy::none())
|
.redirect(redirect::Policy::none())
|
||||||
// Decompression is handled by HttpTransaction, not reqwest
|
// Decompression is handled by HttpTransaction, not reqwest
|
||||||
@@ -79,7 +104,7 @@ impl HttpConnectionOptions {
|
|||||||
self.client_certificate.is_some()
|
self.client_certificate.is_some()
|
||||||
);
|
);
|
||||||
|
|
||||||
Ok((client.build()?, resolver))
|
Ok((ConfiguredClient::from_inner(client.build()?), resolver))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
use crate::client::HttpConnectionOptions;
|
use crate::client::{ConfiguredClient, HttpConnectionOptions};
|
||||||
use crate::dns::LocalhostResolver;
|
use crate::dns::LocalhostResolver;
|
||||||
use crate::error::Result;
|
use crate::error::Result;
|
||||||
use reqwest::Client;
|
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::time::{Duration, Instant};
|
use std::time::{Duration, Instant};
|
||||||
@@ -10,7 +9,7 @@ use tokio::sync::RwLock;
|
|||||||
/// A cached HTTP client along with its DNS resolver.
|
/// A cached HTTP client along with its DNS resolver.
|
||||||
/// The resolver is needed to set the event sender per-request.
|
/// The resolver is needed to set the event sender per-request.
|
||||||
pub struct CachedClient {
|
pub struct CachedClient {
|
||||||
pub client: Client,
|
pub client: ConfiguredClient,
|
||||||
pub resolver: Arc<LocalhostResolver>,
|
pub resolver: Arc<LocalhostResolver>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ use async_trait::async_trait;
|
|||||||
use bytes::Bytes;
|
use bytes::Bytes;
|
||||||
use futures_util::StreamExt;
|
use futures_util::StreamExt;
|
||||||
use http_body::{Body as HttpBody, Frame, SizeHint};
|
use http_body::{Body as HttpBody, Frame, SizeHint};
|
||||||
use reqwest::{Client, Method, Version};
|
use reqwest::{Method, Version};
|
||||||
use std::fmt::Display;
|
use std::fmt::Display;
|
||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
use std::task::{Context, Poll};
|
use std::task::{Context, Poll};
|
||||||
@@ -411,18 +411,18 @@ pub trait HttpSender: Send + Sync {
|
|||||||
|
|
||||||
/// Reqwest-based implementation of HttpSender
|
/// Reqwest-based implementation of HttpSender
|
||||||
pub struct ReqwestSender {
|
pub struct ReqwestSender {
|
||||||
client: Client,
|
client: crate::client::ConfiguredClient,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ReqwestSender {
|
impl ReqwestSender {
|
||||||
/// Create a new ReqwestSender with a default client
|
/// Create a new ReqwestSender with a default client
|
||||||
pub fn new() -> Result<Self> {
|
pub fn new() -> Result<Self> {
|
||||||
let client = Client::builder().build().map_err(Error::Client)?;
|
let client = crate::client::ConfiguredClient::build_default()?;
|
||||||
Ok(Self { client })
|
Ok(Self { client })
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a new ReqwestSender with a custom client
|
/// Create a new ReqwestSender with a configured client
|
||||||
pub fn with_client(client: Client) -> Self {
|
pub fn with_client(client: crate::client::ConfiguredClient) -> Self {
|
||||||
Self { client }
|
Self { client }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -444,7 +444,7 @@ impl HttpSender for ReqwestSender {
|
|||||||
.map_err(|e| Error::RequestError(format!("Invalid HTTP method: {}", e)))?;
|
.map_err(|e| Error::RequestError(format!("Invalid HTTP method: {}", e)))?;
|
||||||
|
|
||||||
// Build the request
|
// Build the request
|
||||||
let mut req_builder = self.client.request(method, &request.url);
|
let mut req_builder = self.client.inner().request(method, &request.url);
|
||||||
|
|
||||||
// Add headers
|
// Add headers
|
||||||
for header in request.headers {
|
for header in request.headers {
|
||||||
@@ -513,7 +513,7 @@ impl HttpSender for ReqwestSender {
|
|||||||
send_event(HttpResponseEvent::Info("Sending request to server".to_string()));
|
send_event(HttpResponseEvent::Info("Sending request to server".to_string()));
|
||||||
|
|
||||||
// Map some errors to our own, so they look nicer
|
// Map some errors to our own, so they look nicer
|
||||||
let response = self.client.execute(sendable_req).await.map_err(|e| {
|
let response = self.client.inner().execute(sendable_req).await.map_err(|e| {
|
||||||
if reqwest::Error::is_timeout(&e) {
|
if reqwest::Error::is_timeout(&e) {
|
||||||
Error::RequestTimeout(
|
Error::RequestTimeout(
|
||||||
request.options.timeout.unwrap_or(Duration::from_secs(0)).clone(),
|
request.options.timeout.unwrap_or(Duration::from_secs(0)).clone(),
|
||||||
|
|||||||
Reference in New Issue
Block a user