Repositories

grarr

(mirrored on github)

Wim Looman <wim@nemo157.com>
e20bca Get sideband progress going
Wim Looman committed at 2016-05-20 23:03:25

Little bit silly when the response is buffered, but oh well, that will hopefully get fixed at some point

Modified src/handler/git_smart_http/refs.rs

@@ -17,7 +17,7 @@ fn format_refs(head: git2::Reference, refs: git2::References) -> Result<Vec<u8>,
try!(result.write_pkt_line("# service=git-upload-pack"));
try!(result.write_pkt_line_flush());
let head_id = try!(head.target().ok_or(Error::from("HEAD missing target")));
try!(result.write_pkt_line(format!("{} HEAD\0{}", head_id, "i-am-incapable")));
try!(result.write_pkt_line(format!("{} HEAD\0{}", head_id, "side-band side-band-64k")));
// TODO: Sort refs by name in C locale
for reff in refs {
try!(result.write_pkt_line(try!(format_ref(try!(reff)))));

Modified src/handler/git_smart_http/upload_pack.rs

@@ -15,11 +15,18 @@ use git2::{ self, Oid, Repository, Buf };
#[derive(Clone)]
pub struct UploadPack;
#[derive(Debug, Eq, PartialEq)]
enum Capability {
SideBand,
SideBand64K,
Unknown(String),
}
#[derive(Debug)]
struct UploadPackRequest {
wants: Vec<Oid>,
haves: Vec<Oid>,
capabilities: Vec<String>,
capabilities: Vec<Capability>,
done: bool,
}
@@ -60,11 +67,10 @@ fn parse_request(req: &mut Request) -> Result<UploadPackRequest, Error> {
if line.len() < 4 { continue }
match &line[0..4] {
"want" => {
let end = line.find(|c| c == '\n' || c == '\0').unwrap_or(line.len());
request.wants.push(try!(line[5..end].parse()));
if let Some(nul) = line.find('\0') {
request.capabilities.extend(line[nul..].trim().split(' ').map(ToOwned::to_owned));
}
let line = line[5..].trim();
let (want, caps) = line.split_at(line.find(' ').unwrap_or(line.len()));
request.wants.push(try!(want.parse()));
request.capabilities.extend(caps.split(' ').map(Capability::from));
},
"have" => {
let end = line.find(|c| c == '\n' || c == '\0').unwrap_or(line.len());
@@ -161,17 +167,22 @@ fn compute_response(context: &UploadPackContext, request: &UploadPackRequest) ->
}
}
fn build_pack(repository: &Repository, commits: Vec<Oid>) -> Result<Vec<u8>, Error> {
fn build_pack(repository: &Repository, commits: Vec<Oid>, mut output: Multiplexer) -> Result<(), Error> {
let mut builder = try!(repository.packbuilder());
for id in commits {
try!(builder.insert_commit(id));
}
let mut buf = Vec::new();
let mut i = 0;
try!(builder.foreach(|object| {
buf.write_all(object).unwrap();
i += 1;
write!(output.progress(), "\rpacked object {}", i).unwrap();
output.packfile().write_all(object).unwrap();
true
}));
Ok(buf)
try!(write!(output.progress(), "\r\n"));
try!(write!(output.progress(), "packed all {} objects\r\n", i));
try!(output.close());
Ok(())
}
impl Handler for UploadPack {
@@ -195,7 +206,18 @@ impl Handler for UploadPack {
let result = itry!(compute_response(&context2, &request), status::InternalServerError);
match result {
UploadPackResponse::Pack(commits) => {
let pack = itry!(build_pack(&context.repository, commits), status::InternalServerError);
let mut pack = Vec::new();
{
let limit = if request.capabilities.contains(&Capability::SideBand64K) {
Some(65520)
} else if request.capabilities.contains(&Capability::SideBand) {
Some(1000)
} else {
None
};
let output = Multiplexer::new(&mut pack, limit);
itry!(build_pack(&context.repository, commits, output), status::InternalServerError);
}
println!("built {} byte pack", pack.len());
let mut response = Vec::with_capacity(pack.len() + 8);
response.write_pkt_line("NAK");
@@ -215,3 +237,13 @@ impl Route for UploadPack {
"/git-upload-pack".into()
}
}
impl<S: AsRef<str>> From<S> for Capability {
fn from(s: S) -> Capability {
match s.as_ref() {
"side-band" => Capability::SideBand,
"side-band-64k" => Capability::SideBand64K,
s => Capability::Unknown(s.to_owned()),
}
}
}

Modified src/handler/git_smart_http/utils.rs

@@ -1,5 +1,52 @@
use std::io::{ self, Write };
use std::str;
use std::cmp;
pub struct Multiplexer<'a> {
stream: &'a mut io::Write,
limit: Option<usize>,
current_band: u8,
}
impl<'a> Multiplexer<'a> {
pub fn new(stream: &mut io::Write, limit: Option<usize>) -> Multiplexer {
Multiplexer { stream: stream, limit: limit, current_band: 0 }
}
pub fn packfile(&mut self) -> &mut io::Write {
self.current_band = 1;
self
}
pub fn progress(&mut self) -> &mut io::Write {
self.current_band = 2;
self
}
pub fn error(&mut self) -> &mut io::Write {
self.current_band = 3;
self
}
pub fn close(&mut self) -> io::Result<()> {
self.stream.write_all(b"0000")
}
}
impl<'a> io::Write for Multiplexer<'a> {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
if let Some(limit) = self.limit {
let len = cmp::min(limit - 2, buf.len());
try!(write!(self.stream, "{:04x}", len + 5));
try!(self.stream.write(&[self.current_band]));
try!(self.stream.write_all(&buf[0..len]));
Ok(len)
} else if self.current_band == 1 {
self.stream.write(buf)
} else {
Ok(buf.len())
}
}
fn flush(&mut self) -> io::Result<()> {
self.stream.flush()
}
}
pub trait WritePktLine {
fn write_pkt_line<S: AsRef<str>>(&mut self, buf: S) -> io::Result<()>;
@@ -7,7 +54,7 @@ pub trait WritePktLine {
fn write_pkt_line_flush(&mut self) -> io::Result<()>;
}
impl WritePktLine for Vec<u8> {
impl<W: io::Write> WritePktLine for W {
fn write_pkt_line<S: AsRef<str>>(&mut self, buf: S) -> io::Result<()> {
let buf = buf.as_ref().as_bytes();
if buf.len() >= 65520 {