Repositories

grarr

(mirrored on github)

Wim Looman <wim@nemo157.com>
6e323f Pull refs handler out to helper
Wim Looman committed at 2017-05-21 09:37:31

Modified Cargo.lock

@@ -27,6 +27,7 @@ dependencies = [
"toml 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"typemap 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
"unicase 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"url 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"walkdir 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)",
]

Modified Cargo.toml

@@ -27,6 +27,7 @@ time = "0.1.36"
toml = "0.4"
typemap = "0.3"
unicase = "1.4"
url = "1.4.0"
walkdir = "1.0"
[dependencies.git-appraise]

Modified src/git_ship/capability.rs

@@ -1,3 +1,4 @@
use std::fmt;
use std::str::FromStr;
#[derive(Debug, Eq, PartialEq, Clone, Copy)]
@@ -17,6 +18,10 @@ pub enum Capability {
pub struct Capabilities(Vec<Capability>);
impl Capabilities {
pub fn new(caps: Vec<Capability>) -> Capabilities {
Capabilities(caps)
}
pub fn empty() -> Capabilities {
Capabilities(Vec::new())
}
@@ -39,6 +44,18 @@ impl FromStr for Capability {
}
}
impl fmt::Display for Capability {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str(match *self {
Capability::SideBand => "side-band",
Capability::SideBand64K => "side-band-64k",
Capability::MultiAck => "multi_ack",
Capability::MultiAckDetailed => "multi_ack_detailed",
Capability::Unknown(ref s) => &**s,
})
}
}
impl FromStr for Capabilities {
type Err = Void;
fn from_str(s: &str) -> Result<Self, Self::Err> {
@@ -49,3 +66,13 @@ impl FromStr for Capabilities {
.collect()))
}
}
impl fmt::Display for Capabilities {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
for cap in &self.0 {
fmt::Display::fmt(cap, f)?;
f.write_str(" ")?;
}
Ok(())
}
}

Modified src/git_ship/mod.rs

@@ -1,10 +1,13 @@
extern crate url;
extern crate git2;
pub mod multiplexer;
pub mod pkt_line;
pub mod capability;
pub mod refs;
pub use self::multiplexer::Multiplexer;
pub use self::pkt_line::PktLine;
pub use self::capability::{Capability, Capabilities};
// pub use self::refs::Refs;
// pub use self::upload_pack::UploadPack;

Added src/git_ship/refs.rs

@@ -0,0 +1,94 @@
use std::io;
use std::borrow::Borrow;
use super::url::Url;
use super::git2::{self, Oid};
use super::{pkt_line, Capability, Capabilities};
#[derive(Debug)]
pub struct UploadPack {
head: Oid,
refs: Vec<(String, Oid)>,
capabilities: Capabilities,
}
#[derive(Debug)]
pub enum Response {
UploadPack(UploadPack),
Error(&'static str),
}
pub fn prepare(repo: &git2::Repository, url: &Url) -> Result<Response, git2::Error> {
let service = url.query_pairs()
.find(|&(ref key, _)| key == "service")
.map(|(_, id)| id.clone());
let service = service.as_ref().map(Borrow::borrow);
match service {
Some("git-upload-pack") => {
let head = repo.head()?.target().expect("TODO: Better handling of non-HEAD containing repos");
let refs = repo.references()?
.map(|r| {
let r = r?;
let name = r.name()
.expect("TODO: Better handling of non-unicode refs")
.to_owned();
let target = r.resolve()?
.target()
.expect("Resolved references always have a target");
Ok((name, target))
})
.collect::<Result<Vec<_>, git2::Error>>()?;
// TODO: Sort refs by name in C locale
let capabilities = Capabilities::new(vec![
Capability::SideBand,
Capability::SideBand64K,
Capability::MultiAck,
Capability::MultiAckDetailed,
]);
Ok(Response::UploadPack(UploadPack {
head: head,
refs: refs,
capabilities: capabilities,
}))
}
Some(_) => Ok(Response::Error("Unknown git service name")),
None => Ok(Response::Error("Please upgrade your git client.")),
}
}
impl UploadPack {
pub fn write_to(&self, mut writer: &mut io::Write) -> io::Result<()> {
pkt_line::write_str(&mut writer, "# service=git-upload-pack")?;
pkt_line::flush(&mut writer)?;
pkt_line::write_str(&mut writer, format!("{} HEAD\0{}", self.head, self.capabilities))?;
for &(ref name, ref target) in &self.refs {
pkt_line::write_str(&mut writer, format!("{} {}", target, name))?;
}
pkt_line::flush(&mut writer)?;
Ok(())
}
}
impl Response {
pub fn status_code(&self) -> u16 {
match *self {
Response::UploadPack(_) => 200,
Response::Error(_) => 403,
}
}
pub fn mime_type(&self) -> &'static str {
match *self {
Response::UploadPack(_) => "application/x-git-upload-pack-advertisement",
Response::Error(_) => "text/plain; charset=utf-8",
}
}
pub fn write_to(&self, mut writer: &mut io::Write) -> io::Result<()> {
match *self {
Response::UploadPack(ref pack) => pack.write_to(writer),
Response::Error(ref msg) => writer.write_all(msg.as_bytes()),
}
}
}

