upleb.uk

Public git repos — served from a NIP-34 GRASP relay at git.upleb.uk

summaryrefslogtreecommitdiff
path: root/src/http
diff options
context:
space:
mode:
authorDanConwayDev <DanConwayDev@protonmail.com>2025-11-26 03:53:31 +0000
committerDanConwayDev <DanConwayDev@protonmail.com>2025-11-26 03:53:31 +0000
commit963b2971ec2f43b1c2f669a969c294fc1d291d3b (patch)
tree9495d5bd350ebf5123a9c72b9da3367b17e5534f /src/http
parent75f3da90edb66b81dbb6ed9806155f6bd7925fe1 (diff)
add cors support
Diffstat (limited to 'src/http')
-rw-r--r--src/http/mod.rs46
1 files changed, 41 insertions, 5 deletions
diff --git a/src/http/mod.rs b/src/http/mod.rs
index c676bda..85b72f4 100644
--- a/src/http/mod.rs
+++ b/src/http/mod.rs
@@ -24,6 +24,19 @@ use base64::Engine;
24use crate::config::Config; 24use crate::config::Config;
25use crate::git; 25use crate::git;
26 26
27/// CORS headers required by GRASP-01 specification (lines 40-47)
28const CORS_ALLOW_ORIGIN: &str = "*";
29const CORS_ALLOW_METHODS: &str = "GET, POST";
30const CORS_ALLOW_HEADERS: &str = "Content-Type";
31
32/// Add CORS headers to a response builder
33fn add_cors_headers(builder: hyper::http::response::Builder) -> hyper::http::response::Builder {
34 builder
35 .header("Access-Control-Allow-Origin", CORS_ALLOW_ORIGIN)
36 .header("Access-Control-Allow-Methods", CORS_ALLOW_METHODS)
37 .header("Access-Control-Allow-Headers", CORS_ALLOW_HEADERS)
38}
39
27/// HTTP Service that serves both WebSocket (relay) and HTML landing page 40/// HTTP Service that serves both WebSocket (relay) and HTML landing page
28struct HttpService { 41struct HttpService {
29 relay: LocalRelay, 42 relay: LocalRelay,
@@ -47,12 +60,23 @@ impl Service<Request<Incoming>> for HttpService {
47 type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>> + Send>>; 60 type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>> + Send>>;
48 61
49 fn call(&self, req: Request<Incoming>) -> Self::Future { 62 fn call(&self, req: Request<Incoming>) -> Self::Future {
50 let base = Response::builder().header("server", "ngit-grasp"); 63 let base = add_cors_headers(Response::builder().header("server", "ngit-grasp"));
51 let path = req.uri().path().to_string(); 64 let path = req.uri().path().to_string();
52 let query = req.uri().query().map(|s| s.to_string()); 65 let query = req.uri().query().map(|s| s.to_string());
53 let method = req.method().clone(); 66 let method = req.method().clone();
54 let git_data_path = self.config.git_data_path.clone(); 67 let git_data_path = self.config.git_data_path.clone();
55 68
69 // Handle OPTIONS preflight requests (CORS)
70 // GRASP-01 spec line 47: Respond to OPTIONS with 204 No Content
71 if method == Method::OPTIONS {
72 return Box::pin(async move {
73 Ok(add_cors_headers(Response::builder().header("server", "ngit-grasp"))
74 .status(204)
75 .body(Full::new(Bytes::new()))
76 .unwrap())
77 });
78 }
79
56 // Check for Git HTTP requests first 80 // Check for Git HTTP requests first
57 if let Some((npub, identifier, subpath)) = git::parse_git_url(&path) { 81 if let Some((npub, identifier, subpath)) = git::parse_git_url(&path) {
58 let npub = npub.to_string(); 82 let npub = npub.to_string();
@@ -104,11 +128,24 @@ impl Service<Request<Incoming>> for HttpService {
104 }; 128 };
105 129
106 match result { 130 match result {
107 Ok(response) => Ok(response), 131 Ok(response) => {
132 // Add CORS headers to successful Git responses
133 let (parts, body) = response.into_parts();
134 Ok(add_cors_headers(Response::builder()
135 .status(parts.status))
136 .header("content-type", parts.headers.get("content-type")
137 .and_then(|v| v.to_str().ok())
138 .unwrap_or("application/octet-stream"))
139 .header("cache-control", parts.headers.get("cache-control")
140 .and_then(|v| v.to_str().ok())
141 .unwrap_or("no-cache"))
142 .body(body)
143 .unwrap())
144 }
108 Err(e) => { 145 Err(e) => {
109 tracing::error!("Git handler error: {}", e); 146 tracing::error!("Git handler error: {}", e);
110 let error_msg = format!("Git error: {}", e); 147 let error_msg = format!("Git error: {}", e);
111 Ok(Response::builder() 148 Ok(add_cors_headers(Response::builder())
112 .status(e.status_code()) 149 .status(e.status_code())
113 .body(Full::new(Bytes::from(error_msg))) 150 .body(Full::new(Bytes::from(error_msg)))
114 .unwrap()) 151 .unwrap())
@@ -133,10 +170,9 @@ impl Service<Request<Incoming>> for HttpService {
133 tracing::debug!("Serving NIP-11 relay information document to {}", self.remote); 170 tracing::debug!("Serving NIP-11 relay information document to {}", self.remote);
134 171
135 return Box::pin(async move { 172 return Box::pin(async move {
136 Ok(base 173 Ok(add_cors_headers(Response::builder().header("server", "ngit-grasp"))
137 .status(200) 174 .status(200)
138 .header("content-type", "application/nostr+json") 175 .header("content-type", "application/nostr+json")
139 .header("access-control-allow-origin", "*")
140 .body(Full::new(Bytes::from(json))) 176 .body(Full::new(Bytes::from(json)))
141 .unwrap()) 177 .unwrap())
142 }); 178 });