firecracker_rs_sdk/events/
mod.rs1use std::any::TypeId;
2
3use serde::{de::DeserializeOwned, Serialize};
4
5use crate::{Error, Result};
6
7const HTTP_VERSION: &'static str = "HTTP/1.0";
8
9pub trait RequestTrait {
11 type Payload: Serialize + 'static;
13
14 fn encode(&self) -> Result<Vec<u8>> {
16 let mut request = format!("{} {} {}\r\n", self.method(), self.path(), HTTP_VERSION);
19
20 let request = if TypeId::of::<Self::Payload>() == TypeId::of::<Empty>() {
21 request.push_str("\r\n");
22 request.as_bytes().to_vec()
23 } else {
24 let payload = self.payload();
25 let mut payload = serde_json::to_vec(&payload)
26 .map_err(|e| Error::Event(format!("serde_json encode: {e}")))?;
27 request.push_str(&format!("Content-Length: {}\r\n", payload.len()));
29 request.push_str("\r\n");
31 let mut request = request.as_bytes().to_vec();
33 request.append(&mut payload);
34 request
35 };
36
37 Ok(request)
38 }
39
40 fn method(&self) -> &'static str;
42
43 fn path(&self) -> String;
45
46 fn payload(&self) -> &Self::Payload;
48}
49
50pub trait ResponseTrait {
52 type Payload: DeserializeOwned + 'static;
54
55 fn status_code(response: &Vec<u8>) -> Result<u16> {
57 let mut headers = [httparse::EMPTY_HEADER; 64];
58 let mut res = httparse::Response::new(&mut headers);
59 let body_start = res.parse(&response).unwrap();
60 if body_start.is_partial() {
61 return Err(Error::Event("Incomplete response".into()));
62 }
63 res.code
64 .ok_or_else(|| Error::Event("Bad HTTP response".into()))
65 }
66
67 fn decode(response: &Vec<u8>) -> Result<Self::Payload> {
69 let mut headers = [httparse::EMPTY_HEADER; 64];
70 let mut res = httparse::Response::new(&mut headers);
71
72 let body_start = res.parse(&response).unwrap();
73 if body_start.is_partial() {
74 return Err(Error::Event("Incomplete response".into()));
75 }
76 let body_start = body_start.unwrap(); let content_length = res
79 .headers
80 .iter()
81 .find(|h| h.name.to_lowercase() == "content-length")
82 .and_then(|h| {
83 Some(
84 std::str::from_utf8(h.value)
85 .unwrap()
86 .parse::<usize>()
87 .unwrap(),
88 )
89 });
90
91 match content_length {
92 Some(content_length) => {
93 let body = &response[body_start..(body_start + content_length)];
94 let payload: Self::Payload = serde_json::from_slice(body)
95 .map_err(|e| Error::Event(format!("serde_json decode: {e}")))?;
96 Ok(payload)
97 }
98 None if TypeId::of::<Self::Payload>() == TypeId::of::<Empty>() => {
99 let payload: Self::Payload = serde_json::from_str("null")
102 .map_err(|e| Error::Event(format!("serde_json decode: {e}")))?;
103 Ok(payload)
104 }
105 _ => Err(Error::Event("Bad HTTP response".into())),
106 }
107 }
108}
109
110pub trait EventTrait: RequestTrait + ResponseTrait {}
111
112macro_rules! impl_event_traits {
113 ($struct_name:ident, $method:expr, $path:expr, $req_payload:ty, $res_payload:ty) => {
115 pub struct $struct_name<'a>(pub &'a $req_payload);
116
117 impl<'a> RequestTrait for $struct_name<'a> {
118 type Payload = $req_payload;
119
120 fn method(&self) -> &'static str {
121 $method
122 }
123
124 fn path(&self) -> String {
125 $path.into()
126 }
127
128 fn payload(&self) -> &Self::Payload {
129 &self.0
130 }
131 }
132
133 impl<'a> ResponseTrait for $struct_name<'a> {
134 type Payload = $res_payload;
135 }
136
137 impl<'a> EventTrait for $struct_name<'a> {}
138
139 paste::paste! {
140 pub struct [<$struct_name Owned>](
141 pub $req_payload
142 );
144
145 impl RequestTrait for [<$struct_name Owned>] {
146 type Payload = $req_payload;
147
148 fn method(&self) -> &'static str {
149 $method
150 }
151
152 fn path(&self) -> String {
153 $path.into()
154 }
155
156 fn payload(&self) -> &Self::Payload {
157 &self.0
158 }
159 }
160
161 impl ResponseTrait for [<$struct_name Owned>] {
162 type Payload = $res_payload;
163 }
164
165 impl EventTrait for [<$struct_name Owned>] {}
166 }
167 };
168
169 ($struct_name:ident, $method:expr, $path:expr, $id:ident, $req_payload:ty, $res_payload:ty) => {
170 pub struct $struct_name<'a>(pub &'a $req_payload);
171
172 impl<'a> RequestTrait for $struct_name<'a> {
173 type Payload = $req_payload;
174
175 fn method(&self) -> &'static str {
176 $method
177 }
178
179 fn path(&self) -> String {
180 format!("{}/{}", $path, &self.0.$id)
181 }
182
183 fn payload(&self) -> &Self::Payload {
184 &self.0
185 }
186 }
187
188 impl<'a> ResponseTrait for $struct_name<'a> {
189 type Payload = $res_payload;
190 }
191
192 impl<'a> EventTrait for $struct_name<'a> {}
193
194 paste::paste! {
195 pub struct [<$struct_name Owned>](pub $req_payload);
196
197 impl RequestTrait for [<$struct_name Owned>] {
198 type Payload = $req_payload;
199
200 fn method(&self) -> &'static str {
201 $method
202 }
203
204 fn path(&self) -> String {
205 format!("{}/{}", $path, &self.0.$id)
206 }
207
208 fn payload(&self) -> &Self::Payload {
209 &self.0
210 }
211 }
212
213 impl ResponseTrait for [<$struct_name Owned>] {
214 type Payload = $res_payload;
215 }
216
217 impl EventTrait for [<$struct_name Owned>] {}
218 }
219 };
220}
221
222use crate::models::*;
223const GET: &'static str = "GET";
224const PUT: &'static str = "PUT";
225const PATCH: &'static str = "PATCH";
226
227impl_event_traits!(DescribeInstance, GET, "/", Empty, InstanceInfo);
228impl_event_traits!(CreateSyncAction, PUT, "/actions", InstanceActionInfo, Empty);
229impl_event_traits!(DescribeBalloonConfig, GET, "/balloon", Empty, Balloon);
230impl_event_traits!(PutBalloon, PUT, "/balloon", Balloon, Empty);
231impl_event_traits!(PatchBalloon, PATCH, "/balloon", BalloonUpdate, Empty);
232impl_event_traits!(
233 DescribeBalloonStats,
234 GET,
235 "/balloon/statistics",
236 Empty,
237 BalloonStats
238);
239impl_event_traits!(
240 PatchBalloonStatsInterval,
241 PATCH,
242 "/balloon/statistics",
243 BalloonStatsUpdate,
244 Empty
245);
246impl_event_traits!(PutGuestBootSource, PUT, "/boot-source", BootSource, Empty);
247impl_event_traits!(PutCpuConfiguration, PUT, "/cpu-config", CPUConfig, Empty);
248impl_event_traits!(PutGuestDriveByID, PUT, "/drives", drive_id, Drive, Empty);
249impl_event_traits!(
250 PatchGuestDriveByID,
251 PATCH,
252 "/drives",
253 drive_id,
254 PartialDrive,
255 Empty
256);
257impl_event_traits!(PutLogger, PUT, "/logger", Logger, Empty);
258impl_event_traits!(
259 GetMachineConfiguration,
260 GET,
261 "/machine-config",
262 Empty,
263 MachineConfiguration
264);
265impl_event_traits!(
266 PutMachineConfiguration,
267 PUT,
268 "/machine-config",
269 MachineConfiguration,
270 Empty
271);
272impl_event_traits!(
273 PatchMachineConfiguration,
274 PATCH,
275 "/machine-config",
276 MachineConfiguration,
277 Empty
278);
279impl_event_traits!(PutMetrics, PUT, "/metrics", Metrics, Empty);
280impl_event_traits!(PutMmds, PUT, "/mmds", MmdsContentsObject, Empty);
281impl_event_traits!(PatchMmds, PATCH, "/mmds", MmdsContentsObject, Empty);
282impl_event_traits!(GetMmds, GET, "/mmds", Empty, MmdsContentsObject);
283impl_event_traits!(PutMmdsConfig, PUT, "/mmds/config", MmdsConfig, Empty);
284impl_event_traits!(PutEntropyDevice, PUT, "/entropy", EntropyDevice, Empty);
285impl_event_traits!(
286 PutGuestNetworkInterfaceByID,
287 PUT,
288 "/network-interfaces",
289 iface_id,
290 NetworkInterface,
291 Empty
292);
293impl_event_traits!(
294 PatchGuestNetworkInterfaceByID,
295 PATCH,
296 "/network-interfaces",
297 iface_id,
298 PartialNetworkInterface,
299 Empty
300);
301impl_event_traits!(
302 CreateSnapshot,
303 PUT,
304 "/snapshot/create",
305 SnapshotCreateParams,
306 Empty
307);
308impl_event_traits!(
309 LoadSnapshot,
310 PUT,
311 "/snapshot/load",
312 SnapshotLoadParams,
313 Empty
314);
315impl_event_traits!(
316 GetFirecrackerVersion,
317 GET,
318 "/version",
319 Empty,
320 FirecrackerVersion
321);
322impl_event_traits!(PatchVm, PATCH, "/vm", Vm, Empty);
323impl_event_traits!(
324 GetExportVmConfig,
325 GET,
326 "/vm/config",
327 Empty,
328 FullVmConfiguration
329);
330impl_event_traits!(PutGuestVsock, PUT, "/vsock", Vsock, Empty);