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