11// Copyright 2018-2025 the Deno authors. MIT license.
22
3- use std:: collections:: HashMap ;
43use std:: sync:: Arc ;
5- use std:: thread:: ThreadId ;
64
75use boxed_error:: Boxed ;
86use deno_cache_dir:: file_fetcher:: RedirectHeaderParseError ;
97use deno_core:: error:: AnyError ;
108use deno_core:: futures:: StreamExt ;
11- use deno_core:: parking_lot:: Mutex ;
129use deno_core:: serde;
1310use deno_core:: serde_json;
1411use deno_core:: url:: Url ;
@@ -26,6 +23,7 @@ use http::header::CONTENT_LENGTH;
2623use http:: header:: HeaderName ;
2724use http:: header:: HeaderValue ;
2825use http_body_util:: BodyExt ;
26+ use once_cell:: sync:: OnceCell ;
2927use thiserror:: Error ;
3028
3129use crate :: util:: progress_bar:: UpdateGuard ;
@@ -41,10 +39,7 @@ pub enum SendError {
4139pub struct HttpClientProvider {
4240 options : CreateHttpClientOptions ,
4341 root_cert_store_provider : Option < Arc < dyn RootCertStoreProvider > > ,
44- // it's not safe to share a reqwest::Client across tokio runtimes,
45- // so we store these Clients keyed by thread id
46- // https://github.com/seanmonstar/reqwest/issues/1148#issuecomment-910868788
47- clients_by_thread_id : Mutex < HashMap < ThreadId , deno_fetch:: Client > > ,
42+ client : OnceCell < deno_fetch:: Client > ,
4843}
4944
5045impl std:: fmt:: Debug for HttpClientProvider {
@@ -66,33 +61,25 @@ impl HttpClientProvider {
6661 ..Default :: default ( )
6762 } ,
6863 root_cert_store_provider,
69- clients_by_thread_id : Default :: default ( ) ,
64+ client : OnceCell :: new ( ) ,
7065 }
7166 }
7267
7368 pub fn get_or_create ( & self ) -> Result < HttpClient , JsErrorBox > {
74- use std:: collections:: hash_map:: Entry ;
75- let thread_id = std:: thread:: current ( ) . id ( ) ;
76- let mut clients = self . clients_by_thread_id . lock ( ) ;
77- let entry = clients. entry ( thread_id) ;
78- match entry {
79- Entry :: Occupied ( entry) => Ok ( HttpClient :: new ( entry. get ( ) . clone ( ) ) ) ,
80- Entry :: Vacant ( entry) => {
81- let client = create_http_client (
82- DENO_VERSION_INFO . user_agent ,
83- CreateHttpClientOptions {
84- root_cert_store : match & self . root_cert_store_provider {
85- Some ( provider) => Some ( provider. get_or_try_init ( ) ?. clone ( ) ) ,
86- None => None ,
87- } ,
88- ..self . options . clone ( )
69+ let client = self . client . get_or_try_init ( || {
70+ create_http_client (
71+ DENO_VERSION_INFO . user_agent ,
72+ CreateHttpClientOptions {
73+ root_cert_store : match & self . root_cert_store_provider {
74+ Some ( provider) => Some ( provider. get_or_try_init ( ) ?. clone ( ) ) ,
75+ None => None ,
8976 } ,
90- )
91- . map_err ( JsErrorBox :: from_err ) ? ;
92- entry . insert ( client . clone ( ) ) ;
93- Ok ( HttpClient :: new ( client ) )
94- }
95- }
77+ .. self . options . clone ( )
78+ } ,
79+ )
80+ . map_err ( JsErrorBox :: from_err )
81+ } ) ? ;
82+ Ok ( HttpClient :: new ( client . clone ( ) ) )
9683 }
9784}
9885
@@ -179,21 +166,13 @@ impl HttpClientResponse {
179166#[ derive( Debug ) ]
180167pub struct HttpClient {
181168 client : deno_fetch:: Client ,
182- // don't allow sending this across threads because then
183- // it might be shared accidentally across tokio runtimes
184- // which will cause issues
185- // https://github.com/seanmonstar/reqwest/issues/1148#issuecomment-910868788
186- _unsend_marker : deno_core:: unsync:: UnsendMarker ,
187169}
188170
189171impl HttpClient {
190172 // DO NOT make this public. You should always be creating one of these from
191173 // the HttpClientProvider
192174 fn new ( client : deno_fetch:: Client ) -> Self {
193- Self {
194- client,
195- _unsend_marker : deno_core:: unsync:: UnsendMarker :: default ( ) ,
196- }
175+ Self { client }
197176 }
198177
199178 pub fn get ( & self , url : Url ) -> Result < RequestBuilder , http:: Error > {
0 commit comments