clawft_kernel/infer_proxy/
wire.rs1use serde::{Deserialize, Serialize};
17
18use super::http::{safe_header_value, validate_path};
19use super::types::{Method, ProxyError, ProxyLimits, ProxyRequest};
20use crate::workload_pkg::manifest::valid_token;
21
22pub const WIRE_VERSION: u8 = 1;
24pub const CHUNK_BYTES: usize = 64 * 1024;
26pub const MAX_ROLE: usize = 64;
28
29#[derive(Serialize, Deserialize)]
30#[serde(deny_unknown_fields)]
31struct RequestHead {
32 role: String,
33 method: String,
34 path: String,
35 #[serde(default)]
36 content_type: Option<String>,
37 #[serde(default)]
38 accept: Option<String>,
39}
40
41pub fn encode_request(req: &ProxyRequest) -> Result<Vec<u8>, ProxyError> {
43 let head = serde_json::to_vec(&RequestHead {
44 role: req.role.clone(),
45 method: req.method.as_str().to_string(),
46 path: req.path.clone(),
47 content_type: req.content_type.clone(),
48 accept: req.accept.clone(),
49 })
50 .map_err(|e| ProxyError::Mesh(e.to_string()))?;
51 let mut out = Vec::with_capacity(5 + head.len() + req.body.len());
52 out.push(WIRE_VERSION);
53 out.extend_from_slice(&(head.len() as u32).to_be_bytes());
54 out.extend_from_slice(&head);
55 out.extend_from_slice(&req.body);
56 Ok(out)
57}
58
59pub fn decode_request(payload: &[u8], limits: &ProxyLimits) -> Result<ProxyRequest, ProxyError> {
61 let bad = |m: &str| ProxyError::BadRequest(m.into());
62 if payload.len() < 5 || payload[0] != WIRE_VERSION {
63 return Err(bad("unsupported infer request version"));
64 }
65 let head_len = u32::from_be_bytes([payload[1], payload[2], payload[3], payload[4]]) as usize;
66 if head_len > limits.max_head_bytes || payload.len() < 5 + head_len {
67 return Err(bad("bad infer request head length"));
68 }
69 let head: RequestHead =
70 serde_json::from_slice(&payload[5..5 + head_len]).map_err(|_| bad("bad infer request head"))?;
71 let body = &payload[5 + head_len..];
72 if body.len() > limits.max_request_body {
73 return Err(ProxyError::TooLarge("request body".into()));
74 }
75 if !valid_token(&head.role, MAX_ROLE) {
76 return Err(bad("bad role"));
77 }
78 let method = match head.method.as_str() {
79 "GET" => Method::Get,
80 "POST" => Method::Post,
81 _ => return Err(ProxyError::MethodNotAllowed),
82 };
83 validate_path(&head.path)?;
84 let clean = |v: Option<String>| -> Result<Option<String>, ProxyError> {
85 match v {
86 None => Ok(None),
87 Some(s) if safe_header_value(&s).is_some() => Ok(Some(s)),
88 Some(_) => Err(bad("bad header value")),
89 }
90 };
91 Ok(ProxyRequest {
92 role: head.role,
93 method,
94 path: head.path,
95 content_type: clean(head.content_type)?,
96 accept: clean(head.accept)?,
97 authorization: None,
98 body: body.to_vec(),
99 })
100}
101
102#[derive(Debug, Clone, PartialEq, Eq)]
104pub enum Resp {
105 Head {
107 status: u16,
109 content_type: Option<String>,
111 },
112 Chunk(Vec<u8>),
114 End,
116 Error(String),
118}
119
120#[derive(Serialize, Deserialize)]
121struct HeadJson {
122 status: u16,
123 #[serde(default)]
124 content_type: Option<String>,
125}
126
127pub fn encode_resp(r: &Resp) -> Vec<u8> {
129 match r {
130 Resp::Head {
131 status,
132 content_type,
133 } => {
134 let mut v = vec![0];
135 v.extend(
136 serde_json::to_vec(&HeadJson {
137 status: *status,
138 content_type: content_type.clone(),
139 })
140 .unwrap_or_default(),
141 );
142 v
143 }
144 Resp::Chunk(d) => {
145 let mut v = Vec::with_capacity(1 + d.len());
146 v.push(1);
147 v.extend_from_slice(d);
148 v
149 }
150 Resp::End => vec![2],
151 Resp::Error(m) => {
152 let mut v = vec![3];
153 v.extend_from_slice(m.as_bytes());
154 v
155 }
156 }
157}
158
159pub fn decode_resp(p: &[u8]) -> Result<Resp, ProxyError> {
161 let bad = |m: &str| ProxyError::Mesh(m.into());
162 let (kind, data) = p.split_first().ok_or_else(|| bad("empty infer response"))?;
163 match kind {
164 0 => {
165 let h: HeadJson = serde_json::from_slice(data).map_err(|_| bad("bad response head"))?;
166 if !(200..=599).contains(&h.status) {
167 return Err(bad("bad response status"));
168 }
169 Ok(Resp::Head {
170 status: h.status,
171 content_type: h
172 .content_type
173 .filter(|c| safe_header_value(c).is_some()),
174 })
175 }
176 1 => Ok(Resp::Chunk(data.to_vec())),
177 2 => Ok(Resp::End),
178 3 => Ok(Resp::Error(
179 String::from_utf8_lossy(&data[..data.len().min(512)]).into_owned(),
180 )),
181 _ => Err(bad("unknown infer response kind")),
182 }
183}