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 serde_json::{Value, json};
13use std::any::Any;
14use std::sync::atomic::{AtomicBool, Ordering};
15use std::sync::{Arc, Mutex};
16
17#[derive(Clone)]
23pub struct Route {
24 base: ChannelOwnerImpl,
25 handled: Arc<AtomicBool>,
28 api_request_context: Arc<Mutex<Option<APIRequestContext>>>,
31}
32
33impl Route {
34 pub fn new(
39 parent: Arc<dyn ChannelOwner>,
40 type_name: String,
41 guid: Arc<str>,
42 initializer: Value,
43 ) -> Result<Self> {
44 let base = ChannelOwnerImpl::new(
45 ParentOrConnection::Parent(parent.clone()),
46 type_name,
47 guid,
48 initializer,
49 );
50
51 Ok(Self {
52 base,
53 handled: Arc::new(AtomicBool::new(false)),
54 api_request_context: Arc::new(Mutex::new(None)),
55 })
56 }
57
58 pub(crate) fn was_handled(&self) -> bool {
63 self.handled.load(Ordering::SeqCst)
64 }
65
66 pub(crate) fn set_api_request_context(&self, ctx: APIRequestContext) {
71 *self.api_request_context.lock().unwrap() = Some(ctx);
72 }
73
74 pub fn request(&self) -> Request {
78 if let Some(parent) = self.parent() {
81 if let Some(request) = parent.as_any().downcast_ref::<Request>() {
82 return request.clone();
83 }
84 }
85
86 let request_data = self
89 .initializer()
90 .get("request")
91 .cloned()
92 .unwrap_or_else(|| {
93 serde_json::json!({
94 "url": "",
95 "method": "GET"
96 })
97 });
98
99 let parent = self
100 .parent()
101 .unwrap_or_else(|| Arc::new(self.clone()) as Arc<dyn ChannelOwner>);
102
103 let request_guid = request_data
104 .get("guid")
105 .and_then(|v| v.as_str())
106 .unwrap_or("request-stub");
107
108 Request::new(
109 parent,
110 "Request".to_string(),
111 Arc::from(request_guid),
112 request_data,
113 )
114 .unwrap()
115 }
116
117 pub async fn abort(&self, error_code: Option<&str>) -> Result<()> {
136 self.handled.store(true, Ordering::SeqCst);
137 let params = json!({
138 "errorCode": error_code.unwrap_or("failed")
139 });
140
141 self.channel()
142 .send::<_, serde_json::Value>("abort", params)
143 .await
144 .map(|_| ())
145 }
146
147 pub async fn continue_(&self, overrides: Option<ContinueOptions>) -> Result<()> {
158 self.handled.store(true, Ordering::SeqCst);
159 self.continue_internal(overrides, false).await
160 }
161
162 pub async fn fallback(&self, overrides: Option<ContinueOptions>) -> Result<()> {
174 self.continue_internal(overrides, true).await
176 }
177
178 async fn continue_internal(
180 &self,
181 overrides: Option<ContinueOptions>,
182 is_fallback: bool,
183 ) -> Result<()> {
184 let mut params = json!({
185 "isFallback": is_fallback
186 });
187
188 if let Some(opts) = overrides {
190 if let Some(headers) = opts.headers {
192 let headers_array: Vec<serde_json::Value> = headers
193 .into_iter()
194 .map(|(name, value)| json!({"name": name, "value": value}))
195 .collect();
196 params["headers"] = json!(headers_array);
197 }
198
199 if let Some(method) = opts.method {
201 params["method"] = json!(method);
202 }
203
204 if let Some(post_data) = opts.post_data {
206 params["postData"] = json!(post_data);
207 } else if let Some(post_data_bytes) = opts.post_data_bytes {
208 use base64::Engine;
209 let encoded = base64::engine::general_purpose::STANDARD.encode(&post_data_bytes);
210 params["postData"] = json!(encoded);
211 }
212
213 if let Some(url) = opts.url {
215 params["url"] = json!(url);
216 }
217 }
218
219 self.channel()
220 .send::<_, serde_json::Value>("continue", params)
221 .await
222 .map(|_| ())
223 }
224
225 pub async fn fulfill(&self, options: Option<FulfillOptions>) -> Result<()> {
254 self.handled.store(true, Ordering::SeqCst);
255 let opts = options.unwrap_or_default();
256
257 let mut response = json!({
259 "status": opts.status.unwrap_or(200),
260 "headers": []
261 });
262
263 let mut headers_map = opts.headers.unwrap_or_default();
265
266 let body_bytes = opts.body.as_ref();
268 if let Some(body) = body_bytes {
269 let content_length = body.len().to_string();
270 headers_map.insert("content-length".to_string(), content_length);
271 }
272
273 if let Some(ref ct) = opts.content_type {
275 headers_map.insert("content-type".to_string(), ct.clone());
276 }
277
278 let headers_array: Vec<Value> = headers_map
280 .into_iter()
281 .map(|(name, value)| json!({"name": name, "value": value}))
282 .collect();
283 response["headers"] = json!(headers_array);
284
285 if let Some(body) = body_bytes {
287 if let Ok(body_str) = std::str::from_utf8(body) {
289 response["body"] = json!(body_str);
290 } else {
291 use base64::Engine;
292 let encoded = base64::engine::general_purpose::STANDARD.encode(body);
293 response["body"] = json!(encoded);
294 response["isBase64"] = json!(true);
295 }
296 }
297
298 let params = json!({
299 "response": response
300 });
301
302 self.channel()
303 .send::<_, serde_json::Value>("fulfill", params)
304 .await
305 .map(|_| ())
306 }
307
308 pub async fn fetch(&self, options: Option<FetchOptions>) -> Result<FetchResponse> {
320 self.handled.store(true, Ordering::SeqCst);
321
322 let api_ctx = self
323 .api_request_context
324 .lock()
325 .unwrap()
326 .clone()
327 .ok_or_else(|| {
328 crate::error::Error::ProtocolError(
329 "No APIRequestContext available for route.fetch(). \
330 This can happen if the route was not dispatched through \
331 a BrowserContext with an associated request context."
332 .to_string(),
333 )
334 })?;
335
336 let request = self.request();
337 let opts = options.unwrap_or_default();
338
339 let url = opts.url.unwrap_or_else(|| request.url().to_string());
341
342 let inner_opts = InnerFetchOptions {
343 method: opts.method.or_else(|| Some(request.method().to_string())),
344 headers: opts.headers,
345 post_data: opts.post_data,
346 post_data_bytes: opts.post_data_bytes,
347 max_redirects: opts.max_redirects,
348 max_retries: opts.max_retries,
349 timeout: opts.timeout,
350 };
351
352 api_ctx.inner_fetch(&url, Some(inner_opts)).await
353 }
354}
355
356pub(crate) fn matches_pattern(pattern: &str, url: &str) -> bool {
363 use glob::Pattern;
364
365 match Pattern::new(pattern) {
366 Ok(glob_pattern) => glob_pattern.matches(url),
367 Err(_) => {
368 pattern == url
370 }
371 }
372}
373
374#[derive(Debug, Clone, Copy, PartialEq, Eq)]
378pub enum UnrouteBehavior {
379 Wait,
381 IgnoreErrors,
383 Default,
385}
386
387#[derive(Debug, Clone)]
391pub struct FetchResponse {
392 pub status: u16,
394 pub status_text: String,
396 pub headers: Vec<(String, String)>,
398 pub body: Vec<u8>,
400}
401
402impl FetchResponse {
403 pub fn status(&self) -> u16 {
405 self.status
406 }
407
408 pub fn status_text(&self) -> &str {
410 &self.status_text
411 }
412
413 pub fn headers(&self) -> &[(String, String)] {
415 &self.headers
416 }
417
418 pub fn body(&self) -> &[u8] {
420 &self.body
421 }
422
423 pub fn text(&self) -> Result<String> {
425 String::from_utf8(self.body.clone()).map_err(|e| {
426 crate::error::Error::ProtocolError(format!("Response body is not valid UTF-8: {}", e))
427 })
428 }
429
430 pub fn json<T: serde::de::DeserializeOwned>(&self) -> Result<T> {
432 serde_json::from_slice(&self.body).map_err(|e| {
433 crate::error::Error::ProtocolError(format!("Failed to parse response JSON: {}", e))
434 })
435 }
436
437 pub fn ok(&self) -> bool {
439 (200..300).contains(&self.status)
440 }
441}
442
443#[derive(Debug, Clone, Default)]
450pub struct ContinueOptions {
451 pub headers: Option<std::collections::HashMap<String, String>>,
453 pub method: Option<String>,
455 pub post_data: Option<String>,
457 pub post_data_bytes: Option<Vec<u8>>,
459 pub url: Option<String>,
461}
462
463impl ContinueOptions {
464 pub fn builder() -> ContinueOptionsBuilder {
466 ContinueOptionsBuilder::default()
467 }
468}
469
470#[derive(Debug, Clone, Default)]
472pub struct ContinueOptionsBuilder {
473 headers: Option<std::collections::HashMap<String, String>>,
474 method: Option<String>,
475 post_data: Option<String>,
476 post_data_bytes: Option<Vec<u8>>,
477 url: Option<String>,
478}
479
480impl ContinueOptionsBuilder {
481 pub fn headers(mut self, headers: std::collections::HashMap<String, String>) -> Self {
483 self.headers = Some(headers);
484 self
485 }
486
487 pub fn method(mut self, method: String) -> Self {
489 self.method = Some(method);
490 self
491 }
492
493 pub fn post_data(mut self, post_data: String) -> Self {
495 self.post_data = Some(post_data);
496 self.post_data_bytes = None; self
498 }
499
500 pub fn post_data_bytes(mut self, post_data_bytes: Vec<u8>) -> Self {
502 self.post_data_bytes = Some(post_data_bytes);
503 self.post_data = None; self
505 }
506
507 pub fn url(mut self, url: String) -> Self {
509 self.url = Some(url);
510 self
511 }
512
513 pub fn build(self) -> ContinueOptions {
515 ContinueOptions {
516 headers: self.headers,
517 method: self.method,
518 post_data: self.post_data,
519 post_data_bytes: self.post_data_bytes,
520 url: self.url,
521 }
522 }
523}
524
525#[derive(Debug, Clone, Default)]
529pub struct FulfillOptions {
530 pub status: Option<u16>,
532 pub headers: Option<std::collections::HashMap<String, String>>,
534 pub body: Option<Vec<u8>>,
536 pub content_type: Option<String>,
538}
539
540impl FulfillOptions {
541 pub fn builder() -> FulfillOptionsBuilder {
543 FulfillOptionsBuilder::default()
544 }
545}
546
547#[derive(Debug, Clone, Default)]
549pub struct FulfillOptionsBuilder {
550 status: Option<u16>,
551 headers: Option<std::collections::HashMap<String, String>>,
552 body: Option<Vec<u8>>,
553 content_type: Option<String>,
554}
555
556impl FulfillOptionsBuilder {
557 pub fn status(mut self, status: u16) -> Self {
559 self.status = Some(status);
560 self
561 }
562
563 pub fn headers(mut self, headers: std::collections::HashMap<String, String>) -> Self {
565 self.headers = Some(headers);
566 self
567 }
568
569 pub fn body(mut self, body: Vec<u8>) -> Self {
571 self.body = Some(body);
572 self
573 }
574
575 pub fn body_string(mut self, body: impl Into<String>) -> Self {
577 self.body = Some(body.into().into_bytes());
578 self
579 }
580
581 pub fn json(mut self, value: &impl serde::Serialize) -> Result<Self> {
583 let json_str = serde_json::to_string(value).map_err(|e| {
584 crate::error::Error::ProtocolError(format!("JSON serialization failed: {}", e))
585 })?;
586 self.body = Some(json_str.into_bytes());
587 self.content_type = Some("application/json".to_string());
588 Ok(self)
589 }
590
591 pub fn content_type(mut self, content_type: impl Into<String>) -> Self {
593 self.content_type = Some(content_type.into());
594 self
595 }
596
597 pub fn build(self) -> FulfillOptions {
599 FulfillOptions {
600 status: self.status,
601 headers: self.headers,
602 body: self.body,
603 content_type: self.content_type,
604 }
605 }
606}
607
608#[derive(Debug, Clone, Default)]
612pub struct FetchOptions {
613 pub headers: Option<std::collections::HashMap<String, String>>,
615 pub method: Option<String>,
617 pub post_data: Option<String>,
619 pub post_data_bytes: Option<Vec<u8>>,
621 pub url: Option<String>,
623 pub max_redirects: Option<u32>,
625 pub max_retries: Option<u32>,
627 pub timeout: Option<f64>,
629}
630
631impl FetchOptions {
632 pub fn builder() -> FetchOptionsBuilder {
634 FetchOptionsBuilder::default()
635 }
636}
637
638#[derive(Debug, Clone, Default)]
640pub struct FetchOptionsBuilder {
641 headers: Option<std::collections::HashMap<String, String>>,
642 method: Option<String>,
643 post_data: Option<String>,
644 post_data_bytes: Option<Vec<u8>>,
645 url: Option<String>,
646 max_redirects: Option<u32>,
647 max_retries: Option<u32>,
648 timeout: Option<f64>,
649}
650
651impl FetchOptionsBuilder {
652 pub fn headers(mut self, headers: std::collections::HashMap<String, String>) -> Self {
654 self.headers = Some(headers);
655 self
656 }
657
658 pub fn method(mut self, method: String) -> Self {
660 self.method = Some(method);
661 self
662 }
663
664 pub fn post_data(mut self, post_data: String) -> Self {
666 self.post_data = Some(post_data);
667 self.post_data_bytes = None;
668 self
669 }
670
671 pub fn post_data_bytes(mut self, post_data_bytes: Vec<u8>) -> Self {
673 self.post_data_bytes = Some(post_data_bytes);
674 self.post_data = None;
675 self
676 }
677
678 pub fn url(mut self, url: String) -> Self {
680 self.url = Some(url);
681 self
682 }
683
684 pub fn max_redirects(mut self, n: u32) -> Self {
686 self.max_redirects = Some(n);
687 self
688 }
689
690 pub fn max_retries(mut self, n: u32) -> Self {
692 self.max_retries = Some(n);
693 self
694 }
695
696 pub fn timeout(mut self, ms: f64) -> Self {
698 self.timeout = Some(ms);
699 self
700 }
701
702 pub fn build(self) -> FetchOptions {
704 FetchOptions {
705 headers: self.headers,
706 method: self.method,
707 post_data: self.post_data,
708 post_data_bytes: self.post_data_bytes,
709 url: self.url,
710 max_redirects: self.max_redirects,
711 max_retries: self.max_retries,
712 timeout: self.timeout,
713 }
714 }
715}
716
717impl ChannelOwner for Route {
718 fn guid(&self) -> &str {
719 self.base.guid()
720 }
721
722 fn type_name(&self) -> &str {
723 self.base.type_name()
724 }
725
726 fn parent(&self) -> Option<Arc<dyn ChannelOwner>> {
727 self.base.parent()
728 }
729
730 fn connection(&self) -> Arc<dyn crate::server::connection::ConnectionLike> {
731 self.base.connection()
732 }
733
734 fn initializer(&self) -> &Value {
735 self.base.initializer()
736 }
737
738 fn channel(&self) -> &crate::server::channel::Channel {
739 self.base.channel()
740 }
741
742 fn dispose(&self, reason: crate::server::channel_owner::DisposeReason) {
743 self.base.dispose(reason)
744 }
745
746 fn adopt(&self, child: Arc<dyn ChannelOwner>) {
747 self.base.adopt(child)
748 }
749
750 fn add_child(&self, guid: Arc<str>, child: Arc<dyn ChannelOwner>) {
751 self.base.add_child(guid, child)
752 }
753
754 fn remove_child(&self, guid: &str) {
755 self.base.remove_child(guid)
756 }
757
758 fn on_event(&self, _method: &str, _params: Value) {
759 }
761
762 fn was_collected(&self) -> bool {
763 self.base.was_collected()
764 }
765
766 fn as_any(&self) -> &dyn Any {
767 self
768 }
769}
770
771impl std::fmt::Debug for Route {
772 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
773 f.debug_struct("Route")
774 .field("guid", &self.guid())
775 .field("request", &self.request().guid())
776 .finish()
777 }
778}