Skip to content

Commit d802b75

Browse files
committed
WIP: access Arc<RequestInfo> in render context
- Pass `RequestInfo` as an `Arc<RequestInfo>` to avoid cloning the struct in many places. - The `source_path` in `HeaderContext` and `HtmlRenderContext` are now available through `RequestInfo`. - The logging component now receives the `RequestInfo` object. - Added `is_embedded()` method to `RequestInfo`.
1 parent 31a4c29 commit d802b75

File tree

3 files changed

+27
-25
lines changed

3 files changed

+27
-25
lines changed

src/render.rs

Lines changed: 12 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
4444
use crate::templates::SplitTemplate;
4545
use crate::webserver::http::RequestContext;
46+
use crate::webserver::http_request_info::RequestInfo;
4647
use crate::webserver::response_writer::{AsyncResponseWriter, ResponseWriter};
4748
use crate::webserver::ErrorWithStatus;
4849
use crate::AppState;
@@ -80,7 +81,7 @@ pub enum PageContext {
8081
/// Handles the first SQL statements, before the headers have been sent to
8182
pub struct HeaderContext {
8283
app_state: Arc<AppState>,
83-
request_context: RequestContext,
84+
request_context: Arc<RequestInfo>,
8485
pub writer: ResponseWriter,
8586
response: HttpResponseBuilder,
8687
has_status: bool,
@@ -90,7 +91,7 @@ impl HeaderContext {
9091
#[must_use]
9192
pub fn new(
9293
app_state: Arc<AppState>,
93-
request_context: RequestContext,
94+
request_context: Arc<RequestInfo>,
9495
writer: ResponseWriter,
9596
) -> Self {
9697
let mut response = HttpResponseBuilder::new(StatusCode::OK);
@@ -364,7 +365,7 @@ impl HeaderContext {
364365
}
365366

366367
fn log(self, data: &JsonValue) -> anyhow::Result<PageContext> {
367-
handle_log_component(&self.request_context.source_path, Option::None, data)?;
368+
handle_log_component(&self.request_context, Option::None, data)?;
368369
Ok(PageContext::Header(self))
369370
}
370371

@@ -651,7 +652,7 @@ pub struct HtmlRenderContext<W: std::io::Write> {
651652
current_component: Option<SplitTemplateRenderer>,
652653
shell_renderer: SplitTemplateRenderer,
653654
current_statement: usize,
654-
request_context: RequestContext,
655+
request_context: Arc<RequestInfo>,
655656
}
656657

657658
const DEFAULT_COMPONENT: &str = "table";
@@ -661,20 +662,21 @@ const FRAGMENT_SHELL_COMPONENT: &str = "shell-empty";
661662
impl<W: std::io::Write> HtmlRenderContext<W> {
662663
pub async fn new(
663664
app_state: Arc<AppState>,
664-
request_context: RequestContext,
665+
request_context: Arc<RequestInfo>,
665666
mut writer: W,
666667
initial_row: JsonValue,
667668
) -> anyhow::Result<HtmlRenderContext<W>> {
668669
log::debug!("Creating the shell component for the page");
669670

670671
let mut initial_rows = vec![Cow::Borrowed(&initial_row)];
671672

673+
let is_embedded = request_context.is_embedded();
672674
if !initial_rows
673675
.first()
674676
.and_then(|c| get_object_str(c, "component"))
675677
.is_some_and(Self::is_shell_component)
676678
{
677-
let default_shell = if request_context.is_embedded {
679+
let default_shell = if is_embedded {
678680
FRAGMENT_SHELL_COMPONENT
679681
} else {
680682
PAGE_SHELL_COMPONENT
@@ -692,7 +694,7 @@ impl<W: std::io::Write> HtmlRenderContext<W> {
692694
.expect("shell row should exist at this point");
693695
let mut shell_component =
694696
get_object_str(&shell_row, "component").expect("shell should exist");
695-
if request_context.is_embedded && shell_component != FRAGMENT_SHELL_COMPONENT {
697+
if is_embedded && shell_component != FRAGMENT_SHELL_COMPONENT {
696698
log::warn!(
697699
"Embedded pages cannot use a shell component! Ignoring the '{shell_component}' component and its properties: {shell_row}"
698700
);
@@ -739,11 +741,7 @@ impl<W: std::io::Write> HtmlRenderContext<W> {
739741
}
740742

741743
if component_name == "log" {
742-
return handle_log_component(
743-
&self.request_context.source_path,
744-
Some(self.current_statement),
745-
data,
746-
);
744+
return handle_log_component(&self.request_context, Some(self.current_statement), data);
747745
}
748746

749747
match self.open_component_with_data(component_name, &data).await {
@@ -910,14 +908,14 @@ impl<W: std::io::Write> HtmlRenderContext<W> {
910908
}
911909

912910
fn handle_log_component(
913-
source_path: &Path,
911+
request_context: &RequestInfo,
914912
current_statement: Option<usize>,
915913
data: &JsonValue,
916914
) -> anyhow::Result<()> {
917915
let level_name = get_object_str(data, "level").unwrap_or("info");
918916
let log_level = log::Level::from_str(level_name).with_context(|| "Invalid log level value")?;
919917

920-
let mut target = format!("sqlpage::log from \"{}\"", source_path.display());
918+
let mut target = format!("sqlpage::log from \"{}\"", request_context.path);
921919
if let Some(current_statement) = current_statement {
922920
write!(&mut target, " statement {current_statement}")?;
923921
}

src/webserver/http.rs

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use crate::render::{AnyRenderBodyContext, HeaderContext, PageContext};
66
use crate::webserver::content_security_policy::ContentSecurityPolicy;
77
use crate::webserver::database::execute_queries::stop_at_first_error;
88
use crate::webserver::database::{execute_queries::stream_query_results_with_conn, DbItem};
9-
use crate::webserver::http_request_info::extract_request_info;
9+
use crate::webserver::http_request_info::{extract_request_info, RequestInfo};
1010
use crate::webserver::ErrorWithStatus;
1111
use crate::{AppConfig, AppState, ParsedSqlFile, DEFAULT_404_FILE};
1212
use actix_web::dev::{fn_service, ServiceFactory, ServiceRequest};
@@ -97,12 +97,12 @@ async fn stream_response(stream: impl Stream<Item = DbItem>, mut renderer: AnyRe
9797
async fn build_response_header_and_stream<S: Stream<Item = DbItem>>(
9898
app_state: Arc<AppState>,
9999
database_entries: S,
100-
request_context: RequestContext,
100+
request_params: Arc<RequestInfo>,
101101
) -> anyhow::Result<ResponseWithWriter<S>> {
102102
let chan_size = app_state.config.max_pending_rows;
103103
let (sender, receiver) = mpsc::channel(chan_size);
104104
let writer = ResponseWriter::new(sender);
105-
let mut head_context = HeaderContext::new(app_state, request_context, writer);
105+
let mut head_context = HeaderContext::new(app_state, request_params, writer);
106106
let mut stream = Box::pin(database_entries);
107107
while let Some(item) = stream.next().await {
108108
let page_context = match item {
@@ -173,24 +173,20 @@ async fn render_sql(
173173
let mut req_param = extract_request_info(srv_req, Arc::clone(&app_state))
174174
.await
175175
.map_err(|e| anyhow_err_to_actix(e, &app_state))?;
176-
log::debug!("Received a request with the following parameters: {req_param:?}");
176+
let sql_path = &sql_file.source_path;
177+
log::debug!("Received a request to {sql_path:?} with the following parameters: {req_param:?}");
177178

178179
let (resp_send, resp_recv) = tokio::sync::oneshot::channel::<HttpResponse>();
179-
let source_path: PathBuf = sql_file.source_path.clone();
180180
actix_web::rt::spawn(async move {
181-
let request_context = RequestContext {
182-
is_embedded: req_param.get_variables.contains_key("_sqlpage_embed"),
183-
source_path,
184-
content_security_policy: ContentSecurityPolicy::with_random_nonce(),
185-
};
186181
let mut conn = None;
187182
let database_entries_stream =
188183
stream_query_results_with_conn(&sql_file, &mut req_param, &mut conn);
189184
let database_entries_stream = stop_at_first_error(database_entries_stream);
185+
let req_param = Arc::new(req_param);
190186
let response_with_writer = build_response_header_and_stream(
191187
Arc::clone(&app_state),
192188
database_entries_stream,
193-
request_context,
189+
Arc::clone(&req_param),
194190
)
195191
.await;
196192
match response_with_writer {

src/webserver/http_request_info.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use crate::webserver::content_security_policy::ContentSecurityPolicy;
12
use crate::AppState;
23
use actix_multipart::form::bytes::Bytes;
34
use actix_multipart::form::tempfile::TempFile;
@@ -42,6 +43,7 @@ pub struct RequestInfo {
4243
pub clone_depth: u8,
4344
pub raw_body: Option<Vec<u8>>,
4445
pub oidc_claims: Option<OidcClaims>,
46+
pub content_security_policy: ContentSecurityPolicy,
4547
}
4648

4749
impl RequestInfo {
@@ -62,8 +64,13 @@ impl RequestInfo {
6264
clone_depth: self.clone_depth + 1,
6365
raw_body: self.raw_body.clone(),
6466
oidc_claims: self.oidc_claims.clone(),
67+
content_security_policy: self.content_security_policy.clone(),
6568
}
6669
}
70+
71+
pub fn is_embedded(&self) -> bool {
72+
self.get_variables.contains_key("_sqlpage_embed")
73+
}
6774
}
6875

6976
impl Clone for RequestInfo {
@@ -123,6 +130,7 @@ pub(crate) async fn extract_request_info(
123130
clone_depth: 0,
124131
raw_body,
125132
oidc_claims,
133+
content_security_policy: ContentSecurityPolicy::with_random_nonce()
126134
})
127135
}
128136

0 commit comments

Comments
 (0)