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
#![deny(warnings)]
extern crate conduit;
extern crate flate2;
use std::ascii::AsciiExt;
use std::collections::HashMap;
use std::error::Error;
use std::io::prelude::*;
use std::io;
use std::path::PathBuf;
use std::process::{Command, Child, Stdio};
use conduit::{Request, Response};
use flate2::read::GzDecoder;
pub struct Serve(pub PathBuf);
impl Serve {
fn doit(&self, req: &mut Request) -> io::Result<Response> {
let mut cmd = Command::new("git");
cmd.arg("http-backend");
cmd.env("REQUEST_METHOD",
&format!("{:?}", req.method()).to_ascii_uppercase());
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(""));
cmd.env("CONTENT_TYPE", header(req, "Content-Type"));
cmd.stderr(Stdio::inherit())
.stdout(Stdio::piped())
.stdin(Stdio::piped());
let mut p = try!(cmd.spawn());
if header(req, "Content-Encoding") == "gzip" {
let mut body = try!(GzDecoder::new(req.body()));
try!(io::copy(&mut body, &mut p.stdin.take().unwrap()));
} else {
try!(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 = try!(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(Vec::new())
.push(value.to_string());
}
let (status_code, status_desc) = {
let line = headers.remove("Status").unwrap_or(Vec::new());
let line = line.into_iter().next().unwrap_or(String::new());
let mut parts = line.splitn(1, ' ');
(parts.next().unwrap_or("").parse().unwrap_or(200),
match parts.next() {
Some("Not Found") => "Not Found",
_ => "Ok",
})
};
struct ProcessAndBuffer<R> { _p: Child, buf: io::BufReader<R> }
impl<R: Read> Read for ProcessAndBuffer<R> {
fn read(&mut self, b: &mut [u8]) -> io::Result<usize> {
self.buf.read(b)
}
}
return Ok(Response {
status: (status_code, status_desc),
headers: headers,
body: Box::new(ProcessAndBuffer { _p: p, buf: rdr }),
});
fn header<'a>(req: &'a Request, name: &str) -> &'a str {
let h = req.headers().find(name).unwrap_or(Vec::new());
h.get(0).map(|s| *s).unwrap_or("")
}
}
}
impl conduit::Handler for Serve {
fn call(&self, req: &mut Request) -> Result<Response, Box<Error+Send>> {
self.doit(req).map_err(|e| Box::new(e) as Box<Error+Send>)
}
}