rivet_envoy_client/
http.rs1use std::collections::{HashMap, VecDeque};
2use std::sync::Arc;
3
4use anyhow::{Context, Result as AnyhowResult, ensure};
5use parking_lot::Mutex as SyncMutex;
6use rivet_envoy_protocol as protocol;
7use tokio::sync::{Mutex, Notify, mpsc};
8
9pub const HTTP_BODY_STREAM_CHANNEL_CAPACITY: usize = 16;
10pub const HTTP_BODY_MAX_CHUNK_SIZE: usize = 64 * 1024;
11
12#[derive(Clone, Debug)]
13pub struct HttpRequestBodyError {
14 pub reason: protocol::HttpStreamAbortReason,
15}
16
17impl std::fmt::Display for HttpRequestBodyError {
18 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
19 match &self.reason.detail {
20 Some(detail) => write!(f, "{:?}: {detail}", self.reason.kind),
21 None => write!(f, "{:?}", self.reason.kind),
22 }
23 }
24}
25
26impl std::error::Error for HttpRequestBodyError {}
27
28#[derive(Debug)]
29pub struct HttpRequestBodyStream {
30 queue: Arc<HttpRequestBodyQueue>,
31 event_tx: Option<mpsc::UnboundedSender<RequestBodyEvent>>,
32 terminal: bool,
33}
34
35#[derive(Debug)]
36struct HttpRequestBodyQueueState {
37 chunks: VecDeque<Vec<u8>>,
38 finished: bool,
39 abort: Option<HttpRequestBodyError>,
40 receiver_closed: bool,
41}
42
43#[derive(Debug)]
47pub struct HttpRequestBodyQueue {
48 state: SyncMutex<HttpRequestBodyQueueState>,
49 ready: Notify,
50}
51
52impl HttpRequestBodyQueue {
53 pub fn new() -> Arc<Self> {
54 Arc::new(Self {
55 state: SyncMutex::new(HttpRequestBodyQueueState {
56 chunks: VecDeque::new(),
57 finished: false,
58 abort: None,
59 receiver_closed: false,
60 }),
61 ready: Notify::new(),
62 })
63 }
64
65 pub fn push(&self, data: impl AsRef<[u8]>) -> bool {
67 let mut data = data.as_ref();
68 let mut state = self.state.lock();
69 if state.finished || state.abort.is_some() || state.receiver_closed {
70 return false;
71 }
72
73 if let Some(last) = state.chunks.back_mut() {
74 let available = HTTP_BODY_MAX_CHUNK_SIZE.saturating_sub(last.len());
75 let append = available.min(data.len());
76 last.extend_from_slice(&data[..append]);
77 data = &data[append..];
78 }
79 for chunk in data.chunks(HTTP_BODY_MAX_CHUNK_SIZE) {
80 state.chunks.push_back(chunk.to_vec());
81 }
82 drop(state);
83 self.ready.notify_one();
84 true
85 }
86
87 pub fn finish(&self) {
88 let mut state = self.state.lock();
89 if state.abort.is_none() {
90 state.finished = true;
91 }
92 drop(state);
93 self.ready.notify_waiters();
94 }
95
96 pub fn abort(&self, error: HttpRequestBodyError) {
97 let mut state = self.state.lock();
98 if state.abort.is_none() && !state.finished {
99 state.chunks.clear();
100 state.abort = Some(error);
101 }
102 drop(state);
103 self.ready.notify_waiters();
104 }
105
106 fn close_receiver(&self) {
107 let mut state = self.state.lock();
108 state.receiver_closed = true;
109 state.chunks.clear();
110 drop(state);
111 self.ready.notify_waiters();
112 }
113
114 async fn recv(&self) -> Result<Option<Vec<u8>>, HttpRequestBodyError> {
115 loop {
116 let notified = self.ready.notified();
117 {
118 let mut state = self.state.lock();
119 if let Some(error) = &state.abort {
120 return Err(error.clone());
121 }
122 if let Some(chunk) = state.chunks.pop_front() {
123 return Ok(Some(chunk));
124 }
125 if state.finished || state.receiver_closed {
126 return Ok(None);
127 }
128 }
129 notified.await;
130 }
131 }
132}
133
134#[derive(Debug)]
135pub(crate) enum RequestBodyEvent {
136 Consumed(u64),
137 Cancelled,
138}
139
140impl HttpRequestBodyStream {
141 fn handle_chunk(
142 &mut self,
143 chunk: Option<Vec<u8>>,
144 ) -> Result<Option<Vec<u8>>, HttpRequestBodyError> {
145 match chunk {
146 Some(chunk) => {
147 if let Some(event_tx) = &self.event_tx {
148 let _ = event_tx.send(RequestBodyEvent::Consumed(chunk.len() as u64));
149 }
150 Ok(Some(chunk))
151 }
152 None => {
153 self.terminal = true;
154 Ok(None)
155 }
156 }
157 }
158
159 pub fn new(queue: Arc<HttpRequestBodyQueue>) -> Self {
161 Self {
162 queue,
163 event_tx: None,
164 terminal: false,
165 }
166 }
167
168 pub(crate) fn new_with_flow_control(
169 queue: Arc<HttpRequestBodyQueue>,
170 event_tx: mpsc::UnboundedSender<RequestBodyEvent>,
171 ) -> Self {
172 Self {
173 queue,
174 event_tx: Some(event_tx),
175 terminal: false,
176 }
177 }
178
179 pub async fn recv(&mut self) -> Result<Option<Vec<u8>>, HttpRequestBodyError> {
180 let chunk = match self.queue.recv().await {
181 Ok(chunk) => chunk,
182 Err(error) => {
183 self.terminal = true;
184 return Err(error);
185 }
186 };
187 self.handle_chunk(chunk)
188 }
189}
190
191impl Drop for HttpRequestBodyStream {
192 fn drop(&mut self) {
193 self.queue.close_receiver();
194 if !self.terminal
195 && let Some(event_tx) = &self.event_tx
196 {
197 let _ = event_tx.send(RequestBodyEvent::Cancelled);
198 }
199 }
200}
201
202#[derive(Debug)]
203struct HttpBodySendWindowState {
204 sent_bytes: u64,
205 consumed_bytes: u64,
206}
207
208#[derive(Debug)]
209pub(crate) struct HttpBodySendWindow {
210 state: Mutex<HttpBodySendWindowState>,
211 credit_available: Notify,
212}
213
214impl HttpBodySendWindow {
215 pub(crate) fn new() -> Arc<Self> {
216 Arc::new(Self {
217 state: Mutex::new(HttpBodySendWindowState {
218 sent_bytes: 0,
219 consumed_bytes: 0,
220 }),
221 credit_available: Notify::new(),
222 })
223 }
224
225 pub(crate) async fn reserve(&self, bytes: u64) -> AnyhowResult<()> {
226 ensure!(
227 bytes <= protocol::HTTP_STREAM_INITIAL_WINDOW_BYTES,
228 "HTTP body frame exceeds the flow-control window"
229 );
230 if bytes == 0 {
231 return Ok(());
232 }
233
234 loop {
235 let notified = self.credit_available.notified();
236 {
237 let mut state = self.state.lock().await;
238 let outstanding = state
239 .sent_bytes
240 .checked_sub(state.consumed_bytes)
241 .context("HTTP body flow-control accounting underflow")?;
242 let available = protocol::HTTP_STREAM_INITIAL_WINDOW_BYTES
243 .checked_sub(outstanding)
244 .context("HTTP body flow-control window exceeded")?;
245 if bytes <= available {
246 state.sent_bytes = state
247 .sent_bytes
248 .checked_add(bytes)
249 .context("HTTP body sent-byte counter overflow")?;
250 return Ok(());
251 }
252 }
253 notified.await;
254 }
255 }
256
257 pub(crate) async fn update_consumed(&self, consumed_bytes: u64) -> AnyhowResult<()> {
258 let mut state = self.state.lock().await;
259 ensure!(
260 consumed_bytes >= state.consumed_bytes,
261 "HTTP body consumed-byte counter moved backwards"
262 );
263 ensure!(
264 consumed_bytes <= state.sent_bytes,
265 "HTTP body consumed-byte counter exceeds sent bytes"
266 );
267 if consumed_bytes == state.consumed_bytes {
268 return Ok(());
269 }
270 state.consumed_bytes = consumed_bytes;
271 drop(state);
272 self.credit_available.notify_waiters();
273 Ok(())
274 }
275}
276
277pub struct HttpRequest {
279 pub method: String,
280 pub path: String,
281 pub headers: HashMap<String, String>,
282 pub body: Option<Vec<u8>>,
283 pub body_stream: Option<HttpRequestBodyStream>,
285}
286
287pub struct HttpResponse {
288 pub status: u16,
289 pub headers: HashMap<String, String>,
290 pub body: Option<Vec<u8>>,
291 pub body_stream: Option<HttpResponseBodyStream>,
294}
295
296pub enum ResponseChunk {
298 Data { data: Vec<u8>, finish: bool },
299 Error(String),
300}
301
302pub struct HttpResponseBodyStream {
303 rx: mpsc::Receiver<ResponseChunk>,
304 on_drop: Option<Box<dyn FnOnce() + Send>>,
305}
306
307impl HttpResponseBodyStream {
308 pub fn set_on_drop(&mut self, on_drop: impl FnOnce() + Send + 'static) {
309 self.on_drop = Some(Box::new(on_drop));
310 }
311
312 pub async fn recv(&mut self) -> Option<ResponseChunk> {
313 self.rx.recv().await
314 }
315}
316
317impl From<mpsc::Receiver<ResponseChunk>> for HttpResponseBodyStream {
318 fn from(rx: mpsc::Receiver<ResponseChunk>) -> Self {
319 Self { rx, on_drop: None }
320 }
321}
322
323impl Drop for HttpResponseBodyStream {
324 fn drop(&mut self) {
325 if let Some(on_drop) = self.on_drop.take() {
326 on_drop();
327 }
328 }
329}
330
331