|
5 | 5 | use paris::error; |
6 | 6 | use std::path::PathBuf; |
7 | 7 |
|
8 | | -use rouille::{match_assets, Server}; |
| 8 | +use rouille::{match_assets, Response, Server}; |
9 | 9 | use std::sync::mpsc::Sender; |
10 | 10 |
|
11 | 11 | /// An error that can occur while starting the HTTP server. |
@@ -33,13 +33,41 @@ impl ServeStaticFiles { |
33 | 33 | /// Creates a new HTTP server that serves static files from a directory. |
34 | 34 | /// Note: This server is only available for testing purposes. |
35 | 35 | pub fn from(static_path: impl Into<PathBuf>) -> Result<Self, HttpServerError> { |
| 36 | + Self::from_impl(static_path, None) |
| 37 | + } |
| 38 | + |
| 39 | + /// Same as [`Self::from`], but requires `Authorization: Bearer <token>` on every request. |
| 40 | + /// Note: This server is only available for testing purposes. |
| 41 | + pub fn from_with_bearer( |
| 42 | + static_path: impl Into<PathBuf>, |
| 43 | + token: impl Into<String>, |
| 44 | + ) -> Result<Self, HttpServerError> { |
| 45 | + Self::from_impl(static_path, Some(token.into())) |
| 46 | + } |
| 47 | + |
| 48 | + /// Internal helper to keep the original implementation effectively intact. |
| 49 | + fn from_impl( |
| 50 | + static_path: impl Into<PathBuf>, |
| 51 | + token: Option<String>, |
| 52 | + ) -> Result<Self, HttpServerError> { |
36 | 53 | let static_path = static_path.into(); |
| 54 | + |
37 | 55 | let server = Server::new("127.0.0.1:0", move |request| { |
| 56 | + if let Some(token) = token.as_ref() { |
| 57 | + if !request |
| 58 | + .header("Authorization") |
| 59 | + .map(|h| h == format!("Bearer {}", token)) |
| 60 | + .unwrap_or(false) |
| 61 | + { |
| 62 | + return Response::text("Unauthorized").with_status_code(401); |
| 63 | + } |
| 64 | + } |
38 | 65 | match_assets(request, &static_path) |
39 | 66 | }) |
40 | 67 | .map_err(|e| HttpServerError { |
41 | 68 | error: e.to_string(), |
42 | 69 | })?; |
| 70 | + |
43 | 71 | let port = server.server_addr().port(); |
44 | 72 | let (_, kill_switch) = server.stoppable(); |
45 | 73 | Ok(Self { kill_switch, port }) |
@@ -93,4 +121,27 @@ mod tests { |
93 | 121 | assert!(result.is_err()); |
94 | 122 | assert!(matches!(result.unwrap_err(), ureq::Error::Status(404, _))); |
95 | 123 | } |
| 124 | + |
| 125 | + #[test] |
| 126 | + fn test_http_server_with_bearer_auth() { |
| 127 | + let token = "token"; |
| 128 | + let server = ServeStaticFiles::from_with_bearer("tests/test_data", token).unwrap(); |
| 129 | + |
| 130 | + let resp = ureq::get(&server.relative_path_to_url("file_a.yaml")).call(); |
| 131 | + assert!(resp.is_err()); |
| 132 | + assert!(matches!(resp.unwrap_err(), ureq::Error::Status(401, _))); |
| 133 | + |
| 134 | + let resp = ureq::get(&server.relative_path_to_url("file_a.yaml")) |
| 135 | + .set("Authorization", "wrong_token") |
| 136 | + .call(); |
| 137 | + assert!(resp.is_err()); |
| 138 | + assert!(matches!(resp.unwrap_err(), ureq::Error::Status(401, _))); |
| 139 | + |
| 140 | + let content = ureq::get(&server.relative_path_to_url("file_a.yaml")) |
| 141 | + .set("Authorization", &format!("Bearer {}", token)) |
| 142 | + .call() |
| 143 | + .unwrap(); |
| 144 | + assert_eq!(content.status(), 200); |
| 145 | + assert_eq!(content.into_string().unwrap(), "file: A"); |
| 146 | + } |
96 | 147 | } |
0 commit comments