|
1 | | -use std::{ |
2 | | - thread::{self, sleep}, |
3 | | - time::Duration, |
4 | | -}; |
5 | | - |
6 | | -use napi::{ |
7 | | - bindgen_prelude::{ |
8 | | - FromNapiValue, Function, JsObjectValue, JsValuesTupleIntoVec, Object, Promise, Uint8Array, |
9 | | - }, |
10 | | - threadsafe_function::ThreadsafeFunction, |
11 | | - Env, Status, |
12 | | -}; |
| 1 | +use napi::bindgen_prelude::Uint8Array; |
13 | 2 |
|
14 | 3 | use napi_derive::napi; |
15 | | -use reqwest::{cookie::CookieStore, header::HeaderValue, Url}; |
16 | | -use tokio::sync::oneshot; |
17 | 4 |
|
18 | 5 | #[derive(Default, Clone)] |
19 | 6 | #[napi(string_enum = "UPPERCASE")] |
@@ -59,145 +46,3 @@ pub struct RequestInit { |
59 | 46 | /// Force the request to use HTTP/3. If the server doesn't expect HTTP/3 or the Impit instance doesn't have HTTP/3 enabled (via the {@link ImpitOptions.http3} option), the request will fail. |
60 | 47 | pub force_http3: Option<bool>, |
61 | 48 | } |
62 | | - |
63 | | -fn await_promise< |
64 | | - T: Send, |
65 | | - CallbackArgs: JsValuesTupleIntoVec, |
66 | | - RustReturn: FromNapiValue + std::fmt::Debug + Sync + Send, |
67 | | ->( |
68 | | - tsfn: &ThreadsafeFunction<T, Promise<RustReturn>, CallbackArgs, Status, false>, |
69 | | - args: T, |
70 | | -) -> Result<RustReturn, napi::Error> { |
71 | | - thread::scope(|scope| { |
72 | | - let (tx, mut rx) = oneshot::channel(); |
73 | | - |
74 | | - scope.spawn(move || match tokio::runtime::Runtime::new() { |
75 | | - Ok(runtime) => { |
76 | | - runtime.block_on(async { |
77 | | - match tsfn.call_async(args).await { |
78 | | - Ok(result) => { |
79 | | - let _ = tx.send(result.await); |
80 | | - } |
81 | | - Err(e) => { |
82 | | - let _ = tx.send(Err(napi::Error::new( |
83 | | - napi::Status::GenericFailure, |
84 | | - format!("[impit] failed to retrieve cookies from the external cookie store: {e}"), |
85 | | - ))); |
86 | | - } |
87 | | - } |
88 | | - }); |
89 | | - } |
90 | | - Err(e) => { |
91 | | - let _ = tx.send(Err(napi::Error::new( |
92 | | - napi::Status::GenericFailure, |
93 | | - format!("[impit] failed to retrieve cookies from the external cookie store: {e}"), |
94 | | - ))); |
95 | | - } |
96 | | - }); |
97 | | - |
98 | | - let mut result = rx.try_recv(); |
99 | | - |
100 | | - let max_retries = 5; |
101 | | - let mut retries = 0; |
102 | | - |
103 | | - while result.is_err() && retries < max_retries { |
104 | | - sleep(Duration::from_millis(5)); |
105 | | - result = rx.try_recv(); |
106 | | - retries += 1; |
107 | | - } |
108 | | - |
109 | | - match result { |
110 | | - Ok(Ok(result)) => Ok(result), |
111 | | - Ok(Err(e)) => Err(e), |
112 | | - Err(_) => Err(napi::Error::new( |
113 | | - napi::Status::GenericFailure, |
114 | | - "[impit] failed to retrieve cookies from the external cookie store".to_string(), |
115 | | - )), |
116 | | - } |
117 | | - }) |
118 | | -} |
119 | | - |
120 | | -pub struct NodeCookieJar { |
121 | | - set_cookie_tsfn: |
122 | | - ThreadsafeFunction<(String, String), Promise<()>, (String, String), Status, false>, |
123 | | - get_cookies_tsfn: ThreadsafeFunction<String, Promise<String>, String, Status, false>, |
124 | | -} |
125 | | - |
126 | | -impl CookieStore for NodeCookieJar { |
127 | | - fn set_cookies( |
128 | | - &self, |
129 | | - cookie_headers: &mut dyn Iterator<Item = &reqwest::header::HeaderValue>, |
130 | | - url: &Url, |
131 | | - ) { |
132 | | - for header in cookie_headers { |
133 | | - let header = header.to_str().unwrap_or_default().to_string(); |
134 | | - let url = url.as_str().to_string(); |
135 | | - |
136 | | - let _ = await_promise(&self.set_cookie_tsfn, (header.clone(), url.clone())); |
137 | | - } |
138 | | - } |
139 | | - |
140 | | - fn cookies(&self, url: &Url) -> Option<reqwest::header::HeaderValue> { |
141 | | - let url = url.as_str().to_string(); |
142 | | - |
143 | | - await_promise(&self.get_cookies_tsfn, url.clone()) |
144 | | - .ok() |
145 | | - .and_then(|header| { |
146 | | - if header.is_empty() { |
147 | | - return None; |
148 | | - } |
149 | | - |
150 | | - HeaderValue::from_str(&header).ok() |
151 | | - }) |
152 | | - } |
153 | | -} |
154 | | - |
155 | | -impl NodeCookieJar { |
156 | | - pub fn new(env: &Env, tough_cookie: Object) -> Result<Self, napi::Error> { |
157 | | - let set_cookie_js_method = match tough_cookie |
158 | | - .get_named_property::<Function<'_, (String, String), Promise<()>>>("setCookie") |
159 | | - { |
160 | | - Ok(method) => method, |
161 | | - Err(e) => { |
162 | | - return Err(napi::Error::new( |
163 | | - napi::Status::GenericFailure, |
164 | | - format!("[impit] Couldn't find `setCookie` method on the external cookie store: {e}"), |
165 | | - )); |
166 | | - } |
167 | | - }; |
168 | | - |
169 | | - let get_cookie_js_method = match tough_cookie |
170 | | - .get_named_property::<Function<'_, String, Promise<String>>>("getCookieString") |
171 | | - { |
172 | | - Ok(method) => method, |
173 | | - Err(e) => { |
174 | | - return Err(napi::Error::new( |
175 | | - napi::Status::GenericFailure, |
176 | | - format!( |
177 | | - "[impit] Couldn't find `getCookieString` method on the external cookie store: {e}" |
178 | | - ), |
179 | | - )); |
180 | | - } |
181 | | - }; |
182 | | - |
183 | | - let mut set_cookie = set_cookie_js_method |
184 | | - .build_threadsafe_function::<(std::string::String, std::string::String)>() |
185 | | - .build_callback(|ctx| Ok(ctx.value))?; |
186 | | - |
187 | | - let mut get_cookies = get_cookie_js_method |
188 | | - .build_threadsafe_function::<std::string::String>() |
189 | | - .build_callback(|ctx| Ok(ctx.value))?; |
190 | | - |
191 | | - // Unless the `ThreadsafeFunction` is unreferenced, the Node.JS application will hang on exit |
192 | | - // https://nodejs.github.io/node-addon-examples/special-topics/thread-safe-functions/#q-my-application-isnt-exiting-correctly-it-just-hangs |
193 | | - #[allow(deprecated)] |
194 | | - let _ = set_cookie.unref(env); |
195 | | - #[allow(deprecated)] |
196 | | - let _ = get_cookies.unref(env); |
197 | | - |
198 | | - Ok(Self { |
199 | | - set_cookie_tsfn: set_cookie, |
200 | | - get_cookies_tsfn: get_cookies, |
201 | | - }) |
202 | | - } |
203 | | -} |
0 commit comments