Skip to main content

redevplugin_worker_sdk/
http.rs

1use crate::api;
2use crate::error::{Error, Result};
3use crate::resource::{Handle, IO_FLAG_EOF, MAX_IO_CHUNK_BYTES};
4use serde::{Deserialize, Serialize};
5
6#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
7pub struct Header {
8    pub name: String,
9    pub value: String,
10}
11
12#[derive(Debug, Clone, Copy, Default, Serialize)]
13#[serde(rename_all = "snake_case")]
14pub enum RedirectMode {
15    #[default]
16    Follow,
17    Manual,
18    Error,
19}
20
21#[derive(Debug, Clone, Serialize)]
22pub struct HttpRequest {
23    /// Connector declared by the current method; no implicit default is used.
24    pub connector_id: String,
25    pub method: String,
26    pub url: String,
27    #[serde(default)]
28    pub headers: Vec<Header>,
29    #[serde(default)]
30    pub redirect: RedirectMode,
31    #[serde(default)]
32    #[serde(skip_serializing_if = "Option::is_none")]
33    pub timeout_ms: Option<u32>,
34}
35
36pub type RequestOptions = HttpRequest;
37
38#[derive(Deserialize)]
39struct BeginResult {
40    upload_handle: u64,
41}
42
43#[derive(Deserialize)]
44struct FinishResult {
45    status: u16,
46    #[serde(default)]
47    headers: Vec<Header>,
48    final_url: String,
49    body_handle: u64,
50}
51
52pub struct RequestBody {
53    handle: Handle,
54}
55
56impl RequestBody {
57    pub fn begin(options: HttpRequest) -> Result<Self> {
58        let opened: BeginResult = api::call("net.http.begin", &options)?;
59        Ok(Self {
60            handle: Handle::new(opened.upload_handle)?,
61        })
62    }
63
64    pub fn write_all(&mut self, bytes: &[u8]) -> Result<()> {
65        for chunk in bytes.chunks(MAX_IO_CHUNK_BYTES) {
66            self.handle.write(chunk, 0)?;
67        }
68        Ok(())
69    }
70
71    pub fn finish(mut self) -> Result<Response> {
72        #[derive(Serialize)]
73        struct Arguments {
74            handle: u64,
75        }
76        let finished: FinishResult = api::call(
77            "net.http.finish",
78            &Arguments {
79                handle: self.handle.id(),
80            },
81        )?;
82        self.handle.disarm();
83        Ok(Response {
84            status: finished.status,
85            headers: finished.headers,
86            final_url: finished.final_url,
87            body: ResponseBody {
88                handle: Handle::new(finished.body_handle)?,
89            },
90        })
91    }
92
93    pub fn abort(mut self) -> Result<()> {
94        #[derive(Serialize)]
95        struct Arguments {
96            handle: u64,
97        }
98        let _: serde_json::Value = api::call(
99            "net.http.abort",
100            &Arguments {
101                handle: self.handle.id(),
102            },
103        )?;
104        self.handle.disarm();
105        Ok(())
106    }
107}
108
109pub struct HttpResponse {
110    pub status: u16,
111    pub headers: Vec<Header>,
112    pub final_url: String,
113    pub body: ResponseBody,
114}
115
116pub type Response = HttpResponse;
117
118pub struct ResponseBody {
119    handle: Handle,
120}
121
122impl ResponseBody {
123    pub fn read(&mut self, capacity: usize) -> Result<(Vec<u8>, u32)> {
124        self.handle.read(capacity)
125    }
126
127    pub fn read_all(mut self) -> Result<Vec<u8>> {
128        let mut result = Vec::new();
129        loop {
130            let (chunk, flags) = self.handle.read(MAX_IO_CHUNK_BYTES)?;
131            if chunk.is_empty() && flags & IO_FLAG_EOF == 0 {
132                return Err(Error::internal("HTTP response body made no progress"));
133            }
134            result.extend_from_slice(&chunk);
135            if flags & IO_FLAG_EOF != 0 {
136                self.handle.close()?;
137                return Ok(result);
138            }
139        }
140    }
141
142    pub fn close(mut self) -> Result<()> {
143        self.handle.close()
144    }
145}