credence_lib/middleware/
render.rs1use super::{
2 super::{configuration::*, render::*},
3 defer::*,
4 socket::*,
5};
6
7use {
8 ::axum::{
9 extract::{Request, *},
10 http::{header::*, *},
11 middleware::*,
12 response::Response,
13 },
14 bytestring::*,
15 kutil_http::*,
16 std::{cmp::*, result::Result},
17};
18
19#[derive(Clone, Debug)]
25pub struct RenderMiddleware {
26 pub configuration: CredenceConfiguration,
28
29 pub templates: Templates,
31}
32
33impl RenderMiddleware {
34 pub fn new(configuration: CredenceConfiguration) -> Self {
36 let templates = configuration.files.templates();
37 Self::new_with(configuration, templates)
38 }
39
40 pub fn new_with(configuration: CredenceConfiguration, templates: Templates) -> Self {
42 Self { configuration, templates }
43 }
44
45 pub async fn function(
47 State(state_self): State<Self>,
48 mut request: Request,
49 next: Next,
50 ) -> Result<Response, StatusCode> {
51 let uri_path: ByteString = match request.uri().decoded_path() {
52 Some(uri_path) => uri_path.as_ref().into(),
53 None => {
54 tracing::error!("cannot decode path: {}", request.uri());
56 return Err(StatusCode::INTERNAL_SERVER_ERROR);
57 }
58 };
59
60 let original_uri_path = DeferredResponse::get(&request).and_then(|deferred_response| {
61 if let DeferredResponse::RewriteFrom(original_uri_path) = deferred_response {
62 Some(original_uri_path.clone())
63 } else {
64 None
65 }
66 });
67
68 if let Some(rendered_page_type) = state_self.configuration.render.is_rendered_page(&uri_path) {
69 let mut last_modified = None;
71 if let Some(if_modified_since) = request.headers().if_modified_since() {
72 let path = state_self.configuration.files.asset(&uri_path);
73 let mut modified = file_modified(&path).map_err_internal_server("file modified")?;
74
75 if let Some(coordinator_modified) = state_self
76 .configuration
77 .files
78 .coordinate
79 .coordinator_modified()
80 .map_err_internal_server("coordinator modified")?
81 {
82 modified = max(modified, coordinator_modified);
83 }
84
85 if modified_since(Some(modified), Some(if_modified_since)) {
86 last_modified = Some(modified);
87
88 let headers = request.headers_mut();
90 headers.remove(IF_MODIFIED_SINCE);
91 headers.remove(IF_NONE_MATCH);
92 }
93 }
94
95 let is_json = is_json(&request);
96 let socket = request.extensions().get::<Socket>().cloned();
97
98 let response = next.run(request).await;
99
100 if response.status() == StatusCode::OK {
101 tracing::debug!("rendered page: {}", uri_path);
102
103 let rendered_page = RenderedPage::new_from_response(
104 &uri_path,
105 rendered_page_type,
106 response,
107 &state_self.configuration.render,
108 )
109 .await?;
110
111 let mut context = rendered_page.context(
112 socket,
113 uri_path,
114 original_uri_path,
115 last_modified,
116 is_json,
117 &state_self.templates,
118 &state_self.configuration,
119 );
120
121 context.prepare_default().await?;
122 context.prepare(CatalogPreparer).await?;
123
124 context.into_response().await
125 } else {
126 Ok(response)
127 }
128 } else {
129 Ok(next.run(request).await)
130 }
131 }
132}
133
134fn is_json(request: &Request) -> (bool, bool) {
135 if request
136 .headers()
137 .accept()
138 .best(RENDERED_PAGE_MEDIA_TYPES)
139 .map(|media_type_selector| media_type_selector == &JSON_MEDIA_TYPE)
140 .unwrap_or_default()
141 {
142 if let Some(query) = request.uri().decoded_query_map() {
143 if let Some(pretty) = query.get("pretty") {
144 return (true, pretty == "true");
145 }
146 }
147
148 (true, false)
149 } else {
150 (false, false)
151 }
152}