native-dumper 0.3.5.2__cp313-cp313-win_amd64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,669 @@
1
+ use pyo3::prelude::*;
2
+ use pyo3::types::{PyBytes, PyList};
3
+ use reqwest::{Client, Response, header::{HeaderMap, HeaderName, HeaderValue}};
4
+ use std::collections::HashMap;
5
+ use std::sync::{Arc, Mutex};
6
+ use std::time::Duration;
7
+ use tokio::runtime::Runtime;
8
+
9
+ #[pyclass]
10
+ struct HttpResponse {
11
+ response: Arc<Mutex<Option<Response>>>,
12
+ buffer: Arc<Mutex<Vec<u8>>>,
13
+ position: Arc<Mutex<u64>>,
14
+ is_complete: Arc<Mutex<bool>>,
15
+ rt: Arc<Runtime>,
16
+ status_code: Arc<Mutex<Option<u16>>>,
17
+ headers: Arc<Mutex<Option<HashMap<String, String>>>>,
18
+ content_length: Arc<Mutex<Option<u64>>>,
19
+ first_read_data: Arc<Mutex<Option<Vec<u8>>>>,
20
+ seek_allowed: Arc<Mutex<bool>>,
21
+ has_used_seek: Arc<Mutex<bool>>,
22
+ }
23
+
24
+ impl HttpResponse {
25
+ fn is_reading_complete(&self) -> bool {
26
+ let complete_guard = self.is_complete.lock().unwrap();
27
+ *complete_guard
28
+ }
29
+
30
+ fn set_complete(&self) {
31
+ let mut complete_guard = self.is_complete.lock().unwrap();
32
+ *complete_guard = true;
33
+ }
34
+
35
+ fn internal_close(&self) {
36
+ let mut response_guard = self.response.lock().unwrap();
37
+ let mut buffer_guard = self.buffer.lock().unwrap();
38
+ let mut first_read_data_guard = self.first_read_data.lock().unwrap();
39
+ *response_guard = None;
40
+ buffer_guard.clear();
41
+ *first_read_data_guard = None;
42
+ }
43
+
44
+ async fn read_chunk(response: &mut Response) -> Result<Option<Vec<u8>>, reqwest::Error> {
45
+ match response.chunk().await {
46
+ Ok(Some(chunk)) => Ok(Some(chunk.to_vec())),
47
+ Ok(None) => Ok(None),
48
+ Err(e) => Err(e),
49
+ }
50
+ }
51
+
52
+ fn initialize_metadata(&self) {
53
+ let response_guard = self.response.lock().unwrap();
54
+ if let Some(ref resp) = *response_guard {
55
+ {
56
+ let mut status_guard = self.status_code.lock().unwrap();
57
+ *status_guard = Some(resp.status().as_u16());
58
+ }
59
+
60
+ {
61
+ let mut headers_guard = self.headers.lock().unwrap();
62
+ let mut headers_map = HashMap::new();
63
+ for (key, value) in resp.headers() {
64
+ if let Ok(value_str) = value.to_str() {
65
+ headers_map.insert(key.as_str().to_lowercase(), value_str.to_string());
66
+ }
67
+ }
68
+ *headers_guard = Some(headers_map);
69
+ }
70
+
71
+ {
72
+ let mut content_length_guard = self.content_length.lock().unwrap();
73
+ *content_length_guard = resp.content_length();
74
+ }
75
+ }
76
+ }
77
+
78
+ fn from_response(response: Response, rt: Arc<Runtime>) -> Self {
79
+ let response_obj = Self {
80
+ response: Arc::new(Mutex::new(Some(response))),
81
+ buffer: Arc::new(Mutex::new(Vec::new())),
82
+ position: Arc::new(Mutex::new(0)),
83
+ is_complete: Arc::new(Mutex::new(false)),
84
+ rt: rt.clone(),
85
+ status_code: Arc::new(Mutex::new(None)),
86
+ headers: Arc::new(Mutex::new(None)),
87
+ content_length: Arc::new(Mutex::new(None)),
88
+ first_read_data: Arc::new(Mutex::new(None)),
89
+ seek_allowed: Arc::new(Mutex::new(false)),
90
+ has_used_seek: Arc::new(Mutex::new(false)),
91
+ };
92
+
93
+ response_obj.initialize_metadata();
94
+ response_obj
95
+ }
96
+ }
97
+
98
+ #[pymethods]
99
+ impl HttpResponse {
100
+
101
+ fn read(&self, py: Python, size: Option<usize>) -> PyResult<Py<PyBytes>> {
102
+ if self.is_reading_complete() {
103
+ return Ok(PyBytes::new(py, &[]).into());
104
+ }
105
+
106
+ if let Some(1) = size {
107
+ return self.read1(py);
108
+ }
109
+
110
+ let rt = self.rt.clone();
111
+ let response = self.response.clone();
112
+ let buffer = self.buffer.clone();
113
+ let position = self.position.clone();
114
+ let is_complete = self.is_complete.clone();
115
+ let first_read_data = self.first_read_data.clone();
116
+ let seek_allowed = self.seek_allowed.clone();
117
+ let data: Vec<u8> = py.detach(move || {
118
+ rt.block_on(async {
119
+ let mut response_guard = response.lock().unwrap();
120
+ let mut buffer_guard = buffer.lock().unwrap();
121
+ let mut pos_guard = position.lock().unwrap();
122
+
123
+ if *is_complete.lock().unwrap() {
124
+ return Vec::new();
125
+ }
126
+
127
+ if let Some(ref mut resp) = *response_guard {
128
+ let target_size = size.unwrap_or(usize::MAX);
129
+ let mut collected_data = Vec::new();
130
+
131
+ if !buffer_guard.is_empty() {
132
+ if buffer_guard.len() <= target_size {
133
+ collected_data = std::mem::take(&mut *buffer_guard);
134
+ } else {
135
+ collected_data = buffer_guard.drain(..target_size).collect();
136
+ }
137
+ }
138
+
139
+ if collected_data.len() < target_size {
140
+ let remaining_needed = target_size - collected_data.len();
141
+
142
+ if target_size == usize::MAX {
143
+ loop {
144
+ match Self::read_chunk(resp).await {
145
+ Ok(Some(chunk_data)) => {
146
+ collected_data.extend_from_slice(&chunk_data);
147
+ }
148
+ Ok(None) => {
149
+ *is_complete.lock().unwrap() = true;
150
+ break;
151
+ }
152
+ Err(_) => {
153
+ *is_complete.lock().unwrap() = true;
154
+ break;
155
+ }
156
+ }
157
+ }
158
+ } else {
159
+ let mut remaining = remaining_needed;
160
+
161
+ while remaining > 0 {
162
+ match Self::read_chunk(resp).await {
163
+ Ok(Some(chunk_data)) => {
164
+ if chunk_data.len() > remaining {
165
+ collected_data.extend_from_slice(&chunk_data[..remaining]);
166
+ buffer_guard.extend_from_slice(&chunk_data[remaining..]);
167
+ break;
168
+ } else {
169
+ collected_data.extend_from_slice(&chunk_data);
170
+ remaining -= chunk_data.len();
171
+ }
172
+ }
173
+ Ok(None) => {
174
+ *is_complete.lock().unwrap() = true;
175
+ break;
176
+ }
177
+ Err(_) => {
178
+ *is_complete.lock().unwrap() = true;
179
+ break;
180
+ }
181
+ }
182
+ }
183
+ }
184
+ }
185
+
186
+ let should_save_first_read = {
187
+ let first_read_data_guard = first_read_data.lock().unwrap();
188
+ first_read_data_guard.is_none() && !collected_data.is_empty()
189
+ };
190
+
191
+ if should_save_first_read {
192
+ let mut first_read_data_guard = first_read_data.lock().unwrap();
193
+ let mut seek_allowed_guard = seek_allowed.lock().unwrap();
194
+ *first_read_data_guard = Some(collected_data.clone());
195
+ *seek_allowed_guard = true;
196
+ }
197
+
198
+ *pos_guard += collected_data.len() as u64;
199
+ collected_data
200
+ } else {
201
+ *is_complete.lock().unwrap() = true;
202
+ Vec::new()
203
+ }
204
+ })
205
+ });
206
+
207
+ Ok(PyBytes::new(py, &data).into())
208
+ }
209
+
210
+ fn read1(&self, py: Python) -> PyResult<Py<PyBytes>> {
211
+ if self.is_reading_complete() {
212
+ return Ok(PyBytes::new(py, &[]).into());
213
+ }
214
+
215
+ let rt = self.rt.clone();
216
+ let response = self.response.clone();
217
+ let buffer = self.buffer.clone();
218
+ let position = self.position.clone();
219
+ let is_complete = self.is_complete.clone();
220
+ let first_read_data = self.first_read_data.clone();
221
+ let seek_allowed = self.seek_allowed.clone();
222
+ let data: Vec<u8> = py.detach(move || {
223
+ rt.block_on(async {
224
+ let mut response_guard = response.lock().unwrap();
225
+ let mut buffer_guard = buffer.lock().unwrap();
226
+ let mut pos_guard = position.lock().unwrap();
227
+
228
+ if *is_complete.lock().unwrap() {
229
+ return Vec::new();
230
+ }
231
+
232
+ if let Some(ref mut resp) = *response_guard {
233
+ let result_byte = if !buffer_guard.is_empty() {
234
+ let byte = buffer_guard.remove(0);
235
+ Some(byte)
236
+ } else {
237
+ match Self::read_chunk(resp).await {
238
+ Ok(Some(chunk_data)) => {
239
+ if !chunk_data.is_empty() {
240
+ let byte = chunk_data[0];
241
+ if chunk_data.len() > 1 {
242
+ buffer_guard.extend_from_slice(&chunk_data[1..]);
243
+ }
244
+ Some(byte)
245
+ } else {
246
+ None
247
+ }
248
+ }
249
+ Ok(None) => {
250
+ *is_complete.lock().unwrap() = true;
251
+ None
252
+ }
253
+ Err(_) => {
254
+ *is_complete.lock().unwrap() = true;
255
+ None
256
+ }
257
+ }
258
+ };
259
+
260
+ if let Some(byte) = result_byte {
261
+ let should_save_first_read = {
262
+ let first_read_data_guard = first_read_data.lock().unwrap();
263
+ first_read_data_guard.is_none()
264
+ };
265
+
266
+ if should_save_first_read {
267
+ let mut first_read_data_guard = first_read_data.lock().unwrap();
268
+ let mut seek_allowed_guard = seek_allowed.lock().unwrap();
269
+ *first_read_data_guard = Some(vec![byte]);
270
+ *seek_allowed_guard = true;
271
+ }
272
+
273
+ *pos_guard += 1;
274
+ vec![byte]
275
+ } else {
276
+ Vec::new()
277
+ }
278
+ } else {
279
+ *is_complete.lock().unwrap() = true;
280
+ Vec::new()
281
+ }
282
+ })
283
+ });
284
+
285
+ Ok(PyBytes::new(py, &data).into())
286
+ }
287
+
288
+ fn get_status(&self) -> PyResult<Option<u16>> {
289
+ let status_guard = self.status_code.lock().unwrap();
290
+ Ok(*status_guard)
291
+ }
292
+
293
+ fn get_headers(&self) -> PyResult<Option<HashMap<String, String>>> {
294
+ let headers_guard = self.headers.lock().unwrap();
295
+ Ok(headers_guard.clone())
296
+ }
297
+
298
+ fn get_header(&self, name: String) -> PyResult<Option<String>> {
299
+ let headers_guard = self.headers.lock().unwrap();
300
+ if let Some(ref headers) = *headers_guard {
301
+ Ok(headers.get(&name.to_lowercase()).cloned())
302
+ } else {
303
+ Ok(None)
304
+ }
305
+ }
306
+
307
+ fn get_content_length(&self) -> PyResult<Option<u64>> {
308
+ let content_length_guard = self.content_length.lock().unwrap();
309
+ Ok(*content_length_guard)
310
+ }
311
+
312
+ fn is_success(&self) -> PyResult<bool> {
313
+ let status_guard = self.status_code.lock().unwrap();
314
+ if let Some(status) = *status_guard {
315
+ Ok(status >= 200 && status < 300)
316
+ } else {
317
+ Ok(false)
318
+ }
319
+ }
320
+
321
+ fn is_redirect(&self) -> PyResult<bool> {
322
+ let status_guard = self.status_code.lock().unwrap();
323
+ if let Some(status) = *status_guard {
324
+ Ok(status >= 300 && status < 400)
325
+ } else {
326
+ Ok(false)
327
+ }
328
+ }
329
+
330
+ fn is_client_error(&self) -> PyResult<bool> {
331
+ let status_guard = self.status_code.lock().unwrap();
332
+ if let Some(status) = *status_guard {
333
+ Ok(status >= 400 && status < 500)
334
+ } else {
335
+ Ok(false)
336
+ }
337
+ }
338
+
339
+ fn is_server_error(&self) -> PyResult<bool> {
340
+ let status_guard = self.status_code.lock().unwrap();
341
+ if let Some(status) = *status_guard {
342
+ Ok(status >= 500 && status < 600)
343
+ } else {
344
+ Ok(false)
345
+ }
346
+ }
347
+
348
+ fn get_content_type(&self) -> PyResult<Option<String>> {
349
+ self.get_header("content-type".to_string())
350
+ }
351
+
352
+ fn get_url(&self) -> PyResult<Option<String>> {
353
+ let response_guard = self.response.lock().unwrap();
354
+ if let Some(ref resp) = *response_guard {
355
+ Ok(Some(resp.url().to_string()))
356
+ } else {
357
+ Ok(None)
358
+ }
359
+ }
360
+
361
+ fn seek(&self, pos: u64) -> PyResult<()> {
362
+ if pos == 0 {
363
+ let (is_seek_allowed, has_first_data) = {
364
+ let seek_allowed_guard = self.seek_allowed.lock().unwrap();
365
+ let has_used_seek_guard = self.has_used_seek.lock().unwrap();
366
+ let first_read_data_guard = self.first_read_data.lock().unwrap();
367
+
368
+ (
369
+ *seek_allowed_guard && !*has_used_seek_guard,
370
+ first_read_data_guard.is_some()
371
+ )
372
+ };
373
+
374
+ if !is_seek_allowed {
375
+ return Err(PyErr::new::<pyo3::exceptions::PyIOError, _>(
376
+ "Seek to position 0 is allowed only once after first read"
377
+ ));
378
+ }
379
+
380
+ if !has_first_data {
381
+ return Err(PyErr::new::<pyo3::exceptions::PyIOError, _>(
382
+ "No data has been read yet, cannot seek to position 0"
383
+ ));
384
+ }
385
+
386
+ let first_read_data_guard = self.first_read_data.lock().unwrap();
387
+ if let Some(ref first_data) = *first_read_data_guard {
388
+ let mut position = self.position.lock().unwrap();
389
+ let mut buffer = self.buffer.lock().unwrap();
390
+ let mut complete = self.is_complete.lock().unwrap();
391
+ let mut has_used_seek_guard = self.has_used_seek.lock().unwrap();
392
+
393
+ buffer.clear();
394
+ buffer.extend_from_slice(first_data);
395
+ *position = 0;
396
+ *complete = false;
397
+ *has_used_seek_guard = true;
398
+
399
+ Ok(())
400
+ } else {
401
+ Err(PyErr::new::<pyo3::exceptions::PyIOError, _>(
402
+ "No data has been read yet, cannot seek to position 0"
403
+ ))
404
+ }
405
+ } else {
406
+ Err(PyErr::new::<pyo3::exceptions::PyIOError, _>(
407
+ "Seek is only supported to position 0 for HTTP streams"
408
+ ))
409
+ }
410
+ }
411
+
412
+ fn seekable(&self) -> PyResult<bool> {
413
+ let seek_allowed_guard = self.seek_allowed.lock().unwrap();
414
+ let has_used_seek_guard = self.has_used_seek.lock().unwrap();
415
+
416
+ Ok(*seek_allowed_guard && !*has_used_seek_guard)
417
+ }
418
+
419
+ fn close(&self) -> PyResult<()> {
420
+ self.internal_close();
421
+ self.set_complete();
422
+ Ok(())
423
+ }
424
+
425
+ fn is_closed(&self) -> PyResult<bool> {
426
+ Ok(self.is_reading_complete())
427
+ }
428
+
429
+ fn get_info(&self) -> PyResult<HashMap<String, String>> {
430
+ let mut info = HashMap::new();
431
+
432
+ if let Some(status) = self.get_status()? {
433
+ info.insert("status".to_string(), status.to_string());
434
+ info.insert("status_text".to_string(), if self.is_success()? { "OK".to_string() } else { "Error".to_string() });
435
+ }
436
+
437
+ if let Some(length) = self.get_content_length()? {
438
+ info.insert("content_length".to_string(), length.to_string());
439
+ }
440
+
441
+ if let Some(content_type) = self.get_content_type()? {
442
+ info.insert("content_type".to_string(), content_type);
443
+ }
444
+
445
+ if let Some(url) = self.get_url()? {
446
+ info.insert("url".to_string(), url);
447
+ }
448
+
449
+ let position = self.tell()?;
450
+ info.insert("bytes_read".to_string(), position.to_string());
451
+
452
+ info.insert("closed".to_string(), self.is_closed()?.to_string());
453
+ info.insert("complete".to_string(), self.is_reading_complete().to_string());
454
+
455
+ let seek_allowed = self.seek_allowed.lock().unwrap();
456
+ let has_used_seek = self.has_used_seek.lock().unwrap();
457
+ let first_read_data = self.first_read_data.lock().unwrap();
458
+
459
+ info.insert("seek_allowed".to_string(), (*seek_allowed).to_string());
460
+ info.insert("seek_used".to_string(), (*has_used_seek).to_string());
461
+ info.insert("has_first_read_data".to_string(), first_read_data.is_some().to_string());
462
+
463
+ let seekable = *seek_allowed && !*has_used_seek;
464
+ info.insert("seekable".to_string(), seekable.to_string());
465
+
466
+ if let Some(ref data) = *first_read_data {
467
+ info.insert("first_read_size".to_string(), data.len().to_string());
468
+ }
469
+
470
+ Ok(info)
471
+ }
472
+
473
+ fn tell(&self) -> PyResult<u64> {
474
+ let position = self.position.lock().unwrap();
475
+ Ok(*position)
476
+ }
477
+ }
478
+
479
+ impl Clone for HttpResponse {
480
+ fn clone(&self) -> Self {
481
+ Self {
482
+ response: self.response.clone(),
483
+ buffer: self.buffer.clone(),
484
+ position: self.position.clone(),
485
+ is_complete: self.is_complete.clone(),
486
+ rt: self.rt.clone(),
487
+ status_code: self.status_code.clone(),
488
+ headers: self.headers.clone(),
489
+ content_length: self.content_length.clone(),
490
+ first_read_data: self.first_read_data.clone(),
491
+ seek_allowed: self.seek_allowed.clone(),
492
+ has_used_seek: self.has_used_seek.clone(),
493
+ }
494
+ }
495
+ }
496
+
497
+ #[pyclass]
498
+ struct HttpSession {
499
+ client: Client,
500
+ rt: Arc<Runtime>,
501
+ }
502
+
503
+ #[pymethods]
504
+ impl HttpSession {
505
+ #[new]
506
+ fn new(timeout: Option<f64>) -> PyResult<Self> {
507
+ let client_builder = Client::builder()
508
+ .tcp_keepalive(Duration::from_secs(60))
509
+ .pool_max_idle_per_host(10);
510
+
511
+ let client = if let Some(timeout_secs) = timeout {
512
+ client_builder
513
+ .timeout(Duration::from_secs_f64(timeout_secs))
514
+ .build()
515
+ .map_err(|e| PyErr::new::<pyo3::exceptions::PyRuntimeError, _>(
516
+ format!("Failed to create HTTP client: {}", e)
517
+ ))?
518
+ } else {
519
+ client_builder
520
+ .timeout(Duration::from_secs(30))
521
+ .build()
522
+ .map_err(|e| PyErr::new::<pyo3::exceptions::PyRuntimeError, _>(
523
+ format!("Failed to create HTTP client: {}", e)
524
+ ))?
525
+ };
526
+
527
+ let rt = Arc::new(Runtime::new().map_err(|e| {
528
+ PyErr::new::<pyo3::exceptions::PyRuntimeError, _>(format!("Failed to create runtime: {}", e))
529
+ })?);
530
+
531
+ Ok(HttpSession { client, rt })
532
+ }
533
+
534
+ fn post(
535
+ &self,
536
+ py: Python,
537
+ url: String,
538
+ headers: Option<HashMap<String, String>>,
539
+ params: Option<HashMap<String, String>>,
540
+ data: Option<Bound<'_, PyAny>>,
541
+ timeout: Option<f64>,
542
+ ) -> PyResult<HttpResponse> {
543
+ let client = self.client.clone();
544
+ let rt = self.rt.clone();
545
+ let mut header_map = HeaderMap::new();
546
+
547
+ if let Some(headers) = headers {
548
+ for (key, value) in headers {
549
+ if let Ok(header_name) = key.parse::<HeaderName>() {
550
+ if let Ok(header_value) = value.parse::<HeaderValue>() {
551
+ header_map.insert(header_name, header_value);
552
+ }
553
+ }
554
+ }
555
+ }
556
+
557
+ let mut request_builder = client.post(&url).headers(header_map);
558
+
559
+ if let Some(params) = params {
560
+ request_builder = request_builder.query(&params);
561
+ }
562
+
563
+ if let Some(timeout_secs) = timeout {
564
+ request_builder = request_builder.timeout(Duration::from_secs_f64(timeout_secs));
565
+ }
566
+
567
+ if let Some(data_obj) = data {
568
+ let data_bytes = if let Ok(bytes) = data_obj.downcast::<PyBytes>() {
569
+ bytes.as_bytes().to_vec()
570
+ } else if let Ok(list) = data_obj.downcast::<PyList>() {
571
+ let mut result = Vec::new();
572
+ for (idx, item) in list.iter().enumerate() {
573
+ if let Ok(bytes) = item.downcast::<PyBytes>() {
574
+ result.extend_from_slice(bytes.as_bytes());
575
+ } else if let Ok(vec_bytes) = item.extract::<Vec<u8>>() {
576
+ result.extend_from_slice(&vec_bytes);
577
+ } else {
578
+ return Err(PyErr::new::<pyo3::exceptions::PyTypeError, _>(
579
+ format!("List element at index {} must be bytes or byte array", idx)
580
+ ));
581
+ }
582
+ }
583
+ result
584
+ } else if let Ok(bytes_vec) = data_obj.extract::<Vec<u8>>() {
585
+ bytes_vec
586
+ } else {
587
+ let mut result = Vec::new();
588
+ let iter = data_obj.getattr("__iter__")?;
589
+ let iterator = iter.call0()?;
590
+
591
+ loop {
592
+ let next_result = iterator.call_method0("__next__");
593
+ match next_result {
594
+ Ok(item) => {
595
+ if let Ok(bytes) = item.downcast::<PyBytes>() {
596
+ result.extend_from_slice(bytes.as_bytes());
597
+ } else if let Ok(vec_bytes) = item.extract::<Vec<u8>>() {
598
+ result.extend_from_slice(&vec_bytes);
599
+ } else if let Ok(byte_array) = item.extract::<[u8; 1]>() {
600
+ result.extend_from_slice(&byte_array);
601
+ } else if let Ok(byte) = item.extract::<u8>() {
602
+ result.push(byte);
603
+ } else {
604
+ return Err(PyErr::new::<pyo3::exceptions::PyTypeError, _>(
605
+ "Iterator yielded unsupported type, expected bytes, bytearray, or u8"
606
+ ));
607
+ }
608
+ }
609
+ Err(e) => {
610
+ if e.is_instance_of::<pyo3::exceptions::PyStopIteration>(py) {
611
+ break;
612
+ } else {
613
+ return Err(PyErr::new::<pyo3::exceptions::PyTypeError, _>(
614
+ format!("Error during iteration: {}", e)
615
+ ));
616
+ }
617
+ }
618
+ }
619
+ }
620
+
621
+ result
622
+ };
623
+
624
+ request_builder = request_builder.body(data_bytes);
625
+ } else {
626
+ request_builder = request_builder.header("Content-Length", "0");
627
+ }
628
+
629
+ let response = rt.block_on(async {
630
+ request_builder.send().await
631
+ });
632
+
633
+ match response {
634
+ Ok(resp) => {
635
+ let response_obj = HttpResponse::from_response(resp, rt.clone());
636
+ Ok(response_obj)
637
+ }
638
+ Err(e) => Err(PyErr::new::<pyo3::exceptions::PyIOError, _>(
639
+ format!("HTTP request failed: {}", e)
640
+ )),
641
+ }
642
+ }
643
+
644
+ fn post_stream(
645
+ &self,
646
+ py: Python,
647
+ url: String,
648
+ headers: Option<HashMap<String, String>>,
649
+ params: Option<HashMap<String, String>>,
650
+ data: Option<Bound<'_, PyAny>>,
651
+ timeout: Option<f64>,
652
+ ) -> PyResult<HttpResponse> {
653
+ self.post(py, url, headers, params, data, timeout)
654
+ }
655
+
656
+ fn close(&mut self) {
657
+ if let Ok(rt) = Arc::try_unwrap(std::mem::replace(&mut self.rt, Arc::new(Runtime::new().unwrap()))) {
658
+ rt.shutdown_background();
659
+ }
660
+ let _ = std::mem::replace(&mut self.client, Client::new());
661
+ }
662
+ }
663
+
664
+ #[pymodule]
665
+ fn pyo3http(m: &Bound<'_, PyModule>) -> PyResult<()> {
666
+ m.add_class::<HttpSession>()?;
667
+ m.add_class::<HttpResponse>()?;
668
+ Ok(())
669
+ }