actix_prerender/
builder.rs

1use crate::middleware::Inner;
2use crate::{middleware, PrerenderError, PrerenderMiddleware};
3use actix_service::{Service, Transform};
4use actix_utils::future;
5use actix_utils::future::Ready;
6use actix_web::body::{EitherBody, MessageBody};
7use actix_web::dev::{ServiceRequest, ServiceResponse};
8use actix_web::Error;
9use reqwest::header::HeaderMap;
10use reqwest::Client;
11use std::rc::Rc;
12use url::Url;
13
14/// Builder for Prerender middleware.
15///
16/// To construct a Prerender middleware, call [`Prerender::build()`] to create a builder.
17/// Then you can choose between `self..use_prerender_io`
18///
19/// # Errors
20///
21/// TODO
22///
23/// # Prerender.io example
24/// ```
25/// use actix_prerender::Prerender;
26/// use actix_web::http::header;
27///
28/// let token = "prerender service token".to_string();
29/// let prerender = Prerender::build().use_prerender_io(token);
30///
31/// // `prerender` can now be used in `App::wrap`.
32/// ```
33/// # Custom service URL example
34/// ```
35/// use actix_prerender::Prerender;
36/// use actix_web::http::header;
37///
38/// let token = "prerender service token".to_string();
39/// let prerender = Prerender::build().use_custom_prerender_url("https://localhost:5001");
40///
41/// // `prerender` can now be used in `App::wrap`.
42/// ```
43#[derive(Clone)]
44pub struct Prerender {
45    inner: Rc<Inner>,
46}
47
48#[derive()]
49pub struct PrerenderBuilder {
50    pub(crate) forward_headers: bool,
51    pub(crate) before_render_fn: Option<fn(&ServiceRequest, &mut HeaderMap)>,
52}
53
54fn default_client() -> Client {
55    Client::builder()
56        .gzip(true)
57        .timeout(std::time::Duration::new(25, 0))
58        .build()
59        .unwrap()
60}
61
62impl PrerenderBuilder {
63    /// Creates a `Prerender` middleware that delegate requests to the web `prerender.io` service.
64    pub fn use_prerender_io(self, token: String) -> Prerender {
65        let inner = Inner {
66            before_render_fn: self.before_render_fn,
67            forward_headers: self.forward_headers,
68            inner_client: default_client(),
69            prerender_service_url: middleware::prerender_url(),
70            prerender_token: Some(token),
71        };
72
73        Prerender { inner: Rc::new(inner) }
74    }
75
76    /// Creates a `Prerender` middleware that delegates crawler requests to the custom `prerender_service_url`.
77    pub fn use_custom_prerender_url(self, prerender_service_url: &str) -> Result<Prerender, PrerenderError> {
78        let prerender_service_url = Url::parse(prerender_service_url).map_err(|_| PrerenderError::InvalidUrl)?;
79
80        let inner = Inner {
81            before_render_fn: self.before_render_fn,
82            forward_headers: self.forward_headers,
83            inner_client: default_client(),
84            prerender_service_url,
85            prerender_token: None,
86        };
87
88        Ok(Prerender { inner: Rc::new(inner) })
89    }
90
91    /// Allow you to inspect and modify the `HeaderMap` before the request is sent to the `Prerender` service.
92    pub fn set_before_render_fn(mut self, prerender_func: fn(req: &ServiceRequest, headers: &mut HeaderMap)) -> Self {
93        self.before_render_fn = Some(prerender_func);
94        self
95    }
96
97    /// Sets the middleware to forward all request headers to the `Prerender` service.
98    pub const fn forward_headers(mut self) -> Self {
99        self.forward_headers = true;
100        self
101    }
102}
103
104impl Prerender {
105    pub fn build() -> PrerenderBuilder {
106        PrerenderBuilder {
107            forward_headers: false,
108            before_render_fn: None,
109        }
110    }
111}
112
113impl<S, B> Transform<S, ServiceRequest> for Prerender
114where
115    S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error>,
116    S::Future: 'static,
117
118    B: MessageBody + 'static,
119{
120    type Response = ServiceResponse<EitherBody<B>>;
121    type Error = Error;
122    type Transform = PrerenderMiddleware<S>;
123    type InitError = ();
124    type Future = Ready<Result<Self::Transform, Self::InitError>>;
125
126    fn new_transform(&self, service: S) -> Self::Future {
127        future::ok(PrerenderMiddleware {
128            service,
129            inner: Rc::clone(&self.inner),
130        })
131    }
132}