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-21 13:37:57 +0000
committerDanConwayDev <DanConwayDev@protonmail.com>2025-11-21 13:37:57 +0000
commit9a8c551adfada379704d594a6ff3862f13857b8e (patch)
treea902c6b313ab40a8914a34380ee194524dd67604 /src/http
parent12756fa66e3ec7f9dd24c66598085772829a8063 (diff)
add git http handling
Diffstat (limited to 'src/http')
-rw-r--r--src/http/mod.rs78
1 files changed, 76 insertions, 2 deletions
diff --git a/src/http/mod.rs b/src/http/mod.rs
index 7c0e7bb..28ccd7b 100644
--- a/src/http/mod.rs
+++ b/src/http/mod.rs
@@ -8,12 +8,13 @@ use std::future::Future;
8use std::net::SocketAddr; 8use std::net::SocketAddr;
9use std::pin::Pin; 9use std::pin::Pin;
10 10
11use hyper::body::Incoming; 11use hyper::body::{Bytes, Incoming};
12use hyper::header::{CONNECTION, SEC_WEBSOCKET_ACCEPT, UPGRADE}; 12use hyper::header::{CONNECTION, SEC_WEBSOCKET_ACCEPT, UPGRADE};
13use hyper::server::conn::http1; 13use hyper::server::conn::http1;
14use hyper::service::Service; 14use hyper::service::Service;
15use hyper::{Request, Response}; 15use hyper::{Method, Request, Response};
16use hyper_util::rt::TokioIo; 16use hyper_util::rt::TokioIo;
17use http_body_util::BodyExt;
17use nostr_sdk::hashes::sha1::Hash as Sha1Hash; 18use nostr_sdk::hashes::sha1::Hash as Sha1Hash;
18use nostr_sdk::hashes::{Hash, HashEngine}; 19use nostr_sdk::hashes::{Hash, HashEngine};
19use nostr_relay_builder::LocalRelay; 20use nostr_relay_builder::LocalRelay;
@@ -21,6 +22,7 @@ use tokio::net::TcpListener;
21use base64::Engine; 22use base64::Engine;
22 23
23use crate::config::Config; 24use crate::config::Config;
25use crate::git;
24 26
25/// HTTP Service that serves both WebSocket (relay) and HTML landing page 27/// HTTP Service that serves both WebSocket (relay) and HTML landing page
26struct HttpService { 28struct HttpService {
@@ -46,6 +48,78 @@ impl Service<Request<Incoming>> for HttpService {
46 48
47 fn call(&self, req: Request<Incoming>) -> Self::Future { 49 fn call(&self, req: Request<Incoming>) -> Self::Future {
48 let base = Response::builder().header("server", "ngit-grasp"); 50 let base = Response::builder().header("server", "ngit-grasp");
51 let path = req.uri().path().to_string();
52 let query = req.uri().query().map(|s| s.to_string());
53 let method = req.method().clone();
54 let git_data_path = self.config.git_data_path.clone();
55
56 // Check for Git HTTP requests first
57 if let Some((npub, identifier, subpath)) = git::parse_git_url(&path) {
58 let npub = npub.to_string();
59 let identifier = identifier.to_string();
60 let subpath = subpath.to_string();
61
62 tracing::debug!("Git request: {} {} (npub={}, id={}, subpath={})",
63 method, path, npub, identifier, subpath);
64
65 let repo_path = git::resolve_repo_path(&git_data_path, &npub, &identifier);
66
67 return Box::pin(async move {
68 let result = match (method.as_ref(), subpath.as_str()) {
69 // GET /info/refs?service=git-upload-pack or git-receive-pack
70 (m, sp) if m == Method::GET && sp.starts_with("info/refs") => {
71 // Parse query string for service parameter
72 let service = query.as_deref().unwrap_or("")
73 .strip_prefix("service=")
74 .and_then(git::protocol::GitService::from_query_param);
75
76 match service {
77 Some(svc) => {
78 git::handlers::handle_info_refs(repo_path, svc).await
79 }
80 None => {
81 Err(git::handlers::GitError::RepositoryNotFound)
82 }
83 }
84 }
85
86 // POST /git-upload-pack (clone/fetch)
87 (m, "git-upload-pack") if m == Method::POST => {
88 // Read request body
89 let body_bytes = req.collect().await
90 .map(|collected| collected.to_bytes())
91 .unwrap_or_else(|_| Bytes::new());
92
93 git::handlers::handle_upload_pack(repo_path, body_bytes).await
94 }
95
96 // POST /git-receive-pack (push)
97 (m, "git-receive-pack") if m == Method::POST => {
98 // Read request body
99 let body_bytes = req.collect().await
100 .map(|collected| collected.to_bytes())
101 .unwrap_or_else(|_| Bytes::new());
102
103 git::handlers::handle_receive_pack(repo_path, body_bytes).await
104 }
105
106 _ => {
107 Err(git::handlers::GitError::RepositoryNotFound)
108 }
109 };
110
111 match result {
112 Ok(response) => Ok(response),
113 Err(e) => {
114 tracing::error!("Git handler error: {}", e);
115 Ok(Response::builder()
116 .status(e.status_code())
117 .body(format!("Git error: {}", e))
118 .unwrap())
119 }
120 }
121 });
122 }
49 123
50 // Check for NIP-11 relay information request (Accept: application/nostr+json) 124 // Check for NIP-11 relay information request (Accept: application/nostr+json)
51 if let Some(accept) = req.headers().get("accept") { 125 if let Some(accept) = req.headers().get("accept") {