Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 12 additions & 14 deletions src/render.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@

use crate::templates::SplitTemplate;
use crate::webserver::http::RequestContext;
use crate::webserver::http_request_info::RequestInfo;
use crate::webserver::response_writer::{AsyncResponseWriter, ResponseWriter};
use crate::webserver::ErrorWithStatus;
use crate::AppState;
Expand Down Expand Up @@ -80,7 +81,7 @@ pub enum PageContext {
/// Handles the first SQL statements, before the headers have been sent to
pub struct HeaderContext {
app_state: Arc<AppState>,
request_context: RequestContext,
request_context: Arc<RequestInfo>,
pub writer: ResponseWriter,
response: HttpResponseBuilder,
has_status: bool,
Expand All @@ -90,7 +91,7 @@ impl HeaderContext {
#[must_use]
pub fn new(
app_state: Arc<AppState>,
request_context: RequestContext,
request_context: Arc<RequestInfo>,
writer: ResponseWriter,
) -> Self {
let mut response = HttpResponseBuilder::new(StatusCode::OK);
Expand Down Expand Up @@ -364,7 +365,7 @@ impl HeaderContext {
}

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

Expand Down Expand Up @@ -651,7 +652,7 @@ pub struct HtmlRenderContext<W: std::io::Write> {
current_component: Option<SplitTemplateRenderer>,
shell_renderer: SplitTemplateRenderer,
current_statement: usize,
request_context: RequestContext,
request_context: Arc<RequestInfo>,
}

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

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

let is_embedded = request_context.is_embedded();
if !initial_rows
.first()
.and_then(|c| get_object_str(c, "component"))
.is_some_and(Self::is_shell_component)
{
let default_shell = if request_context.is_embedded {
let default_shell = if is_embedded {
FRAGMENT_SHELL_COMPONENT
} else {
PAGE_SHELL_COMPONENT
Expand All @@ -692,7 +694,7 @@ impl<W: std::io::Write> HtmlRenderContext<W> {
.expect("shell row should exist at this point");
let mut shell_component =
get_object_str(&shell_row, "component").expect("shell should exist");
if request_context.is_embedded && shell_component != FRAGMENT_SHELL_COMPONENT {
if is_embedded && shell_component != FRAGMENT_SHELL_COMPONENT {
log::warn!(
"Embedded pages cannot use a shell component! Ignoring the '{shell_component}' component and its properties: {shell_row}"
);
Expand Down Expand Up @@ -739,11 +741,7 @@ impl<W: std::io::Write> HtmlRenderContext<W> {
}

if component_name == "log" {
return handle_log_component(
&self.request_context.source_path,
Some(self.current_statement),
data,
);
return handle_log_component(&self.request_context, Some(self.current_statement), data);
}

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

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

let mut target = format!("sqlpage::log from \"{}\"", source_path.display());
let mut target = format!("sqlpage::log from \"{}\"", request_context.path);
if let Some(current_statement) = current_statement {
write!(&mut target, " statement {current_statement}")?;
}
Expand Down
18 changes: 7 additions & 11 deletions src/webserver/http.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use crate::render::{AnyRenderBodyContext, HeaderContext, PageContext};
use crate::webserver::content_security_policy::ContentSecurityPolicy;
use crate::webserver::database::execute_queries::stop_at_first_error;
use crate::webserver::database::{execute_queries::stream_query_results_with_conn, DbItem};
use crate::webserver::http_request_info::extract_request_info;
use crate::webserver::http_request_info::{extract_request_info, RequestInfo};
use crate::webserver::ErrorWithStatus;
use crate::{AppConfig, AppState, ParsedSqlFile, DEFAULT_404_FILE};
use actix_web::dev::{fn_service, ServiceFactory, ServiceRequest};
Expand Down Expand Up @@ -97,12 +97,12 @@ async fn stream_response(stream: impl Stream<Item = DbItem>, mut renderer: AnyRe
async fn build_response_header_and_stream<S: Stream<Item = DbItem>>(
app_state: Arc<AppState>,
database_entries: S,
request_context: RequestContext,
request_params: Arc<RequestInfo>,
) -> anyhow::Result<ResponseWithWriter<S>> {
let chan_size = app_state.config.max_pending_rows;
let (sender, receiver) = mpsc::channel(chan_size);
let writer = ResponseWriter::new(sender);
let mut head_context = HeaderContext::new(app_state, request_context, writer);
let mut head_context = HeaderContext::new(app_state, request_params, writer);
let mut stream = Box::pin(database_entries);
while let Some(item) = stream.next().await {
let page_context = match item {
Expand Down Expand Up @@ -173,24 +173,20 @@ async fn render_sql(
let mut req_param = extract_request_info(srv_req, Arc::clone(&app_state))
.await
.map_err(|e| anyhow_err_to_actix(e, &app_state))?;
log::debug!("Received a request with the following parameters: {req_param:?}");
let sql_path = &sql_file.source_path;
log::debug!("Received a request to {sql_path:?} with the following parameters: {req_param:?}");

let (resp_send, resp_recv) = tokio::sync::oneshot::channel::<HttpResponse>();
let source_path: PathBuf = sql_file.source_path.clone();
actix_web::rt::spawn(async move {
let request_context = RequestContext {
is_embedded: req_param.get_variables.contains_key("_sqlpage_embed"),
source_path,
content_security_policy: ContentSecurityPolicy::with_random_nonce(),
};
let mut conn = None;
let database_entries_stream =
stream_query_results_with_conn(&sql_file, &mut req_param, &mut conn);
let database_entries_stream = stop_at_first_error(database_entries_stream);
let req_param = Arc::new(req_param);
let response_with_writer = build_response_header_and_stream(
Arc::clone(&app_state),
database_entries_stream,
request_context,
Arc::clone(&req_param),
)
.await;
match response_with_writer {
Expand Down
8 changes: 8 additions & 0 deletions src/webserver/http_request_info.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use crate::webserver::content_security_policy::ContentSecurityPolicy;
use crate::AppState;
use actix_multipart::form::bytes::Bytes;
use actix_multipart::form::tempfile::TempFile;
Expand Down Expand Up @@ -42,6 +43,7 @@ pub struct RequestInfo {
pub clone_depth: u8,
pub raw_body: Option<Vec<u8>>,
pub oidc_claims: Option<OidcClaims>,
pub content_security_policy: ContentSecurityPolicy,
}

impl RequestInfo {
Expand All @@ -62,8 +64,13 @@ impl RequestInfo {
clone_depth: self.clone_depth + 1,
raw_body: self.raw_body.clone(),
oidc_claims: self.oidc_claims.clone(),
content_security_policy: self.content_security_policy.clone(),
}
}

pub fn is_embedded(&self) -> bool {
self.get_variables.contains_key("_sqlpage_embed")
}
}

impl Clone for RequestInfo {
Expand Down Expand Up @@ -123,6 +130,7 @@ pub(crate) async fn extract_request_info(
clone_depth: 0,
raw_body,
oidc_claims,
content_security_policy: ContentSecurityPolicy::with_random_nonce()
})
}

Expand Down
Loading