From 1cf27c189fb012aab01595dbe6f2999e93d7e87f Mon Sep 17 00:00:00 2001 From: Scott Lamb Date: Thu, 2 Mar 2017 19:29:28 -0800 Subject: [PATCH] upgrade to async hyper serve_generated_bytes is >3X faster. One caveat is that the reactor thread may stall when reading from the memory-mapped slice. Moonfire NVR is basically a single-user program, so that may not be so bad, but we'll see. --- Cargo.lock | 752 ++++++++++++++++++++++++++++++++++------------- Cargo.toml | 20 +- src/cmds/run.rs | 44 +-- src/db.rs | 9 +- src/error.rs | 2 +- src/main.rs | 9 +- src/mmapfile.rs | 71 ----- src/mp4.rs | 353 ++++++++++++---------- src/recording.rs | 3 +- src/slices.rs | 236 ++++++--------- src/web.rs | 342 ++++++++++----------- 11 files changed, 1067 insertions(+), 774 deletions(-) delete mode 100644 src/mmapfile.rs diff --git a/Cargo.lock b/Cargo.lock index 352bd1e..6891542 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3,40 +3,52 @@ name = "moonfire-nvr" version = "0.1.0" dependencies = [ "byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "chan 0.1.19 (registry+https://github.com/rust-lang/crates.io-index)", - "chan-signal 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "cursive 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "cursive 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", "docopt 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "ffmpeg 0.2.0-alpha.2 (git+https://github.com/scottlamb/rust-ffmpeg?branch=2.x)", "ffmpeg-sys 2.8.9 (registry+https://github.com/rust-lang/crates.io-index)", "fnv 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", - "http-entity 0.0.1 (git+https://github.com/scottlamb/http-entity)", - "hyper 0.10.4 (git+https://github.com/scottlamb/hyper?branch=moonfire-on-0.10.x)", + "futures 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "http-entity 0.0.1 (git+https://github.com/scottlamb/http-entity?branch=hyper-0.11.x)", + "hyper 0.11.0-a.0 (git+https://github.com/scottlamb/hyper?branch=moonfire-on-0.11.x)", "lazy_static 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "lazycell 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.20 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "lru-cache 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "memmap 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "memmap 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", "mime 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "openssl 0.9.6 (registry+https://github.com/rust-lang/crates.io-index)", + "openssl 0.9.7 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "reffers 0.4.2 (git+https://github.com/diwic/reffers-rs)", "regex 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "reqwest 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "rusqlite 0.9.5 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-serialize 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 0.9.6 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 0.9.6 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 0.9.5 (registry+https://github.com/rust-lang/crates.io-index)", - "slog 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 0.9.10 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 0.9.10 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 0.9.8 (registry+https://github.com/rust-lang/crates.io-index)", + "slog 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)", "slog-envlogger 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", "slog-stdlog 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "slog-term 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)", "smallvec 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "tempdir 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", "time 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-core 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-signal 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "url 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "uuid 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "advapi32-sys" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "aho-corasick" version = "0.5.3" @@ -47,33 +59,20 @@ dependencies = [ [[package]] name = "aho-corasick" -version = "0.6.1" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "memchr 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] -name = "bit-set" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "bit-vec 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "bit-set" +name = "base64" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "bit-vec 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "bit-vec" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "bitflags" version = "0.3.3" @@ -89,6 +88,11 @@ name = "byteorder" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "cfg-if" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "chan" version = "0.1.19" @@ -97,54 +101,57 @@ dependencies = [ "rand 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "chan-signal" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "bit-set 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "chan 0.1.19 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.20 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "chan-signal" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "bit-set 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "chan 0.1.19 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.20 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "chrono" version = "0.2.25" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "num 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)", + "num 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)", "time 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "core-foundation" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "core-foundation-sys 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "core-foundation-sys" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "crossbeam" version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "crypt32-sys" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "cursive" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "chan 0.1.19 (registry+https://github.com/rust-lang/crates.io-index)", - "chan-signal 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", "ncurses 5.85.0 (registry+https://github.com/rust-lang/crates.io-index)", - "num 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)", + "num 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)", "odds 0.2.25 (registry+https://github.com/rust-lang/crates.io-index)", "toml 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-segmentation 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-segmentation 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "unicode-width 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -161,12 +168,12 @@ dependencies = [ [[package]] name = "dtoa" -version = "0.4.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] -name = "error-chain" -version = "0.7.2" +name = "dtoa" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -176,7 +183,7 @@ source = "git+https://github.com/scottlamb/rust-ffmpeg?branch=2.x#a67d25cb853695 dependencies = [ "bitflags 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "ffmpeg-sys 2.8.9 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.20 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -184,7 +191,7 @@ name = "ffmpeg-sys" version = "2.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.20 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)", "num_cpus 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -193,16 +200,39 @@ name = "fnv" version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "foreign-types" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "fs2" -version = "0.3.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.20 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "futures" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "futures-cpupool" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "crossbeam 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "num_cpus 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "gcc" version = "0.3.43" @@ -220,9 +250,10 @@ dependencies = [ [[package]] name = "http-entity" version = "0.0.1" -source = "git+https://github.com/scottlamb/http-entity#9ff2911937fa90b34ae6a2c5a60cb73ac61544cf" +source = "git+https://github.com/scottlamb/http-entity?branch=hyper-0.11.x#9788441f8f66f715d12769fe694cc5ef7cf46a61" dependencies = [ - "hyper 0.10.4 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "hyper 0.11.0-a.0 (git+https://github.com/hyperium/hyper)", "mime 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "smallvec 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "time 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)", @@ -235,14 +266,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "hyper" -version = "0.10.4" -source = "git+https://github.com/scottlamb/hyper?branch=moonfire-on-0.10.x#a8db574da9df986f0b05cac2788a2971162de695" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "httparse 1.2.1 (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.2 (registry+https://github.com/rust-lang/crates.io-index)", - "num_cpus 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "num_cpus 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-serialize 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)", "rustc_version 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", "time 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)", @@ -254,9 +285,31 @@ dependencies = [ [[package]] name = "hyper" -version = "0.10.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -replace = "hyper 0.10.4 (git+https://github.com/scottlamb/hyper?branch=moonfire-on-0.10.x)" +version = "0.11.0-a.0" +source = "git+https://github.com/hyperium/hyper#fac3d70c0b716157ba689ae2b8a0089b6afc9bdc" +replace = "hyper 0.11.0-a.0 (git+https://github.com/scottlamb/hyper?branch=moonfire-on-0.11.x)" + +[[package]] +name = "hyper" +version = "0.11.0-a.0" +source = "git+https://github.com/scottlamb/hyper?branch=moonfire-on-0.11.x#4e053ca4f9c81d884bbe19f76f8a80e2c935298a" +dependencies = [ + "base64 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-cpupool 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "httparse 1.2.1 (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.2 (registry+https://github.com/rust-lang/crates.io-index)", + "relay 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc_version 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "time 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-core 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-proto 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-service 0.1.0 (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)", +] [[package]] name = "idna" @@ -264,7 +317,7 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "matches 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-bidi 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-bidi 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", "unicode-normalization 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -274,10 +327,15 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.20 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "itoa" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "itoa" version = "0.3.1" @@ -297,11 +355,6 @@ 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.2" @@ -309,12 +362,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "lazycell" -version = "0.5.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "libc" -version = "0.2.20" +version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -323,7 +376,7 @@ version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "gcc 0.3.43 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.20 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)", "pkg-config 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -355,7 +408,7 @@ name = "memchr" version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.20 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -363,30 +416,20 @@ name = "memchr" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.20 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "memmap" -version = "0.5.0" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "fs2 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "fs2 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.20 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "metadeps" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "error-chain 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", - "pkg-config 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", - "toml 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "mime" version = "0.2.2" @@ -395,79 +438,138 @@ dependencies = [ "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "mio" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "lazycell 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "miow 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "net2 0.2.26 (registry+https://github.com/rust-lang/crates.io-index)", + "slab 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "mio-uds" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)", + "mio 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "miow" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "net2 0.2.26 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "native-tls" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "openssl 0.9.7 (registry+https://github.com/rust-lang/crates.io-index)", + "schannel 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "security-framework 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "security-framework-sys 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "tempdir 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "ncurses" version = "5.85.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "gcc 0.3.43 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.20 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "net2" +version = "0.2.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "num" -version = "0.1.36" +version = "0.1.37" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "num-bigint 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)", - "num-complex 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)", - "num-integer 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)", - "num-iter 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)", - "num-rational 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)", + "num-bigint 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)", + "num-complex 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)", + "num-integer 0.1.33 (registry+https://github.com/rust-lang/crates.io-index)", + "num-iter 0.1.33 (registry+https://github.com/rust-lang/crates.io-index)", + "num-rational 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "num-bigint" -version = "0.1.35" +version = "0.1.36" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "num-integer 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)", + "num-integer 0.1.33 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-serialize 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "num-complex" -version = "0.1.35" +version = "0.1.36" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "num-traits 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-serialize 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "num-integer" -version = "0.1.32" +version = "0.1.33" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "num-traits 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "num-iter" -version = "0.1.32" +version = "0.1.33" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "num-integer 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)", + "num-integer 0.1.33 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "num-rational" -version = "0.1.35" +version = "0.1.36" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "num-bigint 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)", - "num-integer 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)", + "num-bigint 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)", + "num-integer 0.1.33 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-serialize 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "num-traits" -version = "0.1.36" +version = "0.1.37" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -475,15 +577,15 @@ name = "num_cpus" version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.20 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "num_cpus" -version = "1.2.1" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.20 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -493,26 +595,55 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "openssl" -version = "0.9.6" +version = "0.9.7" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "foreign-types 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.20 (registry+https://github.com/rust-lang/crates.io-index)", - "openssl-sys 0.9.6 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)", + "openssl-sys 0.9.7 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "openssl-sys" -version = "0.9.6" +version = "0.9.7" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ + "gcc 0.3.43 (registry+https://github.com/rust-lang/crates.io-index)", "gdi32-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.20 (registry+https://github.com/rust-lang/crates.io-index)", - "metadeps 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)", + "pkg-config 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", "user32-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "owning_ref" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "parking_lot" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "owning_ref 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot_core 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "thread-id 3.0.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "parking_lot_core" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", + "smallvec 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "pkg-config" version = "0.3.9" @@ -520,7 +651,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "quote" -version = "0.3.12" +version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -528,7 +659,7 @@ name = "rand" version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.20 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -536,6 +667,11 @@ name = "redox_syscall" version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "reffers" +version = "0.4.2" +source = "git+https://github.com/diwic/reffers-rs#22b9f2eb77f9ca97f217a098a2a50c80b9fc2a80" + [[package]] name = "regex" version = "0.1.80" @@ -553,10 +689,10 @@ name = "regex" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "aho-corasick 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", + "aho-corasick 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", "memchr 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", "regex-syntax 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "thread_local 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "thread_local 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "utf8-ranges 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -570,13 +706,35 @@ name = "regex-syntax" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "relay" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "futures 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "reqwest" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "hyper 0.10.5 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "native-tls 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 0.8.23 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 0.8.6 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_urlencoded 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "url 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "rusqlite" version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.20 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)", "libsqlite3-sys 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", "lru-cache 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "time 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)", @@ -595,6 +753,54 @@ dependencies = [ "semver 0.1.20 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "schannel" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "advapi32-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "crypt32-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "secur32-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "scoped-tls" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "secur32-sys" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "security-framework" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "core-foundation 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "core-foundation-sys 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)", + "security-framework-sys 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "security-framework-sys" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "core-foundation-sys 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "semver" version = "0.1.20" @@ -602,41 +808,71 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "serde" -version = "0.9.6" +version = "0.8.23" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "serde" +version = "0.9.10" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "serde_codegen_internals" -version = "0.13.0" +version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "syn 0.11.4 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.11.8 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "serde_derive" -version = "0.9.6" +version = "0.9.10" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "quote 0.3.12 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_codegen_internals 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.11.4 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_codegen_internals 0.14.1 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.11.8 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "serde_json" -version = "0.9.5" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "dtoa 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "itoa 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 0.8.23 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "serde_json" +version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "dtoa 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", "itoa 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 0.9.6 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 0.9.10 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "serde_urlencoded" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "serde 0.8.23 (registry+https://github.com/rust-lang/crates.io-index)", + "url 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "slab" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "slog" -version = "1.5.0" +version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -646,18 +882,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "regex 0.1.80 (registry+https://github.com/rust-lang/crates.io-index)", - "slog 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "slog 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)", "slog-stdlog 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "slog-term 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "slog-extra" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "slog 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "thread_local 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)", + "slog 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)", + "thread_local 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -668,7 +904,7 @@ dependencies = [ "crossbeam 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 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)", - "slog 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "slog 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)", "slog-term 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -677,8 +913,8 @@ name = "slog-stream" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "slog 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "slog-extra 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "slog 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)", + "slog-extra 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "thread_local 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -689,11 +925,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "chrono 0.2.25 (registry+https://github.com/rust-lang/crates.io-index)", "isatty 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "slog 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "slog 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)", "slog-stream 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "thread_local 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "thread_local 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "smallvec" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "smallvec" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "smallvec" version = "0.3.1" @@ -706,13 +952,27 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "syn" -version = "0.11.4" +version = "0.11.8" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "quote 0.3.12 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", + "synom 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)", "unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "synom" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "take" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "tempdir" version = "0.3.5" @@ -727,7 +987,7 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.20 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -736,7 +996,7 @@ version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.20 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -749,7 +1009,7 @@ dependencies = [ [[package]] name = "thread_local" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "thread-id 3.0.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -762,11 +1022,61 @@ version = "0.1.36" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.20 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)", "redox_syscall 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "tokio-core" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "futures 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "mio 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", + "scoped-tls 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "slab 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio-proto" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "futures 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "net2 0.2.26 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", + "slab 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "smallvec 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "take 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-core 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-service 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio-service" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "futures 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio-signal" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "futures 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)", + "mio 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", + "mio-uds 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-core 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "toml" version = "0.2.1" @@ -795,7 +1105,7 @@ dependencies = [ [[package]] name = "unicode-bidi" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "matches 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", @@ -808,7 +1118,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "unicode-segmentation" -version = "1.0.3" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -863,7 +1173,7 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "rand 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 0.9.6 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 0.9.10 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -881,43 +1191,57 @@ name = "winapi-build" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "ws2_32-sys" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + [metadata] +"checksum advapi32-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e06588080cb19d0acb6739808aafa5f26bfb2ca015b2b6370028b44cf7cb8a9a" "checksum aho-corasick 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ca972c2ea5f742bfce5687b9aef75506a764f61d37f8f649047846a9686ddb66" -"checksum aho-corasick 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4f660b942762979b56c9f07b4b36bb559776fbad102f05d6771e1b629e8fd5bf" -"checksum bit-set 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e6e1e6fb1c9e3d6fcdec57216a74eaa03e41f52a22f13a16438251d8e88b89da" -"checksum bit-set 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d9bf6104718e80d7b26a68fdbacff3481cfc05df670821affc7e9cbc1884400c" -"checksum bit-vec 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "5b97c2c8e8bbb4251754f559df8af22fb264853c7d009084a576cdf12565089d" +"checksum aho-corasick 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "0638fd549427caa90c499814196d1b9e3725eb4d15d7339d6de073a680ed0ca2" +"checksum base64 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "065a0ce220ab84d0b6d5ae3e7bb77232209519c366f51f946fe28c19e84989d0" "checksum bitflags 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "32866f4d103c4e438b1db1158aa1b1a80ee078e5d77a59a2f906fd62a577389c" "checksum bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "aad18937a628ec6abcd26d1489012cc0e18c21798210f491af69ded9b881106d" "checksum byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c40977b0ee6b9885c9013cd41d9feffdd22deb3bb4dc3a71d901cc7a77de18c8" +"checksum cfg-if 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "de1e760d7b6535af4241fca8bd8adf68e2e7edacc6b29f5d399050c5e48cf88c" "checksum chan 0.1.19 (registry+https://github.com/rust-lang/crates.io-index)" = "f93bfe971116428a9066c1c3c69a09ae3ef69432f8418be28ab50f96783e6a50" -"checksum chan-signal 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "365122ab60a9dc6240b48e39d011b4389c3853093d98bf586edd2b79bfb4fbfa" -"checksum chan-signal 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0f3bb6c3bc387004ad914f0c5b7f33ace8bf7604bbec35f228b1a017f52cd3a0" "checksum chrono 0.2.25 (registry+https://github.com/rust-lang/crates.io-index)" = "9213f7cd7c27e95c2b57c49f0e69b1ea65b27138da84a170133fd21b07659c00" +"checksum core-foundation 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "25bfd746d203017f7d5cbd31ee5d8e17f94b6521c7af77ece6c9e4b2d4b16c67" +"checksum core-foundation-sys 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "065a5d7ffdcbc8fa145d6f0746f3555025b9097a9e9cda59f7467abae670c78d" "checksum crossbeam 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)" = "0c5ea215664ca264da8a9d9c3be80d2eaf30923c259d03e870388eb927508f97" -"checksum cursive 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8d226ba768f44025aeec3485ce3ca9c7e93c3f2a7e02e8f00d5b4c70c490011b" +"checksum crypt32-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e34988f7e069e0b2f3bfc064295161e489b2d4e04a2e4248fb94360cdf00b4ec" +"checksum cursive 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e12e82dfbff181bd5795f773477f745b30b3eed753c6e3df10532f24654c3693" "checksum docopt 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ab32ea6e284d87987066f21a9e809a73c14720571ef34516f0890b3d355ccfd8" +"checksum dtoa 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "0dd841b58510c9618291ffa448da2e4e0f699d984d436122372f446dae62263d" "checksum dtoa 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "80c8b71fd71146990a9742fc06dcbbde19161a267e0ad4e572c35162f4578c90" -"checksum error-chain 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "318cb3c71ee4cdea69fdc9e15c173b245ed6063e1709029e8fd32525a881120f" "checksum ffmpeg 0.2.0-alpha.2 (git+https://github.com/scottlamb/rust-ffmpeg?branch=2.x)" = "" "checksum ffmpeg-sys 2.8.9 (registry+https://github.com/rust-lang/crates.io-index)" = "683f0fa41ebe43e7162184d172190e24318af66f7065006a36c2cc877f0e0edd" "checksum fnv 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "6cc484842f1e2884faf56f529f960cc12ad8c71ce96cc7abba0a067c98fee344" -"checksum fs2 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "640001e1bd865c7c32806292822445af576a6866175b5225aa2087ca5e3de551" +"checksum foreign-types 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3e4056b9bd47f8ac5ba12be771f77a0dae796d1bbaaf5fd0b9c2d38b69b8a29d" +"checksum fs2 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "34edaee07555859dc13ca387e6ae05686bb4d0364c95d649b6dab959511f4baf" +"checksum futures 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "c1913eb7083840b1bbcbf9631b7fda55eaf35fe7ead13cca034e8946f9e2bc41" +"checksum futures-cpupool 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "9e48a3fff6a58fe9df1eed13d2599650416a987386c43a19aec656c3e6a2c229" "checksum gcc 0.3.43 (registry+https://github.com/rust-lang/crates.io-index)" = "c07c758b972368e703a562686adb39125707cc1ef3399da8c019fc6c2498a75d" "checksum gdi32-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0912515a8ff24ba900422ecda800b52f4016a56251922d397c576bf92c690518" -"checksum http-entity 0.0.1 (git+https://github.com/scottlamb/http-entity)" = "" +"checksum http-entity 0.0.1 (git+https://github.com/scottlamb/http-entity?branch=hyper-0.11.x)" = "" "checksum httparse 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a6e7a63e511f9edffbab707141fbb8707d1a3098615fb2adbd5769cdfcc9b17d" -"checksum hyper 0.10.4 (git+https://github.com/scottlamb/hyper?branch=moonfire-on-0.10.x)" = "" -"checksum hyper 0.10.4 (registry+https://github.com/rust-lang/crates.io-index)" = "220407e5a263f110ec30a071787c9535918fdfc97def5680c90013c3f30c38c1" +"checksum hyper 0.10.5 (registry+https://github.com/rust-lang/crates.io-index)" = "43a15e3273b2133aaac0150478ab443fb89f15c3de41d8d93d8f3bb14bf560f6" +"checksum hyper 0.11.0-a.0 (git+https://github.com/hyperium/hyper)" = "" +"checksum hyper 0.11.0-a.0 (git+https://github.com/scottlamb/hyper?branch=moonfire-on-0.11.x)" = "" "checksum idna 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1053236e00ce4f668aeca4a769a09b3bf5a682d802abd6f3cb39374f6b162c11" "checksum isatty 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "fa500db770a99afe2a0f2229be2a3d09c7ed9d7e4e8440bf71253141994e240f" +"checksum itoa 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ae3088ea4baeceb0284ee9eea42f591226e6beaecf65373e41b38d95a1b8e7a1" "checksum itoa 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "eb2f404fbc66fd9aac13e998248505e7ecb2ad8e44ab6388684c5fb11c6c251c" "checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" "checksum language-tags 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a91d884b6667cd606bb5a69aa0c99ba811a115fc68915e7056ec08a46e93199a" -"checksum lazy_static 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "cf186d1a8aa5f5bee5fd662bc9c1b949e0259e1bcc379d1f006847b0080c7417" "checksum lazy_static 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6abe0ee2e758cd6bc8a2cd56726359007748fbf4128da998b65d0b70f881e19b" -"checksum lazycell 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ec38a5c22f1ef3e30d2642aa875620d60edeef36cef43c4739d86215ce816331" -"checksum libc 0.2.20 (registry+https://github.com/rust-lang/crates.io-index)" = "684f330624d8c3784fb9558ca46c4ce488073a8d22450415c5eb4f4cfb0d11b5" +"checksum lazycell 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ce12306c4739d86ee97c23139f3a34ddf0387bbf181bc7929d287025a8c3ef6b" +"checksum libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)" = "88ee81885f9f04bff991e306fea7c1c60a5f0f9e409e99f6b40e3311a3363135" "checksum libsqlite3-sys 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b6de3eea39ba6ed0cddf04e1c7a78486e3f750441e0a0b15b6ea39d0dd8e1b8c" "checksum linked-hash-map 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "bda158e0dabeb97ee8a401f4d17e479d6b891a14de0bba79d5cc2d4d325b5e48" "checksum log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "ab83497bf8bf4ed2a74259c1c802351fcd67a65baa86394b6ba73c36f4838054" @@ -925,60 +1249,87 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum matches 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "efd7622e3022e1a6eaa602c4cea8912254e5582c9c692e9167714182244801b1" "checksum memchr 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)" = "d8b629fb514376c675b98c1421e80b151d3817ac42d7c667717d282761418d20" "checksum memchr 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1dbccc0e46f1ea47b9f17e6d67c5a96bd27030519c519c9c91327e31275a47b4" -"checksum memmap 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "065ce59af31c18ea2c419100bda6247dd4ec3099423202b12f0bd32e529fabd2" -"checksum metadeps 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "829fffe7ea1d747e23f64be972991bc516b2f1ac2ae4a3b33d8bea150c410151" +"checksum memmap 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "46f3c7359028b31999287dae4e5047ddfe90a23b7dca2282ce759b491080c99b" "checksum mime 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b5c93a4bd787ddc6e7833c519b73a50883deb5863d76d9b71eb8216fb7f94e66" +"checksum mio 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "eecdbdd49a849336e77b453f021c89972a2cfb5b51931a0026ae0ac4602de681" +"checksum mio-uds 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "78437f00d9615c366932cbfe79790b5c2945706ba67cf78378ffacc0069ed9de" +"checksum miow 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3a78d2605eb97302c10cf944b8d96b0a2a890c52957caf92fcd1f24f69049579" +"checksum native-tls 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b805ee0e8fa268f67a4e5c7f4f80adb8af1fc4428ea0ce5b0ecab1430ef17ec0" "checksum ncurses 5.85.0 (registry+https://github.com/rust-lang/crates.io-index)" = "21f71f0e1ae96558612b1e9d188ec4f23149a11ee4fb4b96e130523bea52d605" -"checksum num 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)" = "bde7c03b09e7c6a301ee81f6ddf66d7a28ec305699e3d3b056d2fc56470e3120" -"checksum num-bigint 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)" = "88b14378471f7c2adc5262f05b4701ef53e8da376453a8d8fee48e51db745e49" -"checksum num-complex 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)" = "f0c78e054dd19c3fd03419ade63fa661e9c49bb890ce3beb4eee5b7baf93f92f" -"checksum num-integer 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)" = "fb24d9bfb3f222010df27995441ded1e954f8f69cd35021f6bef02ca9552fb92" -"checksum num-iter 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)" = "287a1c9969a847055e1122ec0ea7a5c5d6f72aad97934e131c83d5c08ab4e45c" -"checksum num-rational 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)" = "54ff603b8334a72fbb27fe66948aac0abaaa40231b3cecd189e76162f6f38aaf" -"checksum num-traits 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)" = "a16a42856a256b39c6d3484f097f6713e14feacd9bfb02290917904fae46c81c" +"checksum net2 0.2.26 (registry+https://github.com/rust-lang/crates.io-index)" = "5edf9cb6be97212423aed9413dd4729d62b370b5e1c571750e882cebbbc1e3e2" +"checksum num 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)" = "98b15ba84e910ea7a1973bccd3df7b31ae282bf9d8bd2897779950c9b8303d40" +"checksum num-bigint 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)" = "5e2955fede25639c4f4f797e864b7585f20d98069c45e0c86b1d22a808eb9f77" +"checksum num-complex 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)" = "3534898d8a1f6b16c12f9fc2f4eaabc7ecdcc55f267213caa8988fdc7d60ff94" +"checksum num-integer 0.1.33 (registry+https://github.com/rust-lang/crates.io-index)" = "21e4df1098d1d797d27ef0c69c178c3fab64941559b290fcae198e0825c9c8b5" +"checksum num-iter 0.1.33 (registry+https://github.com/rust-lang/crates.io-index)" = "f7d1891bd7b936f12349b7d1403761c8a0b85a18b148e9da4429d5d102c1a41e" +"checksum num-rational 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)" = "c2dc5ea04020a8f18318ae485c751f8cfa1c0e69dcf465c29ddaaa64a313cc44" +"checksum num-traits 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)" = "e1cbfa3781f3fe73dc05321bed52a06d2d491eaa764c52335cf4399f046ece99" "checksum num_cpus 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)" = "cee7e88156f3f9e19bdd598f8d6c9db7bf4078f99f8381f43a55b09648d1a6e3" -"checksum num_cpus 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a225d1e2717567599c24f88e49f00856c6e825a12125181ee42c4257e3688d39" +"checksum num_cpus 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a18c392466409c50b87369414a2680c93e739aedeb498eb2bff7d7eb569744e2" "checksum odds 0.2.25 (registry+https://github.com/rust-lang/crates.io-index)" = "c3df9b730298cea3a1c3faa90b7e2f9df3a9c400d0936d6015e6165734eefcba" -"checksum openssl 0.9.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0c00da69323449142e00a5410f0e022b39e8bbb7dc569cee8fc6af279279483c" -"checksum openssl-sys 0.9.6 (registry+https://github.com/rust-lang/crates.io-index)" = "b1482f9a06f56c906007e17ea14d73d102210b5d27bc948bf5e175f493f3f7c3" +"checksum openssl 0.9.7 (registry+https://github.com/rust-lang/crates.io-index)" = "f9871ecf7629da3760599e3e547d35940cff3cead49159b49f81cd1250f24f1d" +"checksum openssl-sys 0.9.7 (registry+https://github.com/rust-lang/crates.io-index)" = "5dd48381e9e8a6dce9c4c402db143b2e243f5f872354532f7a009c289b3998ca" +"checksum owning_ref 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "9d52571ddcb42e9c900c901a18d8d67e393df723fcd51dd59c5b1a85d0acb6cc" +"checksum parking_lot 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "fa12d706797d42551663426a45e2db2e0364bd1dbf6aeada87e89c5f981f43e9" +"checksum parking_lot_core 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fb1b97670a2ffadce7c397fb80a3d687c4f3060140b885621ef1653d0e5d5068" "checksum pkg-config 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "3a8b4c6b8165cd1a1cd4b9b120978131389f64bdaf456435caa41e630edba903" -"checksum quote 0.3.12 (registry+https://github.com/rust-lang/crates.io-index)" = "e7b44fd83db28b83c1c58187159934906e5e955c812e211df413b76b03c909a5" +"checksum quote 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)" = "7375cf7ad34a92e8fd18dd9c42f58b9a11def59ab48bec955bf359a788335592" "checksum rand 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "022e0636ec2519ddae48154b028864bdce4eaf7d35226ab8e65c611be97b189d" "checksum redox_syscall 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "8dd35cc9a8bdec562c757e3d43c1526b5c6d2653e23e2315065bc25556550753" +"checksum reffers 0.4.2 (git+https://github.com/diwic/reffers-rs)" = "" "checksum regex 0.1.80 (registry+https://github.com/rust-lang/crates.io-index)" = "4fd4ace6a8cf7860714a2c2280d6c1f7e6a413486c13298bbc86fd3da019402f" "checksum regex 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4278c17d0f6d62dfef0ab00028feb45bd7d2102843f80763474eeb1be8a10c01" "checksum regex-syntax 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "f9ec002c35e86791825ed294b50008eea9ddfc8def4420124fbc6b08db834957" "checksum regex-syntax 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2f9191b1f57603095f105d317e375d19b1c9c5c3185ea9633a99a6dcbed04457" +"checksum relay 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f301bafeb60867c85170031bdb2fcf24c8041f33aee09e7b116a58d4e9f781c5" +"checksum reqwest 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "dfc011675ace22e9dd00d0734b1d00854859e6309c9545b6eb3e98cc088cf1eb" "checksum rusqlite 0.9.5 (registry+https://github.com/rust-lang/crates.io-index)" = "90bff7b0113c476086ea8b3c5fc2df177fe86c0a945a0665ea704f36dc230286" "checksum rustc-serialize 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)" = "237546c689f20bb44980270c73c3b9edd0891c1be49cc1274406134a66d3957b" "checksum rustc_version 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "c5f5376ea5e30ce23c03eb77cbe4962b988deead10910c372b226388b594c084" +"checksum schannel 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "0168331892e26bcd763535c1edd4b850708d0288b0e73942c116bbbf8e903c7f" +"checksum scoped-tls 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f417c22df063e9450888a7561788e9bd46d3bb3c1466435b4eccb903807f147d" +"checksum secur32-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3f412dfa83308d893101dd59c10d6fda8283465976c28c287c5c855bf8d216bc" +"checksum security-framework 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "d7c1ff1c71e4e4474b46ded6687f0c28c721de2f5a05577e7f533d36330e4e3a" +"checksum security-framework-sys 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "5103c988054803538fe4d85333abf4c633f069510ab687dc71a50572104216d0" "checksum semver 0.1.20 (registry+https://github.com/rust-lang/crates.io-index)" = "d4f410fedcf71af0345d7607d246e7ad15faaadd49d240ee3b24e5dc21a820ac" -"checksum serde 0.9.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0ae9a3c8b07c09dbe43022486d55a18c629a0618d2241e49829aaef9b6d862f9" -"checksum serde_codegen_internals 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c3172bf2940b975c0e4f6ab42a511c0a4407d4f46ccef87a9d3615db5c26fa96" -"checksum serde_derive 0.9.6 (registry+https://github.com/rust-lang/crates.io-index)" = "ecc6e0379ca933ece58302d2d3034443f06fbf38fd535857c1dc516195cbc3bf" -"checksum serde_json 0.9.5 (registry+https://github.com/rust-lang/crates.io-index)" = "cf37ce931677e98b4fa5e6469aaa3ab4b6228309ea33b1b22d3ec055adfc4515" -"checksum slog 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5e41636febfa6c1897311e41dec817fcc364a7fdd9d4cfc30e3cd0c26a0ad3a6" +"checksum serde 0.8.23 (registry+https://github.com/rust-lang/crates.io-index)" = "9dad3f759919b92c3068c696c15c3d17238234498bbdcc80f2c469606f948ac8" +"checksum serde 0.9.10 (registry+https://github.com/rust-lang/crates.io-index)" = "a78def33a828eb05eb7f0167499f19cca368faf27601f6c43bc70316825d9adf" +"checksum serde_codegen_internals 0.14.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4d52006899f910528a10631e5b727973fe668f3228109d1707ccf5bad5490b6e" +"checksum serde_derive 0.9.10 (registry+https://github.com/rust-lang/crates.io-index)" = "789ee9f3cd78c850948b94121020147f5220b47dafbf230d7098a93a58f726cf" +"checksum serde_json 0.8.6 (registry+https://github.com/rust-lang/crates.io-index)" = "67f7d2e9edc3523a9c8ec8cd6ec481b3a27810aafee3e625d311febd3e656b4c" +"checksum serde_json 0.9.8 (registry+https://github.com/rust-lang/crates.io-index)" = "6501ac6f8b74f9b1033f7ddf79a08edfa0f58d6f8e3190cb8dc97736afa257a8" +"checksum serde_urlencoded 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "53d4ebaa8d1d4f90d1b63dfca81ccd98ac20e1e479dbae393cbaf60f6fecd8d8" +"checksum slab 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "17b4fcaed89ab08ef143da37bc52adbcc04d4a69014f4c1208d6b51f0c47bc23" +"checksum slog 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "bab9d589681f7d6b9ca4ed5cc861779a392bca7beaae2f69f2341617415a78dc" "checksum slog-envlogger 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "dfea715bb310c33c8f90e659bce5b95e39851348b9a7e2a77495a069662def78" -"checksum slog-extra 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f571614f815a4dc3aad7b9052d1e3eefd5ab76bb36efa90d4dc9ac134142b445" +"checksum slog-extra 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "511581f4dd1dc90e4eca99b60be8a692d9c975e8757558aa774f16007d27492a" "checksum slog-stdlog 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "56cc08f40c45e0ab41dcfde0a19a22c5b7176d3827fc7d078450ebfdc080a37c" "checksum slog-stream 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e6f0fee00b80a7a44f82c5cf44ba03b6dc2712f9c14469a62ad90ea0911635c5" "checksum slog-term 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cb53c0bae0745898fd5a7b75b1c389507333470ac4c645ae431890c0f828b6ca" +"checksum smallvec 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "fcc8d19212aacecf95e4a7a2179b26f7aeb9732a915cf01f05b0d3e044865410" +"checksum smallvec 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4c8cbcd6df1e117c2210e13ab5109635ad68a929fcbb8964dc965b76cb5ee013" "checksum smallvec 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3a3c84984c278afe61a46e19868e8b23e2ee3be5b3cc6dea6edad4893bc6c841" "checksum strsim 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b4d15c810519a91cf877e7e36e63fe068815c678181439f2f29e2562147c3694" -"checksum syn 0.11.4 (registry+https://github.com/rust-lang/crates.io-index)" = "f4f94368aae82bb29656c98443a7026ca931a659e8d19dcdc41d6e273054e820" +"checksum syn 0.11.8 (registry+https://github.com/rust-lang/crates.io-index)" = "37c279fb816210c9bb28b2c292664581e7b87b4561e86b94df462664d8620bb8" +"checksum synom 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a393066ed9010ebaed60b9eafa373d4b1baac186dd7e008555b0f702b51945b6" +"checksum take 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b157868d8ac1f56b64604539990685fa7611d8fa9e5476cf0c02cf34d32917c5" "checksum tempdir 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "87974a6f5c1dfb344d733055601650059a3363de2a6104819293baff662132d6" "checksum thread-id 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a9539db560102d1cef46b8b78ce737ff0bb64e7e18d35b2a5688f7d097d0ff03" "checksum thread-id 3.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4437c97558c70d129e40629a5b385b3fb1ffac301e63941335e4d354081ec14a" "checksum thread_local 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "8576dbbfcaef9641452d5cf0df9b0e7eeab7694956dd33bb61515fb8f18cfdd5" -"checksum thread_local 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7793b722f0f77ce716e7f1acf416359ca32ff24d04ffbac4269f44a4a83be05d" +"checksum thread_local 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c85048c6260d17cf486ceae3282d9fb6b90be220bf5b28c400f5485ffc29f0c7" "checksum time 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)" = "211b63c112206356ef1ff9b19355f43740fc3f85960c598a93d3a3d3ba7beade" +"checksum tokio-core 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "3d1be481b55126f02ef88ff86748086473cb537a949fc4a8f4be403a530ae54b" +"checksum tokio-proto 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7c0d6031f94d78d7b4d509d4a7c5e1cdf524a17e7b08d1c188a83cf720e69808" +"checksum tokio-service 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "24da22d077e0f15f55162bdbdc661228c1581892f52074fb242678d015b45162" +"checksum tokio-signal 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "3d121715f6917878a0df69f39365d01dd66c4463e4ba19efdcddcdfeb1bcb2bc" "checksum toml 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "736b60249cb25337bc196faa43ee12c705e426f3d55c214d73a4e7be06f92cb4" "checksum traitobject 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "efd1f82c56340fdf16f2a953d7bda4f8fdffba13d93b00844c25572110b26079" "checksum typeable 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1410f6f91f21d1612654e7cc69193b0334f909dcf2c790c4826254fbb86f8887" "checksum unicase 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "13a5906ca2b98c799f4b1ab4557b76367ebd6ae5ef14930ec841c74aed5f3764" -"checksum unicode-bidi 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "b61814f3e7fd0e0f15370f767c7c943e08bc2e3214233ae8f88522b334ceb778" +"checksum unicode-bidi 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "d3a078ebdd62c0e71a709c3d53d2af693fe09fe93fbff8344aebe289b78f9032" "checksum unicode-normalization 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "e28fa37426fceeb5cf8f41ee273faa7c82c47dc8fba5853402841e665fcd86ff" -"checksum unicode-segmentation 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3c5336c5173d8a77ae0b36151c706e32ae10f4985e29d704ad5b5f9565d6d4b6" +"checksum unicode-segmentation 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "18127285758f0e2c6cf325bb3f3d138a12fee27de4f23e146cd6a179f26c2cf3" "checksum unicode-width 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "bf3a113775714a22dcb774d8ea3655c53a32debae63a063acc00a91cc586245f" "checksum unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f860d7d29cf02cb2f3f359fd35991af3d30bac52c57d265a3c461074cb4dc" "checksum unreachable 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1f2ae5ddb18e1c92664717616dd9549dde73f539f01bd7b77c2edb2446bdff91" @@ -990,3 +1341,4 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" "checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" "checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" +"checksum ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e" diff --git a/Cargo.toml b/Cargo.toml index 0da4fb6..820191a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,17 +4,18 @@ version = "0.1.0" authors = ["Scott Lamb "] [features] -nightly = [] + +# The nightly feature is used within moonfire-nvr itself to gate the +# benchmarks. Also pass it along to crates that can benefit from it. +nightly = ["parking_lot/nightly"] [dependencies] byteorder = "1.0" -chan = "0.1" -chan-signal = "0.2" docopt = "0.7" +futures = "0.1" fnv = "1.0" -http-entity = { git = "https://github.com/scottlamb/http-entity" } -hyper = { git = "https://github.com/scottlamb/hyper", branch = "moonfire-on-0.10.x" } -lazycell = "0.5" +http-entity = { git = "https://github.com/scottlamb/http-entity", branch = "hyper-0.11.x" } +hyper = { git = "https://github.com/scottlamb/hyper", branch = "moonfire-on-0.11.x" } lazy_static = "0.2" libc = "0.2" log = { version = "0.3", features = ["release_max_level_info"] } @@ -22,6 +23,8 @@ lru-cache = "0.1" memmap = "0.5" mime = "0.2" openssl = "0.9" +parking_lot = { version = "0.3.8", features = [] } +reffers = { git = "https://github.com/diwic/reffers-rs" } regex = "0.2" rusqlite = "0.9" rustc-serialize = "0.3" @@ -34,10 +37,13 @@ slog-stdlog = "1.1" slog-term = "1.3" smallvec = "0.3" time = "0.1" +tokio-core = "0.1" +tokio-signal = "0.1" url = "1.4" uuid = { version = "0.4", features = ["serde", "v4"] } [dev-dependencies] +reqwest = "0.3" tempdir = "0.3" [dependencies.cursive] @@ -65,4 +71,4 @@ lto = true debug = true [replace] -"hyper:0.10.4" = { git = "https://github.com/scottlamb/hyper", branch = "moonfire-on-0.10.x" } +"https://github.com/hyperium/hyper#hyper:0.11.0-a.0" = { git = "https://github.com/scottlamb/hyper", branch = "moonfire-on-0.11.x" } diff --git a/src/cmds/run.rs b/src/cmds/run.rs index 1a94cb4..b002213 100644 --- a/src/cmds/run.rs +++ b/src/cmds/run.rs @@ -28,17 +28,18 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -use chan_signal; use clock; use db; use dir; use error::Error; -use hyper::server::Server; +use futures::{BoxFuture, Future, Stream}; use std::sync::Arc; use std::sync::atomic::{AtomicBool, Ordering}; use std::thread; use stream; use streamer; +use tokio_core::reactor; +use tokio_signal::unix::{Signal, SIGINT, SIGTERM}; use web; const USAGE: &'static str = r#" @@ -65,13 +66,18 @@ struct Args { flag_read_only: bool, } +fn setup_shutdown_future(h: &reactor::Handle) -> BoxFuture<(), ()> { + let int = Signal::new(SIGINT, h).flatten_stream().into_future(); + let term = Signal::new(SIGTERM, h).flatten_stream().into_future(); + int.select(term) + .map(|_| ()) + .map_err(|_| ()) + .boxed() +} + pub fn run() -> Result<(), Error> { let args: Args = super::parse_args(USAGE)?; - // Watch for termination signals. - // This must be started before any threads are spawned (such as the async logger thread) so - // that signals will be blocked in all threads. - let signal = chan_signal::notify(&[chan_signal::Signal::INT, chan_signal::Signal::TERM]); super::install_logger(true); let (_db_dir, conn) = super::open_conn( &args.flag_db_dir, @@ -81,7 +87,7 @@ pub fn run() -> Result<(), Error> { info!("Database is loaded."); // Start a streamer for each camera. - let shutdown = Arc::new(AtomicBool::new(false)); + let shutdown_streamers = Arc::new(AtomicBool::new(false)); let mut streamers = Vec::new(); let syncer = if !args.flag_read_only { let (syncer_channel, syncer_join) = dir::start_syncer(dir.clone()).unwrap(); @@ -92,7 +98,7 @@ pub fn run() -> Result<(), Error> { dir: &dir, clocks: &clock::REAL, opener: &*stream::FFMPEG, - shutdown: &shutdown, + shutdown: &shutdown_streamers, }; for (i, (id, camera)) in l.cameras_by_id().iter().enumerate() { let rotate_offset_sec = streamer::ROTATE_INTERVAL_SEC * i as i64 / cameras as i64; @@ -108,24 +114,28 @@ pub fn run() -> Result<(), Error> { } else { None }; // Start the web interface. - let server = Server::http(args.flag_http_addr.as_str()).unwrap(); - let h = web::Handler::new(db.clone(), dir.clone()); - let _guard = server.handle(h); - info!("Ready to serve HTTP requests"); + let addr = args.flag_http_addr.parse().unwrap(); + let server = ::hyper::server::Http::new() + .bind(&addr, move || Ok(web::Service::new(db.clone(), dir.clone()))) + .unwrap(); - // Wait for a signal and shut down. - chan_select! { - signal.recv() -> signal => info!("Received signal {:?}; shutting down streamers.", signal), - } - shutdown.store(true, Ordering::SeqCst); + let shutdown = setup_shutdown_future(&server.handle()); + + info!("Ready to serve HTTP requests"); + server.run_until(shutdown).unwrap(); + + info!("Shutting down streamers."); + shutdown_streamers.store(true, Ordering::SeqCst); for streamer in streamers.drain(..) { streamer.join().unwrap(); } + if let Some((syncer_channel, syncer_join)) = syncer { info!("Shutting down syncer."); drop(syncer_channel); syncer_join.join().unwrap(); } + info!("Exiting."); ::std::process::exit(0); } diff --git a/src/db.rs b/src/db.rs index e4fe733..c3fa7ee 100644 --- a/src/db.rs +++ b/src/db.rs @@ -55,6 +55,7 @@ use error::{Error, ResultExt}; use fnv; use lru_cache::LruCache; use openssl::hash; +use parking_lot::{Mutex,MutexGuard}; use recording::{self, TIME_UNITS_PER_SEC}; use rusqlite; use std::collections::BTreeMap; @@ -64,7 +65,7 @@ use std::io::Write; use std::ops::Range; use std::str; use std::string::String; -use std::sync::{Arc,Mutex,MutexGuard}; +use std::sync::Arc; use std::vec::Vec; use time; use uuid::Uuid; @@ -1347,7 +1348,7 @@ impl Database { }, })); { - let mut l = &mut *db.0.lock().unwrap(); + let mut l = &mut *db.lock(); l.init_video_sample_entries().annotate_err("init_video_sample_entries")?; l.init_cameras().annotate_err("init_cameras")?; for (&camera_id, ref mut camera) in &mut l.state.cameras_by_id { @@ -1361,13 +1362,13 @@ impl Database { /// Locks the database; the returned reference is the only way to perform (read or write) /// operations. - pub fn lock(&self) -> MutexGuard { self.0.lock().unwrap() } + pub fn lock(&self) -> MutexGuard { self.0.lock() } /// For testing. Closes the database and return the connection. This allows verification that /// a newly opened database is in an acceptable state. #[cfg(test)] fn close(self) -> rusqlite::Connection { - self.0.into_inner().unwrap().conn + self.0.into_inner().conn } } diff --git a/src/error.rs b/src/error.rs index ef2b659..5eb974e 100644 --- a/src/error.rs +++ b/src/error.rs @@ -85,7 +85,7 @@ impl error::Error for Error { impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter) -> result::Result<(), fmt::Error> { - write!(f, "Error: {}", self.description) + write!(f, "Error: {}\ncause: {:?}", self.description, self.cause) } } diff --git a/src/main.rs b/src/main.rs index 4f8f2e2..b1c1d12 100644 --- a/src/main.rs +++ b/src/main.rs @@ -32,23 +32,23 @@ extern crate byteorder; extern crate core; -#[macro_use] extern crate chan; -extern crate chan_signal; extern crate docopt; #[macro_use] extern crate ffmpeg; extern crate ffmpeg_sys; +extern crate futures; extern crate fnv; extern crate http_entity; extern crate hyper; #[macro_use] extern crate lazy_static; -extern crate lazycell; extern crate libc; #[macro_use] extern crate log; extern crate lru_cache; +extern crate reffers; extern crate rusqlite; extern crate memmap; #[macro_use] extern crate mime; extern crate openssl; +extern crate parking_lot; extern crate regex; extern crate rustc_serialize; extern crate serde; @@ -60,6 +60,8 @@ extern crate slog_stdlog; extern crate slog_term; extern crate smallvec; extern crate time; +extern crate tokio_core; +extern crate tokio_signal; extern crate url; extern crate uuid; @@ -71,7 +73,6 @@ mod dir; mod error; mod h264; mod json; -mod mmapfile; mod mp4; mod recording; mod slices; diff --git a/src/mmapfile.rs b/src/mmapfile.rs deleted file mode 100644 index d6594b2..0000000 --- a/src/mmapfile.rs +++ /dev/null @@ -1,71 +0,0 @@ -// This file is part of Moonfire NVR, a security camera digital video recorder. -// Copyright (C) 2016 Scott Lamb -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// In addition, as a special exception, the copyright holders give -// permission to link the code of portions of this program with the -// OpenSSL library under certain conditions as described in each -// individual source file, and distribute linked combinations including -// the two. -// -// You must obey the GNU General Public License in all respects for all -// of the code used other than OpenSSL. If you modify file(s) with this -// exception, you may extend this exception to your version of the -// file(s), but you are not obligated to do so. If you do not wish to do -// so, delete this exception statement from your version. If you delete -// this exception statement from all source files in the program, then -// also delete it here. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -//! Memory-mapped file serving. - -extern crate memmap; - -use error::Result; -use std::fs::File; -use std::io; -use std::ops::Range; - -/// Memory-mapped file slice. -/// This struct is meant to be used in constructing an implementation of the `http_entity::Entity` -/// or `pieces::ContextWriter` traits. The file in question should be immutable, as files shrinking -/// during `mmap` will cause the process to fail with `SIGBUS`. Moonfire NVR sample files satisfy -/// this requirement: -/// -/// * They should only be modified by Moonfire NVR itself. Installation instructions encourage -/// creating a dedicated user/group for Moonfire NVR and ensuring only this group has -/// permissions to Moonfire NVR's directories. -/// * Moonfire NVR never modifies sample files after inserting their matching recording entries -/// into the database. They are kept as-is until they are deleted. -pub struct MmapFileSlice { - f: File, - range: Range, -} - -impl MmapFileSlice { - pub fn new(f: File, range: Range) -> MmapFileSlice { - MmapFileSlice{f: f, range: range} - } - - pub fn write_to(&self, range: Range, out: &mut io::Write) -> Result<()> { - // TODO: overflow check (in case u64 is larger than usize). - let r = self.range.start + range.start .. self.range.start + range.end; - assert!(r.end <= self.range.end, - "requested={:?} within={:?}", range, self.range); - let mmap = memmap::Mmap::open_with_offset( - &self.f, memmap::Protection::Read, r.start as usize, (r.end - r.start) as usize)?; - unsafe { out.write_all(mmap.as_slice())?; } - Ok(()) - } -} diff --git a/src/mp4.rs b/src/mp4.rs index 09387ef..a1c2512 100644 --- a/src/mp4.rs +++ b/src/mp4.rs @@ -83,14 +83,18 @@ use byteorder::{BigEndian, ByteOrder, WriteBytesExt}; use db; use dir; use error::Error; +use futures::{Future, Stream}; +use futures::stream; use http_entity; use hyper::header; -use mmapfile; +use memmap; use openssl::hash; +use parking_lot::{Once, ONCE_INIT}; use recording::{self, TIME_UNITS_PER_SEC}; -use slices::{self, Slices}; +use reffers::ARefs; +use slices::{self, Body, Chunk, Slices}; use smallvec::SmallVec; -use lazycell::LazyCell; +use std::cell::UnsafeCell; use std::cmp; use std::io; use std::ops::Range; @@ -100,7 +104,7 @@ use strutil; use time::Timespec; /// This value should be incremented any time a change is made to this file that causes different -/// bytes to be output for a particular set of `Builder` options. Incrementing this value will +/// bytes to be output for a particular set of `Mp4Builder` options. Incrementing this value will /// cause the etag to change as well. const FORMAT_VERSION: [u8; 1] = [0x03]; @@ -329,42 +333,46 @@ struct Segment { /// 1. stts: `slice[.. stsz_start]` /// 2. stsz: `slice[stsz_start .. stss_start]` /// 3. stss: `slice[stss_start ..]` - /// Access only through `write_index`. - index: LazyCell, ()>>, + /// Access only through `get_index`. + index: UnsafeCell, ()>>, /// The 1-indexed frame number in the `File` of the first frame in this segment. first_frame_num: u32, - num_subtitle_samples: u32, + num_subtitle_samples: u16, + + index_once: Once, } +unsafe impl Sync for Segment {} + impl Segment { fn new(db: &db::LockedDatabase, row: &db::ListRecordingsRow, rel_range_90k: Range, first_frame_num: u32) -> Result { Ok(Segment{ s: recording::Segment::new(db, row, rel_range_90k)?, - index: LazyCell::new(), + index: UnsafeCell::new(Err(())), + index_once: ONCE_INIT, first_frame_num: first_frame_num, num_subtitle_samples: 0, }) } - fn write_index(&self, r: Range, db: &db::Database, out: &mut io::Write, f: F) - -> Result<(), Error> + fn get_index<'a, F>(&'a self, db: &db::Database, f: F) -> Result<&'a [u8], Error> where F: FnOnce(&[u8], SegmentLengths) -> &[u8] { - let index = self.index.borrow_with(|| { - db.lock() - .with_recording_playback(self.s.camera_id, self.s.recording_id, - |playback| self.build_index(playback)) - .map_err(|e| { error!("Unable to build index for segment: {:?}", e); }) + self.index_once.call_once(|| { + let index = unsafe { &mut *self.index.get() }; + *index = db.lock() + .with_recording_playback(self.s.camera_id, self.s.recording_id, + |playback| self.build_index(playback)) + .map_err(|e| { error!("Unable to build index for segment: {:?}", e); }); }); - let index = match *index { - Ok(ref b) => &b[..], + let index: &'a _ = unsafe { &*self.index.get() }; + match *index { + Ok(ref b) => return Ok(f(&b[..], self.lens())), Err(()) => { return Err(Error::new("Unable to build index; see previous error.".to_owned())) }, - }; - out.write_all(&f(&index, self.lens())[r.start as usize .. r.end as usize])?; - Ok(()) + } } fn lens(&self) -> SegmentLengths { @@ -466,8 +474,8 @@ enum SliceType { VideoSampleEntry = 2, // param is index into m.video_sample_entries Stts = 3, // param is index into m.segments Stsz = 4, // param is index into m.segments - Co64 = 5, // param is unused - Stss = 6, // param is index into m.segments + Stss = 5, // param is index into m.segments + Co64 = 6, // param is unused VideoSampleData = 7, // param is index into m.segments SubtitleSampleData = 8, // param is index into m.segments @@ -482,49 +490,69 @@ impl Slice { Ok(Slice(end | ((t as u64) << 40) | ((p as u64) << 44))) } -} -impl Slice { fn t(&self) -> SliceType { // This value is guaranteed to be a valid SliceType because it was copied from a SliceType // in Slice::new. unsafe { ::std::mem::transmute(((self.0 >> 40) & 0xF) as u8) } } fn p(&self) -> usize { (self.0 >> 44) as usize } + + fn wrap_index(&self, mp4: &File, r: Range, f: &F) -> Result + where F: Fn(&[u8], SegmentLengths) -> &[u8] { + let mp4 = ARefs::new(mp4.0.clone()); + let r = r.start as usize .. r.end as usize; + let p = self.p(); + mp4.try_map(|mp4| Ok(&mp4.segments[p].get_index(&mp4.db, f)?[r])) + } } impl slices::Slice for Slice { type Ctx = File; + type Chunk = slices::Chunk; fn end(&self) -> u64 { return self.0 & 0xFF_FF_FF_FF_FF } - fn write_to(&self, f: &File, r: Range, l: u64, out: &mut io::Write) - -> Result<(), Error> { - let t = self.t(); + fn get_range(&self, f: &File, range: Range, len: u64) -> Body { + trace!("getting mp4 slice {:?}'s range {:?} / {}", self, range, len); let p = self.p(); - trace!("write {:?}, range {:?} out of len {}", self, r, l); - match t { + let res = match self.t() { SliceType::Static => { let s = STATIC_BYTESTRINGS[p]; - let part = &s[r.start as usize .. r.end as usize]; - out.write_all(part)?; - Ok(()) + let part = &s[range.start as usize .. range.end as usize]; + Ok(part.into()) }, SliceType::Buf => { - out.write_all(&f.buf[p+r.start as usize .. p+r.end as usize])?; - Ok(()) + let r = ARefs::new(f.0.clone()); + Ok(r.map(|f| &f.buf[p+range.start as usize .. p+range.end as usize])) }, SliceType::VideoSampleEntry => { - out.write_all(&f.video_sample_entries[p].data[r.start as usize .. r.end as usize])?; - Ok(()) + let r = ARefs::new(f.0.clone()); + Ok(r.map(|f| &f.video_sample_entries[p] + .data[range.start as usize .. range.end as usize])) }, - SliceType::Stts => f.segments[p].write_index(r, &f.db, out, Segment::stts), - SliceType::Stsz => f.segments[p].write_index(r, &f.db, out, Segment::stsz), - SliceType::Stss => f.segments[p].write_index(r, &f.db, out, Segment::stss), - SliceType::Co64 => f.write_co64(r, l, out), - SliceType::VideoSampleData => f.write_video_sample_data(p, r, out), - SliceType::SubtitleSampleData => f.write_subtitle_sample_data(p, r, l, out), - } + SliceType::Stts => self.wrap_index(f, range.clone(), &Segment::stts), + SliceType::Stsz => self.wrap_index(f, range.clone(), &Segment::stsz), + SliceType::Stss => self.wrap_index(f, range.clone(), &Segment::stss), + SliceType::Co64 => f.0.get_co64(range.clone(), len), + SliceType::VideoSampleData => f.0.get_video_sample_data(p, range.clone()), + SliceType::SubtitleSampleData => f.0.get_subtitle_sample_data(p, range.clone(), len), + }; + stream::once(res + .map_err(|e| { + error!("Error producing {:?}: {:?}", self, e); + ::hyper::Error::Incomplete + }) + .and_then(move |c| { + if c.len() != (range.end - range.start) as usize { + error!("Error producing {:?}: range {:?} produced incorrect len {}.", + self, range, c.len()); + return Err(::hyper::Error::Incomplete); + } + Ok(c) + })).boxed() } + + fn get_slices(ctx: &File) -> &Slices { &ctx.0.slices } } impl ::std::fmt::Debug for Slice { @@ -557,7 +585,7 @@ macro_rules! write_length { impl FileBuilder { pub fn new() -> Self { - FileBuilder{ + FileBuilder { segments: Vec::new(), video_sample_entries: SmallVec::new(), next_frame_num: 1, @@ -626,8 +654,8 @@ impl FileBuilder { let end_sec = (s.s.start + recording::Duration(d.end as i64 + TIME_UNITS_PER_SEC - 1)) .unix_seconds(); - s.num_subtitle_samples = (end_sec - start_sec) as u32; - self.num_subtitle_samples += s.num_subtitle_samples; + s.num_subtitle_samples = (end_sec - start_sec) as u16; + self.num_subtitle_samples += s.num_subtitle_samples as u32; } // Update the etag to reflect this segment. @@ -692,7 +720,7 @@ impl FileBuilder { debug!("Estimated {} buf bytes; actually were {}", EST_BUF_LEN, self.body.buf.len()); } debug!("slices: {:?}", self.body.slices); - Ok(File{ + Ok(File(Arc::new(FileInner{ db: db, dir: dir, segments: self.segments, @@ -702,7 +730,7 @@ impl FileBuilder { initial_sample_byte_pos: initial_sample_byte_pos, last_modified: header::HttpDate(time::at(Timespec::new(max_end.unix_seconds(), 0))), etag: header::EntityTag::strong(strutil::hex(&etag.finish()?)), - }) + }))) } /// Appends a `MovieBox` (ISO/IEC 14496-12 section 8.2.1). @@ -1015,7 +1043,7 @@ impl FileBuilder { write_length!(self, { self.body.buf.extend_from_slice( b"stsc\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01"); - self.body.append_u32(self.num_subtitle_samples); + self.body.append_u32(self.num_subtitle_samples as u32); self.body.append_u32(1); }) } @@ -1042,7 +1070,7 @@ impl FileBuilder { write_length!(self, { self.body.buf.extend_from_slice(b"stsz\x00\x00\x00\x00"); self.body.append_u32((mem::size_of::() + SUBTITLE_LENGTH) as u32); - self.body.append_u32(self.num_subtitle_samples); + self.body.append_u32(self.num_subtitle_samples as u32); }) } @@ -1123,7 +1151,7 @@ impl BodyState { } } -pub struct File { +struct FileInner { db: Arc, dir: Arc, segments: Vec, @@ -1135,60 +1163,67 @@ pub struct File { etag: header::EntityTag, } -impl File { - fn write_co64(&self, r: Range, l: u64, out: &mut io::Write) -> Result<(), Error> { - slices::clip_to_range(r, l, out, |w| { - let mut pos = self.initial_sample_byte_pos; - for s in &self.segments { - w.write_u64::(pos)?; - let r = s.s.sample_file_range(); - pos += r.end - r.start; - } - Ok(()) - }) +impl FileInner { + fn get_co64(&self, r: Range, l: u64) -> Result { + let mut v = Vec::with_capacity(l as usize); + let mut pos = self.initial_sample_byte_pos; + for s in &self.segments { + v.write_u64::(pos)?; + let r = s.s.sample_file_range(); + pos += r.end - r.start; + } + Ok(ARefs::new(v).map(|v| &v[r.start as usize .. r.end as usize])) } - fn write_video_sample_data(&self, i: usize, r: Range, out: &mut io::Write) - -> Result<(), Error> { + /// Gets a `Chunk` of video sample data from disk. + /// This works by `mmap()`ing in the data. There are a couple caveats: + /// + /// * The thread which reads the resulting slice is likely to experience major page faults. + /// Eventually this will likely be rewritten to `mmap()` the memory in another thread, and + /// `mlock()` and send chunks of it to be read and `munlock()`ed to avoid this problem. + /// + /// * If the backing file is truncated, the program will crash with `SIGBUS`. This shouldn't + /// happen because nothing should be touching Moonfire NVR's files but itself. + fn get_video_sample_data(&self, i: usize, r: Range) -> Result { let s = &self.segments[i]; let uuid = { self.db.lock().with_recording_playback(s.s.camera_id, s.s.recording_id, |p| Ok(p.sample_file_uuid))? }; let f = self.dir.open_sample_file(uuid)?; - mmapfile::MmapFileSlice::new(f, s.s.sample_file_range()).write_to(r, out) + let mmap = Box::new(memmap::Mmap::open_with_offset( + &f, memmap::Protection::Read, r.start as usize, (r.end - r.start) as usize)?); + Ok(ARefs::new(mmap).map(|m| unsafe { m.as_slice() })) } - fn write_subtitle_sample_data(&self, i: usize, r: Range, l: u64, out: &mut io::Write) - -> Result<(), Error> { + fn get_subtitle_sample_data(&self, i: usize, r: Range, l: u64) -> Result { let s = &self.segments[i]; let d = &s.s.desired_range_90k; let start_sec = (s.s.start + recording::Duration(d.start as i64)).unix_seconds(); let end_sec = (s.s.start + recording::Duration(d.end as i64 + TIME_UNITS_PER_SEC - 1)) .unix_seconds(); - slices::clip_to_range(r, l, out, |w| { - for ts in start_sec .. end_sec { - w.write_u16::(SUBTITLE_LENGTH as u16)?; - let tm = time::at(time::Timespec{sec: ts, nsec: 0}); - use std::io::Write; - write!(w, "{}", tm.strftime(SUBTITLE_TEMPLATE)?)?; - } - Ok(()) - })?; - Ok(()) + let mut v = Vec::with_capacity(l as usize); + for ts in start_sec .. end_sec { + v.write_u16::(SUBTITLE_LENGTH as u16)?; + let tm = time::at(time::Timespec{sec: ts, nsec: 0}); + use std::io::Write; + write!(v, "{}", tm.strftime(SUBTITLE_TEMPLATE)?)?; + } + Ok(ARefs::new(v).map(|v| &v[r.start as usize .. r.end as usize])) } } -impl http_entity::Entity for File { +#[derive(Clone)] +pub struct File(Arc); + +impl http_entity::Entity for File { fn add_headers(&self, hdrs: &mut header::Headers) { hdrs.set(header::ContentType("video/mp4".parse().unwrap())); } - fn last_modified(&self) -> Option { Some(self.last_modified) } - fn etag(&self) -> Option { Some(self.etag.clone()) } - fn len(&self) -> u64 { self.slices.len() } - fn write_to(&self, range: Range, out: &mut io::Write) -> Result<(), Error> { - self.slices.write_to(self, range, out) - } + fn last_modified(&self) -> Option { Some(self.0.last_modified) } + fn etag(&self) -> Option { Some(self.0.etag.clone()) } + fn len(&self) -> u64 { self.0.slices.len() } + fn get_range(&self, range: Range) -> Body { self.0.slices.get_range(self, range) } } /// Tests. There are two general strategies used to validate the resulting files: @@ -1206,14 +1241,13 @@ mod tests { use byteorder::{BigEndian, ByteOrder}; use db; use dir; - use error::Error; + use futures::Stream as FuturesStream; use ffmpeg; use hyper::header; use openssl::hash; use recording::{self, TIME_UNITS_PER_SEC}; use http_entity::{self, Entity}; use std::fs; - use std::io; use std::ops::Range; use std::path::Path; use std::sync::Arc; @@ -1223,27 +1257,29 @@ mod tests { use stream::{self, Opener, Stream}; use testutil::{self, TestDb, TEST_CAMERA_ID}; - /// A wrapper around openssl's SHA-1 hashing that implements the `Write` trait. - struct Sha1(hash::Hasher); - - impl Sha1 { - fn new() -> Sha1 { Sha1(hash::Hasher::new(hash::MessageDigest::sha1()).unwrap()) } - fn finish(mut self) -> Vec { self.0.finish().unwrap() } - } - - impl io::Write for Sha1 { - fn write(&mut self, buf: &[u8]) -> io::Result { - self.0.update(buf).unwrap(); - Ok(buf.len()) - } - fn flush(&mut self) -> io::Result<()> { Ok(()) } + fn fill_slice(slice: &mut [u8], e: &http_entity::Entity, start: u64) { + let mut p = 0; + e.get_range(start .. start + slice.len() as u64) + .for_each(|chunk| { + slice[p .. p + chunk.len()].copy_from_slice(&chunk); + p += chunk.len(); + Ok::<_, ::hyper::Error>(()) + }) + .wait() + .unwrap(); } /// Returns the SHA-1 digest of the given `Entity`. - fn digest(e: &http_entity::Entity) -> Vec { - let mut sha1 = Sha1::new(); - e.write_to(0 .. e.len(), &mut sha1).unwrap(); - sha1.finish() + fn digest(e: &http_entity::Entity) -> Vec { + e.get_range(0 .. e.len()) + .fold(hash::Hasher::new(hash::MessageDigest::sha1()).unwrap(), |mut sha1, chunk| { + sha1.update(&chunk).unwrap(); + Ok::<_, ::hyper::Error>(sha1) + }) + .wait() + .unwrap() + .finish() + .unwrap() } /// Information used within `BoxCursor` to describe a box on the stack. @@ -1256,13 +1292,13 @@ mod tests { /// A cursor over the boxes in a `.mp4` file. Supports moving forward and up/down the box /// stack, not backward. Panics on error. #[derive(Clone)] - struct BoxCursor<'a> { - mp4: &'a http_entity::Entity, + struct BoxCursor { + mp4: File, stack: Vec, } - impl<'a> BoxCursor<'a> { - pub fn new(mp4: &'a http_entity::Entity) -> BoxCursor<'a> { + impl BoxCursor { + pub fn new(mp4: File) -> BoxCursor { BoxCursor{ mp4: mp4, stack: Vec::new(), @@ -1274,11 +1310,11 @@ mod tests { fn internal_push(&mut self, pos: u64, max: u64) -> bool { if pos == max { return false; } let mut hdr = [0u8; 16]; - self.mp4.write_to(pos .. pos+8, &mut &mut hdr[..]).unwrap(); + fill_slice(&mut hdr[..8], &self.mp4, pos); let (len, hdr_len, boxtype_slice) = match BigEndian::read_u32(&hdr[..4]) { 0 => (self.mp4.len() - pos, 8, &hdr[4..8]), 1 => { - self.mp4.write_to(pos+8 .. pos+12, &mut &mut hdr[..]).unwrap(); + fill_slice(&mut hdr[8..], &self.mp4, pos + 8); (BigEndian::read_u64(&hdr[4..12]), 16, &hdr[12..]) }, l => (l as u64, 8, &hdr[4..8]), @@ -1306,17 +1342,19 @@ mod tests { /// Gets the specified byte range within the current box, starting after the box type. /// Must not be at EOF. - pub fn get(&self, r: Range, mut buf: &mut [u8]) { + pub fn get(&self, start: u64, buf: &mut [u8]) { let interior = &self.stack.last().expect("at root").interior; - assert!(r.end < interior.end - interior.start); - self.mp4.write_to(r.start+interior.start .. r.end+interior.start, &mut buf).unwrap(); + assert!(start + (buf.len() as u64) < interior.end - interior.start); + fill_slice(buf, &self.mp4, start+interior.start); } pub fn get_all(&self) -> Vec { let interior = self.stack.last().expect("at root").interior.clone(); let len = (interior.end - interior.start) as usize; + trace!("get_all: start={}, len={}", interior.start, len); let mut out = Vec::with_capacity(len); - self.mp4.write_to(interior, &mut out).unwrap(); + unsafe { out.set_len(len) }; + fill_slice(&mut out[..], &self.mp4, interior.start); out } @@ -1324,7 +1362,7 @@ mod tests { /// Must not be at EOF. pub fn get_u32(&self, p: u64) -> u32 { let mut buf = [0u8; 4]; - self.get(p .. p+4, &mut buf); + self.get(p, &mut buf); BigEndian::read_u32(&buf[..]) } @@ -1360,14 +1398,14 @@ mod tests { } /// Information returned by `find_track`. - struct Track<'a> { - edts_cursor: Option>, - stbl_cursor: BoxCursor<'a>, + struct Track { + edts_cursor: Option, + stbl_cursor: BoxCursor, } /// Finds the `moov/trak` that has a `tkhd` associated with the given `track_id`, which must /// exist. - fn find_track(mp4: &http_entity::Entity, track_id: u32) -> Track { + fn find_track(mp4: File, track_id: u32) -> Track { let mut cursor = BoxCursor::new(mp4); cursor.down(); assert!(cursor.find(b"moov")); @@ -1377,7 +1415,7 @@ mod tests { cursor.down(); assert!(cursor.find(b"tkhd")); let mut version = [0u8; 1]; - cursor.get(0 .. 1, &mut version); + cursor.get(0, &mut version); // Let id_pos be the offset after the FullBox section of the track_id. let id_pos = match version[0] { @@ -1459,7 +1497,7 @@ mod tests { db.list_recordings_by_time(TEST_CAMERA_ID, all_time, |r| { let d = r.duration_90k; assert!(skip_90k + shorten_90k < d); - builder.append(&db, r, skip_90k .. d - shorten_90k).unwrap(); + builder.append(&*db, r, skip_90k .. d - shorten_90k).unwrap(); Ok(()) }).unwrap(); } @@ -1470,7 +1508,14 @@ mod tests { let mut filename = dir.to_path_buf(); filename.push("clip.new.mp4"); let mut out = fs::OpenOptions::new().write(true).create_new(true).open(&filename).unwrap(); - mp4.write_to(0 .. mp4.len(), &mut out).unwrap(); + use ::std::io::Write; + mp4.get_range(0 .. mp4.len()) + .for_each(|chunk| { + out.write_all(&chunk)?; + Ok(()) + }) + .wait() + .unwrap(); filename.to_str().unwrap().to_string() } @@ -1536,7 +1581,7 @@ mod tests { // Time range [2, 2+4+6+8) means the 2nd, 3rd, and 4th samples should be included. let mp4 = make_mp4_from_encoder(&db, encoder, 2 .. 2+4+6+8); - let track = find_track(&mp4, 1); + let track = find_track(mp4, 1); assert!(track.edts_cursor.is_none()); let mut cursor = track.stbl_cursor; cursor.down(); @@ -1590,7 +1635,7 @@ mod tests { // Time range [2+4+6, 2+4+6+8) means the 4th sample should be included. // The 3rd gets pulled in also because it's a sync frame and the 4th isn't. let mp4 = make_mp4_from_encoder(&db, encoder, 2+4+6 .. 2+4+6+8); - let track = find_track(&mp4, 1); + let track = find_track(mp4, 1); // Examine edts. It should skip the 3rd frame. let mut cursor = track.edts_cursor.unwrap(); @@ -1721,26 +1766,23 @@ mod tests { #[cfg(all(test, feature="nightly"))] mod bench { + extern crate reqwest; extern crate test; + use futures::future; + use futures::stream::BoxStream; use hyper; - use hyper::header; use http_entity; use recording; + use reffers::ARefs; use self::test::Bencher; use std::str; use super::tests::create_mp4_from_db; use testutil::{self, TestDb}; /// An HTTP server for benchmarking. - /// It's used as a singleton via `lazy_static!` for two reasons: - /// - /// * to avoid running out of file descriptors. `#[bench]` functions apparently get called - /// many times as the number of iterations is tuned, and hyper servers - /// [can't be shut down](https://github.com/hyperium/hyper/issues/338), so - /// otherwise the default Ubuntu 16.04.1 ulimit of 1024 files is quickly exhausted. - /// * so that when getting a CPU profile of the benchmark, more of the profile focuses - /// on the HTTP serving rather than the setup. + /// It's used as a singleton via `lazy_static!` so that when getting a CPU profile of the + /// benchmark, more of the profile focuses on the HTTP serving rather than the setup. /// /// Currently this only serves a single `.mp4` file but we could set up variations to benchmark /// different scenarios: with/without subtitles and edit lists, different lengths, serving @@ -1752,32 +1794,40 @@ mod bench { impl BenchServer { fn new() -> BenchServer { - let mut listener = hyper::net::HttpListener::new("127.0.0.1:0").unwrap(); - use hyper::net::NetworkListener; - let addr = listener.local_addr().unwrap(); - let server = hyper::Server::new(listener); - let url = hyper::Url::parse( - format!("http://{}:{}/", addr.ip(), addr.port()).as_str()).unwrap(); let db = TestDb::new(); testutil::add_dummy_recordings_to_db(&db.db, 60); let mp4 = create_mp4_from_db(db.db.clone(), db.dir.clone(), 0, 0, false); - let p = mp4.initial_sample_byte_pos; - use std::thread::spawn; - spawn(move || { - use hyper::server::{Request, Response, Fresh}; - let (db, dir) = (db.db.clone(), db.dir.clone()); - let _ = server.handle(move |req: Request, res: Response| { - let mp4 = create_mp4_from_db(db.clone(), dir.clone(), 0, 0, false); - http_entity::serve(&mp4, &req, res).unwrap(); - }); + let p = mp4.0.initial_sample_byte_pos; + let (tx, rx) = ::std::sync::mpsc::channel(); + ::std::thread::spawn(move || { + let addr = "127.0.0.1:0".parse().unwrap(); + let server = hyper::server::Http::new() + .bind(&addr, move || Ok(MyService(mp4.clone()))) + .unwrap(); + tx.send(server.local_addr().unwrap()).unwrap(); + server.run().unwrap(); }); + let addr = rx.recv().unwrap(); BenchServer{ - url: url, + url: hyper::Url::parse(&format!("http://{}:{}/", addr.ip(), addr.port())).unwrap(), generated_len: p, } } } + struct MyService(super::File); + + impl hyper::server::Service for MyService { + type Request = hyper::server::Request; + type Response = hyper::server::Response, hyper::Error>>; + type Error = hyper::Error; + type Future = future::FutureResult; + + fn call(&self, req: hyper::server::Request) -> Self::Future { + future::ok(http_entity::serve(self.0.clone(), &req)) + } + } + lazy_static! { static ref SERVER: BenchServer = { BenchServer::new() }; } @@ -1816,11 +1866,12 @@ mod bench { let p = server.generated_len; let mut buf = Vec::with_capacity(p as usize); b.bytes = p; - let client = hyper::Client::new(); + let client = reqwest::Client::new().unwrap(); let mut run = || { + use self::reqwest::header::{Range, ByteRangeSpec}; let mut resp = client.get(server.url.clone()) - .header(header::Range::Bytes(vec![header::ByteRangeSpec::FromTo(0, p - 1)])) + .header(Range::Bytes(vec![ByteRangeSpec::FromTo(0, p - 1)])) .send() .unwrap(); buf.clear(); diff --git a/src/recording.rs b/src/recording.rs index 378d032..c12ee2e 100644 --- a/src/recording.rs +++ b/src/recording.rs @@ -375,7 +375,8 @@ impl Segment { /// desired start time. (The caller is responsible for creating an edit list to skip the /// undesired portion.) It will end at the first frame after the desired range (unless the /// desired range extends beyond the recording). - pub fn new(db: &db::LockedDatabase, recording: &db::ListRecordingsRow, + pub fn new(db: &db::LockedDatabase, + recording: &db::ListRecordingsRow, desired_range_90k: Range) -> Result { let mut self_ = Segment{ camera_id: recording.camera_id, diff --git a/src/slices.rs b/src/slices.rs index 3ced687..3404ab3 100644 --- a/src/slices.rs +++ b/src/slices.rs @@ -30,15 +30,20 @@ //! Tools for implementing a `http_entity::Entity` body composed from many "slices". -use error::{Error, Result}; +use reffers::ARefs; +use futures::stream::{self, BoxStream}; +use futures::Stream; use std::fmt; -use std::io; use std::ops::Range; +pub type Chunk = ARefs<'static, [u8]>; +pub type Body = stream::BoxStream; + /// Writes a byte range to the given `io::Write` given a context argument; meant for use with /// `Slices`. -pub trait Slice { - type Ctx; +pub trait Slice : Sync + Sized + 'static { + type Ctx: Send + Clone; + type Chunk: Send; /// The byte position (relative to the start of the `Slices`) beyond the end of this slice. /// Note the starting position (and thus length) are inferred from the previous slice. @@ -47,22 +52,10 @@ pub trait Slice { /// Writes `r` to `out`, as in `http_entity::Entity::write_to`. /// The additional argument `ctx` is as supplied to the `Slices`. /// The additional argument `l` is the length of this slice, as determined by the `Slices`. - fn write_to(&self, ctx: &Self::Ctx, r: Range, l: u64, out: &mut io::Write) -> Result<()>; -} + fn get_range(&self, ctx: &Self::Ctx, r: Range, len: u64) + -> stream::BoxStream; -/// Calls `f` with an `io::Write` which delegates to `inner` only for the section defined by `r`. -/// This is useful for easily implementing the `ContextWriter` interface for pieces that generate -/// data on-the-fly rather than simply copying a buffer. -pub fn clip_to_range(r: Range, l: u64, inner: &mut io::Write, mut f: F) -> Result<()> -where F: FnMut(&mut Vec) -> Result<()> { - // Just create a buffer for the whole slice and copy out the relevant portion. - // One might expect it to be faster to avoid this memory allocation and extra copying, but - // benchmarks show when making many 4-byte writes it's better to be able to inline many - // Vec::write_all calls then make one call through traits to hyper's write logic. - let mut buf = Vec::with_capacity(l as usize); - f(&mut buf)?; - inner.write_all(&buf[r.start as usize .. r.end as usize])?; - Ok(()) + fn get_slices(ctx: &Self::Ctx) -> &Slices; } /// Helper to serve byte ranges from a body which is broken down into many "slices". @@ -114,56 +107,53 @@ impl Slices where S: Slice { /// Writes `range` to `out`. /// This interface mirrors `http_entity::Entity::write_to`, with the additional `ctx` argument. - pub fn write_to(&self, ctx: &S::Ctx, range: Range, out: &mut io::Write) -> Result<()> { + pub fn get_range(&self, ctx: &S::Ctx, range: Range) + -> BoxStream { if range.start > range.end || range.end > self.len { - return Err(Error{ - description: format!("Bad range {:?} for slice of length {}", range, self.len), - cause: None}); + error!("Bad range {:?} for slice of length {}", range, self.len); + return stream::once(Err(::hyper::Error::Incomplete)).boxed(); } // Binary search for the first slice of the range to write, determining its index and // (from the preceding slice) the start of its range. - let (mut i, mut slice_start) = match self.slices.binary_search_by_key(&range.start, - |s| s.end()) { - Ok(i) if i == self.slices.len() - 1 => return Ok(()), // at end. + let (i, slice_start) = match self.slices.binary_search_by_key(&range.start, |s| s.end()) { Ok(i) => (i+1, self.slices[i].end()), // desired start == slice i's end; first is i+1! Err(i) if i == 0 => (0, 0), // desired start < slice 0's end; first is 0. Err(i) => (i, self.slices[i-1].end()), // desired start < slice i's end; first is i. }; - // There is at least one slice to write. // Iterate through and write each slice until the end. - let mut start_pos = range.start - slice_start; - loop { - let s = &self.slices[i]; - let end = s.end(); - let l = end - slice_start; - if range.end <= end { // last slice. - return s.write_to(ctx, start_pos .. range.end - slice_start, l, out); - } - s.write_to(ctx, start_pos .. end - slice_start, l, out)?; - // Setup next iteration. - start_pos = 0; - slice_start = end; - i += 1; - } + let start_pos = range.start - slice_start; + let bodies = stream::unfold( + (ctx.clone(), i, start_pos, slice_start), move |(c, i, start_pos, slice_start)| { + let (body, end); + { + let self_ = S::get_slices(&c); + if i == self_.slices.len() { return None } + let s = &self_.slices[i]; + if range.end == slice_start + start_pos { return None } + end = ::std::cmp::min(range.end, s.end()); + let l = end - slice_start; + body = s.get_range(&c, start_pos .. end - slice_start, l); + }; + Some(Ok::<_, ::hyper::Error>((body, (c, i+1, 0, end)))) + }); + bodies.flatten().boxed() } } #[cfg(test)] mod tests { - use error::{Error, Result}; - use std::cell::RefCell; - use std::error::Error as E; - use std::io::Write; + use futures::{Future, Stream}; + use futures::stream::{self, BoxStream}; use std::ops::Range; - use std::vec::Vec; - use super::{Slice, Slices, clip_to_range}; + use super::{Slice, Slices}; + use testutil; #[derive(Debug, Eq, PartialEq)] - pub struct FakeWrite { - writer: &'static str, + pub struct FakeChunk { + slice: &'static str, range: Range, } @@ -173,146 +163,84 @@ mod tests { } impl Slice for FakeSlice { - type Ctx = RefCell>; + type Ctx = &'static Slices; + type Chunk = FakeChunk; fn end(&self) -> u64 { self.end } - fn write_to(&self, ctx: &RefCell>, r: Range, _l: u64, _out: &mut Write) - -> Result<()> { - ctx.borrow_mut().push(FakeWrite{writer: self.name, range: r}); - Ok(()) + fn get_range(&self, _ctx: &&'static Slices, r: Range, _l: u64) + -> BoxStream { + stream::once(Ok(FakeChunk{slice: self.name, range: r})).boxed() } + + fn get_slices(ctx: &&'static Slices) -> &'static Slices { *ctx } } - pub fn new_slices() -> Slices { - let mut s = Slices::new(); - s.append(FakeSlice{end: 5, name: "a"}); - s.append(FakeSlice{end: 5+13, name: "b"}); - s.append(FakeSlice{end: 5+13+7, name: "c"}); - s.append(FakeSlice{end: 5+13+7+17, name: "d"}); - s.append(FakeSlice{end: 5+13+7+17+19, name: "e"}); - s + lazy_static! { + static ref SLICES: Slices = { + let mut s = Slices::new(); + s.append(FakeSlice{end: 5, name: "a"}); + s.append(FakeSlice{end: 5+13, name: "b"}); + s.append(FakeSlice{end: 5+13+7, name: "c"}); + s.append(FakeSlice{end: 5+13+7+17, name: "d"}); + s.append(FakeSlice{end: 5+13+7+17+19, name: "e"}); + s + }; } #[test] pub fn size() { - assert_eq!(5 + 13 + 7 + 17 + 19, new_slices().len()); + testutil::init(); + assert_eq!(5 + 13 + 7 + 17 + 19, SLICES.len()); } #[test] pub fn exact_slice() { // Test writing exactly slice b. - let s = new_slices(); - let w = RefCell::new(Vec::new()); - let mut dummy = Vec::new(); - s.write_to(&w, 5 .. 18, &mut dummy).unwrap(); - assert_eq!(&[FakeWrite{writer: "b", range: 0 .. 13}], &w.borrow()[..]); + testutil::init(); + let out = SLICES.get_range(&&*SLICES, 5 .. 18).collect().wait().unwrap(); + assert_eq!(&[FakeChunk{slice: "b", range: 0 .. 13}], &out[..]); } #[test] pub fn offset_first() { // Test writing part of slice a. - let s = new_slices(); - let w = RefCell::new(Vec::new()); - let mut dummy = Vec::new(); - s.write_to(&w, 1 .. 3, &mut dummy).unwrap(); - assert_eq!(&[FakeWrite{writer: "a", range: 1 .. 3}], &w.borrow()[..]); + testutil::init(); + let out = SLICES.get_range(&&*SLICES, 1 .. 3).collect().wait().unwrap(); + assert_eq!(&[FakeChunk{slice: "a", range: 1 .. 3}], &out[..]); } #[test] pub fn offset_mid() { // Test writing part of slice b, all of slice c, and part of slice d. - let s = new_slices(); - let w = RefCell::new(Vec::new()); - let mut dummy = Vec::new(); - s.write_to(&w, 17 .. 26, &mut dummy).unwrap(); + testutil::init(); + let out = SLICES.get_range(&&*SLICES, 17 .. 26).collect().wait().unwrap(); assert_eq!(&[ - FakeWrite{writer: "b", range: 12 .. 13}, - FakeWrite{writer: "c", range: 0 .. 7}, - FakeWrite{writer: "d", range: 0 .. 1}, - ], &w.borrow()[..]); + FakeChunk{slice: "b", range: 12 .. 13}, + FakeChunk{slice: "c", range: 0 .. 7}, + FakeChunk{slice: "d", range: 0 .. 1}, + ], &out[..]); } #[test] pub fn everything() { // Test writing the whole Slices. - let s = new_slices(); - let w = RefCell::new(Vec::new()); - let mut dummy = Vec::new(); - s.write_to(&w, 0 .. 61, &mut dummy).unwrap(); + testutil::init(); + let out = SLICES.get_range(&&*SLICES, 0 .. 61).collect().wait().unwrap(); assert_eq!(&[ - FakeWrite{writer: "a", range: 0 .. 5}, - FakeWrite{writer: "b", range: 0 .. 13}, - FakeWrite{writer: "c", range: 0 .. 7}, - FakeWrite{writer: "d", range: 0 .. 17}, - FakeWrite{writer: "e", range: 0 .. 19}, - ], &w.borrow()[..]); + FakeChunk{slice: "a", range: 0 .. 5}, + FakeChunk{slice: "b", range: 0 .. 13}, + FakeChunk{slice: "c", range: 0 .. 7}, + FakeChunk{slice: "d", range: 0 .. 17}, + FakeChunk{slice: "e", range: 0 .. 19}, + ], &out[..]); } #[test] pub fn at_end() { - let s = new_slices(); - let w = RefCell::new(Vec::new()); - let mut dummy = Vec::new(); - s.write_to(&w, 61 .. 61, &mut dummy).unwrap(); - let empty: &[FakeWrite] = &[]; - assert_eq!(empty, &w.borrow()[..]); - } - - #[test] - pub fn test_clip_to_range() { - let mut out = Vec::new(); - - // Simple case: one write with everything. - clip_to_range(0 .. 5, 5, &mut out, |w| { - w.write_all(b"01234").unwrap(); - Ok(()) - }).unwrap(); - assert_eq!(b"01234", &out[..]); - - // Same in a few writes. - out.clear(); - clip_to_range(0 .. 5, 5, &mut out, |w| { - w.write_all(b"0").unwrap(); - w.write_all(b"123").unwrap(); - w.write_all(b"4").unwrap(); - Ok(()) - }).unwrap(); - assert_eq!(b"01234", &out[..]); - - // Limiting to a prefix. - out.clear(); - clip_to_range(0 .. 2, 5, &mut out, |w| { - w.write_all(b"0").unwrap(); // all of this write - w.write_all(b"123").unwrap(); // some of this write - w.write_all(b"4").unwrap(); // none of this write - Ok(()) - }).unwrap(); - assert_eq!(b"01", &out[..]); - - // Limiting to part in the middle. - out.clear(); - clip_to_range(2 .. 4, 5, &mut out, |w| { - w.write_all(b"0").unwrap(); // none of this write - w.write_all(b"1234").unwrap(); // middle of this write - w.write_all(b"5678").unwrap(); // none of this write - Ok(()) - }).unwrap(); - assert_eq!(b"23", &out[..]); - - // If the callback returns an error, it should be propagated (fast path or not). - out.clear(); - assert_eq!( - clip_to_range(0 .. 4, 4, &mut out, |_| Err(Error::new("some error".to_owned()))) - .unwrap_err().description(), - "some error"); - out.clear(); - assert_eq!( - clip_to_range(0 .. 1, 4, &mut out, |_| Err(Error::new("some error".to_owned()))) - .unwrap_err().description(), - "some error"); - - // TODO: if inner.write does a partial write, the next try should start at the correct - // position. + testutil::init(); + let out = SLICES.get_range(&&*SLICES, 61 .. 61).collect().wait().unwrap(); + let empty: &[FakeChunk] = &[]; + assert_eq!(empty, &out[..]); } } diff --git a/src/web.rs b/src/web.rs index f384f44..351016e 100644 --- a/src/web.rs +++ b/src/web.rs @@ -35,15 +35,20 @@ use core::str::FromStr; use db; use dir::SampleFileDir; use error::Error; +use futures::Stream; +use futures::{future, stream}; use json; use http_entity; -use hyper::{header,server,status}; -use hyper::uri::RequestUri; +use hyper::{header, status}; +use hyper::server::{self, Request, Response}; use mime; use mp4; +use parking_lot::MutexGuard; use recording; +use reffers::ARefs; use regex::Regex; use serde_json; +use slices; use std::cmp; use std::fmt; use std::io::Write; @@ -75,17 +80,6 @@ enum Path { NotFound, } -fn get_path_and_query(uri: &RequestUri) -> (&str, &str) { - match *uri { - RequestUri::AbsolutePath(ref both) => match both.find('?') { - Some(split) => (&both[..split], &both[split+1..]), - None => (both, ""), - }, - RequestUri::AbsoluteUri(ref u) => (u.path(), u.query().unwrap_or("")), - _ => ("", ""), - } -} - fn decode_path(path: &str) -> Path { if path == "/" { return Path::CamerasList; @@ -116,8 +110,8 @@ fn decode_path(path: &str) -> Path { } } -fn is_json(req: &server::Request) -> bool { - if let Some(accept) = req.headers.get::() { +fn is_json(req: &Request) -> bool { + if let Some(accept) = req.headers().get::() { return accept.len() == 1 && accept[0].item == *JSON && accept[0].quality == header::Quality(1000); } @@ -182,11 +176,6 @@ impl fmt::Display for HumanizedTimestamp { } } -pub struct Handler { - db: Arc, - dir: Arc, -} - #[derive(Debug, Eq, PartialEq)] struct Segments { ids: Range, @@ -227,33 +216,40 @@ impl Segments { } } -impl Handler { +pub struct Service { + db: Arc, + dir: Arc, +} + +impl Service { pub fn new(db: Arc, dir: Arc) -> Self { - Handler{db: db, dir: dir} + Service{db: db, dir: dir} } - fn not_found(&self, mut res: server::Response) -> Result<(), Error> { - *res.status_mut() = status::StatusCode::NotFound; - res.send(b"not found")?; - Ok(()) + fn not_found(&self) -> Result, Error> { + Ok(Response::new() + .with_status(status::StatusCode::NotFound) + .with_header(header::ContentType(mime!(Text/Plain))) + .with_body(stream::once(Ok(ARefs::new(&b"not found"[..]))).boxed())) } - fn list_cameras(&self, req: &server::Request, mut res: server::Response) -> Result<(), Error> { + fn list_cameras(&self, req: &Request) -> Result, Error> { let json = is_json(req); let buf = { let db = self.db.lock(); if json { serde_json::to_vec(&json::ListCameras{cameras: db.cameras_by_id()})? } else { - self.list_cameras_html(&db)? + self.list_cameras_html(db)? } }; - res.headers_mut().set(header::ContentType(if json { JSON.clone() } else { HTML.clone() })); - res.send(&buf)?; - Ok(()) + Ok(Response::new() + .with_header(header::ContentType(if json { JSON.clone() } else { HTML.clone() })) + .with_header(header::ContentLength(buf.len() as u64)) + .with_body(stream::once(Ok(ARefs::new(buf))).boxed())) } - fn list_cameras_html(&self, db: &db::LockedDatabase) -> Result, Error> { + fn list_cameras_html(&self, db: MutexGuard) -> Result, Error> { let mut buf = Vec::new(); buf.extend_from_slice(b"\ \n\ @@ -287,8 +283,8 @@ impl Handler { Ok(buf) } - fn camera(&self, uuid: Uuid, query: &str, req: &server::Request, mut res: server::Response) - -> Result<(), Error> { + fn camera(&self, uuid: Uuid, query: Option<&str>, req: &Request) + -> Result, Error> { let json = is_json(req); let buf = { let db = self.db.lock(); @@ -297,28 +293,31 @@ impl Handler { .ok_or_else(|| Error::new("no such camera".to_owned()))?; serde_json::to_vec(&json::Camera::new(camera, true))? } else { - self.camera_html(&db, query, uuid)? + self.camera_html(db, query, uuid)? } }; - res.headers_mut().set(header::ContentType(if json { JSON.clone() } else { HTML.clone() })); - res.send(&buf)?; - Ok(()) + Ok(Response::new() + .with_header(header::ContentType(if json { JSON.clone() } else { HTML.clone() })) + .with_header(header::ContentLength(buf.len() as u64)) + .with_body(stream::once(Ok(ARefs::new(buf))).boxed())) } - fn camera_html(&self, db: &db::LockedDatabase, query: &str, uuid: Uuid) - -> Result, Error> { + fn camera_html(&self, db: MutexGuard, query: Option<&str>, + uuid: Uuid) -> Result, Error> { let (r, trim) = { let mut time = recording::Time(i64::min_value()) .. recording::Time(i64::max_value()); let mut trim = false; - for (key, value) in form_urlencoded::parse(query.as_bytes()) { - let (key, value) = (key.borrow(), value.borrow()); - match key { - "start_time" => time.start = recording::Time::parse(value)?, - "end_time" => time.end = recording::Time::parse(value)?, - "trim" if value == "true" => trim = true, - _ => {}, - } - }; + if let Some(q) = query { + for (key, value) in form_urlencoded::parse(q.as_bytes()) { + let (key, value) = (key.borrow(), value.borrow()); + match key { + "start_time" => time.start = recording::Time::parse(value)?, + "end_time" => time.end = recording::Time::parse(value)?, + "trim" if value == "true" => trim = true, + _ => {}, + } + }; + } (time, trim) }; let camera = db.get_camera(uuid) @@ -396,13 +395,14 @@ impl Handler { Ok(buf) } - fn camera_recordings(&self, uuid: Uuid, query: &str, req: &server::Request, - mut res: server::Response) -> Result<(), Error> { - let r = Handler::get_optional_range(query)?; + fn camera_recordings(&self, uuid: Uuid, query: Option<&str>, req: &Request) + -> Result, Error> { + let r = Service::get_optional_range(query)?; if !is_json(req) { - *res.status_mut() = status::StatusCode::NotAcceptable; - res.send(b"only available for JSON requests")?; - return Ok(()); + return Ok(Response::new() + .with_status(status::StatusCode::NotAcceptable) + .with_body(stream::once( + Ok(ARefs::new(&b"only available for JSON requests"[..]))).boxed())); } let mut out = json::ListRecordings{recordings: Vec::new()}; { @@ -424,13 +424,14 @@ impl Handler { })?; } let buf = serde_json::to_vec(&out)?; - res.headers_mut().set(header::ContentType(JSON.clone())); - res.send(&buf)?; - Ok(()) + Ok(Response::new() + .with_header(header::ContentType(JSON.clone())) + .with_header(header::ContentLength(buf.len() as u64)) + .with_body(stream::once(Ok(ARefs::new(buf))).boxed())) } - fn camera_view_mp4(&self, uuid: Uuid, query: &str, req: &server::Request, - res: server::Response) -> Result<(), Error> { + fn camera_view_mp4(&self, uuid: Uuid, query: Option<&str>, req: &Request) + -> Result, Error> { let camera_id = { let db = self.db.lock(); let camera = db.get_camera(uuid) @@ -438,117 +439,127 @@ impl Handler { camera.id }; let mut builder = mp4::FileBuilder::new(); - for (key, value) in form_urlencoded::parse(query.as_bytes()) { - let (key, value) = (key.borrow(), value.borrow()); - match key { - "s" => { - let s = Segments::parse(value).map_err( - |_| Error::new(format!("invalid s parameter: {}", value)))?; - debug!("camera_view_mp4: appending s={:?}", s); - let mut est_segments = (s.ids.end - s.ids.start) as usize; - if let Some(end) = s.end_time { - // There should be roughly ceil((end - start) / desired_recording_duration) - // recordings in the desired timespan if there are no gaps or overlap, - // possibly another for misalignment of the requested timespan with the - // rotate offset and another because rotation only happens at key frames. - let ceil_durations = (end - s.start_time + - recording::DESIRED_RECORDING_DURATION - 1) / - recording::DESIRED_RECORDING_DURATION; - est_segments = cmp::min(est_segments, (ceil_durations + 2) as usize); - } - builder.reserve(est_segments); - let db = self.db.lock(); - let mut prev = None; - let mut cur_off = 0; - db.list_recordings_by_id(camera_id, s.ids.clone(), |r| { + if let Some(q) = query { + for (key, value) in form_urlencoded::parse(q.as_bytes()) { + let (key, value) = (key.borrow(), value.borrow()); + match key { + "s" => { + let s = Segments::parse(value).map_err( + |_| Error::new(format!("invalid s parameter: {}", value)))?; + debug!("camera_view_mp4: appending s={:?}", s); + let mut est_segments = (s.ids.end - s.ids.start) as usize; + if let Some(end) = s.end_time { + // There should be roughly ceil((end - start) / + // desired_recording_duration) recordings in the desired timespan if + // there are no gaps or overlap, possibly another for misalignment of + // the requested timespan with the rotate offset and another because + // rotation only happens at key frames. + let ceil_durations = (end - s.start_time + + recording::DESIRED_RECORDING_DURATION - 1) / + recording::DESIRED_RECORDING_DURATION; + est_segments = cmp::min(est_segments, (ceil_durations + 2) as usize); + } + builder.reserve(est_segments); + let db = self.db.lock(); + let mut prev = None; + let mut cur_off = 0; + db.list_recordings_by_id(camera_id, s.ids.clone(), |r| { + // Check for missing recordings. + match prev { + None if r.id == s.ids.start => {}, + None => return Err(Error::new(format!("no such recording {}/{}", + camera_id, s.ids.start))), + Some(id) if r.id != id + 1 => { + return Err(Error::new(format!("no such recording {}/{}", + camera_id, id + 1))); + }, + _ => {}, + }; + prev = Some(r.id); + + // Add a segment for the relevant part of the recording, if any. + let end_time = s.end_time.unwrap_or(i64::max_value()); + let d = r.duration_90k as i64; + if s.start_time <= cur_off + d && cur_off < end_time { + let start = cmp::max(0, s.start_time - cur_off); + let end = cmp::min(d, end_time - cur_off); + let times = start as i32 .. end as i32; + debug!("...appending recording {}/{} with times {:?} (out of dur {})", + r.camera_id, r.id, times, d); + builder.append(&db, r, start as i32 .. end as i32)?; + } else { + debug!("...skipping recording {}/{} dur {}", r.camera_id, r.id, d); + } + cur_off += d; + Ok(()) + })?; + // Check for missing recordings. match prev { - None if r.id == s.ids.start => {}, - None => return Err(Error::new(format!("no such recording {}/{}", - camera_id, s.ids.start))), - Some(id) if r.id != id + 1 => { + Some(id) if s.ids.end != id + 1 => { return Err(Error::new(format!("no such recording {}/{}", - camera_id, id + 1))); + camera_id, s.ids.end - 1))); + }, + None => { + return Err(Error::new(format!("no such recording {}/{}", + camera_id, s.ids.start))); }, _ => {}, }; - prev = Some(r.id); - - // Add a segment for the relevant part of the recording, if any. - let end_time = s.end_time.unwrap_or(i64::max_value()); - let d = r.duration_90k as i64; - if s.start_time <= cur_off + d && cur_off < end_time { - let start = cmp::max(0, s.start_time - cur_off); - let end = cmp::min(d, end_time - cur_off); - let times = start as i32 .. end as i32; - debug!("...appending recording {}/{} with times {:?} (out of dur {})", - r.camera_id, r.id, times, d); - builder.append(&db, r, start as i32 .. end as i32)?; - } else { - debug!("...skipping recording {}/{} dur {}", r.camera_id, r.id, d); + if let Some(end) = s.end_time { + if end > cur_off { + return Err(Error::new( + format!("end time {} is beyond specified recordings", end))); + } } - cur_off += d; - Ok(()) - })?; - - // Check for missing recordings. - match prev { - Some(id) if s.ids.end != id + 1 => { - return Err(Error::new(format!("no such recording {}/{}", - camera_id, s.ids.end - 1))); - }, - None => { - return Err(Error::new(format!("no such recording {}/{}", - camera_id, s.ids.start))); - }, - _ => {}, - }; - if let Some(end) = s.end_time { - if end > cur_off { - return Err(Error::new( - format!("end time {} is beyond specified recordings", end))); - } - } - }, - "ts" => builder.include_timestamp_subtitle_track(value == "true"), - _ => return Err(Error::new(format!("parameter {} not understood", key))), - } - }; + }, + "ts" => builder.include_timestamp_subtitle_track(value == "true"), + _ => return Err(Error::new(format!("parameter {} not understood", key))), + } + }; + } let mp4 = builder.build(self.db.clone(), self.dir.clone())?; - http_entity::serve(&mp4, req, res)?; - Ok(()) + Ok(http_entity::serve(mp4, req)) } /// Parses optional `start_time_90k` and `end_time_90k` query parameters, defaulting to the /// full range of possible values. - fn get_optional_range(query: &str) -> Result, Error> { + fn get_optional_range(query: Option<&str>) -> Result, Error> { let mut start = i64::min_value(); let mut end = i64::max_value(); - for (key, value) in form_urlencoded::parse(query.as_bytes()) { - let (key, value) = (key.borrow(), value.borrow()); - match key { - "start_time_90k" => start = i64::from_str(value)?, - "end_time_90k" => end = i64::from_str(value)?, - _ => {}, - } - }; + if let Some(q) = query { + for (key, value) in form_urlencoded::parse(q.as_bytes()) { + let (key, value) = (key.borrow(), value.borrow()); + match key { + "start_time_90k" => start = i64::from_str(value)?, + "end_time_90k" => end = i64::from_str(value)?, + _ => {}, + } + }; + } Ok(recording::Time(start) .. recording::Time(end)) } } -impl server::Handler for Handler { - fn handle(&self, req: server::Request, res: server::Response) { - let (path, query) = get_path_and_query(&req.uri); - let res = match decode_path(path) { - Path::CamerasList => self.list_cameras(&req, res), - Path::Camera(uuid) => self.camera(uuid, query, &req, res), - Path::CameraRecordings(uuid) => self.camera_recordings(uuid, query, &req, res), - Path::CameraViewMp4(uuid) => self.camera_view_mp4(uuid, query, &req, res), - Path::NotFound => self.not_found(res), +impl server::Service for Service { + type Request = Request; + type Response = Response; + type Error = hyper::Error; + type Future = future::FutureResult; + + fn call(&self, req: Request) -> Self::Future { + debug!("request on: {}", req.uri()); + let res = match decode_path(req.uri().path()) { + Path::CamerasList => self.list_cameras(&req), + Path::Camera(uuid) => self.camera(uuid, req.uri().query(), &req), + Path::CameraRecordings(uuid) => self.camera_recordings(uuid, req.uri().query(), &req), + Path::CameraViewMp4(uuid) => self.camera_view_mp4(uuid, req.uri().query(), &req), + Path::NotFound => self.not_found(), }; - if let Err(ref e) = res { - warn!("Error handling request: {}", e); - } + future::result(res.map_err(|e| { + error!("error: {}", e); + hyper::Error::Incomplete + })) } } @@ -599,6 +610,7 @@ mod tests { #[cfg(all(test, feature="nightly"))] mod bench { + extern crate reqwest; extern crate test; use hyper; @@ -612,18 +624,20 @@ mod bench { impl Server { fn new() -> Server { - let mut listener = hyper::net::HttpListener::new("127.0.0.1:0").unwrap(); - use hyper::net::NetworkListener; - let addr = listener.local_addr().unwrap(); - let server = hyper::Server::new(listener); - let url = format!("http://{}:{}", addr.ip(), addr.port()); let db = TestDb::new(); testutil::add_dummy_recordings_to_db(&db.db, 1440); + let (tx, rx) = ::std::sync::mpsc::channel(); ::std::thread::spawn(move || { - let h = super::Handler::new(db.db.clone(), db.dir.clone()); - let _ = server.handle(h); + let addr = "127.0.0.1:0".parse().unwrap(); + let (db, dir) = (db.db.clone(), db.dir.clone()); + let server = hyper::server::Http::new() + .bind(&addr, move || Ok(super::Service::new(db.clone(), dir.clone()))) + .unwrap(); + tx.send(server.local_addr().unwrap()).unwrap(); + server.run().unwrap(); }); - Server{base_url: url} + let addr = rx.recv().unwrap(); + Server{base_url: format!("http://{}:{}", addr.ip(), addr.port())} } } @@ -635,13 +649,13 @@ mod bench { fn serve_camera_html(b: &mut Bencher) { testutil::init(); let server = &*SERVER; - let url = hyper::Url::parse(&format!("{}/cameras/{}/", server.base_url, - *testutil::TEST_CAMERA_UUID)).unwrap(); + let url = reqwest::Url::parse(&format!("{}/cameras/{}/", server.base_url, + *testutil::TEST_CAMERA_UUID)).unwrap(); let mut buf = Vec::new(); b.iter(|| { - let client = hyper::Client::new(); + let client = reqwest::Client::new().unwrap(); let mut resp = client.get(url.clone()).send().unwrap(); - assert_eq!(resp.status, hyper::status::StatusCode::Ok); + assert_eq!(*resp.status(), reqwest::StatusCode::Ok); buf.clear(); use std::io::Read; resp.read_to_end(&mut buf).unwrap();