1use crate::error::Result;
9use crate::protocol::Request;
10use crate::protocol::api_request_context::{APIRequestContext, InnerFetchOptions};
11use crate::server::channel_owner::{ChannelOwner, ChannelOwnerImpl, ParentOrConnection};
12use crate::server::connection::downcast_parent;
13use serde_json::{Value, json};
14use std::any::Any;
15use std::sync::atomic::{AtomicBool, Ordering};
16use std::sync::{Arc, Mutex};
17
18#[derive(Clone)]
24pub struct Route {
25 base: ChannelOwnerImpl,
26 handled: Arc<AtomicBool>,
29 api_request_context: Arc<Mutex<Option<APIRequestContext>>>,
32}
33
34impl Route {
35 pub fn new(
40 parent: Arc<dyn ChannelOwner>,
41 type_name: String,
42 guid: Arc<str>,
43 initializer: Value,
44 ) -> Result<Self> {
45 let base = ChannelOwnerImpl::new(
46 ParentOrConnection::Parent(parent.clone()),
47 type_name,
48 guid,
49 initializer,
50 );
51
52 Ok(Self {
53 base,
54 handled: Arc::new(AtomicBool::new(false)),
55 api_request_context: Arc::new(Mutex::new(None)),
56 })
57 }
58
59 pub(crate) fn was_handled(&self) -> bool {
64 self.handled.load(Ordering::SeqCst)
65 }
66
67 pub(crate) fn set_api_request_context(&self, ctx: APIRequestContext) {
72 *self.api_request_context.lock().unwrap() = Some(ctx);
73 }
74
75 pub fn request(&self) -> Request {
79 if let Some(request) = downcast_parent::<Request>(self) {
81 return request;
82 }
83
84 let request_data = self
87 .initializer()
88 .get("request")
89 .cloned()
90 .unwrap_or_else(|| {
91 serde_json::json!({
92 "url": "",
93 "method": "GET"
94 })
95 });
96
97 let parent = self
98 .parent()
99 .unwrap_or_else(|| Arc::new(self.clone()) as Arc<dyn ChannelOwner>);
100
101 let request_guid = request_data
102 .get("guid")
103 .and_then(|v| v.as_str())
104 .unwrap_or("request-stub");
105
106 Request::new(
107 parent,
108 "Request".to_string(),
109 Arc::from(request_guid),
110 request_data,
111 )
112 .unwrap()
113 }
114
115 pub async fn abort(&self, error_code: Option<&str>) -> Result<()> {
134 self.handled.store(true, Ordering::SeqCst);
135 let params = json!({
136 "errorCode": error_code.unwrap_or("failed")
137 });
138
139 self.channel()
140 .send::<_, serde_json::Value>("abort", params)
141 .await
142 .map(|_| ())
143 }
144
145 pub async fn continue_(&self, overrides: Option<ContinueOptions>) -> Result<()> {
156 self.handled.store(true, Ordering::SeqCst);
157 self.continue_internal(overrides, false).await
158 }
159
160 pub async fn fallback(&self, overrides: Option<ContinueOptions>) -> Result<()> {
172 self.continue_internal(overrides, true).await
174 }
175
176 async fn continue_internal(
178 &self,
179 overrides: Option<ContinueOptions>,
180 is_fallback: bool,
181 ) -> Result<()> {
182 let mut params = json!({
183 "isFallback": is_fallback
184 });
185
186 if let Some(opts) = overrides {
188 if let Some(headers) = opts.headers {
190 let headers_array: Vec<serde_json::Value> = headers
191 .into_iter()
192 .map(|(name, value)| json!({"name": name, "value": value}))
193 .collect();
194 params["headers"] = json!(headers_array);
195 }
196
197 if let Some(method) = opts.method {
199 params["method"] = json!(method);
200 }
201
202 if let Some(post_data) = opts.post_data {
204 params["postData"] = json!(post_data);
205 } else if let Some(post_data_bytes) = opts.post_data_bytes {
206 use base64::Engine;
207 let encoded = base64::engine::general_purpose::STANDARD.encode(&post_data_bytes);
208 params["postData"] = json!(encoded);
209 }
210
211 if let Some(url) = opts.url {
213 params["url"] = json!(url);
214 }
215 }
216
217 self.channel()
218 .send::<_, serde_json::Value>("continue", params)
219 .await
220 .map(|_| ())
221 }
222
223 pub async fn fulfill(&self, options: Option<FulfillOptions>) -> Result<()> {
252 self.handled.store(true, Ordering::SeqCst);
253 let opts = options.unwrap_or_default();
254
255 let mut response = json!({
257 "status": opts.status.unwrap_or(200),
258 "headers": []
259 });
260
261 let mut headers_map = opts.headers.unwrap_or_default();
263
264 let body_bytes = opts.body.as_ref();
266 if let Some(body) = body_bytes {
267 let content_length = body.len().to_string();
268 headers_map.insert("content-length".to_string(), content_length);
269 }
270
271 if let Some(ref ct) = opts.content_type {
273 headers_map.insert("content-type".to_string(), ct.clone());
274 }
275
276 let headers_array: Vec<Value> = headers_map
278 .into_iter()
279 .map(|(name, value)| json!({"name": name, "value": value}))
280 .collect();
281 response["headers"] = json!(headers_array);
282
283 if let Some(body) = body_bytes {
285 if let Ok(body_str) = std::str::from_utf8(body) {
287 response["body"] = json!(body_str);
288 } else {
289 use base64::Engine;
290 let encoded = base64::engine::general_purpose::STANDARD.encode(body);
291 response["body"] = json!(encoded);
292 response["isBase64"] = json!(true);
293 }
294 }
295
296 let params = json!({
297 "response": response
298 });
299
300 self.channel()
301 .send::<_, serde_json::Value>("fulfill", params)
302 .await
303 .map(|_| ())
304 }
305
306 pub async fn fetch(&self, options: Option<FetchOptions>) -> Result<FetchResponse> {
318 self.handled.store(true, Ordering::SeqCst);
319
320 let api_ctx = self
321 .api_request_context
322 .lock()
323 .unwrap()
324 .clone()
325 .ok_or_else(|| {
326 crate::error::Error::ProtocolError(
327 "No APIRequestContext available for route.fetch(). \
328 This can happen if the route was not dispatched through \
329 a BrowserContext with an associated request context."
330 .to_string(),
331 )
332 })?;
333
334 let request = self.request();
335 let opts = options.unwrap_or_default();
336
337 let url = opts.url.unwrap_or_else(|| request.url().to_string());
339
340 let inner_opts = InnerFetchOptions {
341 method: opts.method.or_else(|| Some(request.method().to_string())),
342 headers: opts.headers,
343 post_data: opts.post_data,
344 post_data_bytes: opts.post_data_bytes,
345 max_redirects: opts.max_redirects,
346 max_retries: opts.max_retries,
347 timeout: opts.timeout,
348 };
349
350 api_ctx.inner_fetch(&url, Some(inner_opts)).await
351 }
352}
353
354pub(crate) fn matches_pattern(pattern: &str, url: &str) -> bool {
361 use glob::Pattern;
362
363 match Pattern::new(pattern) {
364 Ok(glob_pattern) => glob_pattern.matches(url),
365 Err(_) => {
366 pattern == url
368 }
369 }
370}
371
372#[derive(Debug, Clone, Copy, PartialEq, Eq)]
376pub enum UnrouteBehavior {
377 Wait,
379 IgnoreErrors,
381 Default,
383}
384
385#[derive(Debug, Clone)]
389pub struct FetchResponse {
390 pub status: u16,
392 pub status_text: String,
394 pub headers: Vec<(String, String)>,
396 pub body: Vec<u8>,
398}
399
400impl FetchResponse {
401 pub fn status(&self) -> u16 {
403 self.status
404 }
405
406 pub fn status_text(&self) -> &str {
408 &self.status_text
409 }
410
411 pub fn headers(&self) -> &[(String, String)] {
413 &self.headers
414 }
415
416 pub fn body(&self) -> &[u8] {
418 &self.body
419 }
420
421 pub fn text(&self) -> Result<String> {
423 String::from_utf8(self.body.clone()).map_err(|e| {
424 crate::error::Error::ProtocolError(format!("Response body is not valid UTF-8: {}", e))
425 })
426 }
427
428 pub fn json<T: serde::de::DeserializeOwned>(&self) -> Result<T> {
430 serde_json::from_slice(&self.body).map_err(|e| {
431 crate::error::Error::ProtocolError(format!("Failed to parse response JSON: {}", e))
432 })
433 }
434
435 pub fn ok(&self) -> bool {
437 (200..300).contains(&self.status)
438 }
439}
440
441#[derive(Debug, Clone, Default)]
448pub struct ContinueOptions {
449 pub headers: Option<std::collections::HashMap<String, String>>,
451 pub method: Option<String>,
453 pub post_data: Option<String>,
455 pub post_data_bytes: Option<Vec<u8>>,
457 pub url: Option<String>,
459}
460
461impl ContinueOptions {
462 pub fn builder() -> ContinueOptionsBuilder {
464 ContinueOptionsBuilder::default()
465 }
466}
467
468#[derive(Debug, Clone, Default)]
470pub struct ContinueOptionsBuilder {
471 headers: Option<std::collections::HashMap<String, String>>,
472 method: Option<String>,
473 post_data: Option<String>,
474 post_data_bytes: Option<Vec<u8>>,
475 url: Option<String>,
476}
477
478impl ContinueOptionsBuilder {
479 pub fn headers(mut self, headers: std::collections::HashMap<String, String>) -> Self {
481 self.headers = Some(headers);
482 self
483 }
484
485 pub fn method(mut self, method: String) -> Self {
487 self.method = Some(method);
488 self
489 }
490
491 pub fn post_data(mut self, post_data: String) -> Self {
493 self.post_data = Some(post_data);
494 self.post_data_bytes = None; self
496 }
497
498 pub fn post_data_bytes(mut self, post_data_bytes: Vec<u8>) -> Self {
500 self.post_data_bytes = Some(post_data_bytes);
501 self.post_data = None; self
503 }
504
505 pub fn url(mut self, url: String) -> Self {
507 self.url = Some(url);
508 self
509 }
510
511 pub fn build(self) -> ContinueOptions {
513 ContinueOptions {
514 headers: self.headers,
515 method: self.method,
516 post_data: self.post_data,
517 post_data_bytes: self.post_data_bytes,
518 url: self.url,
519 }
520 }
521}
522
523#[derive(Debug, Clone, Default)]
527pub struct FulfillOptions {
528 pub status: Option<u16>,
530 pub headers: Option<std::collections::HashMap<String, String>>,
532 pub body: Option<Vec<u8>>,
534 pub content_type: Option<String>,
536}
537
538impl FulfillOptions {
539 pub fn builder() -> FulfillOptionsBuilder {
541 FulfillOptionsBuilder::default()
542 }
543}
544
545#[derive(Debug, Clone, Default)]
547pub struct FulfillOptionsBuilder {
548 status: Option<u16>,
549 headers: Option<std::collections::HashMap<String, String>>,
550 body: Option<Vec<u8>>,
551 content_type: Option<String>,
552}
553
554impl FulfillOptionsBuilder {
555 pub fn status(mut self, status: u16) -> Self {
557 self.status = Some(status);
558 self
559 }
560
561 pub fn headers(mut self, headers: std::collections::HashMap<String, String>) -> Self {
563 self.headers = Some(headers);
564 self
565 }
566
567 pub fn body(mut self, body: Vec<u8>) -> Self {
569 self.body = Some(body);
570 self
571 }
572
573 pub fn body_string(mut self, body: impl Into<String>) -> Self {
575 self.body = Some(body.into().into_bytes());
576 self
577 }
578
579 pub fn json(mut self, value: &impl serde::Serialize) -> Result<Self> {
581 let json_str = serde_json::to_string(value).map_err(|e| {
582 crate::error::Error::ProtocolError(format!("JSON serialization failed: {}", e))
583 })?;
584 self.body = Some(json_str.into_bytes());
585 self.content_type = Some("application/json".to_string());
586 Ok(self)
587 }
588
589 pub fn content_type(mut self, content_type: impl Into<String>) -> Self {
591 self.content_type = Some(content_type.into());
592 self
593 }
594
595 pub fn build(self) -> FulfillOptions {
597 FulfillOptions {
598 status: self.status,
599 headers: self.headers,
600 body: self.body,
601 content_type: self.content_type,
602 }
603 }
604}
605
606#[derive(Debug, Clone, Default)]
610pub struct FetchOptions {
611 pub headers: Option<std::collections::HashMap<String, String>>,
613 pub method: Option<String>,
615 pub post_data: Option<String>,
617 pub post_data_bytes: Option<Vec<u8>>,
619 pub url: Option<String>,
621 pub max_redirects: Option<u32>,
623 pub max_retries: Option<u32>,
625 pub timeout: Option<f64>,
627}
628
629impl FetchOptions {
630 pub fn builder() -> FetchOptionsBuilder {
632 FetchOptionsBuilder::default()
633 }
634}
635
636#[derive(Debug, Clone, Default)]
638pub struct FetchOptionsBuilder {
639 headers: Option<std::collections::HashMap<String, String>>,
640 method: Option<String>,
641 post_data: Option<String>,
642 post_data_bytes: Option<Vec<u8>>,
643 url: Option<String>,
644 max_redirects: Option<u32>,
645 max_retries: Option<u32>,
646 timeout: Option<f64>,
647}
648
649impl FetchOptionsBuilder {
650 pub fn headers(mut self, headers: std::collections::HashMap<String, String>) -> Self {
652 self.headers = Some(headers);
653 self
654 }
655
656 pub fn method(mut self, method: String) -> Self {
658 self.method = Some(method);
659 self
660 }
661
662 pub fn post_data(mut self, post_data: String) -> Self {
664 self.post_data = Some(post_data);
665 self.post_data_bytes = None;
666 self
667 }
668
669 pub fn post_data_bytes(mut self, post_data_bytes: Vec<u8>) -> Self {
671 self.post_data_bytes = Some(post_data_bytes);
672 self.post_data = None;
673 self
674 }
675
676 pub fn url(mut self, url: String) -> Self {
678 self.url = Some(url);
679 self
680 }
681
682 pub fn max_redirects(mut self, n: u32) -> Self {
684 self.max_redirects = Some(n);
685 self
686 }
687
688 pub fn max_retries(mut self, n: u32) -> Self {
690 self.max_retries = Some(n);
691 self
692 }
693
694 pub fn timeout(mut self, ms: f64) -> Self {
696 self.timeout = Some(ms);
697 self
698 }
699
700 pub fn build(self) -> FetchOptions {
702 FetchOptions {
703 headers: self.headers,
704 method: self.method,
705 post_data: self.post_data,
706 post_data_bytes: self.post_data_bytes,
707 url: self.url,
708 max_redirects: self.max_redirects,
709 max_retries: self.max_retries,
710 timeout: self.timeout,
711 }
712 }
713}
714
715impl ChannelOwner for Route {
716 fn guid(&self) -> &str {
717 self.base.guid()
718 }
719
720 fn type_name(&self) -> &str {
721 self.base.type_name()
722 }
723
724 fn parent(&self) -> Option<Arc<dyn ChannelOwner>> {
725 self.base.parent()
726 }
727
728 fn connection(&self) -> Arc<dyn crate::server::connection::ConnectionLike> {
729 self.base.connection()
730 }
731
732 fn initializer(&self) -> &Value {
733 self.base.initializer()
734 }
735
736 fn channel(&self) -> &crate::server::channel::Channel {
737 self.base.channel()
738 }
739
740 fn dispose(&self, reason: crate::server::channel_owner::DisposeReason) {
741 self.base.dispose(reason)
742 }
743
744 fn adopt(&self, child: Arc<dyn ChannelOwner>) {
745 self.base.adopt(child)
746 }
747
748 fn add_child(&self, guid: Arc<str>, child: Arc<dyn ChannelOwner>) {
749 self.base.add_child(guid, child)
750 }
751
752 fn remove_child(&self, guid: &str) {
753 self.base.remove_child(guid)
754 }
755
756 fn on_event(&self, _method: &str, _params: Value) {
757 }
759
760 fn was_collected(&self) -> bool {
761 self.base.was_collected()
762 }
763
764 fn as_any(&self) -> &dyn Any {
765 self
766 }
767}
768
769impl std::fmt::Debug for Route {
770 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
771 f.debug_struct("Route")
772 .field("guid", &self.guid())
773 .field("request", &self.request().guid())
774 .finish()
775 }
776}