git_internal/protocol/
http.rs1use super::core::{AuthenticationService, GitProtocol, RepositoryAccess};
2use super::types::{ProtocolError, ProtocolStream};
3use serde::Deserialize;
9use std::collections::HashMap;
10
11pub struct HttpGitHandler<R: RepositoryAccess, A: AuthenticationService> {
13 protocol: GitProtocol<R, A>,
14}
15
16impl<R: RepositoryAccess, A: AuthenticationService> HttpGitHandler<R, A> {
17 pub fn new(repo_access: R, auth_service: A) -> Self {
19 let mut protocol = GitProtocol::new(repo_access, auth_service);
20 protocol.set_transport(super::types::TransportProtocol::Http);
21 Self { protocol }
22 }
23
24 pub async fn authenticate_http(
27 &self,
28 headers: &HashMap<String, String>,
29 ) -> Result<(), ProtocolError> {
30 self.protocol.authenticate_http(headers).await
31 }
32
33 pub async fn handle_info_refs(
38 &mut self,
39 request_path: &str,
40 query: &str,
41 ) -> Result<(Vec<u8>, &'static str), ProtocolError> {
42 extract_repo_path(request_path)
44 .ok_or_else(|| ProtocolError::InvalidRequest("Invalid repository path".to_string()))?;
45
46 let service = get_service_from_query(query).ok_or_else(|| {
48 ProtocolError::InvalidRequest("Missing service parameter".to_string())
49 })?;
50
51 if !is_git_request(request_path) {
53 return Err(ProtocolError::InvalidRequest(
54 "Not a Git request".to_string(),
55 ));
56 }
57
58 let response_data = self.protocol.info_refs(service).await?;
59 let content_type = get_advertisement_content_type(service);
60
61 Ok((response_data, content_type))
62 }
63
64 pub async fn handle_upload_pack(
68 &mut self,
69 request_path: &str,
70 request_body: &[u8],
71 ) -> Result<(ProtocolStream, &'static str), ProtocolError> {
72 extract_repo_path(request_path)
74 .ok_or_else(|| ProtocolError::InvalidRequest("Invalid repository path".to_string()))?;
75
76 if !is_git_request(request_path) {
78 return Err(ProtocolError::InvalidRequest(
79 "Not a Git request".to_string(),
80 ));
81 }
82
83 let response_stream = self.protocol.upload_pack(request_body).await?;
84 let content_type = get_content_type("git-upload-pack");
85
86 Ok((response_stream, content_type))
87 }
88
89 pub async fn handle_receive_pack(
93 &mut self,
94 request_path: &str,
95 request_stream: ProtocolStream,
96 ) -> Result<(ProtocolStream, &'static str), ProtocolError> {
97 extract_repo_path(request_path)
99 .ok_or_else(|| ProtocolError::InvalidRequest("Invalid repository path".to_string()))?;
100
101 if !is_git_request(request_path) {
103 return Err(ProtocolError::InvalidRequest(
104 "Not a Git request".to_string(),
105 ));
106 }
107
108 let response_stream = self.protocol.receive_pack(request_stream).await?;
109 let content_type = get_content_type("git-receive-pack");
110
111 Ok((response_stream, content_type))
112 }
113}
114
115pub fn get_content_type(service: &str) -> &'static str {
118 match service {
119 "git-upload-pack" => "application/x-git-upload-pack-result",
120 "git-receive-pack" => "application/x-git-receive-pack-result",
121 _ => "application/x-git-upload-pack-advertisement",
122 }
123}
124
125pub fn get_advertisement_content_type(service: &str) -> &'static str {
127 match service {
128 "git-upload-pack" => "application/x-git-upload-pack-advertisement",
129 "git-receive-pack" => "application/x-git-receive-pack-advertisement",
130 _ => "application/x-git-upload-pack-advertisement",
131 }
132}
133
134pub fn is_git_request(path: &str) -> bool {
136 path.ends_with("/info/refs")
137 || path.ends_with("/git-upload-pack")
138 || path.ends_with("/git-receive-pack")
139}
140
141pub fn extract_repo_path(path: &str) -> Option<&str> {
143 if let Some(pos) = path.rfind("/info/refs") {
144 Some(&path[..pos])
145 } else if let Some(pos) = path.rfind("/git-upload-pack") {
146 Some(&path[..pos])
147 } else if let Some(pos) = path.rfind("/git-receive-pack") {
148 Some(&path[..pos])
149 } else {
150 None
151 }
152}
153
154pub fn get_service_from_query(query: &str) -> Option<&str> {
156 for param in query.split('&') {
157 if let Some(("service", value)) = param.split_once('=') {
158 return Some(value);
159 }
160 }
161 None
162}
163
164#[derive(Debug, Deserialize)]
166pub struct InfoRefsParams {
167 pub service: String,
168}