mirror of
https://github.com/BerriAI/litellm.git
synced 2026-11-06 07:48:06 +00:00
fix(http): preserve native WebSocket trust and optional protocols
This commit is contained in:
parent
af811e2fc6
commit
49728dd028
8 changed files with 60 additions and 3 deletions
1
litellm-rust/Cargo.lock
generated
1
litellm-rust/Cargo.lock
generated
|
|
@ -3980,6 +3980,7 @@ dependencies = [
|
|||
"reqwest",
|
||||
"rstest",
|
||||
"rustls 0.23.42",
|
||||
"rustls-native-certs",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"strum",
|
||||
|
|
|
|||
|
|
@ -100,6 +100,7 @@ rstest = "0.26.1"
|
|||
wiremock = "0.6.5"
|
||||
rstest_reuse = "0.7.0"
|
||||
rustls = { version = "0.23", default-features = false, features = ["ring", "std", "tls12"] }
|
||||
rustls-native-certs = "0.8"
|
||||
rustify = "=0.7.0"
|
||||
rustify_derive = "=0.5.5"
|
||||
vaultrs = { version = "=0.8.0", default-features = false, features = ["rustls"] }
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ litellm-core-utils.workspace = true
|
|||
hyper-util.workspace = true
|
||||
reqwest.workspace = true
|
||||
rustls.workspace = true
|
||||
rustls-native-certs.workspace = true
|
||||
tokio-tungstenite.workspace = true
|
||||
serde.workspace = true
|
||||
serde_json.workspace = true
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ pub enum Verify {
|
|||
Disabled,
|
||||
CaBundle(PathBuf),
|
||||
BuiltInRoots,
|
||||
NativeRoots,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||
|
|
|
|||
|
|
@ -6,7 +6,12 @@ use std::{
|
|||
|
||||
use reqwest::dns::Resolve;
|
||||
|
||||
use crate::{client::Client, config::HttpClientConfig, error::Error, proxy::EnvironmentProxies};
|
||||
use crate::{
|
||||
client::Client,
|
||||
config::{HttpClientConfig, Verify},
|
||||
error::Error,
|
||||
proxy::EnvironmentProxies,
|
||||
};
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||
pub enum ClientVariant {
|
||||
|
|
@ -62,6 +67,10 @@ impl HttpClientPool {
|
|||
},
|
||||
ClientVariant::WebSocket => HttpClientConfig {
|
||||
http2: false,
|
||||
verify: match &config.verify {
|
||||
Verify::BuiltInRoots => Verify::NativeRoots,
|
||||
verify => verify.clone(),
|
||||
},
|
||||
..config.clone()
|
||||
},
|
||||
ClientVariant::Provider | ClientVariant::NoRedirect => config.clone(),
|
||||
|
|
|
|||
|
|
@ -195,6 +195,15 @@ impl TryFrom<&HttpClientConfig> for ClientConfig {
|
|||
Verify::BuiltInRoots => builder.with_root_certificates(RootCertStore {
|
||||
roots: webpki_roots::TLS_SERVER_ROOTS.to_vec(),
|
||||
}),
|
||||
Verify::NativeRoots => {
|
||||
let native = rustls_native_certs::load_native_certs();
|
||||
let mut roots = RootCertStore::empty();
|
||||
roots.add_parsable_certificates(native.certs);
|
||||
if roots.is_empty() {
|
||||
return Err(Error::Client("no usable native root certificates".into()));
|
||||
}
|
||||
builder.with_root_certificates(roots)
|
||||
}
|
||||
Verify::CaBundle(path) => {
|
||||
builder.with_root_certificates(bundle_roots(path, TlsSource::CaBundle)?)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -79,7 +79,7 @@ fn validate_response_headers(
|
|||
));
|
||||
}
|
||||
let protocol_matches = match (offered_protocols, headers.get(SEC_WEBSOCKET_PROTOCOL)) {
|
||||
(None, None) => true,
|
||||
(_, None) => true,
|
||||
(Some(offered), Some(selected)) => offered
|
||||
.to_str()
|
||||
.ok()
|
||||
|
|
|
|||
|
|
@ -274,6 +274,41 @@ async fn wss_honors_ssl_verify(#[case] verify: fn(&Pki) -> Verify, #[case] trust
|
|||
}
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
#[tokio::test]
|
||||
async fn wss_uses_system_roots() {
|
||||
const CHILD_URL: &str = "LITELLM_TEST_SYSTEM_ROOTS_WSS_URL";
|
||||
if let Ok(url) = std::env::var(CHILD_URL) {
|
||||
let mut socket = open(&config(Verify::BuiltInRoots), &url, HeaderMap::new())
|
||||
.await
|
||||
.unwrap();
|
||||
round_trip(&mut socket).await;
|
||||
return;
|
||||
}
|
||||
|
||||
let pki = Pki::new();
|
||||
let (address, _) = serve_tls(&pki, false).await;
|
||||
let url = format!("wss://localhost:{}/responses", address.port());
|
||||
let ca = pki.path("ca.pem");
|
||||
let output = tokio::task::spawn_blocking(move || {
|
||||
std::process::Command::new(std::env::current_exe().unwrap())
|
||||
.args(["--exact", "wss_uses_system_roots", "--nocapture"])
|
||||
.env(CHILD_URL, url)
|
||||
.env("SSL_CERT_FILE", ca)
|
||||
.env("SSL_CERT_DIR", "")
|
||||
.output()
|
||||
.unwrap()
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
assert!(
|
||||
output.status.success(),
|
||||
"system-root WSS child failed:\n{}\n{}",
|
||||
String::from_utf8_lossy(&output.stdout),
|
||||
String::from_utf8_lossy(&output.stderr)
|
||||
);
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
#[case::with_certificate(true)]
|
||||
#[case::without_certificate(false)]
|
||||
|
|
@ -390,7 +425,7 @@ async fn invalid_url_is_rejected_before_dispatch(#[case] url: &str) {
|
|||
Some("other"),
|
||||
false
|
||||
)]
|
||||
#[case::missing_protocol(Some("websocket"), Some("Upgrade"), Some("events"), None, false)]
|
||||
#[case::declined_optional_protocol(Some("websocket"), Some("Upgrade"), Some("events"), None, true)]
|
||||
#[case::matching_protocol(
|
||||
Some("websocket"),
|
||||
Some("Upgrade"),
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue