|
1 | 1 | use anyhow::Context;
|
2 | 2 | use config::Config;
|
| 3 | +use percent_encoding::AsciiSet; |
3 | 4 | use serde::de::Error;
|
4 | 5 | use serde::{Deserialize, Deserializer};
|
5 | 6 | use std::net::{SocketAddr, ToSocketAddrs};
|
@@ -152,21 +153,41 @@ fn deserialize_socket_addr<'de, D: Deserializer<'de>>(
|
152 | 153 | .transpose()
|
153 | 154 | }
|
154 | 155 |
|
155 |
| -/// The site prefix should always start and end with a `/`. |
156 | 156 | fn deserialize_site_prefix<'de, D: Deserializer<'de>>(deserializer: D) -> Result<String, D::Error> {
|
157 |
| - let prefix: Option<String> = Deserialize::deserialize(deserializer)?; |
158 |
| - Ok(prefix.map_or_else( |
159 |
| - || '/'.to_string(), |
160 |
| - |h| { |
161 |
| - std::iter::once("/") |
162 |
| - .chain(percent_encoding::utf8_percent_encode( |
163 |
| - &h, |
164 |
| - percent_encoding::NON_ALPHANUMERIC, |
165 |
| - )) |
166 |
| - .chain(std::iter::once("/")) |
167 |
| - .collect::<String>() |
168 |
| - }, |
169 |
| - )) |
| 157 | + let prefix: String = Deserialize::deserialize(deserializer)?; |
| 158 | + Ok(normalize_site_prefix(prefix.as_str())) |
| 159 | +} |
| 160 | + |
| 161 | +/// We standardize the site prefix to always be stored with both leading and trailing slashes. |
| 162 | +/// We also percent-encode special characters in the prefix, but allow it to contain slashes (to allow |
| 163 | +/// hosting on a sub-sub-path). |
| 164 | +fn normalize_site_prefix(prefix: &str) -> String { |
| 165 | + const TO_ENCODE: AsciiSet = percent_encoding::NON_ALPHANUMERIC.remove(b'/'); |
| 166 | + |
| 167 | + let prefix = prefix.trim_start_matches('/').trim_end_matches('/'); |
| 168 | + if prefix.is_empty() { |
| 169 | + return default_site_prefix(); |
| 170 | + } |
| 171 | + let encoded_prefix = percent_encoding::percent_encode(prefix.as_bytes(), &TO_ENCODE); |
| 172 | + |
| 173 | + std::iter::once("/") |
| 174 | + .chain(encoded_prefix) |
| 175 | + .chain(std::iter::once("/")) |
| 176 | + .collect::<String>() |
| 177 | +} |
| 178 | + |
| 179 | +#[test] |
| 180 | +fn test_normalize_site_prefix() { |
| 181 | + assert_eq!(normalize_site_prefix(""), "/"); |
| 182 | + assert_eq!(normalize_site_prefix("/"), "/"); |
| 183 | + assert_eq!(normalize_site_prefix("a"), "/a/"); |
| 184 | + assert_eq!(normalize_site_prefix("a/"), "/a/"); |
| 185 | + assert_eq!(normalize_site_prefix("/a"), "/a/"); |
| 186 | + assert_eq!(normalize_site_prefix("a/b"), "/a/b/"); |
| 187 | + assert_eq!(normalize_site_prefix("a/b/"), "/a/b/"); |
| 188 | + assert_eq!(normalize_site_prefix("a/b/c"), "/a/b/c/"); |
| 189 | + assert_eq!(normalize_site_prefix("a b"), "/a%20b/"); |
| 190 | + assert_eq!(normalize_site_prefix("a b/c"), "/a%20b/c/"); |
170 | 191 | }
|
171 | 192 |
|
172 | 193 | fn default_site_prefix() -> String {
|
|
0 commit comments