Modified src/handler/git_smart_http/refs.rs

@@ -1,67 +1,28 @@
use handler::base::*;
use git_ship::pkt_line;
use std::io;
use git2;
use handler::base::*;
use git_ship::refs;
use iron::mime::Mime;
use iron::response::WriteBody;
#[derive(Clone)]
pub struct Refs;
fn format_ref(reff: git2::Reference) -> Result<String, Error> {
let target = try!(try!(reff.resolve()).target().ok_or(Error::from("Ref missing target")));
let name = try!(reff.name().ok_or(Error::from("Ref missing name")));
Ok(format!("{} {}", target, name))
}
fn format_refs(head: git2::Reference, refs: git2::References) -> Result<Vec<u8>, Error> {
let mut result = Vec::new();
pkt_line::write_str(&mut result, "# service=git-upload-pack")?;
pkt_line::flush(&mut result)?;
let head_id = head.target().ok_or(Error::from("HEAD missing target"))?;
pkt_line::write_str(&mut result, format!("{} HEAD\0{}", head_id, "side-band side-band-64k multi_ack_detailed"))?;
// TODO: Sort refs by name in C locale
for reff in refs {
pkt_line::write_str(&mut result, try!(format_ref(try!(reff))))?;
}
pkt_line::flush(&mut result)?;
Ok(result)
}
#[allow(deprecated)]
fn find_service(req: &Request) -> Option<String> {
req.url.clone().into_generic_url()
.query_pairs()
.into_iter()
.find(|&(ref key, _)| key == "service")
.map(|(_, ref id)| id.to_string())
}
impl Handler for Refs {
fn handle(&self, req: &mut Request) -> IronResult<Response> {
let context = itry!(req.extensions.get::<RepositoryContext>().ok_or(Error::from("missing extension")), status::InternalServerError);
match find_service(req).as_ref().map(|s| &**s) {
Some("git-upload-pack") => {
let head = itry!(context.repository.head());
let refs = itry!(context.repository.references());
let buffer = itry!(format_refs(head, refs), status::InternalServerError);
Ok(Response::with((
status::Ok,
mime!(Application/("x-git-upload-pack-advertisement")),
buffer)))
},
Some(_) => {
Ok(Response::with((
status::Forbidden,
mime!(Text/Plain; Charset=Utf8),
"Unknown git service name")))
}
None => {
// Assumed dumb client
Ok(Response::with((
status::Forbidden,
mime!(Text/Plain; Charset=Utf8),
"Please upgrade your git client.")))
}
}
let response = itry!(refs::prepare(&context.repository, &req.url.clone().into()), status::InternalServerError);
println!("response: {:?}", response);
let status_code = status::Unregistered(response.status_code());
let mime: Mime = response.mime_type().parse().unwrap();
Ok(Response::with((status_code, mime, Box::new(W(response)) as Box<WriteBody>)))
}
}
struct W(refs::Response);
impl WriteBody for W {
fn write_body(&mut self, res: &mut io::Write) -> io::Result<()> {
self.0.write_to(res)
}
}

Modified src/main.rs

@@ -4,7 +4,6 @@
#![warn(unsafe_code)]
#![warn(unused_extern_crates)]
#![warn(unused_qualifications)]
#![warn(variant_size_differences)]
#![allow(unknown_lints)]
extern crate ammonia;