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
#![deny(warnings)]
#![warn(rust_2018_idioms)]
use std::collections::HashMap;
use std::io;
use std::io::prelude::*;
use std::path::PathBuf;
use std::process::{Command, Stdio};
use conduit::{box_error, header, Body, HandlerResult, RequestExt, Response};
use flate2::read::GzDecoder;
pub struct Serve(pub PathBuf);
impl Serve {
fn doit(&self, req: &mut dyn RequestExt) -> io::Result<Response<Body>> {
let mut cmd = Command::new("git");
cmd.arg("http-backend");
cmd.env("REQUEST_METHOD", req.method().as_str());
cmd.env("GIT_PROJECT_ROOT", &self.0);
cmd.env(
"PATH_INFO",
if req.path().starts_with('/') {
req.path().to_string()
} else {
format!("/{}", req.path())
},
);
cmd.env("REMOTE_USER", "");
cmd.env("REMOTE_ADDR", req.remote_addr().to_string());
cmd.env("QUERY_STRING", req.query_string().unwrap_or_default());
cmd.env("CONTENT_TYPE", header(req, header::CONTENT_TYPE));
cmd.stderr(Stdio::inherit())
.stdout(Stdio::piped())
.stdin(Stdio::piped());
let mut p = cmd.spawn()?;
if header(req, header::CONTENT_ENCODING) == "gzip" {
let mut body = GzDecoder::new(req.body());
io::copy(&mut body, &mut p.stdin.take().unwrap())?;
} else {
io::copy(&mut req.body(), &mut p.stdin.take().unwrap())?;
}
let mut rdr = io::BufReader::new(p.stdout.take().unwrap());
let mut headers = HashMap::new();
for line in rdr.by_ref().lines() {
let line = line?;
if line == "" || line == "\r" {
break;
}
let mut parts = line.splitn(2, ':');
let key = parts.next().unwrap();
let value = parts.next().unwrap();
let value = &value[1..];
headers
.entry(key.to_string())
.or_insert_with(Vec::new)
.push(value.to_string());
}
let status_code = {
let line = headers.remove("Status").unwrap_or_default();
let line = line.into_iter().next().unwrap_or_default();
let mut parts = line.splitn(1, ' ');
parts.next().unwrap_or("").parse().unwrap_or(200)
};
let mut builder = Response::builder().status(status_code);
for (name, vec) in headers.iter() {
for value in vec {
builder = builder.header(name, value);
}
}
let mut body = Vec::new();
rdr.read_to_end(&mut body)?;
return Ok(builder.body(Body::from_vec(body)).unwrap());
fn header(req: &dyn RequestExt, name: header::HeaderName) -> &str {
req.headers()
.get(name)
.map(|value| value.to_str().unwrap_or_default())
.unwrap_or_default()
}
}
}
impl conduit::Handler for Serve {
fn call(&self, req: &mut dyn RequestExt) -> HandlerResult {
self.doit(req).map_err(box_error)
}
}