From 1d3db0a13ce130e382b68ff41454545c75e0cc89 Mon Sep 17 00:00:00 2001 From: Onur Aslan Date: Sun, 29 May 2016 21:27:56 +0300 Subject: [PATCH] Add a simple redirector server for rustdocs --- Cargo.lock | 142 +++++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 2 + src/lib.rs | 3 + src/web/mod.rs | 23 ++++++++ src/web/rustdoc.rs | 130 +++++++++++++++++++++++++++++++++++++++++ 5 files changed, 300 insertions(+) create mode 100644 src/web/mod.rs create mode 100644 src/web/rustdoc.rs diff --git a/Cargo.lock b/Cargo.lock index 868fbc4fa..ffc5b4aad 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6,12 +6,14 @@ dependencies = [ "clap 2.5.1 (registry+https://github.com/rust-lang/crates.io-index)", "env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "hyper 0.9.6 (registry+https://github.com/rust-lang/crates.io-index)", + "iron 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "postgres 0.11.8 (registry+https://github.com/rust-lang/crates.io-index)", "regex 0.1.71 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)", "semver 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "slug 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "staticfile 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "tempdir 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", "time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -123,6 +125,14 @@ dependencies = [ "gcc 0.3.28 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "conduit-mime-types" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "cookie" version = "0.2.4" @@ -192,6 +202,15 @@ dependencies = [ "regex 0.1.71 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "error" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "traitobject 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "typeable 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "filetime" version = "0.1.10" @@ -278,6 +297,26 @@ name = "httparse" version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "hyper" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cookie 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", + "httparse 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "language-tags 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "mime 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "num_cpus 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)", + "solicit 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", + "time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)", + "traitobject 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "typeable 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "unicase 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "url 0.5.9 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "hyper" version = "0.9.6" @@ -310,6 +349,23 @@ dependencies = [ "unicode-normalization 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "iron" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "conduit-mime-types 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", + "error 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", + "hyper 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "modifier 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "num_cpus 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)", + "plugin 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", + "typemap 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "url 0.5.9 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "kernel32-sys" version = "0.2.2" @@ -324,6 +380,11 @@ name = "language-tags" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "lazy_static" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "lazy_static" version = "0.2.1" @@ -413,6 +474,20 @@ dependencies = [ "libc 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "modifier" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "mount" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "iron 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "sequence_trie 0.0.13 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "net2" version = "0.2.23" @@ -499,6 +574,14 @@ name = "pkg-config" version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "plugin" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "typemap 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "pnacl-build-helper" version = "1.4.10" @@ -573,6 +656,11 @@ dependencies = [ "nom 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "sequence_trie" +version = "0.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "slug" version = "0.1.1" @@ -590,6 +678,19 @@ dependencies = [ "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "staticfile" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "filetime 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "iron 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "mount 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)", + "url 0.5.9 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "strsim" version = "0.3.0" @@ -666,11 +767,24 @@ name = "traitobject" version = "0.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "traitobject" +version = "0.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "typeable" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "typemap" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "unsafe-any 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "unicase" version = "1.4.0" @@ -702,6 +816,26 @@ name = "unidecode" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "unsafe-any" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "traitobject 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "url" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "matches 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-bidi 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-normalization 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "uuid 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "url" version = "1.1.1" @@ -725,6 +859,14 @@ name = "utf8-ranges" version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "uuid" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "vec_map" version = "0.6.0" diff --git a/Cargo.toml b/Cargo.toml index 660ae1ded..0ccff1c7e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,6 +18,8 @@ hyper = "0.9.6" semver = "0.2.2" slug = "0.1.1" env_logger = "0.3" +iron = "0.3.0" +staticfile = { version = "0.2.0", features = [ "cache" ] } [dependencies.cargo] git = "https://github.com/rust-lang/cargo.git" diff --git a/src/lib.rs b/src/lib.rs index b5ca8a12c..8b3d3cb34 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -9,6 +9,8 @@ extern crate hyper; extern crate time; extern crate semver; extern crate slug; +extern crate iron; +extern crate staticfile; pub use self::build_doc::{build_doc, get_package, source_path, update_sources}; pub use self::copy::{copy_dir, copy_doc_dir}; @@ -19,6 +21,7 @@ pub use self::docbuilder::options::DocBuilderOptions; pub mod db; pub mod utils; +pub mod web; mod build_doc; mod copy; mod docbuilder; diff --git a/src/web/mod.rs b/src/web/mod.rs new file mode 100644 index 000000000..0853dccd4 --- /dev/null +++ b/src/web/mod.rs @@ -0,0 +1,23 @@ +//! Web interface of cratesfyi + +pub use self::rustdoc::start_rustdoc_web_server; + +mod rustdoc; + +use ::db; +use iron::prelude::*; +use iron::{BeforeMiddleware, typemap}; +use postgres; + + +/// Simple iron middleware for database connection +struct DbConnection; + +impl typemap::Key for DbConnection { type Value = postgres::Connection; } + +impl BeforeMiddleware for DbConnection { + fn before(&self, req: &mut Request) -> IronResult<()> { + req.extensions.insert::(db::connect_db().unwrap()); + Ok(()) + } +} diff --git a/src/web/rustdoc.rs b/src/web/rustdoc.rs new file mode 100644 index 000000000..efb2d2dba --- /dev/null +++ b/src/web/rustdoc.rs @@ -0,0 +1,130 @@ +//! Simple web server to handle rustdoc.crates.fyi + + +use std::path::Path; + +use iron::prelude::*; +use iron::{status, Url, Handler}; +use iron::modifiers::Redirect; +use staticfile::Static; +use semver::{Version, VersionReq}; +use regex::Regex; +use std::time::Duration; +use db::connect_db; + + + +struct Redirector { + static_handler: Box, +} + + +impl Redirector { + pub fn new>(documentations_path: P) -> Redirector { + let static_handler = Static::new(documentations_path.as_ref()) + .cache(Duration::from_secs(60 * 60 * 24 * 3)); + Redirector { static_handler: Box::new(static_handler) } + } + + + fn redirect_to_doc(&self, + req: &Request, + name: &str, + vers: &str, + target_name: &str) + -> IronResult { + let url = Url::parse(&format!("{}://{}/{}/{}/{}/", + req.url.scheme, + req.url.host, + name, + vers, + target_name)[..]) + .unwrap(); + let mut resp = Response::with((status::Found, Redirect(url.clone()))); + + use iron::headers::{Expires, HttpDate}; + use time; + resp.headers.set(Expires(HttpDate(time::now()))); + + Ok(resp) + } + + + fn check_crate_redirection(&self, + req: &Request, + crate_name: &str, + version: Option<&str>) + -> IronResult { + let req_version = version.unwrap_or("*"); + + let conn = connect_db().unwrap(); + + let mut versions = Vec::new(); + + // get every version of a crate + for row in &conn.query("SELECT version, target_name FROM crates,releases WHERE \ + crates.name = $1 AND crates.id = releases.crate_id", + &[&crate_name]) + .unwrap() { + let version: String = row.get(0); + let target_name: String = row.get(1); + versions.push((version, target_name)); + } + + // first check for exact match + // we can't expect users to use semver in query + for version in &versions { + if version.0 == *req_version { + return self.redirect_to_doc(&req, crate_name, req_version, &version.1[..]); + } + } + + // Now try to match with semver + let req_sem_ver = VersionReq::parse(req_version).unwrap(); + for version in &versions { + let sem_ver = Version::parse(&version.0[..]).unwrap(); + if req_sem_ver.matches(&sem_ver) { + return self.redirect_to_doc(&req, crate_name, &version.0[..], &version.1[..]); + } + } + + Ok(Response::with((status::NotFound, "Crate not found"))) + } +} + +impl Handler for Redirector { + fn handle(&self, req: &mut Request) -> IronResult { + let re = Regex::new(r"^([\w_-]+)/*([\w.-]*)/*$").unwrap(); + let path = req.url.path.join("/"); + + match re.captures(&path[..]) { + Some(caps) => self.check_crate_redirection(req, caps.at(1).unwrap(), caps.at(2)), + None => self.static_handler.handle(req), + } + } +} + + + +/// Starts rustdoc web server +pub fn start_rustdoc_web_server>(documentations_path: P, port: u16) { + let redirector = Redirector::new(documentations_path.as_ref()); + info!("rustdoc web server starting on http://localhost:{}/", port); + Iron::new(redirector).http(("localhost", port)).unwrap(); +} + + +#[cfg(test)] +mod test { + extern crate env_logger; + use super::*; + use std::path::Path; + + #[test] + #[ignore] + fn test_start_rustdoc_web_server() { + // FIXME: This test is doing nothing + let _ = env_logger::init(); + start_rustdoc_web_server(Path::new("../cratesfyi-prefix/documentations"), 3000); + } +}