Skip to content

Commit aadce60

Browse files
committed
remove some useless string copies
we are still re-parsing the csp template on every request
1 parent 93aa1b7 commit aadce60

File tree

2 files changed

+18
-58
lines changed

2 files changed

+18
-58
lines changed

src/webserver/content_security_policy.rs

Lines changed: 17 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -5,20 +5,23 @@ use actix_web::HttpResponseBuilder;
55
use awc::http::header::InvalidHeaderValue;
66
use rand::random;
77
use std::fmt::{Display, Formatter};
8+
use std::sync::Arc;
9+
10+
use crate::AppState;
811

912
pub const DEFAULT_CONTENT_SECURITY_POLICY: &str = "script-src 'self' 'nonce-{NONCE}'";
1013

1114
#[derive(Debug, Clone)]
1215
pub struct ContentSecurityPolicy {
1316
pub nonce: u64,
14-
policy: String,
17+
app_state: Arc<AppState>,
1518
}
1619

1720
impl ContentSecurityPolicy {
18-
pub fn new<S: Into<String>>(policy: S) -> Self {
21+
pub fn new(app_state: Arc<AppState>) -> Self {
1922
Self {
2023
nonce: random(),
21-
policy: policy.into(),
24+
app_state,
2225
}
2326
}
2427

@@ -28,21 +31,23 @@ impl ContentSecurityPolicy {
2831
}
2932
}
3033

31-
fn is_enabled(&self) -> bool {
32-
!self.policy.is_empty()
34+
fn template_string(&self) -> &str {
35+
&self.app_state.config.content_security_policy
3336
}
3437

35-
#[allow(dead_code)]
36-
fn set_nonce(&mut self, nonce: u64) {
37-
self.nonce = nonce;
38+
fn is_enabled(&self) -> bool {
39+
!self.app_state.config.content_security_policy.is_empty()
3840
}
3941
}
4042

4143
impl Display for ContentSecurityPolicy {
4244
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
43-
let value = self.policy.replace("{NONCE}", &self.nonce.to_string());
44-
45-
write!(f, "{value}")
45+
let template = self.template_string();
46+
if let Some((before, after)) = template.split_once("{NONCE}") {
47+
write!(f, "{before}{nonce}{after}", nonce = self.nonce)
48+
} else {
49+
write!(f, "{}", template)
50+
}
4651
}
4752
}
4853

@@ -52,50 +57,7 @@ impl TryIntoHeaderPair for &ContentSecurityPolicy {
5257
fn try_into_pair(self) -> Result<(HeaderName, HeaderValue), Self::Error> {
5358
Ok((
5459
CONTENT_SECURITY_POLICY,
55-
HeaderValue::from_str(&self.to_string())?,
60+
HeaderValue::from_maybe_shared(self.to_string())?,
5661
))
5762
}
5863
}
59-
60-
#[cfg(test)]
61-
mod tests {
62-
use super::*;
63-
64-
#[test]
65-
fn default_csp_response_contains_random_nonce() {
66-
let mut csp = ContentSecurityPolicy::new(DEFAULT_CONTENT_SECURITY_POLICY);
67-
csp.set_nonce(0);
68-
69-
assert!(csp.is_enabled());
70-
assert_eq!(&csp.to_string(), "script-src 'self' 'nonce-0'");
71-
}
72-
73-
#[test]
74-
fn custom_csp_response_without_nonce() {
75-
let csp = ContentSecurityPolicy::new("object-src 'none';");
76-
77-
assert!(csp.is_enabled());
78-
assert_eq!("object-src 'none';", &csp.to_string());
79-
}
80-
81-
#[test]
82-
fn blank_csp_response() {
83-
let csp = ContentSecurityPolicy::new("");
84-
85-
assert!(!csp.is_enabled());
86-
assert_eq!("", &csp.to_string());
87-
}
88-
89-
#[test]
90-
fn custom_csp_with_nonce() {
91-
let mut csp =
92-
ContentSecurityPolicy::new("script-src 'self' 'nonce-{NONCE}'; object-src 'none';");
93-
csp.set_nonce(0);
94-
95-
assert!(csp.is_enabled());
96-
assert_eq!(
97-
"script-src 'self' 'nonce-0'; object-src 'none';",
98-
csp.to_string().as_str()
99-
);
100-
}
101-
}

src/webserver/http.rs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -177,9 +177,7 @@ async fn render_sql(
177177
actix_web::rt::spawn(async move {
178178
let request_context = RequestContext {
179179
is_embedded: req_param.get_variables.contains_key("_sqlpage_embed"),
180-
content_security_policy: ContentSecurityPolicy::new(
181-
&app_state.config.content_security_policy,
182-
),
180+
content_security_policy: ContentSecurityPolicy::new(Arc::clone(&app_state)),
183181
};
184182
let mut conn = None;
185183
let database_entries_stream =

0 commit comments

Comments
 (0)