1use ratatui::widgets::TableState;
2use std::collections::HashMap;
3use tokio::sync::{mpsc, oneshot};
4
5#[derive(Debug, Clone)]
6pub struct JsonRpcMessage {
7 pub id: Option<serde_json::Value>,
8 pub method: Option<String>,
9 pub params: Option<serde_json::Value>,
10 pub result: Option<serde_json::Value>,
11 pub error: Option<serde_json::Value>,
12 pub timestamp: std::time::SystemTime,
13 pub direction: MessageDirection,
14 pub transport: TransportType,
15 pub headers: Option<HashMap<String, String>>,
16}
17
18#[derive(Debug, Clone)]
19pub struct JsonRpcExchange {
20 pub id: Option<serde_json::Value>,
21 pub method: Option<String>,
22 pub request: Option<JsonRpcMessage>,
23 pub response: Option<JsonRpcMessage>,
24 #[allow(dead_code)] pub timestamp: std::time::SystemTime,
26 pub transport: TransportType,
27}
28
29#[derive(Debug, Clone)]
30pub enum MessageDirection {
31 Request,
32 Response,
33}
34
35#[derive(Debug, Clone)]
36pub enum TransportType {
37 Http,
38 #[allow(dead_code)] WebSocket,
40}
41
42#[derive(Debug, Clone, PartialEq)]
43pub enum InputMode {
44 Normal,
45 EditingTarget,
46 FilteringRequests,
47}
48
49#[derive(Debug, Clone, PartialEq)]
50pub enum Focus {
51 MessageList,
52 RequestSection,
53 ResponseSection,
54}
55
56#[derive(Debug, Clone, PartialEq)]
57pub enum AppMode {
58 Normal, Paused, Intercepting, }
62
63#[derive(Debug)]
64pub enum ProxyDecision {
65 Allow(Option<serde_json::Value>, Option<HashMap<String, String>>), Block, Complete(serde_json::Value), }
69
70#[allow(dead_code)]
71pub struct PendingRequest {
72 pub id: String,
73 pub original_request: JsonRpcMessage,
74 pub modified_request: Option<String>, pub modified_headers: Option<HashMap<String, String>>, pub decision_sender: oneshot::Sender<ProxyDecision>,
77}
78
79#[allow(dead_code)]
80pub struct App {
81 pub exchanges: Vec<JsonRpcExchange>,
82 pub selected_exchange: usize,
83 pub filter_text: String,
84 pub table_state: TableState,
85 pub details_scroll: usize,
86 pub request_details_scroll: usize,
87 pub response_details_scroll: usize,
88 pub details_tab: usize,
89 pub request_details_tab: usize,
90 pub response_details_tab: usize,
91 pub intercept_details_scroll: usize, pub proxy_config: ProxyConfig,
93 pub is_running: bool,
94 pub message_receiver: Option<mpsc::UnboundedReceiver<JsonRpcMessage>>,
95 pub input_mode: InputMode,
96 pub input_buffer: String,
97 pub app_mode: AppMode, pub pending_requests: Vec<PendingRequest>, pub selected_pending: usize, pub request_editor_buffer: String, pub focus: Focus, pub request_tab: usize, pub response_tab: usize, }
105
106#[derive(Debug)]
107#[allow(dead_code)]
108pub struct ProxyConfig {
109 pub listen_port: u16,
110 pub target_url: String,
111 pub transport: TransportType,
112}
113
114impl Default for App {
115 fn default() -> Self {
116 Self::new()
117 }
118}
119
120#[allow(dead_code)]
121impl App {
122 pub fn new() -> Self {
123 let mut table_state = TableState::default();
124 table_state.select(Some(0));
125
126 Self {
127 exchanges: Vec::new(),
128 selected_exchange: 0,
129 filter_text: String::new(),
130 table_state,
131 details_scroll: 0,
132 request_details_scroll: 0,
133 response_details_scroll: 0,
134 details_tab: 0,
135 request_details_tab: 0,
136 response_details_tab: 0,
137 intercept_details_scroll: 0,
138 proxy_config: ProxyConfig {
139 listen_port: 8080,
140 target_url: "".to_string(),
141 transport: TransportType::Http,
142 },
143 is_running: true,
144 message_receiver: None,
145 input_mode: InputMode::Normal,
146 input_buffer: String::new(),
147 app_mode: AppMode::Normal,
148 pending_requests: Vec::new(),
149 selected_pending: 0,
150 request_editor_buffer: String::new(),
151 focus: Focus::MessageList,
152 request_tab: 1, response_tab: 1, }
155 }
156
157 pub fn new_with_receiver(receiver: mpsc::UnboundedReceiver<JsonRpcMessage>) -> Self {
158 let mut table_state = TableState::default();
159 table_state.select(Some(0));
160
161 Self {
162 exchanges: Vec::new(),
163 selected_exchange: 0,
164 filter_text: String::new(),
165 table_state,
166 details_scroll: 0,
167 request_details_scroll: 0,
168 response_details_scroll: 0,
169 details_tab: 0,
170 request_details_tab: 0,
171 response_details_tab: 0,
172 intercept_details_scroll: 0,
173 proxy_config: ProxyConfig {
174 listen_port: 8080,
175 target_url: "".to_string(),
176 transport: TransportType::Http,
177 },
178 is_running: true,
179 message_receiver: Some(receiver),
180 input_mode: InputMode::Normal,
181 input_buffer: String::new(),
182 app_mode: AppMode::Normal,
183 pending_requests: Vec::new(),
184 selected_pending: 0,
185 request_editor_buffer: String::new(),
186 focus: Focus::MessageList,
187 request_tab: 1, response_tab: 1, }
190 }
191
192 pub fn check_for_new_messages(&mut self) {
193 if let Some(receiver) = &mut self.message_receiver {
194 let mut new_messages = Vec::new();
195 while let Ok(message) = receiver.try_recv() {
196 new_messages.push(message);
197 }
198 for message in new_messages {
199 self.add_message(message);
200 }
201 }
202 }
203
204 pub fn add_message(&mut self, mut message: JsonRpcMessage) {
205 if let Some(ref mut error) = message.error {
207 if let Some(data) = error.get_mut("data") {
208 if let Some(data_str) = data.as_str() {
209 let sanitized = data_str
210 .chars()
211 .filter(|c| c.is_ascii() && (!c.is_control() || *c == '\n' || *c == '\t'))
212 .take(500)
213 .collect::<String>();
214 *data = serde_json::Value::String(sanitized);
215 }
216 }
217 }
218
219 match message.direction {
220 MessageDirection::Request => {
221 let exchange = JsonRpcExchange {
223 id: message.id.clone(),
224 method: message.method.clone(),
225 request: Some(message.clone()),
226 response: None,
227 timestamp: message.timestamp,
228 transport: message.transport.clone(),
229 };
230 self.exchanges.push(exchange);
231 }
232 MessageDirection::Response => {
233 if let Some(exchange) = self
235 .exchanges
236 .iter_mut()
237 .rev()
238 .find(|e| e.id == message.id && e.response.is_none())
239 {
240 exchange.response = Some(message);
241 } else {
242 let exchange = JsonRpcExchange {
244 id: message.id.clone(),
245 method: None,
246 request: None,
247 response: Some(message.clone()),
248 timestamp: message.timestamp,
249 transport: message.transport.clone(),
250 };
251 self.exchanges.push(exchange);
252 }
253 }
254 }
255 }
256
257 pub fn get_selected_exchange(&self) -> Option<&JsonRpcExchange> {
258 self.exchanges.get(self.selected_exchange)
259 }
260
261 pub fn select_next(&mut self) {
262 if !self.exchanges.is_empty() {
263 self.selected_exchange = (self.selected_exchange + 1) % self.exchanges.len();
264 self.table_state.select(Some(self.selected_exchange));
265 self.reset_details_scroll();
266 self.request_details_scroll = 0;
267 self.response_details_scroll = 0;
268 self.details_tab = 0;
269 self.request_details_tab = 0;
270 self.response_details_tab = 0;
271 }
272 }
273
274 pub fn select_previous(&mut self) {
275 if !self.exchanges.is_empty() {
276 self.selected_exchange = if self.selected_exchange == 0 {
277 self.exchanges.len() - 1
278 } else {
279 self.selected_exchange - 1
280 };
281 self.table_state.select(Some(self.selected_exchange));
282 self.reset_details_scroll();
283 self.request_details_scroll = 0;
284 self.response_details_scroll = 0;
285 self.details_tab = 0;
286 self.request_details_tab = 0;
287 self.response_details_tab = 0;
288 }
289 }
290
291 pub fn toggle_proxy(&mut self) {
292 self.is_running = !self.is_running;
293 }
294
295 pub fn scroll_details_up(&mut self) {
296 if self.details_scroll > 0 {
297 self.details_scroll -= 1;
298 }
299 }
300
301 pub fn scroll_details_down(&mut self, max_lines: usize, visible_lines: usize) {
302 if max_lines > visible_lines && self.details_scroll < max_lines - visible_lines {
303 self.details_scroll += 1;
304 }
305 }
306
307 pub fn reset_details_scroll(&mut self) {
308 self.details_scroll = 0;
309 }
310
311 pub fn scroll_intercept_details_up(&mut self) {
313 if self.intercept_details_scroll > 0 {
314 self.intercept_details_scroll -= 1;
315 }
316 }
317
318 pub fn scroll_intercept_details_down(&mut self, max_lines: usize, visible_lines: usize) {
319 if max_lines > visible_lines && self.intercept_details_scroll < max_lines - visible_lines {
320 self.intercept_details_scroll += 1;
321 }
322 }
323
324 pub fn reset_intercept_details_scroll(&mut self) {
325 self.intercept_details_scroll = 0;
326 }
327
328 pub fn page_down_intercept_details(&mut self) {
329 let page_size = 10; self.intercept_details_scroll += page_size;
331 }
332
333 pub fn page_up_intercept_details(&mut self) {
334 let page_size = 10; self.intercept_details_scroll = self.intercept_details_scroll.saturating_sub(page_size);
336 }
337
338 pub fn goto_top_intercept_details(&mut self) {
339 self.intercept_details_scroll = 0;
340 }
341
342 pub fn goto_bottom_intercept_details(&mut self, max_lines: usize, visible_lines: usize) {
343 if max_lines > visible_lines {
344 self.intercept_details_scroll = max_lines - visible_lines;
345 }
346 }
347
348 pub fn page_down_details(&mut self, visible_lines: usize) {
350 let page_size = visible_lines / 2; self.details_scroll += page_size;
352 }
353
354 pub fn page_up_details(&mut self) {
355 let page_size = 10; self.details_scroll = self.details_scroll.saturating_sub(page_size);
357 }
358
359 pub fn goto_top_details(&mut self) {
360 self.details_scroll = 0;
361 }
362
363 pub fn goto_bottom_details(&mut self, max_lines: usize, visible_lines: usize) {
364 if max_lines > visible_lines {
365 self.details_scroll = max_lines - visible_lines;
366 }
367 }
368
369 pub fn switch_focus(&mut self) {
370 self.focus = match self.focus {
371 Focus::MessageList => Focus::RequestSection,
372 Focus::RequestSection => Focus::ResponseSection,
373 Focus::ResponseSection => Focus::MessageList,
374 };
375 self.reset_details_scroll();
376 self.request_details_scroll = 0;
377 self.response_details_scroll = 0;
378 }
379
380 pub fn switch_focus_reverse(&mut self) {
381 self.focus = match self.focus {
382 Focus::MessageList => Focus::ResponseSection,
383 Focus::RequestSection => Focus::MessageList,
384 Focus::ResponseSection => Focus::RequestSection,
385 };
386 self.reset_details_scroll();
387 self.request_details_scroll = 0;
388 self.response_details_scroll = 0;
389 }
390
391 pub fn is_message_list_focused(&self) -> bool {
392 matches!(self.focus, Focus::MessageList)
393 }
394
395 pub fn is_request_section_focused(&self) -> bool {
396 matches!(self.focus, Focus::RequestSection)
397 }
398
399 pub fn is_response_section_focused(&self) -> bool {
400 matches!(self.focus, Focus::ResponseSection)
401 }
402
403 pub fn next_request_tab(&mut self) {
404 self.request_tab = 1 - self.request_tab; self.reset_details_scroll();
406 }
407
408 pub fn previous_request_tab(&mut self) {
409 self.request_tab = 1 - self.request_tab; self.reset_details_scroll();
411 }
412
413 pub fn next_response_tab(&mut self) {
414 self.response_tab = 1 - self.response_tab; self.reset_details_scroll();
416 }
417
418 pub fn previous_response_tab(&mut self) {
419 self.response_tab = 1 - self.response_tab; self.reset_details_scroll();
421 }
422
423 pub fn start_filtering_requests(&mut self) {
425 self.input_mode = InputMode::FilteringRequests;
426 self.input_buffer.clear();
427 }
428
429 pub fn cancel_filtering(&mut self) {
430 self.input_mode = InputMode::Normal;
431 self.input_buffer.clear();
432 }
433
434 pub fn apply_filter(&mut self) {
435 self.filter_text = self.input_buffer.clone();
436 self.input_mode = InputMode::Normal;
437 self.input_buffer.clear();
438 }
439
440 pub fn start_editing_target(&mut self) {
443 self.input_mode = InputMode::EditingTarget;
444 self.input_buffer.clear();
445 }
446
447 pub fn cancel_editing(&mut self) {
448 self.input_mode = InputMode::Normal;
449 self.input_buffer.clear();
450 }
451
452 pub fn confirm_target_edit(&mut self) {
453 if !self.input_buffer.trim().is_empty() {
454 self.proxy_config.target_url = self.input_buffer.trim().to_string();
455 }
456 self.input_mode = InputMode::Normal;
457 self.input_buffer.clear();
458 }
459
460 pub fn handle_input_char(&mut self, c: char) {
461 if self.input_mode == InputMode::EditingTarget
462 || self.input_mode == InputMode::FilteringRequests
463 {
464 self.input_buffer.push(c);
465 }
466 }
467
468 pub fn handle_backspace(&mut self) {
469 if self.input_mode == InputMode::EditingTarget
470 || self.input_mode == InputMode::FilteringRequests
471 {
472 self.input_buffer.pop();
473 }
474 }
475
476 pub fn get_details_content_lines(&self) -> usize {
477 if let Some(exchange) = self.get_selected_exchange() {
478 let mut line_count = 1; if exchange.method.is_some() {
481 line_count += 1;
482 }
483 if exchange.id.is_some() {
484 line_count += 1;
485 }
486
487 line_count += 1; line_count += 1; line_count += 1; if let Some(request) = &exchange.request {
493 match self.request_details_tab {
494 0 => match &request.headers {
495 Some(headers) if !headers.is_empty() => {
496 line_count += headers.len();
497 }
498 Some(_) | None => {
499 line_count += 1;
500 }
501 },
502 _ => {
503 let mut request_json = serde_json::Map::new();
504 request_json.insert(
505 "jsonrpc".to_string(),
506 serde_json::Value::String("2.0".to_string()),
507 );
508 if let Some(id) = &request.id {
509 request_json.insert("id".to_string(), id.clone());
510 }
511 if let Some(method) = &request.method {
512 request_json.insert(
513 "method".to_string(),
514 serde_json::Value::String(method.clone()),
515 );
516 }
517 if let Some(params) = &request.params {
518 request_json.insert("params".to_string(), params.clone());
519 }
520
521 if let Ok(json_str) =
522 serde_json::to_string_pretty(&serde_json::Value::Object(request_json))
523 {
524 line_count += json_str.lines().count();
525 }
526 }
527 }
528 } else {
529 line_count += 1;
530 }
531
532 line_count += 1; line_count += 1; line_count += 1; if let Some(response) = &exchange.response {
538 match self.response_details_tab {
539 0 => match &response.headers {
540 Some(headers) if !headers.is_empty() => {
541 line_count += headers.len();
542 }
543 Some(_) | None => {
544 line_count += 1;
545 }
546 },
547 _ => {
548 let mut response_json = serde_json::Map::new();
549 response_json.insert(
550 "jsonrpc".to_string(),
551 serde_json::Value::String("2.0".to_string()),
552 );
553 if let Some(id) = &response.id {
554 response_json.insert("id".to_string(), id.clone());
555 }
556 if let Some(result) = &response.result {
557 response_json.insert("result".to_string(), result.clone());
558 }
559 if let Some(error) = &response.error {
560 response_json.insert("error".to_string(), error.clone());
561 }
562
563 if let Ok(json_str) =
564 serde_json::to_string_pretty(&serde_json::Value::Object(response_json))
565 {
566 line_count += json_str.lines().count();
567 }
568 }
569 }
570 } else {
571 line_count += 1;
572 }
573
574 line_count
575 } else {
576 1
577 }
578 }
579
580 pub fn get_request_details_content_lines(&self) -> usize {
581 if let Some(exchange) = self.get_selected_exchange() {
582 let mut line_count = 0;
583
584 line_count += 1; if exchange.method.is_some() {
588 line_count += 1;
589 }
590 if exchange.id.is_some() {
591 line_count += 1;
592 }
593
594 line_count += 1; line_count += 1; line_count += 1; if let Some(request) = &exchange.request {
600 match self.request_details_tab {
601 0 => match &request.headers {
602 Some(headers) if !headers.is_empty() => {
603 line_count += headers.len();
604 }
605 Some(_) | None => {
606 line_count += 1;
607 }
608 },
609 _ => {
610 let mut request_json = serde_json::Map::new();
611 request_json.insert(
612 "jsonrpc".to_string(),
613 serde_json::Value::String("2.0".to_string()),
614 );
615 if let Some(id) = &request.id {
616 request_json.insert("id".to_string(), id.clone());
617 }
618 if let Some(method) = &request.method {
619 request_json.insert(
620 "method".to_string(),
621 serde_json::Value::String(method.clone()),
622 );
623 }
624 if let Some(params) = &request.params {
625 request_json.insert("params".to_string(), params.clone());
626 }
627
628 if let Ok(json_str) =
629 serde_json::to_string_pretty(&serde_json::Value::Object(request_json))
630 {
631 line_count += json_str.lines().count();
632 }
633 }
634 }
635 } else {
636 line_count += 1;
637 }
638
639 line_count
640 } else {
641 1
642 }
643 }
644
645 pub fn get_response_details_content_lines(&self) -> usize {
646 if let Some(exchange) = self.get_selected_exchange() {
647 let mut line_count = 0;
648
649 line_count += 1; line_count += 1; if let Some(response) = &exchange.response {
654 match self.response_details_tab {
655 0 => match &response.headers {
656 Some(headers) if !headers.is_empty() => {
657 line_count += headers.len();
658 }
659 Some(_) | None => {
660 line_count += 1;
661 }
662 },
663 _ => {
664 let mut response_json = serde_json::Map::new();
665 response_json.insert(
666 "jsonrpc".to_string(),
667 serde_json::Value::String("2.0".to_string()),
668 );
669 if let Some(id) = &response.id {
670 response_json.insert("id".to_string(), id.clone());
671 }
672 if let Some(result) = &response.result {
673 response_json.insert("result".to_string(), result.clone());
674 }
675 if let Some(error) = &response.error {
676 response_json.insert("error".to_string(), error.clone());
677 }
678
679 if let Ok(json_str) =
680 serde_json::to_string_pretty(&serde_json::Value::Object(response_json))
681 {
682 line_count += json_str.lines().count();
683 }
684 }
685 }
686 } else {
687 line_count += 1;
688 }
689
690 line_count
691 } else {
692 1
693 }
694 }
695
696 pub fn toggle_pause_mode(&mut self) {
698 self.app_mode = match self.app_mode {
699 AppMode::Normal => AppMode::Paused,
700 AppMode::Paused => AppMode::Normal,
701 AppMode::Intercepting => AppMode::Normal,
702 };
703 }
704
705 pub fn select_next_pending(&mut self) {
706 if !self.pending_requests.is_empty() {
707 self.selected_pending = (self.selected_pending + 1) % self.pending_requests.len();
708 self.reset_intercept_details_scroll();
709 }
710 }
711
712 pub fn select_previous_pending(&mut self) {
713 if !self.pending_requests.is_empty() {
714 self.selected_pending = if self.selected_pending == 0 {
715 self.pending_requests.len() - 1
716 } else {
717 self.selected_pending - 1
718 };
719 self.reset_intercept_details_scroll();
720 }
721 }
722
723 pub fn get_selected_pending(&self) -> Option<&PendingRequest> {
724 self.pending_requests.get(self.selected_pending)
725 }
726
727 pub fn allow_selected_request(&mut self) {
728 if self.selected_pending < self.pending_requests.len() {
729 let pending = self.pending_requests.remove(self.selected_pending);
730 if self.selected_pending > 0 && self.selected_pending >= self.pending_requests.len() {
731 self.selected_pending -= 1;
732 }
733
734 let decision = if let Some(ref modified_json) = pending.modified_request {
736 if let Ok(parsed) = serde_json::from_str::<serde_json::Value>(modified_json) {
737 ProxyDecision::Allow(Some(parsed), pending.modified_headers.clone())
738 } else {
739 ProxyDecision::Allow(None, pending.modified_headers.clone())
740 }
742 } else {
743 ProxyDecision::Allow(None, pending.modified_headers.clone()) };
745
746 let _ = pending.decision_sender.send(decision);
747 }
748 }
749
750 pub fn block_selected_request(&mut self) {
751 if self.selected_pending < self.pending_requests.len() {
752 let pending = self.pending_requests.remove(self.selected_pending);
753 if self.selected_pending > 0 && self.selected_pending >= self.pending_requests.len() {
754 self.selected_pending -= 1;
755 }
756
757 let _ = pending.decision_sender.send(ProxyDecision::Block);
759 }
760 }
761
762 pub fn resume_all_requests(&mut self) {
763 for pending in self.pending_requests.drain(..) {
764 let _ = pending
765 .decision_sender
766 .send(ProxyDecision::Allow(None, None));
767 }
768 self.selected_pending = 0;
769 self.app_mode = AppMode::Normal;
770 }
771
772 pub fn get_pending_request_json(&self) -> Option<String> {
773 if let Some(pending) = self.get_selected_pending() {
774 let json_value = serde_json::json!({
776 "jsonrpc": "2.0",
777 "method": pending.original_request.method,
778 "params": pending.original_request.params,
779 "id": pending.original_request.id
780 });
781
782 serde_json::to_string_pretty(&json_value).ok()
784 } else {
785 None
786 }
787 }
788
789 pub fn apply_edited_json(&mut self, edited_json: String) -> Result<(), String> {
790 if self.selected_pending >= self.pending_requests.len() {
791 return Err("No pending request selected".to_string());
792 }
793
794 let parsed: serde_json::Value =
796 serde_json::from_str(&edited_json).map_err(|e| format!("Invalid JSON: {}", e))?;
797
798 if parsed.get("jsonrpc") != Some(&serde_json::Value::String("2.0".to_string())) {
800 return Err("Missing or invalid 'jsonrpc' field".to_string());
801 }
802
803 if parsed.get("method").is_none() {
804 return Err("Missing 'method' field".to_string());
805 }
806
807 self.pending_requests[self.selected_pending].modified_request = Some(edited_json);
809
810 Ok(())
811 }
812
813 pub fn get_pending_request_headers(&self) -> Option<String> {
814 if let Some(pending) = self.get_selected_pending() {
815 let headers = pending
817 .modified_headers
818 .as_ref()
819 .or(pending.original_request.headers.as_ref());
820
821 if let Some(headers) = headers {
822 let mut header_lines = Vec::new();
824 for (key, value) in headers {
825 header_lines.push(format!("{}: {}", key, value));
826 }
827 Some(header_lines.join("\n"))
828 } else {
829 Some(
830 "# No headers\n# Add headers in the format:\n# header-name: header-value"
831 .to_string(),
832 )
833 }
834 } else {
835 None
836 }
837 }
838
839 pub fn apply_edited_headers(&mut self, edited_headers: String) -> Result<(), String> {
840 if self.selected_pending >= self.pending_requests.len() {
841 return Err("No pending request selected".to_string());
842 }
843
844 let mut headers = HashMap::new();
845
846 for line in edited_headers.lines() {
847 let line = line.trim();
848
849 if line.is_empty() || line.starts_with('#') {
851 continue;
852 }
853
854 if let Some(colon_pos) = line.find(':') {
856 let key = line[..colon_pos].trim().to_string();
857 let value = line[colon_pos + 1..].trim().to_string();
858
859 if !key.is_empty() {
860 headers.insert(key, value);
861 }
862 } else {
863 return Err(format!(
864 "Invalid header format: '{}'. Use 'key: value' format.",
865 line
866 ));
867 }
868 }
869
870 self.pending_requests[self.selected_pending].modified_headers = Some(headers);
872
873 Ok(())
874 }
875
876 pub fn get_pending_response_template(&self) -> Option<String> {
877 if let Some(pending) = self.get_selected_pending() {
878 let response_template = serde_json::json!({
880 "jsonrpc": "2.0",
881 "id": pending.original_request.id,
882 "result": "custom response"
883 });
884
885 serde_json::to_string_pretty(&response_template).ok()
887 } else {
888 None
889 }
890 }
891
892 pub fn complete_selected_request(&mut self, response_json: String) -> Result<(), String> {
893 if self.selected_pending >= self.pending_requests.len() {
894 return Err("No pending request selected".to_string());
895 }
896
897 let parsed: serde_json::Value =
899 serde_json::from_str(&response_json).map_err(|e| format!("Invalid JSON: {}", e))?;
900
901 if parsed.get("jsonrpc") != Some(&serde_json::Value::String("2.0".to_string())) {
903 return Err("Missing or invalid 'jsonrpc' field".to_string());
904 }
905
906 if parsed.get("id").is_none() {
907 return Err("Missing 'id' field".to_string());
908 }
909
910 let has_result = parsed.get("result").is_some();
912 let has_error = parsed.get("error").is_some();
913
914 if !has_result && !has_error {
915 return Err("Response must have either 'result' or 'error' field".to_string());
916 }
917
918 if has_result && has_error {
919 return Err("Response cannot have both 'result' and 'error' fields".to_string());
920 }
921
922 let pending = self.pending_requests.remove(self.selected_pending);
924 if self.selected_pending > 0 && self.selected_pending >= self.pending_requests.len() {
925 self.selected_pending -= 1;
926 }
927
928 let _ = pending
929 .decision_sender
930 .send(ProxyDecision::Complete(parsed));
931
932 Ok(())
933 }
934
935 pub async fn send_new_request(&self, request_json: String) -> Result<(), String> {
936 let parsed: serde_json::Value =
938 serde_json::from_str(&request_json).map_err(|e| format!("Invalid JSON: {}", e))?;
939
940 if parsed.get("jsonrpc") != Some(&serde_json::Value::String("2.0".to_string())) {
942 return Err("Missing or invalid 'jsonrpc' field".to_string());
943 }
944
945 if parsed.get("method").is_none() {
946 return Err("Missing 'method' field".to_string());
947 }
948
949 if self.proxy_config.target_url.trim().is_empty() {
951 return Err("Target URL is not set. Press 't' to set a target URL first.".to_string());
952 }
953
954 let client = reqwest::Client::new();
955
956 let url = if matches!(self.app_mode, AppMode::Paused | AppMode::Intercepting) {
959 &self.proxy_config.target_url
960 } else {
961 &format!("http://localhost:{}", self.proxy_config.listen_port)
963 };
964
965 let response = client
966 .post(url)
967 .header("Content-Type", "application/json")
968 .body(request_json)
969 .send()
970 .await
971 .map_err(|e| format!("Failed to send request: {}", e))?;
972
973 if !response.status().is_success() {
974 return Err(format!("Request failed with status: {}", response.status()));
975 }
976
977 Ok(())
978 }
979}