Repository navigation
Expand file tree
/
Copy pathupload_pack_service.rs
More file actions
117 lines (114 loc) · 3.94 KB
/
Copy pathupload_pack_service.rs
File metadata and controls
117 lines (114 loc) · 3.94 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
use std::env;
use std::io::{self, Read, Write};
use std::path::Path;
use git_rs::{
HostFileSystem, Repository, UploadPackOptions, UploadPackRequest, UploadPackV2Limits,
UploadPackV2Request,
};
fn main() -> git_rs::Result<()> {
let repository_path = env::args()
.nth(1)
.expect("usage: upload_pack_service <repository>");
let repository_path = std::fs::canonicalize(Path::new(&repository_path))?;
let repository = Repository::open(
HostFileSystem::new(repository_path.parent().expect("repository parent"))?,
repository_path.file_name().expect("repository name"),
)?;
let mut stdout = io::stdout().lock();
if env::var("GIT_PROTOCOL").is_ok_and(|value| value.split(':').any(|item| item == "version=2"))
{
stdout.write_all(&repository.advertise_upload_pack_v2()?)?;
stdout.flush()?;
let limits = UploadPackV2Limits::default();
let mut stdin = io::stdin().lock();
while let Some(input) = read_v2_request(&mut stdin)? {
if input == b"0000" {
break;
}
let request = UploadPackV2Request::parse(&input, &limits)?;
stdout.write_all(&repository.respond_upload_pack_v2(
&request,
&UploadPackOptions::default(),
&limits,
)?)?;
stdout.flush()?;
}
return Ok(());
}
stdout.write_all(&repository.advertise_upload_pack()?)?;
stdout.flush()?;
let mut input = Vec::new();
let mut stdin = io::stdin().lock();
let mut saw_want = false;
loop {
let mut header = [0; 4];
if let Err(error) = stdin.read_exact(&mut header) {
if error.kind() == io::ErrorKind::UnexpectedEof {
break;
}
return Err(error.into());
}
input.extend_from_slice(&header);
let length = usize::from_str_radix(
std::str::from_utf8(&header)
.map_err(|_| git_rs::Error::Protocol("packet header is not ASCII".into()))?,
16,
)
.map_err(|_| git_rs::Error::Protocol("invalid packet header".into()))?;
if length == 0 {
if !saw_want {
break;
}
continue;
}
if length < 4 {
return Err(git_rs::Error::Protocol("invalid control packet".into()));
}
let mut payload = vec![0; length - 4];
stdin.read_exact(&mut payload)?;
saw_want |= payload.starts_with(b"want ");
let done = payload == b"done\n" || payload == b"done";
input.extend_from_slice(&payload);
if done {
break;
}
}
if !input.is_empty() {
let request = UploadPackRequest::parse(&input)?;
stdout
.write_all(&repository.respond_upload_pack(&request, &UploadPackOptions::default())?)?;
}
Ok(())
}
fn read_v2_request(input: &mut impl Read) -> git_rs::Result<Option<Vec<u8>>> {
let mut request = Vec::new();
loop {
let mut header = [0; 4];
if let Err(error) = input.read_exact(&mut header) {
return if error.kind() == io::ErrorKind::UnexpectedEof {
Ok(None)
} else {
Err(error.into())
};
}
request.extend_from_slice(&header);
let length = usize::from_str_radix(
std::str::from_utf8(&header)
.map_err(|_| git_rs::Error::Protocol("packet header is not ASCII".into()))?,
16,
)
.map_err(|_| git_rs::Error::Protocol("invalid packet header".into()))?;
if length == 0 {
return Ok(Some(request));
}
if length <= 2 {
continue;
}
if length < 4 {
return Err(git_rs::Error::Protocol("invalid packet length".into()));
}
let mut payload = vec![0; length - 4];
input.read_exact(&mut payload)?;
request.extend_from_slice(&payload);
}
}