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