union_square/domain/commands/
audit_buffer.rs1use crate::proxy::types::{ChunkOffset, RequestId};
7use std::collections::HashMap;
8
9#[derive(Debug, Clone)]
11pub struct BufferedData {
12 chunks: Vec<(ChunkOffset, Vec<u8>)>,
13 total_size: usize,
14}
15
16impl BufferedData {
17 fn new() -> Self {
18 Self {
19 chunks: Vec::new(),
20 total_size: 0,
21 }
22 }
23
24 pub fn add_chunk(&mut self, offset: ChunkOffset, data: Vec<u8>) {
26 self.total_size = (*offset.as_ref() + data.len()).max(self.total_size);
27 self.chunks.push((offset, data));
28 }
29
30 pub fn set_total_size(&mut self, size: usize) {
32 self.total_size = size;
33 }
34
35 pub fn is_complete(&self) -> bool {
37 if self.chunks.is_empty() {
38 return false;
39 }
40
41 if self.total_size == 0 {
43 return false;
44 }
45
46 let mut sorted_chunks = self.chunks.clone();
48 sorted_chunks.sort_by_key(|(offset, _)| *offset.as_ref());
49
50 let mut current_pos = 0;
52 for (offset, data) in &sorted_chunks {
53 if *offset.as_ref() != current_pos {
54 return false; }
56 current_pos += data.len();
57 }
58
59 current_pos == self.total_size
60 }
61
62 pub fn reconstruct(&self) -> Option<Vec<u8>> {
64 if !self.is_complete() {
65 return None;
66 }
67
68 let mut sorted_chunks = self.chunks.clone();
69 sorted_chunks.sort_by_key(|(offset, _)| *offset.as_ref());
70
71 let mut result = Vec::with_capacity(self.total_size);
72 for (_, data) in sorted_chunks {
73 result.extend_from_slice(&data);
74 }
75
76 Some(result)
77 }
78}
79
80pub struct AuditBufferManager {
82 request_buffers: HashMap<RequestId, BufferedData>,
83 response_buffers: HashMap<RequestId, BufferedData>,
84}
85
86impl AuditBufferManager {
87 pub fn new() -> Self {
88 Self {
89 request_buffers: HashMap::new(),
90 response_buffers: HashMap::new(),
91 }
92 }
93
94 pub fn add_request_chunk(&mut self, request_id: RequestId, offset: ChunkOffset, data: Vec<u8>) {
96 self.request_buffers
97 .entry(request_id)
98 .or_insert_with(BufferedData::new)
99 .add_chunk(offset, data);
100 }
101
102 pub fn add_response_chunk(
104 &mut self,
105 request_id: RequestId,
106 offset: ChunkOffset,
107 data: Vec<u8>,
108 ) {
109 self.response_buffers
110 .entry(request_id)
111 .or_insert_with(BufferedData::new)
112 .add_chunk(offset, data);
113 }
114
115 pub fn get_complete_request_body(&mut self, request_id: &RequestId) -> Option<Vec<u8>> {
117 if let Some(buffer) = self.request_buffers.get(request_id) {
118 if let Some(data) = buffer.reconstruct() {
119 self.request_buffers.remove(request_id);
121 return Some(data);
122 }
123 }
124 None
125 }
126
127 pub fn get_complete_response_body(&mut self, request_id: &RequestId) -> Option<Vec<u8>> {
129 if let Some(buffer) = self.response_buffers.get(request_id) {
130 if let Some(data) = buffer.reconstruct() {
131 self.response_buffers.remove(request_id);
133 return Some(data);
134 }
135 }
136 None
137 }
138
139 pub fn cleanup_request(&mut self, request_id: &RequestId) {
141 self.request_buffers.remove(request_id);
142 self.response_buffers.remove(request_id);
143 }
144}
145
146impl Default for AuditBufferManager {
147 fn default() -> Self {
148 Self::new()
149 }
150}
151
152#[cfg(test)]
153mod tests {
154 use super::*;
155
156 #[test]
157 fn test_buffered_data_complete() {
158 let mut buffer = BufferedData::new();
159
160 buffer.add_chunk(ChunkOffset::from(0), vec![1, 2, 3]);
162 buffer.add_chunk(ChunkOffset::from(3), vec![4, 5, 6]);
163
164 assert!(buffer.is_complete());
165 assert_eq!(buffer.reconstruct(), Some(vec![1, 2, 3, 4, 5, 6]));
166 }
167
168 #[test]
169 fn test_buffered_data_with_gap() {
170 let mut buffer = BufferedData::new();
171
172 buffer.add_chunk(ChunkOffset::from(0), vec![1, 2, 3]);
174 buffer.add_chunk(ChunkOffset::from(6), vec![7, 8, 9]); assert!(!buffer.is_complete());
177 assert_eq!(buffer.reconstruct(), None);
178 }
179
180 #[test]
181 fn test_buffered_data_out_of_order() {
182 let mut buffer = BufferedData::new();
183
184 buffer.add_chunk(ChunkOffset::from(3), vec![4, 5, 6]);
186 buffer.add_chunk(ChunkOffset::from(0), vec![1, 2, 3]);
187
188 assert!(buffer.is_complete());
189 assert_eq!(buffer.reconstruct(), Some(vec![1, 2, 3, 4, 5, 6]));
190 }
191
192 #[test]
193 fn test_audit_buffer_manager() {
194 let mut manager = AuditBufferManager::new();
195 let request_id = RequestId::new();
196
197 manager.add_request_chunk(request_id, ChunkOffset::from(0), vec![1, 2, 3]);
200 manager.add_response_chunk(request_id, ChunkOffset::from(0), vec![4, 5, 6]);
201
202 manager.cleanup_request(&request_id);
204
205 assert!(manager.get_complete_request_body(&request_id).is_none());
207 assert!(manager.get_complete_response_body(&request_id).is_none());
208 }
209}