diff --git a/.gitignore b/.gitignore index 4618b8d3..bc2b2fc1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ /target +/.cargo lambda-runtime/libtest.rmeta \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index aae6204d..7635bf55 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,30 +1,36 @@ [[package]] name = "arrayvec" -version = "0.4.7" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "nodrop 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", + "nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "autocfg" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "backtrace" -version = "0.3.9" +version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "backtrace-sys 0.1.24 (registry+https://github.com/rust-lang/crates.io-index)", - "cfg-if 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-demangle 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", + "autocfg 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "backtrace-sys 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.46 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-demangle 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "backtrace-sys" -version = "0.1.24" +version = "0.1.28" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cc 1.0.25 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.28 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.46 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -32,7 +38,7 @@ name = "base64" version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "byteorder 1.2.6 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.2.7 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -42,26 +48,26 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "byteorder" -version = "1.2.6" +version = "1.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "bytes" -version = "0.4.10" +version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "byteorder 1.2.6 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.2.7 (registry+https://github.com/rust-lang/crates.io-index)", "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "cc" -version = "1.0.25" +version = "1.0.28" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "cfg-if" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -71,7 +77,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "num-integer 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)", "num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", - "time 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", + "time 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -84,30 +90,33 @@ dependencies = [ [[package]] name = "crossbeam-deque" -version = "0.6.1" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "crossbeam-epoch 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-utils 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-epoch 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "crossbeam-epoch" -version = "0.5.2" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "arrayvec 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", - "cfg-if 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-utils 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "arrayvec 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "memoffset 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "crossbeam-utils" -version = "0.5.0" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", +] [[package]] name = "dtoa" @@ -116,22 +125,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "failure" -version = "0.1.3" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "backtrace 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", - "failure_derive 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "backtrace 0.3.13 (registry+https://github.com/rust-lang/crates.io-index)", + "failure_derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "failure_derive" -version = "0.1.3" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 0.4.19 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.6 (registry+https://github.com/rust-lang/crates.io-index)", - "synstructure 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 0.4.24 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.23 (registry+https://github.com/rust-lang/crates.io-index)", + "synstructure 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -164,32 +173,32 @@ version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", - "num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "num_cpus 1.9.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "h2" -version = "0.1.13" +version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "byteorder 1.2.6 (registry+https://github.com/rust-lang/crates.io-index)", - "bytes 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.2.7 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", - "http 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", - "indexmap 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", + "http 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)", + "indexmap 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "slab 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", - "string 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", + "string 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "http" -version = "0.1.13" +version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "bytes 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", "itoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -201,26 +210,27 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "hyper" -version = "0.12.12" +version = "0.12.19" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "bytes 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", "futures-cpupool 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", - "h2 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", - "http 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", + "h2 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)", + "http 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)", "httparse 1.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "itoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", - "time 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", + "time 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-executor 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-reactor 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-reactor 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-tcp 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-timer 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-threadpool 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-timer 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", "want 0.0.6 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -236,7 +246,7 @@ dependencies = [ [[package]] name = "indexmap" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -244,7 +254,7 @@ name = "iovec" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.46 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -267,82 +277,105 @@ name = "lambda_http" version = "0.1.0" dependencies = [ "base64 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", - "failure 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "failure_derive 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "http 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", + "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "failure_derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "http 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)", "lambda_runtime 0.1.0", - "log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.31 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.84 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.84 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.34 (registry+https://github.com/rust-lang/crates.io-index)", "serde_urlencoded 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)", "simple_logger 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "lambda_runtime" version = "0.1.0" dependencies = [ - "backtrace 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "lambda_runtime_core 0.1.0", + "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.84 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.84 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.34 (registry+https://github.com/rust-lang/crates.io-index)", + "simple-error 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", + "simple_logger 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "lambda_runtime_client" +version = "0.1.0" +dependencies = [ + "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "http 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)", + "hyper 0.12.19 (registry+https://github.com/rust-lang/crates.io-index)", + "lambda_runtime_errors 0.1.0", + "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.84 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.84 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.34 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "lambda_runtime_core" +version = "0.1.0" +dependencies = [ + "backtrace 0.3.13 (registry+https://github.com/rust-lang/crates.io-index)", "chrono 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "hyper 0.12.12 (registry+https://github.com/rust-lang/crates.io-index)", + "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "hyper 0.12.19 (registry+https://github.com/rust-lang/crates.io-index)", "lambda_runtime_client 0.1.0", - "log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.31 (registry+https://github.com/rust-lang/crates.io-index)", + "lambda_runtime_errors 0.1.0", + "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "simple_logger 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] -name = "lambda_runtime_client" +name = "lambda_runtime_errors" version = "0.1.0" dependencies = [ - "backtrace 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", - "http 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", - "hyper 0.12.12 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.31 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", + "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "lambda_runtime_core 0.1.0", + "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.34 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "lazy_static" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", -] [[package]] name = "lazycell" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "libc" -version = "0.2.43" +version = "0.2.46" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "lock_api" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "owning_ref 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "owning_ref 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "log" -version = "0.4.5" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cfg-if 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -364,9 +397,9 @@ dependencies = [ "fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "lazycell 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", + "lazycell 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.46 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", "slab 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -379,7 +412,7 @@ version = "0.6.7" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.46 (registry+https://github.com/rust-lang/crates.io-index)", "mio 0.6.16 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -399,14 +432,14 @@ name = "net2" version = "0.2.33" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cfg-if 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.46 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "nodrop" -version = "0.1.12" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -424,15 +457,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "num_cpus" -version = "1.8.0" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.46 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "owning_ref" -version = "0.3.3" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -443,7 +476,7 @@ name = "parking_lot" version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "lock_api 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "lock_api 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -452,10 +485,10 @@ name = "parking_lot_core" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.46 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)", "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "smallvec 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", + "smallvec 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -466,7 +499,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "proc-macro2" -version = "0.4.19" +version = "0.4.24" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -474,10 +507,10 @@ dependencies = [ [[package]] name = "quote" -version = "0.6.8" +version = "0.6.10" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 0.4.19 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 0.4.24 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -487,24 +520,92 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", "fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.46 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.46 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_chacha 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_hc 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_isaac 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_pcg 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_xorshift 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "rand_chacha" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "rand_core" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand_core" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "rand_hc" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand_isaac" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand_pcg" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand_xorshift" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", +] [[package]] name = "redox_syscall" -version = "0.1.40" +version = "0.1.50" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "rustc-demangle" -version = "0.1.9" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -517,7 +618,7 @@ dependencies = [ [[package]] name = "ryu" -version = "0.2.6" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -540,27 +641,27 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "serde" -version = "1.0.79" +version = "1.0.84" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "serde_derive" -version = "1.0.79" +version = "1.0.84" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 0.4.19 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.6 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 0.4.24 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.23 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "serde_json" -version = "1.0.31" +version = "1.0.34" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "itoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", - "ryu 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", + "ryu 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.84 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -570,17 +671,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "dtoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", "itoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", - "url 1.7.1 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.84 (registry+https://github.com/rust-lang/crates.io-index)", + "url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "simple-error" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "simple_logger" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "chrono 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -590,7 +696,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "smallvec" -version = "0.6.5" +version = "0.6.7" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -603,59 +709,60 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "string" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "syn" -version = "0.15.6" +version = "0.15.23" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 0.4.19 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 0.4.24 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "synstructure" -version = "0.10.0" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 0.4.19 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.6 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 0.4.24 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.23 (registry+https://github.com/rust-lang/crates.io-index)", "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "time" -version = "0.1.40" +version = "0.1.41" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", - "redox_syscall 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.46 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_syscall 0.1.50 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "tokio" -version = "0.1.11" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "bytes 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", "mio 0.6.16 (registry+https://github.com/rust-lang/crates.io-index)", + "num_cpus 1.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-codec 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-current-thread 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-current-thread 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-executor 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-fs 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-reactor 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-fs 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-reactor 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-tcp 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-threadpool 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-timer 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-udp 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-uds 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-threadpool 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-timer 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-udp 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-uds 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -663,14 +770,14 @@ name = "tokio-codec" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "bytes 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "tokio-current-thread" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", @@ -687,39 +794,39 @@ dependencies = [ [[package]] name = "tokio-fs" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-threadpool 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-threadpool 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "tokio-io" -version = "0.1.9" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "bytes 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "tokio-reactor" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "crossbeam-utils 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "mio 0.6.16 (registry+https://github.com/rust-lang/crates.io-index)", - "num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "num_cpus 1.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", "slab 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-executor 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -727,34 +834,34 @@ name = "tokio-tcp" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "bytes 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "mio 0.6.16 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-reactor 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-reactor 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "tokio-threadpool" -version = "0.1.7" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "crossbeam-deque 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-utils 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-deque 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", - "num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "num_cpus 1.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-executor 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "tokio-timer" -version = "0.2.7" +version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "crossbeam-utils 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", "slab 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-executor 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", @@ -762,32 +869,33 @@ dependencies = [ [[package]] name = "tokio-udp" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "bytes 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "mio 0.6.16 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-codec 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-reactor 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-reactor 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "tokio-uds" -version = "0.2.2" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "bytes 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.46 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "mio 0.6.16 (registry+https://github.com/rust-lang/crates.io-index)", "mio-uds 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-reactor 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-codec 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-reactor 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -823,7 +931,7 @@ dependencies = [ [[package]] name = "url" -version = "1.7.1" +version = "1.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "idna 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", @@ -831,11 +939,6 @@ dependencies = [ "percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "version_check" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "void" version = "1.0.2" @@ -847,7 +950,7 @@ version = "0.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "try-lock 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -890,98 +993,106 @@ dependencies = [ ] [metadata] -"checksum arrayvec 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)" = "a1e964f9e24d588183fcb43503abda40d288c8657dfc27311516ce2f05675aef" -"checksum backtrace 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "89a47830402e9981c5c41223151efcced65a0510c13097c769cede7efb34782a" -"checksum backtrace-sys 0.1.24 (registry+https://github.com/rust-lang/crates.io-index)" = "c66d56ac8dabd07f6aacdaf633f4b8262f5b3601a810a0dcddffd5c22c69daa0" +"checksum arrayvec 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)" = "92c7fb76bc8826a8b33b4ee5bb07a247a81e76764ab4d55e8f73e3a4d8808c71" +"checksum autocfg 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4e5f34df7a019573fb8bdc7e24a2bfebe51a2a1d6bfdbaeccedb3c41fc574727" +"checksum backtrace 0.3.13 (registry+https://github.com/rust-lang/crates.io-index)" = "b5b493b66e03090ebc4343eb02f94ff944e0cbc9ac6571491d170ba026741eb5" +"checksum backtrace-sys 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)" = "797c830ac25ccc92a7f8a7b9862bde440715531514594a6154e3d4a54dd769b6" "checksum base64 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "621fc7ecb8008f86d7fb9b95356cd692ce9514b80a86d85b397f32a22da7b9e2" "checksum bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "228047a76f468627ca71776ecdebd732a3423081fcf5125585bcd7c49886ce12" -"checksum byteorder 1.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "90492c5858dd7d2e78691cfb89f90d273a2800fc11d98f60786e5d87e2f83781" -"checksum bytes 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)" = "0ce55bd354b095246fc34caf4e9e242f5297a7fd938b090cadfea6eee614aa62" -"checksum cc 1.0.25 (registry+https://github.com/rust-lang/crates.io-index)" = "f159dfd43363c4d08055a07703eb7a3406b0dac4d0584d96965a3262db3c9d16" -"checksum cfg-if 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "0c4e7bb64a8ebb0d856483e1e682ea3422f883c5f5615a90d51a2c82fe87fdd3" +"checksum byteorder 1.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "94f88df23a25417badc922ab0f5716cc1330e87f71ddd9203b3a3ccd9cedf75d" +"checksum bytes 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)" = "40ade3d27603c2cb345eb0912aec461a6dec7e06a4ae48589904e808335c7afa" +"checksum cc 1.0.28 (registry+https://github.com/rust-lang/crates.io-index)" = "bb4a8b715cb4597106ea87c7c84b2f1d452c7492033765df7f32651e66fcf749" +"checksum cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "082bb9b28e00d3c9d39cc03e64ce4cea0f1bb9b3fde493f0cbc008472d22bdf4" "checksum chrono 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "45912881121cb26fad7c38c17ba7daa18764771836b34fab7d3fbd93ed633878" "checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" -"checksum crossbeam-deque 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3486aefc4c0487b9cb52372c97df0a48b8c249514af1ee99703bf70d2f2ceda1" -"checksum crossbeam-epoch 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "30fecfcac6abfef8771151f8be4abc9e4edc112c2bcb233314cafde2680536e9" -"checksum crossbeam-utils 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "677d453a17e8bd2b913fa38e8b9cf04bcdbb5be790aa294f2389661d72036015" +"checksum crossbeam-deque 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "05e44b8cf3e1a625844d1750e1f7820da46044ff6d28f4d43e455ba3e5bb2c13" +"checksum crossbeam-epoch 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f10a4f8f409aaac4b16a5474fb233624238fcdeefb9ba50d5ea059aab63ba31c" +"checksum crossbeam-utils 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "41ee4864f4797060e52044376f7d107429ce1fb43460021b126424b7180ee21a" "checksum dtoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6d301140eb411af13d3115f9a562c85cc6b541ade9dfa314132244aaee7489dd" -"checksum failure 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6dd377bcc1b1b7ce911967e3ec24fa19c3224394ec05b54aa7b083d498341ac7" -"checksum failure_derive 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "64c2d913fe8ed3b6c6518eedf4538255b989945c14c2a7d5cbff62a5e2120596" +"checksum failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "795bd83d3abeb9220f257e597aa0080a508b27533824adf336529648f6abf7e2" +"checksum failure_derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "ea1063915fd7ef4309e222a5a07cf9c319fb9c7836b1f89b85458672dbb127e1" "checksum fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "2fad85553e09a6f881f739c29f0b00b0f01357c743266d478b68951ce23285f3" "checksum fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" "checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" "checksum futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)" = "49e7653e374fe0d0c12de4250f0bdb60680b8c80eed558c5c7538eec9c89e21b" "checksum futures-cpupool 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "ab90cde24b3319636588d0c35fe03b1333857621051837ed769faefb4c2162e4" -"checksum h2 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "7dd33bafe2e6370e6c8eb0cf1b8c5f93390b90acde7e9b03723f166b28b648ed" -"checksum http 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "24f58e8c2d8e886055c3ead7b28793e1455270b5fb39650984c224bc538ba581" +"checksum h2 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "1ac030ae20dee464c5d0f36544d8b914a6bc606da44a57e052d2b0f5dae129e0" +"checksum http 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "02096a6d2c55e63f7fcb800690e4f889a25f6ec342e3adb4594e293b625215ab" "checksum httparse 1.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "e8734b0cfd3bc3e101ec59100e101c2eecd19282202e87808b3037b442777a83" -"checksum hyper 0.12.12 (registry+https://github.com/rust-lang/crates.io-index)" = "4aca412c241a2dd53af261efc7adf7736fdebd67dc0d1cc1ffdbcb9407e0e810" +"checksum hyper 0.12.19 (registry+https://github.com/rust-lang/crates.io-index)" = "f1ebec079129e43af5e234ef36ee3d7e6085687d145b7ea653b262d16c6b65f1" "checksum idna 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "38f09e0f0b1fb55fdee1f17470ad800da77af5186a1a76c026b679358b7e844e" -"checksum indexmap 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "08173ba1e906efb6538785a8844dd496f5d34f0a2d88038e95195172fc667220" +"checksum indexmap 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7e81a7c05f79578dbc15793d8b619db9ba32b4577003ef3af1a91c416798c58d" "checksum iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dbe6e417e7d0975db6512b90796e8ce223145ac4e33c377e4a42882a0e88bb08" "checksum itoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "1306f3464951f30e30d12373d31c79fbd52d236e5e896fd92f96ec7babbbe60b" "checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" -"checksum lazy_static 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ca488b89a5657b0a2ecd45b95609b3e848cf1755da332a0da46e2b2b1cb371a7" -"checksum lazycell 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ddba4c30a78328befecec92fc94970e53b3ae385827d28620f0f5bb2493081e0" -"checksum libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)" = "76e3a3ef172f1a0b9a9ff0dd1491ae5e6c948b94479a3021819ba7d860c8645d" -"checksum lock_api 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "775751a3e69bde4df9b38dd00a1b5d6ac13791e4223d4a0506577f0dd27cfb7a" -"checksum log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "d4fcce5fa49cc693c312001daf1d13411c4a5283796bac1084299ea3e567113f" +"checksum lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a374c89b9db55895453a74c1e38861d9deec0b01b405a82516e9d5de4820dea1" +"checksum lazycell 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b294d6fa9ee409a054354afc4352b0b9ef7ca222c69b8812cbea9e7d2bf3783f" +"checksum libc 0.2.46 (registry+https://github.com/rust-lang/crates.io-index)" = "023a4cd09b2ff695f9734c1934145a315594b7986398496841c7031a5a1bbdbd" +"checksum lock_api 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "62ebf1391f6acad60e5c8b43706dde4582df75c06698ab44511d15016bc2442c" +"checksum log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c84ec4b527950aa83a329754b01dbe3f58361d1c5efacd1f6d68c494d08a17c6" "checksum matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" "checksum memoffset 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0f9dc261e2b62d7a622bf416ea3c5245cdd5d9a7fcc428c0d06804dfce1775b3" "checksum mio 0.6.16 (registry+https://github.com/rust-lang/crates.io-index)" = "71646331f2619b1026cc302f87a2b8b648d5c6dd6937846a16cc8ce0f347f432" "checksum mio-uds 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)" = "966257a94e196b11bb43aca423754d87429960a768de9414f3691d6957abf125" "checksum miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919" "checksum net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)" = "42550d9fb7b6684a6d404d9fa7250c2eb2646df731d1c06afc06dcee9e1bcf88" -"checksum nodrop 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "9a2228dca57108069a5262f2ed8bd2e82496d2e074a06d1ccc7ce1687b6ae0a2" +"checksum nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "2f9667ddcc6cc8a43afc9b7917599d7216aa09c463919ea32c59ed6cac8bc945" "checksum num-integer 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)" = "e83d528d2677f0518c570baf2b7abdcf0cd2d248860b68507bdcb3e91d4c0cea" "checksum num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0b3a5d7cc97d6d30d8b9bc8fa19bf45349ffe46241e8816f50f62f6d6aaabee1" -"checksum num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c51a3322e4bca9d212ad9a158a02abc6934d005490c054a2778df73a70aa0a30" -"checksum owning_ref 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "cdf84f41639e037b484f93433aa3897863b561ed65c6e59c7073d7c561710f37" +"checksum num_cpus 1.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5a69d464bdc213aaaff628444e99578ede64e9c854025aa43b9796530afa9238" +"checksum owning_ref 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "49a4b8ea2179e6a2e27411d3bca09ca6dd630821cf6894c6c7c8467a8ee7ef13" "checksum parking_lot 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "f0802bff09003b291ba756dc7e79313e51cc31667e94afbe847def490424cde5" "checksum parking_lot_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ad7f7e6ebdc79edff6fdcb87a55b620174f7a989e3eb31b65231f4af57f00b8c" "checksum percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831" -"checksum proc-macro2 0.4.19 (registry+https://github.com/rust-lang/crates.io-index)" = "ffe022fb8c8bd254524b0b3305906c1921fa37a84a644e29079a9e62200c3901" -"checksum quote 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)" = "dd636425967c33af890042c483632d33fa7a18f19ad1d7ea72e8998c6ef8dea5" +"checksum proc-macro2 0.4.24 (registry+https://github.com/rust-lang/crates.io-index)" = "77619697826f31a02ae974457af0b29b723e5619e113e9397b8b82c6bd253f09" +"checksum quote 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)" = "53fa22a1994bd0f9372d7a816207d8a2677ad0325b073f5c5332760f0fb62b5c" "checksum rand 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)" = "e464cd887e869cddcae8792a4ee31d23c7edd516700695608f5b98c67ee0131c" -"checksum rand_core 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "edecf0f94da5551fc9b492093e30b041a891657db7940ee221f9d2f66e82eef2" -"checksum redox_syscall 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)" = "c214e91d3ecf43e9a4e41e578973adeb14b474f2bee858742d127af75a0112b1" -"checksum rustc-demangle 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "bcfe5b13211b4d78e5c2cadfebd7769197d95c639c35a50057eb4c05de811395" +"checksum rand 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ae9d223d52ae411a33cf7e54ec6034ec165df296ccd23533d671a28252b6f66a" +"checksum rand_chacha 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "771b009e3a508cb67e8823dda454aaa5368c7bc1c16829fb77d3e980440dd34a" +"checksum rand_core 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1961a422c4d189dfb50ffa9320bf1f2a9bd54ecb92792fb9477f99a1045f3372" +"checksum rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0905b6b7079ec73b314d4c748701f6931eb79fd97c668caa3f1899b22b32c6db" +"checksum rand_hc 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7b40677c7be09ae76218dc623efbf7b18e34bced3f38883af07bb75630a21bc4" +"checksum rand_isaac 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ded997c9d5f13925be2a6fd7e66bf1872597f759fd9dd93513dd7e92e5a5ee08" +"checksum rand_pcg 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "086bd09a33c7044e56bb44d5bdde5a60e7f119a9e95b0775f545de759a32fe05" +"checksum rand_xorshift 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "effa3fcaa47e18db002bdde6060944b6d2f9cfd8db471c30e873448ad9187be3" +"checksum redox_syscall 0.1.50 (registry+https://github.com/rust-lang/crates.io-index)" = "52ee9a534dc1301776eff45b4fa92d2c39b1d8c3d3357e6eb593e0d795506fc2" +"checksum rustc-demangle 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "adacaae16d02b6ec37fdc7acfcddf365978de76d1983d3ee22afc260e1ca9619" "checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" -"checksum ryu 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "7153dd96dade874ab973e098cb62fcdbb89a03682e46b144fd09550998d4a4a7" +"checksum ryu 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "eb9e9b8cde282a9fe6a42dd4681319bfb63f121b8a8ee9439c6f4107e58a46f7" "checksum scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "94258f53601af11e6a49f722422f6e3425c52b06245a5cf9bc09908b174f5e27" "checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" "checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" -"checksum serde 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)" = "84257ccd054dc351472528c8587b4de2dbf0dc0fe2e634030c1a90bfdacebaa9" -"checksum serde_derive 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)" = "31569d901045afbff7a9479f793177fe9259819aff10ab4f89ef69bbc5f567fe" -"checksum serde_json 1.0.31 (registry+https://github.com/rust-lang/crates.io-index)" = "bb47a3d5c84320222f66d7db21157c4a7407755de41798f9b4c1c40593397b1a" +"checksum serde 1.0.84 (registry+https://github.com/rust-lang/crates.io-index)" = "0e732ed5a5592c17d961555e3b552985baf98d50ce418b7b655f31f6ba7eb1b7" +"checksum serde_derive 1.0.84 (registry+https://github.com/rust-lang/crates.io-index)" = "b4d6115a3ca25c224e409185325afc16a0d5aaaabc15c42b09587d6f1ba39a5b" +"checksum serde_json 1.0.34 (registry+https://github.com/rust-lang/crates.io-index)" = "bdf540260cfee6da923831f4776ddc495ada940c30117977c70f1313a6130545" "checksum serde_urlencoded 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)" = "d48f9f99cd749a2de71d29da5f948de7f2764cc5a9d7f3c97e3514d4ee6eabf2" +"checksum simple-error 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "c00e871264295428089fb278c4b225be3ca92c227c918857f2e562bac973257b" "checksum simple_logger 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "25111f1d77db1ac3ee11b62ba4b7a162e6bb3be43e28273f0d3935cc8d3ff7fb" "checksum slab 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5f9776d6b986f77b35c6cf846c11ad986ff128fe0b2b63a3628e3755e8d3102d" -"checksum smallvec 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "153ffa32fd170e9944f7e0838edf824a754ec4c1fc64746fcc9fe1f8fa602e5d" +"checksum smallvec 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)" = "b73ea3738b47563803ef814925e69be00799a8c07420be8b996f8e98fb2336db" "checksum stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dba1a27d3efae4351c8051072d619e3ade2820635c3958d826bfea39d59b54c8" -"checksum string 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "00caf261d6f90f588f8450b8e1230fa0d5be49ee6140fdfbcb55335aff350970" -"checksum syn 0.15.6 (registry+https://github.com/rust-lang/crates.io-index)" = "854b08a640fc8f54728fb95321e3ec485b365a97fe47609797c671addd1dde69" -"checksum synstructure 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ec37f4fab4bafaf6b5621c1d54e6aa5d4d059a8f84929e87abfdd7f9f04c6db2" -"checksum time 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)" = "d825be0eb33fda1a7e68012d51e9c7f451dc1a69391e7fdc197060bb8c56667b" -"checksum tokio 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)" = "6e93c78d23cc61aa245a8acd2c4a79c4d7fa7fb5c3ca90d5737029f043a84895" +"checksum string 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "98998cced76115b1da46f63388b909d118a37ae0be0f82ad35773d4a4bc9d18d" +"checksum syn 0.15.23 (registry+https://github.com/rust-lang/crates.io-index)" = "9545a6a093a3f0bd59adb472700acc08cad3776f860f16a897dfce8c88721cbc" +"checksum synstructure 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "73687139bf99285483c96ac0add482c3776528beac1d97d444f6e91f203a2015" +"checksum time 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)" = "847da467bf0db05882a9e2375934a8a55cffdc9db0d128af1518200260ba1f6c" +"checksum tokio 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "a7817d4c98cc5be21360b3b37d6036fe9b7aefa5b7a201b7b16ff33423822f7d" "checksum tokio-codec 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5c501eceaf96f0e1793cf26beb63da3d11c738c4a943fdf3746d81d64684c39f" -"checksum tokio-current-thread 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "f90fcd90952f0a496d438a976afba8e5c205fb12123f813d8ab3aa1c8436638c" +"checksum tokio-current-thread 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "331c8acc267855ec06eb0c94618dcbbfea45bed2d20b77252940095273fb58f6" "checksum tokio-executor 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "c117b6cf86bb730aab4834f10df96e4dd586eff2c3c27d3781348da49e255bde" -"checksum tokio-fs 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "b5cbe4ca6e71cb0b62a66e4e6f53a8c06a6eefe46cc5f665ad6f274c9906f135" -"checksum tokio-io 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "8b8a85fffbec3c5ab1ab62324570230dcd37ee5996a7859da5caf7b9d45e3e8c" -"checksum tokio-reactor 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "4b26fd37f1125738b2170c80b551f69ff6fecb277e6e5ca885e53eec2b005018" +"checksum tokio-fs 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "60ae25f6b17d25116d2cba342083abe5255d3c2c79cb21ea11aa049c53bf7c75" +"checksum tokio-io 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "7392fe0a70d5ce0c882c4778116c519bd5dbaa8a7c3ae3d04578b3afafdcda21" +"checksum tokio-reactor 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "502b625acb4ee13cbb3b90b8ca80e0addd263ddacf6931666ef751e610b07fb5" "checksum tokio-tcp 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7ad235e9dadd126b2d47f6736f65aa1fdcd6420e66ca63f44177bc78df89f912" -"checksum tokio-threadpool 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "bbd8a8b911301c60cbfaa2a6588fb210e5c1038375b8bdecc47aa09a94c3c05f" -"checksum tokio-timer 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "3a52f00c97fedb6d535d27f65cccb7181c8dd4c6edc3eda9ea93f6d45d05168e" -"checksum tokio-udp 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "da941144b816d0dcda4db3a1ba87596e4df5e860a72b70783fe435891f80601c" -"checksum tokio-uds 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "22e3aa6d1fcc19e635418dc0a30ab5bd65d347973d6f43f1a37bf8d9d1335fc9" +"checksum tokio-threadpool 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "56c5556262383032878afad66943926a1d1f0967f17e94bd7764ceceb3b70e7f" +"checksum tokio-timer 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "4f37f0111d76cc5da132fe9bc0590b9b9cfd079bc7e75ac3846278430a299ff8" +"checksum tokio-udp 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "66268575b80f4a4a710ef83d087fdfeeabdce9b74c797535fbac18a2cb906e92" +"checksum tokio-uds 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "99ce87382f6c1a24b513a72c048b2c8efe66cb5161c9061d00bee510f08dc168" "checksum try-lock 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e604eb7b43c06650e854be16a2a03155743d3752dd1c943f6829e26b7a36e382" "checksum unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5" "checksum unicode-normalization 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "6a0180bc61fc5a987082bfa111f4cc95c4caff7f9799f3e46df09163a937aa25" "checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" "checksum unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "382810877fe448991dfc7f0dd6e3ae5d58088fd0ea5e35189655f84e6814fa56" -"checksum url 1.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2a321979c09843d272956e73700d12c4e7d3d92b2ee112b31548aef0d4efc5a6" -"checksum version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "914b1a6776c4c929a602fafd8bc742e06365d4bcbe48c30f9cca5824f70dc9dd" +"checksum url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dd4e7c0d531266369519a4aa4f399d748bd37043b00bde1e4ff1f60a120b355a" "checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" "checksum want 0.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "797464475f30ddb8830cc529aaaae648d581f99e2036a928877dfde027ddf6b3" "checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" diff --git a/Cargo.toml b/Cargo.toml index 2aae614b..a7dff3e7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,7 @@ [workspace] members = [ "lambda-runtime-client", + "lambda-runtime-core", "lambda-runtime", "lambda-http" ] \ No newline at end of file diff --git a/lambda-http/src/ext.rs b/lambda-http/src/ext.rs index d711ff64..6ad2963b 100644 --- a/lambda-http/src/ext.rs +++ b/lambda-http/src/ext.rs @@ -49,7 +49,7 @@ pub enum PayloadError { /// extern crate lambda_runtime as lambda; /// #[macro_use] extern crate serde_derive; /// -/// use lambda::{Context, HandlerError}; +/// use lambda::{Context, error::HandlerError}; /// use lambda_http::{Body, Request, Response, RequestExt}; /// /// #[derive(Debug,Deserialize,Default)] @@ -67,7 +67,7 @@ pub enum PayloadError { /// fn handler( /// request: Request, /// ctx: lambda::Context -/// ) -> Result, lambda::HandlerError> { +/// ) -> Result, HandlerError> { /// let args: Args = request.payload() /// .unwrap_or_else(|_parse_err| None) /// .unwrap_or_default(); diff --git a/lambda-http/src/lib.rs b/lambda-http/src/lib.rs index 56402a1a..aedfcd86 100644 --- a/lambda-http/src/lib.rs +++ b/lambda-http/src/lib.rs @@ -14,7 +14,7 @@ //! //! ```rust,no_run //! use lambda_http::{lambda, IntoResponse, Request, RequestExt}; -//! use lambda_runtime::{Context, HandlerError}; +//! use lambda_runtime::{Context, error::HandlerError}; //! //! fn main() { //! lambda!(hello) diff --git a/lambda-runtime-client/Cargo.toml b/lambda-runtime-client/Cargo.toml index 697878bd..1ae05a91 100644 --- a/lambda-runtime-client/Cargo.toml +++ b/lambda-runtime-client/Cargo.toml @@ -23,4 +23,5 @@ serde = "^1" serde_json = "^1" serde_derive = "^1" log = "0.4" -backtrace = "0.3" +lambda_runtime_errors = { path = "../lambda-runtime-errors", version = "^0.1" } +failure = "^0.1" \ No newline at end of file diff --git a/lambda-runtime-client/src/client.rs b/lambda-runtime-client/src/client.rs index f87a2d2c..63559bd2 100644 --- a/lambda-runtime-client/src/client.rs +++ b/lambda-runtime-client/src/client.rs @@ -1,21 +1,23 @@ -use std::{collections::HashMap, fmt}; - +use crate::error::{ApiError, ApiErrorKind, ErrorResponse}; +use failure::ResultExt; use hyper::{ client::HttpConnector, header::{self, HeaderMap, HeaderValue}, rt::{Future, Stream}, Body, Client, Method, Request, Uri, }; -use serde_derive::Deserialize; +use log::*; +use serde_derive::*; use serde_json; +use std::{collections::HashMap, fmt}; use tokio::runtime::Runtime; -use crate::error::{ApiError, ErrorResponse, RuntimeApiError}; - const RUNTIME_API_VERSION: &str = "2018-06-01"; const API_CONTENT_TYPE: &str = "application/json"; const API_ERROR_CONTENT_TYPE: &str = "application/vnd.aws.lambda.error+json"; const RUNTIME_ERROR_HEADER: &str = "Lambda-Runtime-Function-Error-Type"; +// TODO: Perhaps use a macro to generate this +const DEFAULT_AGENT: &str = "AWS_Lambda_Rust"; /// Enum of the headers returned by Lambda's `/next` API call. pub enum LambdaHeaders { @@ -123,81 +125,94 @@ pub struct EventContext { pub struct RuntimeClient { _runtime: Runtime, http_client: Client, - endpoint: String, + next_endpoint: Uri, + runtime_agent: String, + host: String, } -impl RuntimeClient { +impl<'ev> RuntimeClient { /// Creates a new instance of the Runtime APIclient SDK. The http client has timeouts disabled and - /// will always send a `Connection: keep-alive` header. - pub fn new(endpoint: String, runtime: Option) -> Result { - debug!("Starting new HttpRuntimeClient for {}", endpoint); + /// will always send a `Connection: keep-alive` header. Optionally, the runtime client can receive + /// a user agent string. This string is used to make requests to the runtime APIs and is used to + /// identify the runtime being used by the function. For example, the `lambda_runtime_core` crate + /// uses `AWS_Lambda_Rust/0.1.0 (rustc/1.31.1-stable)`. The runtime client can also receive an + /// instance of Tokio Runtime to use. + pub fn new(host: &str, agent: Option, runtime: Option) -> Result { + debug!("Starting new HttpRuntimeClient for {}", host); + let runtime_agent = match agent { + Some(a) => a, + None => DEFAULT_AGENT.to_owned(), + }; + // start a tokio core main event loop for hyper let runtime = match runtime { Some(r) => r, - None => Runtime::new()?, + None => Runtime::new().context(ApiErrorKind::Unrecoverable("Could not initialize runtime".to_string()))?, }; let http_client = Client::builder().executor(runtime.executor()).build_http(); + // we cached the parsed Uri since this never changes. + let next_endpoint = format!("http://{}/{}/runtime/invocation/next", host, RUNTIME_API_VERSION) + .parse::() + .context(ApiErrorKind::Unrecoverable("Could not parse API uri".to_string()))?; Ok(RuntimeClient { _runtime: runtime, http_client, - endpoint, + next_endpoint, + runtime_agent, + host: host.to_owned(), }) } } -impl RuntimeClient { +impl<'ev> RuntimeClient { /// Polls for new events to the Runtime APIs. pub fn next_event(&self) -> Result<(Vec, EventContext), ApiError> { - let uri = format!( - "http://{}/{}/runtime/invocation/next", - self.endpoint, RUNTIME_API_VERSION - ) - .parse()?; trace!("Polling for next event"); // We wait instead of processing the future asynchronously because AWS Lambda // itself enforces only one event per container at a time. No point in taking on // the additional complexity. - let out = self.http_client.get(uri).wait(); - match out { - Ok(resp) => { - if resp.status().is_client_error() { - error!( - "Runtime API returned client error when polling for new events: {}", - resp.status() - ); - return Err(ApiError::new(&format!( - "Error {} when polling for events", - resp.status() - ))); - } - if resp.status().is_server_error() { - error!( - "Runtime API returned server error when polling for new events: {}", - resp.status() - ); - return Err(ApiError::new("Server error when polling for new events") - .unrecoverable() - .clone()); - } - let ctx = self.get_event_context(&resp.headers())?; - let out = resp.into_body().concat2().wait()?; - let buf: Vec = out.into_bytes().to_vec(); - - trace!( - "Received new event for request id {}. Event length {} bytes", - ctx.aws_request_id, - buf.len() - ); - Ok((buf, ctx)) - } - Err(e) => { - error!("Error when fetching next event from Runtime API: {}", e); - Err(ApiError::from(e)) - } + let resp = self + .http_client + .get(self.next_endpoint.clone()) + .wait() + .context(ApiErrorKind::Unrecoverable("Could not fetch next event".to_string()))?; + + if resp.status().is_client_error() { + error!( + "Runtime API returned client error when polling for new events: {}", + resp.status() + ); + Err(ApiErrorKind::Recoverable(format!( + "Error {} when polling for events", + resp.status() + )))?; + } + if resp.status().is_server_error() { + error!( + "Runtime API returned server error when polling for new events: {}", + resp.status() + ); + Err(ApiErrorKind::Unrecoverable( + "Server error when polling for new events".to_string(), + ))?; } + let ctx = self.get_event_context(&resp.headers())?; + let out = resp + .into_body() + .concat2() + .wait() + .context(ApiErrorKind::Recoverable("Could not read event boxy".to_string()))?; + let buf = out.into_bytes().to_vec(); + + trace!( + "Received new event for request id {}. Event length {} bytes", + ctx.aws_request_id, + buf.len() + ); + Ok((buf, ctx)) } /// Calls the Lambda Runtime APIs to submit a response to an event. In this function we treat @@ -212,40 +227,40 @@ impl RuntimeClient { /// /// # Returns /// A `Result` object containing a bool return value for the call or an `error::ApiError` instance. - pub fn event_response(&self, request_id: &str, output: Vec) -> Result<(), ApiError> { - let uri: Uri = format!( - "http://{}/{}/runtime/invocation/{}/response", - self.endpoint, RUNTIME_API_VERSION, request_id - ) - .parse()?; + pub fn event_response(&self, request_id: &str, output: &[u8]) -> Result<(), ApiError> { trace!( "Posting response for request {} to Runtime API. Response length {} bytes", request_id, output.len() ); + let uri = format!( + "http://{}/{}/runtime/invocation/{}/response", + self.host, RUNTIME_API_VERSION, request_id + ) + .parse::() + .context(ApiErrorKind::Unrecoverable( + "Could not generate response uri".to_owned(), + ))?; let req = self.get_runtime_post_request(&uri, output); - match self.http_client.request(req).wait() { - Ok(resp) => { - if !resp.status().is_success() { - error!( - "Error from Runtime API when posting response for request {}: {}", - request_id, - resp.status() - ); - return Err(ApiError::new(&format!( - "Error {} while sending response", - resp.status() - ))); - } - trace!("Posted response to Runtime API for request {}", request_id); - Ok(()) - } - Err(e) => { - error!("Error when calling runtime API for request {}: {}", request_id, e); - Err(ApiError::from(e)) - } + let resp = self + .http_client + .request(req) + .wait() + .context(ApiErrorKind::Recoverable("Could not post event response".to_string()))?; + if !resp.status().is_success() { + error!( + "Error from Runtime API when posting response for request {}: {}", + request_id, + resp.status() + ); + Err(ApiErrorKind::Recoverable(format!( + "Error {} while sending response", + resp.status() + )))?; } + trace!("Posted response to Runtime API for request {}", request_id); + Ok(()) } /// Calls Lambda's Runtime APIs to send an error generated by the `Handler`. Because it's rust, @@ -260,40 +275,38 @@ impl RuntimeClient { /// /// # Returns /// A `Result` object containing a bool return value for the call or an `error::ApiError` instance. - pub fn event_error(&self, request_id: &str, e: &dyn RuntimeApiError) -> Result<(), ApiError> { - let uri: Uri = format!( - "http://{}/{}/runtime/invocation/{}/error", - self.endpoint, RUNTIME_API_VERSION, request_id - ) - .parse()?; + pub fn event_error(&self, request_id: &str, e: &ErrorResponse) -> Result<(), ApiError> { trace!( "Posting error to runtime API for request {}: {}", request_id, - e.to_response().error_message + e.error_message ); - let req = self.get_runtime_error_request(&uri, &e.to_response()); - - match self.http_client.request(req).wait() { - Ok(resp) => { - if !resp.status().is_success() { - error!( - "Error from Runtime API when posting error response for request {}: {}", - request_id, - resp.status() - ); - return Err(ApiError::new(&format!( - "Error {} while sending response", - resp.status() - ))); - } - trace!("Posted error response for request id {}", request_id); - Ok(()) - } - Err(e) => { - error!("Error when calling runtime API for request {}: {}", request_id, e); - Err(ApiError::from(e)) - } + let uri = format!( + "http://{}/{}/runtime/invocation/{}/error", + self.host, RUNTIME_API_VERSION, request_id + ) + .parse::() + .context(ApiErrorKind::Unrecoverable( + "Could not generate response uri".to_owned(), + ))?; + let req = self.get_runtime_error_request(&uri, &e); + + let resp = self.http_client.request(req).wait().context(ApiErrorKind::Recoverable( + "Could not post event error response".to_string(), + ))?; + if !resp.status().is_success() { + error!( + "Error from Runtime API when posting error response for request {}: {}", + request_id, + resp.status() + ); + Err(ApiErrorKind::Recoverable(format!( + "Error {} while sending response", + resp.status() + )))?; } + trace!("Posted error response for request id {}", request_id); + Ok(()) } /// Calls the Runtime APIs to report a failure during the init process. @@ -307,12 +320,15 @@ impl RuntimeClient { /// # Panics /// If it cannot send the init error. In this case we panic to force the runtime /// to restart. - pub fn fail_init(&self, e: &dyn RuntimeApiError) { - let uri: Uri = format!("http://{}/{}/runtime/init/error", self.endpoint, RUNTIME_API_VERSION) - .parse() - .expect("Could not generate Runtime URI"); - error!("Calling fail_init Runtime API: {}", e.to_response().error_message); - let req = self.get_runtime_error_request(&uri, &e.to_response()); + pub fn fail_init(&self, e: &ErrorResponse) { + error!("Calling fail_init Runtime API: {}", e.error_message); + let uri = format!("http://{}/{}/runtime/init/error", self.host, RUNTIME_API_VERSION) + .parse::() + .map_err(|e| { + error!("Could not parse fail init URI: {}", e); + panic!("Killing runtime"); + }); + let req = self.get_runtime_error_request(&uri.unwrap(), &e); self.http_client .request(req) @@ -328,12 +344,10 @@ impl RuntimeClient { } /// Returns the endpoint configured for this HTTP Runtime client. - pub fn get_endpoint(&self) -> String { - self.endpoint.clone() + pub fn get_endpoint(&self) -> &str { + &self.host } -} -impl RuntimeClient { /// Creates a Hyper `Request` object for the given `Uri` and `Body`. Sets the /// HTTP method to `POST` and the `Content-Type` header value to `application/json`. /// @@ -344,17 +358,18 @@ impl RuntimeClient { /// /// # Returns /// A Populated Hyper `Request` object. - fn get_runtime_post_request(&self, uri: &Uri, body: Vec) -> Request { + fn get_runtime_post_request(&self, uri: &Uri, body: &[u8]) -> Request { Request::builder() .method(Method::POST) .uri(uri.clone()) .header(header::CONTENT_TYPE, header::HeaderValue::from_static(API_CONTENT_TYPE)) - .body(Body::from(body)) + .header(header::USER_AGENT, self.runtime_agent.clone()) + .body(Body::from(body.to_owned())) .unwrap() } fn get_runtime_error_request(&self, uri: &Uri, e: &ErrorResponse) -> Request { - let body = serde_json::to_vec(e).expect("Could not turn error object into response JSON"); + let body = serde_json::to_vec(&e).expect("Could not turn error object into response JSON"); Request::builder() .method(Method::POST) .uri(uri.clone()) @@ -362,7 +377,9 @@ impl RuntimeClient { header::CONTENT_TYPE, header::HeaderValue::from_static(API_ERROR_CONTENT_TYPE), ) - .header(RUNTIME_ERROR_HEADER, HeaderValue::from_static("RuntimeError")) // TODO: We should add this code to the error object. + .header(header::USER_AGENT, self.runtime_agent.clone()) + // this header is static for the runtime APIs and it's likely to go away in the future. + .header(RUNTIME_ERROR_HEADER, HeaderValue::from_static("Unhandled")) .body(Body::from(body)) .unwrap() } @@ -381,37 +398,20 @@ impl RuntimeClient { fn get_event_context(&self, headers: &HeaderMap) -> Result { // let headers = resp.headers(); - let aws_request_id = match headers.get(LambdaHeaders::RequestId.as_str()) { - Some(value) => value.to_str()?.to_owned(), - None => { - error!("Response headers do not contain request id header"); - return Err(ApiError::new(&format!("Missing {} header", LambdaHeaders::RequestId))); - } - }; - - let invoked_function_arn = match headers.get(LambdaHeaders::FunctionArn.as_str()) { - Some(value) => value.to_str()?.to_owned(), - None => { - error!("Response headers do not contain function arn header"); - return Err(ApiError::new(&format!("Missing {} header", LambdaHeaders::FunctionArn))); - } - }; - - let xray_trace_id = match headers.get(LambdaHeaders::TraceId.as_str()) { - Some(value) => value.to_str()?.to_owned(), - None => { - error!("Response headers do not contain trace id header"); - return Err(ApiError::new(&format!("Missing {} header", LambdaHeaders::TraceId))); - } - }; - - let deadline = match headers.get(LambdaHeaders::Deadline.as_str()) { - Some(value) => value.to_str()?.parse()?, - None => { - error!("Response headers do not contain deadline header"); - return Err(ApiError::new(&format!("Missing {} header", LambdaHeaders::Deadline))); - } - }; + let aws_request_id = header_string( + headers.get(LambdaHeaders::RequestId.as_str()), + &LambdaHeaders::RequestId, + )?; + let invoked_function_arn = header_string( + headers.get(LambdaHeaders::FunctionArn.as_str()), + &LambdaHeaders::FunctionArn, + )?; + let xray_trace_id = header_string(headers.get(LambdaHeaders::TraceId.as_str()), &LambdaHeaders::TraceId)?; + let deadline = header_string(headers.get(LambdaHeaders::Deadline.as_str()), &LambdaHeaders::Deadline)? + .parse::() + .context(ApiErrorKind::Recoverable( + "Could not parse deadline header value to int".to_string(), + ))?; let mut ctx = EventContext { aws_request_id, @@ -423,19 +423,43 @@ impl RuntimeClient { }; if let Some(ctx_json) = headers.get(LambdaHeaders::ClientContext.as_str()) { - let ctx_json = ctx_json.to_str()?; + let ctx_json = ctx_json.to_str().context(ApiErrorKind::Recoverable( + "Could not convert context header content to string".to_string(), + ))?; trace!("Found Client Context in response headers: {}", ctx_json); - let ctx_value: ClientContext = serde_json::from_str(&ctx_json)?; + let ctx_value: ClientContext = serde_json::from_str(&ctx_json).context(ApiErrorKind::Recoverable( + "Could not parse client context value as json object".to_string(), + ))?; ctx.client_context = Option::from(ctx_value); }; if let Some(cognito_json) = headers.get(LambdaHeaders::CognitoIdentity.as_str()) { - let cognito_json = cognito_json.to_str()?; + let cognito_json = cognito_json.to_str().context(ApiErrorKind::Recoverable( + "Could not convert congnito context header content to string".to_string(), + ))?; trace!("Found Cognito Identity in response headers: {}", cognito_json); - let identity_value: CognitoIdentity = serde_json::from_str(&cognito_json)?; + let identity_value: CognitoIdentity = serde_json::from_str(&cognito_json).context( + ApiErrorKind::Recoverable("Could not parse cognito context value as json object".to_string()), + )?; ctx.identity = Option::from(identity_value); }; Ok(ctx) } } + +fn header_string(value: Option<&HeaderValue>, header_type: &LambdaHeaders) -> Result { + match value { + Some(value_str) => Ok(value_str + .to_str() + .context(ApiErrorKind::Recoverable(format!( + "Could not parse {} header", + header_type + )))? + .to_owned()), + None => { + error!("Response headers do not contain {} header", header_type); + Err(ApiErrorKind::Recoverable(format!("Missing {} header", header_type)))? + } + } +} diff --git a/lambda-runtime-client/src/error.rs b/lambda-runtime-client/src/error.rs index 6dd5356e..b8627d77 100644 --- a/lambda-runtime-client/src/error.rs +++ b/lambda-runtime-client/src/error.rs @@ -1,20 +1,18 @@ //! This module defines the `RuntimeApiError` trait that developers should implement //! to send their custom errors to the AWS Lambda Runtime Client SDK. The module also //! defines the `ApiError` type returned by the `RuntimeClient` implementations. -use std::{env, error::Error, fmt, io, num::ParseIntError, option::Option}; - -use backtrace; -use http::{header::ToStrError, uri::InvalidUri}; -use hyper; -use serde_derive::Serialize; -use serde_json; - -/// Error type description for the `ErrorResponse` event. This type should be returned -/// for errors that were handled by the function code or framework. -pub const ERROR_TYPE_HANDLED: &str = "Handled"; -/// Error type description for the `ErrorResponse` event. This type is used for unhandled, -/// unexpcted errors. -pub const ERROR_TYPE_UNHANDLED: &str = "Unhandled"; +use failure::{AsFail, Backtrace, Context, Fail}; +use lambda_runtime_errors::LambdaErrorExt; +use log::*; +use serde_derive::*; +use std::{ + fmt::{self, Display}, + option::Option, +}; + +/// Error type for the error responses to the Runtime APIs. In the future, this library +/// should use a customer-generated error code +pub const RUNTIME_ERROR_TYPE: &str = "RustRuntimeError"; /// This object is used to generate requests to the Lambda Runtime APIs. /// It is used for both the error response APIs and fail init calls. @@ -25,9 +23,8 @@ pub struct ErrorResponse { /// The error message generated by the application. #[serde(rename = "errorMessage")] pub error_message: String, - /// The error type for Lambda. This can be `Handled` or `Unhandled`. - /// Developers can use the `ERROR_TYPE_HANDLED` and `ERROR_TYPE_UNHANDLED` - /// constants to populate this field. + /// The error type for Lambda. Normally, this value is populated using the + /// `error_type()` method from the `LambdaErrorExt` trait. #[serde(rename = "errorType")] pub error_type: String, /// The stack trace for the exception as vector of strings. In the framework, @@ -37,148 +34,132 @@ pub struct ErrorResponse { } impl ErrorResponse { - /// Creates a new `RuntimeError` object with the handled error type. + /// Creates a new instance of the `ErrorResponse` object with the given parameters. If the + /// `RUST_BACKTRACE` env variable is `1` the `ErrorResponse` is populated with the backtrace + /// collected through the [`backtrace` craete](https://crates.io/crates/backtrace). /// /// # Arguments /// - /// * `message` The error message for the Lambda Runtime APIs. + /// * `message` The error message to be returned to the APIs. Normally the error description() + /// * `err_type` An error type that identifies the root cause. Normally populated by the + /// `error_type()` method in the `LambdaErrorExt` trait. + /// * `backtrace` The stack trace for the error /// /// # Return - /// A populated `RuntimeError` object that can be used with the Lambda Runtime API. - pub fn handled(message: String) -> ErrorResponse { - ErrorResponse { + /// A new instance of the `ErrorResponse` object. + fn new(message: String, err_type: String, backtrace: Option<&Backtrace>) -> Self { + let mut err = ErrorResponse { error_message: message, - error_type: String::from(ERROR_TYPE_HANDLED), + error_type: err_type, stack_trace: Option::default(), + }; + // assume that failure is smart enough to only collect a backtrace + // if the env variable is enabled + if let Some(stack) = backtrace { + trace!("Begin backtrace collection"); + err.stack_trace = Some( + format!("{:?}", stack) + .lines() + .map(|s| s.to_string()) + .collect::>(), + ); + trace!("Completed backtrace collection"); } - } - /// Creates a new `RuntimeError` object with the unhandled error type. - /// - /// # Arguments - /// - /// * `message` The error message for the Lambda Runtime APIs. - /// - /// # Return - /// A populated `RuntimeError` object that can be used with the Lambda Runtime API. - pub fn unhandled(message: String) -> ErrorResponse { - ErrorResponse { - error_message: message, - error_type: String::from(ERROR_TYPE_UNHANDLED), - stack_trace: Option::default(), - } + err } } -/// Custom errors for the framework should implement this trait. The client calls -/// the `to_response()` method automatically to produce an object that can be serialized -/// and sent to the Lambda Runtime APIs. -pub trait RuntimeApiError { - /// Creates a `RuntimeError` object for the current error. This is - /// then serialized and sent to the Lambda runtime APIs. - /// - /// # Returns - /// A populated `RuntimeError` object. - fn to_response(&self) -> ErrorResponse; +impl From for ErrorResponse { + fn from(e: T) -> Self { + ErrorResponse::new(format!("{}", e), e.error_type().to_owned(), e.as_fail().backtrace()) + } } /// Represents an error generated by the Lambda Runtime API client. -#[derive(Debug, Clone)] +#[derive(Debug)] pub struct ApiError { - msg: String, - /// The `Backtrace` object from the `backtrace` crate used to store - /// the stack trace of the error. - pub backtrace: Option, - /// Whether the current error is recoverable. If the error is not - /// recoverable a runtime should panic to force the Lambda service - /// to restart the execution environment. - pub recoverable: bool, + inner: Context, } impl ApiError { - pub(crate) fn new(description: &str) -> ApiError { - let mut trace: Option = None; - let is_backtrace = env::var("RUST_BACKTRACE"); - if is_backtrace.is_ok() && is_backtrace.unwrap() == "1" { - trace!("Begin backtrace collection"); - trace = Option::from(backtrace::Backtrace::new()); - trace!("Completed backtrace collection"); + /// Returns `true` if the API error is recoverable and should be retried + pub fn is_recoverable(&self) -> bool { + match *self.inner.get_context() { + ApiErrorKind::Recoverable(_) => true, + _ => false, } - ApiError { - msg: String::from(description), - backtrace: trace, - recoverable: true, - } - } - - pub(crate) fn unrecoverable(&mut self) -> &ApiError { - self.recoverable = false; - - self } } - -impl fmt::Display for ApiError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}", self.msg) - } +/// Failure context for the `ApiError` type. The kind is used to indicate whether the +/// error is recoverable and should be retried or not. +#[derive(Clone, PartialEq, Debug, Fail)] +pub enum ApiErrorKind { + /// Runtime implementations that receive recoverable errors should automatically + /// retry requests + #[fail(display = "Recoverable API error: {}", _0)] + Recoverable(String), + /// Unrecoverable error should cause the runtime implementation to call the `fail_init` + /// method of the Runtime APIs if it is appropriate and then shutdown gracefully + #[fail(display = "Unrecoverable API error: {}", _0)] + Unrecoverable(String), } -// This is important for other errors to wrap this one. -impl Error for ApiError { - fn description(&self) -> &str { - &self.msg +impl Fail for ApiError { + fn cause(&self) -> Option<&Fail> { + self.inner.cause() } - fn cause(&self) -> Option<&dyn Error> { - // Generic error, underlying cause isn't tracked. - None + fn backtrace(&self) -> Option<&Backtrace> { + self.inner.backtrace() } } -impl From for ApiError { - fn from(e: serde_json::Error) -> Self { - ApiError::new(e.description()) +impl Display for ApiError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + Display::fmt(&self.inner, f) } } -impl From for ApiError { - fn from(e: InvalidUri) -> Self { - ApiError::new(e.description()) +impl LambdaErrorExt for ApiError { + fn error_type(&self) -> &str { + "RuntimeApiError" } } -impl From for ApiError { - fn from(e: hyper::Error) -> Self { - ApiError::new(e.description()) +impl From for ApiError { + fn from(kind: ApiErrorKind) -> Self { + Self { + inner: Context::new(kind), + } } } -impl From for ApiError { - fn from(e: ToStrError) -> Self { - ApiError::new(e.description()) +impl From> for ApiError { + fn from(inner: Context) -> Self { + Self { inner } } } -impl From for ApiError { - fn from(e: ParseIntError) -> Self { - ApiError::new(e.description()) +#[cfg(test)] +pub(crate) mod tests { + use super::*; + use failure::format_err; + use std::env; + + #[test] + fn does_not_produce_stack_trace() { + env::remove_var("RUST_BACKTRACE"); + let err = format_err!("Test error").compat(); + let resp_err = ErrorResponse::from(err); + assert_eq!(resp_err.stack_trace, None); } -} -impl From for ApiError { - fn from(e: io::Error) -> Self { - ApiError::new(e.description()) - } -} - -impl RuntimeApiError for ApiError { - fn to_response(&self) -> ErrorResponse { - let backtrace = format!("{:?}", self.backtrace); - let trace_vec = backtrace.lines().map(|s| s.to_string()).collect::>(); - let mut err = ErrorResponse::unhandled(self.msg.clone()); - err.stack_trace = Option::from(trace_vec); - - err + #[test] + fn is_recoverable_eq_correctly() { + let rec_err = ApiError::from(ApiErrorKind::Recoverable("Some recoverable kind".to_owned())); + assert_eq!(true, rec_err.is_recoverable()); + let unrec_err = ApiError::from(ApiErrorKind::Unrecoverable("Some unrecovrable kind".to_owned())); + assert_eq!(false, unrec_err.is_recoverable()); } } diff --git a/lambda-runtime-client/src/lib.rs b/lambda-runtime-client/src/lib.rs index efd336bd..85089ecc 100644 --- a/lambda-runtime-client/src/lib.rs +++ b/lambda-runtime-client/src/lib.rs @@ -1,5 +1,6 @@ #![warn(missing_docs)] #![deny(warnings)] +#![allow(clippy::new_ret_no_self)] //! Rust client SDK for the AWS Lambda Runtime APIs. This crate defines //! a `RuntimeClient` that encapsulates interactions with AWS Lambda's Runtime //! APIs. @@ -31,8 +32,7 @@ //! } //! //! fn main() { -//! let runtime_endpoint = String::from("http://localhost:8080"); -//! let client = RuntimeClient::new(runtime_endpoint, None) +//! let client = RuntimeClient::new("http://localhost:8080", None, None) //! .expect("Could not initialize client"); //! //! let (event_data, event_context) = client.next_event() @@ -45,7 +45,7 @@ //! let resp_object = CustomResponse{ surname: String::from("Doe")}; //! let resp_vec = serde_json::to_vec(&resp_object) //! .expect("Could not serialize CustomResponse to Vec"); -//! client.event_response(&event_context.aws_request_id, resp_vec) +//! client.event_response(&event_context.aws_request_id, &resp_vec) //! .expect("Response sent successfully"); //! } else { //! // return a custom error by implementing the RuntimeApiError trait. @@ -56,9 +56,7 @@ //! } //! ``` -#[macro_use] -extern crate log; - mod client; pub mod error; + pub use crate::client::*; diff --git a/lambda-runtime-core/Cargo.toml b/lambda-runtime-core/Cargo.toml new file mode 100644 index 00000000..6578f563 --- /dev/null +++ b/lambda-runtime-core/Cargo.toml @@ -0,0 +1,27 @@ +[package] +name = "lambda_runtime_core" +version = "0.1.0" +authors = ["Stefano Buliani", "David Barsky"] +description = "Rust runtime for AWS Lambda" +keywords = ["AWS", "Lambda", "Runtime", "Rust"] +license = "Apache-2.0" +homepage = "https://github.com/awslabs/aws-lambda-rust-runtime" +repository = "https://github.com/awslabs/aws-lambda-rust-runtime" +documentation = "https://docs.rs/lambda_runtime" +edition = "2018" + +[dependencies] +log = "^0.4" +hyper = "^0.12" +tokio = "^0.1" +backtrace = "^0.3" +chrono = "^0.4" +failure = "^0.1" +lambda_runtime_client = { path = "../lambda-runtime-client", version = "^0.1" } +lambda_runtime_errors = { path = "../lambda-runtime-errors", version = "^0.1" } + +[dev-dependencies] +simple_logger = "^1.0" + +[build-dependencies] +rustc_version = "^0.2" \ No newline at end of file diff --git a/lambda-runtime-core/build.rs b/lambda-runtime-core/build.rs new file mode 100644 index 00000000..b5eb4bc9 --- /dev/null +++ b/lambda-runtime-core/build.rs @@ -0,0 +1,56 @@ +use rustc_version::{Channel, VersionMeta}; +use std::{env, fs, io::Write, path::Path, process::Command}; + +const RUNTIME_METADATA_FILE: &str = "runtime_release"; + +fn main() { + println!("Generating AWS Lambda metadata file"); + let out_dir = env::var("OUT_DIR").unwrap(); + let compiler = env::var("RUSTC").unwrap(); + let cargo_version = env::var("CARGO_PKG_VERSION").unwrap(); + let compiler_version = + VersionMeta::for_command(Command::new(compiler.clone())).expect("Could not load compiler metdata"); + let chn: &str; + match compiler_version.channel { + Channel::Dev => chn = "dev", + Channel::Nightly => chn = "nightly", + Channel::Beta => chn = "beta", + Channel::Stable => chn = "stable", + } + let compiler_str = format!("{}/{}-{}", compiler, compiler_version.semver, chn); + + let agent = format!("AWS_Lambda_Rust/{} ({})", cargo_version, compiler_str); + // we expect this library to be built as a dependency and the output directory + // to be something like: my-lambda-function/target/release/build/lambda_runtime_core-c1abe336a4420096/out. + // we want the metadata file to be generated alongside the executable of the function + // so we travel 3 directories up to my-lambda-function/target/release. + let metadata_path = Path::new(&out_dir) + .parent() + .unwrap() + .parent() + .unwrap() + .parent() + .unwrap() + .join(RUNTIME_METADATA_FILE); + println!("Writing runtime metadata to: {}", metadata_path.to_str().unwrap()); + println!("Runtime metadata: {}", agent); + fs::write(metadata_path, agent.clone()).expect("Could not write runtime metdata file"); + + // next generate the metadata function for the runtime + let dest_path = Path::new(&out_dir).join("metadata.rs"); + let mut f = fs::File::create(&dest_path).unwrap(); + + f.write_all( + format!( + " +/// returns metdata information about the Lambda runtime +pub fn runtime_release() -> &'static str {{ + \"{}\" +}} +", + agent + ) + .as_bytes(), + ) + .unwrap(); +} diff --git a/lambda-runtime-core/examples/simple.rs b/lambda-runtime-core/examples/simple.rs new file mode 100644 index 00000000..f83f3f34 --- /dev/null +++ b/lambda-runtime-core/examples/simple.rs @@ -0,0 +1,21 @@ +use failure::format_err; +use lambda_runtime_core::{lambda, Context, HandlerError}; +use simple_logger; +use std::error::Error; + +fn main() -> Result<(), Box> { + simple_logger::init_with_level(log::Level::Debug)?; + lambda!(my_handler); + + Ok(()) +} + +fn my_handler(data: Vec, _c: Context) -> Result, HandlerError> { + let first_name = String::from_utf8(data)?; + + if first_name == "" { + return Err(format_err!("First name must be valid").into()); + } + + Ok(format!("Hello, {}!", first_name).as_bytes().to_vec()) +} diff --git a/lambda-runtime/src/context.rs b/lambda-runtime-core/src/context.rs similarity index 88% rename from lambda-runtime/src/context.rs rename to lambda-runtime-core/src/context.rs index 0c78a916..4e472076 100644 --- a/lambda-runtime/src/context.rs +++ b/lambda-runtime-core/src/context.rs @@ -1,10 +1,7 @@ -use std::env; - -use backtrace; use chrono::Utc; -use lambda_runtime_client; -use crate::{env as lambda_env, error::HandlerError}; +use crate::env as lambda_env; +use lambda_runtime_client; /// The Lambda function execution context. The values in this struct /// are populated using the [Lambda environment variables](https://docs.aws.amazon.com/lambda/latest/dg/current-supported-versions.html) @@ -85,20 +82,6 @@ impl Context { } } - /// We use the context for each event to store the stack trace. This is the methods - /// clients should use to retrieve an initialized `RuntimeError` with the populated - /// stack trace. - pub fn new_error(&self, msg: &str) -> HandlerError { - let mut trace: Option = None; - let is_backtrace = env::var("RUST_BACKTRACE"); - if is_backtrace.is_ok() && is_backtrace.unwrap() == "1" { - trace!("Begin backtrace collection"); - trace = Option::from(backtrace::Backtrace::new()); - trace!("Completed backtrace collection"); - } - HandlerError::new(msg, trace) - } - /// Returns the remaining time in the execution in milliseconds. This is based on the /// deadline header passed by Lambda's Runtime APIs. pub fn get_time_remaining_millis(&self) -> i64 { diff --git a/lambda-runtime/src/env.rs b/lambda-runtime-core/src/env.rs similarity index 98% rename from lambda-runtime/src/env.rs rename to lambda-runtime-core/src/env.rs index 7c55a2bd..94cde577 100644 --- a/lambda-runtime/src/env.rs +++ b/lambda-runtime-core/src/env.rs @@ -1,6 +1,7 @@ use std::env; use crate::error::RuntimeError; +use log::*; /// The name of the environment variable in the Lambda execution /// environment for the Runtime APIs endpoint. The value of this @@ -39,9 +40,9 @@ pub trait ConfigProvider { /// used by the `start()` method of this module. pub struct EnvConfigProvider; -impl EnvConfigProvider { - pub fn new() -> Self { - EnvConfigProvider {} +impl std::default::Default for EnvConfigProvider { + fn default() -> Self { + EnvConfigProvider } } diff --git a/lambda-runtime-core/src/error.rs b/lambda-runtime-core/src/error.rs new file mode 100644 index 00000000..89cad043 --- /dev/null +++ b/lambda-runtime-core/src/error.rs @@ -0,0 +1,98 @@ +//! The error module defines the error types that can be returned +//! by custom handlers as well as the runtime itself. +use std::{env, error::Error, fmt}; + +use lambda_runtime_client::error::ApiError; +use lambda_runtime_errors::LambdaErrorExt; + +/// The `RuntimeError` object is returned by the custom runtime as it polls +/// for new events and tries to execute the handler function. The error +/// is primarily used by other methods within this crate and should not be relevant +/// to developers building Lambda functions. Handlers are expected to return +/// the `HandlerError` defined in this module. +#[derive(Debug, Clone)] +pub struct RuntimeError { + msg: String, + /// The request id that generated this error + pub(crate) request_id: Option, + /// Whether the error is recoverable or not. + pub(crate) recoverable: bool, +} + +impl RuntimeError { + /// Creates a new `RuntimeError` that is unrecoverable and it will cause the + /// runtime to panic in order to force a restart of the execution environment. + /// When a new `RuntimeError` is created the stack trace for the error is collected + /// automatically using the `backtrace` crate. + /// + /// # Arguments + /// + /// * `msg` The error message to be attached to the error. + /// + /// # Returns + /// A new `RuntimeError` instance with the `recoverable` property set to `false`. + pub(crate) fn unrecoverable(msg: &str) -> RuntimeError { + let mut new_error = RuntimeError::new(msg); + new_error.recoverable = false; + new_error + } + + /// Creates a new `RuntimeError` with the given properties. The stack trace for the + /// error is collected automatically using the `backtrace` crate. + /// + /// # Arguments + /// + /// * `msg` The error message + /// + /// # Returns + /// A new `RuntimeError` instance. + pub(crate) fn new(msg: &str) -> RuntimeError { + RuntimeError { + msg: String::from(msg), + recoverable: true, + request_id: None, + } + } +} + +impl LambdaErrorExt for RuntimeError { + fn error_type(&self) -> &str { + if self.recoverable { + "RecoverableRuntimeError" + } else { + "UnrecoverableRuntimeError" + } + } +} + +impl fmt::Display for RuntimeError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.msg) + } +} + +// This is important for other errors to wrap this one. +impl Error for RuntimeError { + fn description(&self) -> &str { + &self.msg + } + + fn cause(&self) -> Option<&Error> { + // Generic error, underlying cause isn't tracked. + None + } +} + +impl From for RuntimeError { + fn from(e: env::VarError) -> Self { + RuntimeError::unrecoverable(e.description()) + } +} + +impl From for RuntimeError { + fn from(e: ApiError) -> Self { + let mut err = RuntimeError::new(&format!("{}", e)); + err.recoverable = e.is_recoverable(); + err + } +} diff --git a/lambda-runtime-core/src/handler.rs b/lambda-runtime-core/src/handler.rs new file mode 100644 index 00000000..41d2eff5 --- /dev/null +++ b/lambda-runtime-core/src/handler.rs @@ -0,0 +1,20 @@ +use crate::context::Context; +use failure::Fail; +use lambda_runtime_errors::LambdaErrorExt; +use std::fmt::Display; + +/// Functions acting as a handler must conform to this type. +pub trait Handler { + /// Run the handler. + fn run(&mut self, event: Vec, ctx: Context) -> Result, EventError>; +} + +impl<'ev, Function, EventError> Handler for Function +where + Function: FnMut(Vec, Context) -> Result, EventError>, + EventError: Fail + LambdaErrorExt + Display + Send + Sync, +{ + fn run(&mut self, event: Vec, ctx: Context) -> Result, EventError> { + (*self)(event, ctx) + } +} diff --git a/lambda-runtime-core/src/lib.rs b/lambda-runtime-core/src/lib.rs new file mode 100644 index 00000000..7a52c1d1 --- /dev/null +++ b/lambda-runtime-core/src/lib.rs @@ -0,0 +1,25 @@ +#![warn(missing_docs)] +#![deny(warnings)] +//! The Lambda runtime core crate implements [Lambda's custom runtime main loop](https://docs.aws.amazon.com/lambda/latest/dg/runtimes-custom.html#runtimes-custom-build). +//! The crate receives a `Handler` type that consumed events in the form of `Vec` and +//! outputs a `Result` with a `Vec` successful output. +//! +//! **Unless you have specific requirements to consume/produce raw bytes, you should look at the +//! [`lambda_runtime` crate](https://crates.io/crates/lambda_runtime)**. +//! +//! TODO: Add example + +mod context; +mod env; +mod error; +mod handler; +mod runtime; + +pub use crate::{ + context::Context, + env::{ConfigProvider, EnvConfigProvider}, + handler::Handler, + runtime::*, +}; + +pub use lambda_runtime_errors::{HandlerError, LambdaErrorExt, LambdaResultExt}; diff --git a/lambda-runtime/src/runtime.rs b/lambda-runtime-core/src/runtime.rs similarity index 61% rename from lambda-runtime/src/runtime.rs rename to lambda-runtime-core/src/runtime.rs index 6e722465..175a2b7a 100644 --- a/lambda-runtime/src/runtime.rs +++ b/lambda-runtime-core/src/runtime.rs @@ -1,32 +1,20 @@ -use std::{error::Error, marker::PhantomData, result}; - -use lambda_runtime_client::RuntimeClient; -use serde; -use serde_json; -use tokio::runtime::Runtime as TokioRuntime; - use crate::{ context::Context, env::{ConfigProvider, EnvConfigProvider, FunctionSettings}, - error::{HandlerError, RuntimeError}, + error::RuntimeError, + handler::Handler, }; +use failure::Fail; +use lambda_runtime_client::{error::ErrorResponse, RuntimeClient}; +use lambda_runtime_errors::LambdaErrorExt; +use log::*; +use std::{fmt::Display, marker::PhantomData}; +use tokio::runtime::Runtime as TokioRuntime; -const MAX_RETRIES: i8 = 3; - -/// Functions acting as a handler must conform to this type. -pub trait Handler { - /// Run the handler. - fn run(&mut self, event: E, ctx: Context) -> Result; -} +// include file generated during the build process +include!(concat!(env!("OUT_DIR"), "/metadata.rs")); -impl Handler for F -where - F: FnMut(E, Context) -> Result, -{ - fn run(&mut self, event: E, ctx: Context) -> Result { - (*self)(event, ctx) - } -} +const MAX_RETRIES: i8 = 3; /// Creates a new runtime and begins polling for events using Lambda's Runtime APIs. /// @@ -36,12 +24,11 @@ where /// /// # Panics /// The function panics if the Lambda environment variables are not set. -pub fn start(f: impl Handler, runtime: Option) +pub fn start(f: impl Handler, runtime: Option) where - E: serde::de::DeserializeOwned, - O: serde::Serialize, + EventError: Fail + LambdaErrorExt + Display + Send + Sync, { - start_with_config(f, &EnvConfigProvider::new(), runtime) + start_with_config(f, &EnvConfigProvider::default(), runtime) } #[macro_export] @@ -76,11 +63,13 @@ macro_rules! lambda { /// The function panics if the `ConfigProvider` returns an error from the `get_runtime_api_endpoint()` /// or `get_function_settings()` methods. The panic forces AWS Lambda to terminate the environment /// and spin up a new one for the next invocation. -pub(crate) fn start_with_config(f: impl Handler, config: &C, runtime: Option) -where - E: serde::de::DeserializeOwned, - O: serde::Serialize, - C: ConfigProvider, +pub fn start_with_config( + f: impl Handler, + config: &Config, + runtime: Option, +) where + Config: ConfigProvider, + EventError: Fail + LambdaErrorExt + Display + Send + Sync, { // if we cannot find the endpoint we panic, nothing else we can do. let endpoint: String; @@ -102,7 +91,9 @@ where } } - match RuntimeClient::new(endpoint, runtime) { + let info = Option::from(runtime_release().to_owned()); + + match RuntimeClient::new(&endpoint, info, runtime) { Ok(client) => { start_with_runtime_client(f, function_config, client); } @@ -123,21 +114,14 @@ where /// /// # Panics /// The function panics if we cannot instantiate a new `RustRuntime` object. -pub(crate) fn start_with_runtime_client( - f: impl Handler, +pub(crate) fn start_with_runtime_client( + f: impl Handler, func_settings: FunctionSettings, client: RuntimeClient, ) where - E: serde::de::DeserializeOwned, - O: serde::Serialize, + EventError: Fail + LambdaErrorExt + Display + Send + Sync, { - let mut lambda_runtime: Runtime<_, E, O>; - match Runtime::new(f, func_settings, MAX_RETRIES, client) { - Ok(r) => lambda_runtime = r, - Err(e) => { - panic!("Error while starting runtime: {}", e); - } - } + let mut lambda_runtime: Runtime<_, EventError> = Runtime::new(f, func_settings, MAX_RETRIES, client); // start the infinite loop lambda_runtime.start(); @@ -145,16 +129,20 @@ pub(crate) fn start_with_runtime_client( /// Internal representation of the runtime object that polls for events and communicates /// with the Runtime APIs -pub(super) struct Runtime { +pub(super) struct Runtime { runtime_client: RuntimeClient, - handler: F, + handler: Function, max_retries: i8, settings: FunctionSettings, - _phan: PhantomData<(E, O)>, + _phantom: PhantomData, } // generic methods implementation -impl Runtime { +impl Runtime +where + Function: Handler, + EventError: Fail + LambdaErrorExt + Display + Send + Sync, +{ /// Creates a new instance of the `Runtime` object populated with the environment /// settings. /// @@ -168,34 +156,29 @@ impl Runtime { /// A `Result` for the `Runtime` object or a `errors::RuntimeSerror`. The runtime /// fails the init if this function returns an error. If we cannot find the /// `AWS_LAMBDA_RUNTIME_API` variable in the environment the function panics. - pub(super) fn new( - f: F, - config: FunctionSettings, - retries: i8, - client: RuntimeClient, - ) -> result::Result { + pub(super) fn new(f: Function, config: FunctionSettings, retries: i8, client: RuntimeClient) -> Self { debug!( "Creating new runtime with {} max retries for endpoint {}", retries, client.get_endpoint() ); - Ok(Runtime { + + Runtime { runtime_client: client, settings: config, handler: f, max_retries: retries, - _phan: PhantomData, - }) + _phantom: PhantomData, + } } } // implementation of methods that require the Event and Output types // to be compatible with `serde`'s Deserialize/Serialize. -impl Runtime +impl Runtime where - F: Handler, - E: serde::de::DeserializeOwned, - O: serde::Serialize, + Function: Handler, + EventError: Fail + LambdaErrorExt + Display + Send + Sync, { /// Starts the main event loop and begin polling or new events. If one of the /// Runtime APIs returns an unrecoverable error this method calls the init failed @@ -213,49 +196,36 @@ where "Function executed succesfully for {}, pushing response to Runtime API", request_id ); - match serde_json::to_vec(&response) { - Ok(response_bytes) => { - match self.runtime_client.event_response(&request_id, response_bytes) { - Ok(_) => info!("Response for {} accepted by Runtime API", request_id), - // unrecoverable error while trying to communicate with the endpoint. - // we let the Lambda Runtime API know that we have died - Err(e) => { - error!("Could not send response for {} to Runtime API: {}", request_id, e); - if !e.recoverable { - error!( - "Error for {} is not recoverable, sending fail_init signal and panicking.", - request_id - ); - self.runtime_client.fail_init(&e); - panic!("Could not send response"); - } - } - } - } + match self.runtime_client.event_response(&request_id, &response) { + Ok(_) => info!("Response for {} accepted by Runtime API", request_id), + // unrecoverable error while trying to communicate with the endpoint. + // we let the Lambda Runtime API know that we have died Err(e) => { - error!( - "Could not marshal output object to Vec JSON representation for request {}: {}", - request_id, e - ); - self.runtime_client - .fail_init(&RuntimeError::unrecoverable(e.description())); - panic!("Failed to marshal handler output, panic"); + error!("Could not send response for {} to Runtime API: {}", request_id, e); + if !e.is_recoverable() { + error!( + "Error for {} is not recoverable, sending fail_init signal and panicking.", + request_id + ); + self.runtime_client.fail_init(&ErrorResponse::from(e)); + panic!("Could not send response"); + } } } } Err(e) => { debug!("Handler returned an error for {}: {}", request_id, e); debug!("Attempting to send error response to Runtime API for {}", request_id); - match self.runtime_client.event_error(&request_id, &e) { + match self.runtime_client.event_error(&request_id, &ErrorResponse::from(e)) { Ok(_) => info!("Error response for {} accepted by Runtime API", request_id), Err(e) => { error!("Unable to send error response for {} to Runtime API: {}", request_id, e); - if !e.recoverable { + if !e.is_recoverable() { error!( "Error for {} is not recoverable, sending fail_init signal and panicking", request_id ); - self.runtime_client.fail_init(&e); + self.runtime_client.fail_init(&ErrorResponse::from(e)); panic!("Could not send error response"); } } @@ -267,8 +237,8 @@ where /// Invoke the handler function. This method is split out of the main loop to /// make it testable. - pub(super) fn invoke(&mut self, e: E, ctx: Context) -> Result { - (&mut self.handler).run(e, ctx) + pub(super) fn invoke(&mut self, e: Vec, ctx: Context) -> Result, EventError> { + (self.handler).run(e, ctx) } /// Attempts to get the next event from the Runtime APIs and keeps retrying @@ -276,18 +246,18 @@ where /// /// # Return /// The next `Event` object to be processed. - pub(super) fn get_next_event(&self, retries: i8, e: Option) -> (E, Context) { + pub(super) fn get_next_event(&self, retries: i8, e: Option) -> (Vec, Context) { if let Some(err) = e { if retries > self.max_retries { error!("Unrecoverable error while fetching next event: {}", err); match err.request_id.clone() { Some(req_id) => { self.runtime_client - .event_error(&req_id, &err) + .event_error(&req_id, &ErrorResponse::from(err)) .expect("Could not send event error response"); } None => { - self.runtime_client.fail_init(&err); + self.runtime_client.fail_init(&ErrorResponse::from(err)); } } @@ -299,26 +269,15 @@ where match self.runtime_client.next_event() { Ok((ev_data, invocation_ctx)) => { - let parse_result = serde_json::from_slice(&ev_data); - match parse_result { - Ok(ev) => { - let mut handler_ctx = Context::new(self.settings.clone()); - handler_ctx.invoked_function_arn = invocation_ctx.invoked_function_arn; - handler_ctx.aws_request_id = invocation_ctx.aws_request_id; - handler_ctx.xray_trace_id = invocation_ctx.xray_trace_id; - handler_ctx.client_context = invocation_ctx.client_context; - handler_ctx.identity = invocation_ctx.identity; - handler_ctx.deadline = invocation_ctx.deadline; + let mut handler_ctx = Context::new(self.settings.clone()); + handler_ctx.invoked_function_arn = invocation_ctx.invoked_function_arn; + handler_ctx.aws_request_id = invocation_ctx.aws_request_id; + handler_ctx.xray_trace_id = invocation_ctx.xray_trace_id; + handler_ctx.client_context = invocation_ctx.client_context; + handler_ctx.identity = invocation_ctx.identity; + handler_ctx.deadline = invocation_ctx.deadline; - (ev, handler_ctx) - } - Err(e) => { - error!("Could not parse event to type: {}", e); - let mut runtime_err = RuntimeError::from(e); - runtime_err.request_id = Option::from(invocation_ctx.aws_request_id); - self.get_next_event(retries + 1, Option::from(runtime_err)) - } - } + (ev_data, handler_ctx) } Err(e) => self.get_next_event(retries + 1, Option::from(RuntimeError::from(e))), } @@ -330,20 +289,22 @@ pub(crate) mod tests { use super::*; use crate::{context, env}; use lambda_runtime_client::RuntimeClient; + use lambda_runtime_errors::HandlerError; #[test] fn runtime_invokes_handler() { let config: &dyn env::ConfigProvider = &env::tests::MockConfigProvider { error: false }; let client = RuntimeClient::new( - config + &config .get_runtime_api_endpoint() .expect("Could not get runtime endpoint"), None, + None, ) .expect("Could not initialize client"); - let handler = |_e: String, _c: context::Context| -> Result { Ok("hello".to_string()) }; + let handler = |_e: Vec, _c: context::Context| -> Result, HandlerError> { Ok(b"hello".to_vec()) }; let retries: i8 = 3; - let runtime = Runtime::new( + let mut runtime = Runtime::new( handler, config .get_function_settings() @@ -351,22 +312,15 @@ pub(crate) mod tests { retries, client, ); - assert_eq!( - runtime.is_err(), - false, - "Runtime threw an unexpected error: {}", - runtime.err().unwrap() - ); - let output = runtime - .unwrap() - .invoke(String::from("test"), context::tests::test_context(10)); + let output = runtime.invoke(b"test".to_vec(), context::tests::test_context(10)); assert_eq!( output.is_err(), false, "Handler threw an unexpected error: {}", output.err().unwrap() ); - let output_string = output.unwrap(); + let output_bytes = output.ok().unwrap(); + let output_string = String::from_utf8(output_bytes).unwrap(); assert_eq!(output_string, "hello", "Unexpected output message: {}", output_string); } } diff --git a/lambda-runtime-errors-derive/Cargo.toml b/lambda-runtime-errors-derive/Cargo.toml new file mode 100644 index 00000000..55a2fb6c --- /dev/null +++ b/lambda-runtime-errors-derive/Cargo.toml @@ -0,0 +1,24 @@ +[package] +name = "lambda_runtime_errors_derive" +version = "0.1.0" +authors = ["Stefano Buliani", "David Barsky"] +edition = "2018" +description = "Rust runtime errors derive for AWS Lambda" +keywords = ["AWS", "Lambda", "Runtime", "Rust"] +license = "Apache-2.0" +homepage = "https://github.com/awslabs/aws-lambda-rust-runtime" +repository = "https://github.com/awslabs/aws-lambda-rust-runtime" +documentation = "https://docs.rs/lambda_runtime" + +[dependencies] +syn = "^0.15" +synstructure = "^0.10" +proc-macro2 = "^0.4" +quote = "^0.6" + +[dev-dependencies] +lambda_runtime_errors = { path = "../lambda-runtime-errors", version = "^0.1" } +failure = "^0.1" + +[lib] +proc-macro = true \ No newline at end of file diff --git a/lambda-runtime-errors-derive/src/lib.rs b/lambda-runtime-errors-derive/src/lib.rs new file mode 100644 index 00000000..4018ccb6 --- /dev/null +++ b/lambda-runtime-errors-derive/src/lib.rs @@ -0,0 +1,23 @@ +use proc_macro2::TokenStream; +use quote::quote; +use synstructure::decl_derive; + +decl_derive!([LambdaErrorExt, attributes()] => lambda_error_derive); + +fn lambda_error_derive(s: synstructure::Structure) -> TokenStream { + let name = format!("{}", s.ast().ident); + + let err_impl = s.gen_impl(quote! { + use lambda_runtime_errors::LambdaErrorExt; + + gen impl LambdaErrorExt for @Self { + fn error_type(&self) -> &str { + #name + } + } + }); + + (quote! { + #err_impl + }) +} diff --git a/lambda-runtime-errors-derive/tests/tests.rs b/lambda-runtime-errors-derive/tests/tests.rs new file mode 100644 index 00000000..51024742 --- /dev/null +++ b/lambda-runtime-errors-derive/tests/tests.rs @@ -0,0 +1,75 @@ +use failure::{Backtrace, Context, Fail}; +use lambda_runtime_errors::LambdaErrorExt; +use std::fmt; + +#[derive(LambdaErrorExt)] +struct BasicCustomError; + +#[derive(Fail, LambdaErrorExt, Debug)] +#[fail(display = "Input was invalid UTF-8")] +struct FailureCustomError; + +#[derive(Debug, LambdaErrorExt)] +struct FailureCustomWithKind { + inner: Context, +} + +#[derive(Clone, Eq, PartialEq, Debug, Fail, LambdaErrorExt)] +enum FailureErrorKind { + #[fail(display = "First contextual error message.")] + FirstVariant, + #[fail(display = "Second contextual error message: {}.", _0)] + SecondVariant(String), +} + +impl Fail for FailureCustomWithKind { + fn cause(&self) -> Option<&Fail> { + self.inner.cause() + } + + fn backtrace(&self) -> Option<&Backtrace> { + self.inner.backtrace() + } +} + +impl fmt::Display for FailureCustomWithKind { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt::Display::fmt(&self.inner, f) + } +} + +impl FailureCustomWithKind { + pub fn kind(&self) -> FailureErrorKind { + self.inner.get_context().clone() + } +} + +impl From for FailureCustomWithKind { + fn from(kind: FailureErrorKind) -> Self { + FailureCustomWithKind { + inner: Context::new(kind), + } + } +} + +#[test] +fn simple_error_type() { + let err = BasicCustomError {}; + assert_eq!( + err.error_type(), + "BasicCustomError", + "Custom error not implemented correctly" + ); +} + +#[test] +fn fail_custom_error() { + let err = FailureCustomError {}; + assert_eq!(err.error_type(), "FailureCustomError", "Error type wrong") +} + +#[test] +fn fail_variant_first() { + let err = FailureCustomWithKind::from(FailureErrorKind::FirstVariant); + //assert_eq!(err.error_type(), "FailureCustomError", "Error type wrong") +} diff --git a/lambda-runtime-errors/Cargo.toml b/lambda-runtime-errors/Cargo.toml new file mode 100644 index 00000000..c23a2a92 --- /dev/null +++ b/lambda-runtime-errors/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "lambda_runtime_errors" +version = "0.1.0" +authors = ["Stefano Buliani", "David Barsky"] +edition = "2018" +description = "Rust runtime errors for AWS Lambda" +keywords = ["AWS", "Lambda", "Runtime", "Rust"] +license = "Apache-2.0" +homepage = "https://github.com/awslabs/aws-lambda-rust-runtime" +repository = "https://github.com/awslabs/aws-lambda-rust-runtime" +documentation = "https://docs.rs/lambda_runtime" + +[dependencies] +log = "^0.4" +failure = "^0.1" +serde_json = "^1" + +[dev-dependencies] +lambda_runtime_core = { path = "../lambda-runtime-core", version = "^0.1"} diff --git a/lambda-runtime-errors/errorgen.py b/lambda-runtime-errors/errorgen.py new file mode 100755 index 00000000..04538651 --- /dev/null +++ b/lambda-runtime-errors/errorgen.py @@ -0,0 +1,142 @@ +#!/usr/bin/env python3 + +# Generates the LambdaErrorExt implementation for all of the Errors +# in the standard library, excluding unstable APIs and errors that +# require generics. +# +# !! Please note this script is a hacky, short term solution !! +import os +from urllib.request import urlopen +from html.parser import HTMLParser + +RUST_ERROR_DOCS = "https://doc.rust-lang.org/std/error/trait.Error.html" +GENERATED_FILE_NAME = "./src/error_ext_impl.rs" +UNSTABLE_APIS = ["std::alloc::AllocErr", + "std::alloc::CannotReallocInPlace", + "std::char::CharTryFromError", + "std::num::TryFromIntError"] +GENERIC_ERRORS = ["std::sync::TryLockError", + "std::sync::PoisonError", + "std::sync::mpsc::TrySendError", + "std::sync::mpsc::SendError", + "std::io::IntoInnerError"] + + +class ErrorHtmlParser(HTMLParser): + def __init__(self): + super().__init__() + self.reset() + self.errors = [] + self.parsing = False + self.cur_error = self.empty_error() + + def handle_starttag(self, tag, attrs): + if self.parsing and tag == "a": + href = "" + if len(attrs) == 1: + href = attrs[0][1] + else: + href = attrs[1][1] + parts = href.split("/") + cnt = 0 + package = "" + for part in parts: + cnt = cnt + 1 + if part == "..": + continue + if cnt == len(parts): + break + + package += part + "::" + + if package.endswith("::"): + package = package[0:len(package) - 2] + self.cur_error["package"] = package + self.cur_error["href"] = href + + def empty_error(self): + return { + "package": "", + "name": "" + } + + def handle_data(self, data): + if data == " Error for " or data == "impl Error for ": + self.start_parsing() + else: + if self.parsing: + self.cur_error["name"] = data + if self.is_valid_error(self.cur_error): + self.errors.append(self.cur_error) + self.cur_error = self.empty_error + self.parsing = False + + def is_valid_error(self, err): + if err["name"] == "Box": + return False + if ".html" in err["package"]: + return False + if err["package"] == "": + return False + if err["package"] + "::" + err["name"] in UNSTABLE_APIS: + return False + if err["package"] + "::" + err["name"] in GENERIC_ERRORS: + return False + + return True + + def start_parsing(self): + if not self.parsing: + self.parsing = True + self.cur_error = self.empty_error() + else: + if self.cur_error["package"] == "" or self.cur_error["name"] == "": + print("Starting new error with empty existing error") + + +res = urlopen(RUST_ERROR_DOCS) +assert res.getcode() == 200, "Could not retrieve Rust error docs" + +error_docs_html = res.read() +assert error_docs_html != "", "Empty Error docs" + +parser = ErrorHtmlParser() +parser.feed(error_docs_html.decode()) + +print("found {} valid errors. Beginning code generation to {}".format( + len(parser.errors), GENERATED_FILE_NAME)) + +if os.path.isfile(GENERATED_FILE_NAME): + os.remove(GENERATED_FILE_NAME) + +# code gen +with open(GENERATED_FILE_NAME, "a") as f: + f.write("""// Generated code, DO NOT MODIFY! +// This file contains the implementation of the LambdaErrorExt +// trait for most of the standard library errors as well as the +// implementation of the From trait for the HandlerError struct +// to support the same standard library errors.\n\n""") + + # use statements + for err in parser.errors: + f.write("use {}::{};\n".format(err["package"], err["name"])) + f.write( + "use crate::{LambdaErrorExt, HandlerError};\n\n") + + # impl for LambdaErrorExt for the standard library errors + for err in parser.errors: + f.write("""impl LambdaErrorExt for {} {{ + fn error_type(&self) -> &str {{ + "{}::{}" + }} +}}\n""".format(err["name"], err["package"], err["name"])) + + # impl From trait for standard library errors to HandlerError + for err in parser.errors: + f.write("""impl From<{}> for HandlerError {{ + fn from(e: {}) -> Self {{ + HandlerError::new(e) + }} +}}\n""".format(err["name"], err["name"])) + + f.close() diff --git a/lambda-runtime-errors/src/error_ext_impl.rs b/lambda-runtime-errors/src/error_ext_impl.rs new file mode 100644 index 00000000..04a46a6a --- /dev/null +++ b/lambda-runtime-errors/src/error_ext_impl.rs @@ -0,0 +1,252 @@ +// Generated code, DO NOT MODIFY! +// This file contains the implementation of the LambdaErrorExt +// trait for most of the standard library errors as well as the +// implementation of the From trait for the HandlerError struct +// to support the same standard library errors. + +use crate::{HandlerError, LambdaErrorExt}; +use std::{ + alloc::LayoutErr, + cell::{BorrowError, BorrowMutError}, + char::{DecodeUtf16Error, ParseCharError}, + env::{JoinPathsError, VarError}, + ffi::{FromBytesWithNulError, IntoStringError, NulError}, + net::AddrParseError, + num::{ParseFloatError, ParseIntError}, + path::StripPrefixError, + str::{ParseBoolError, Utf8Error}, + string::{FromUtf16Error, FromUtf8Error, ParseError}, + sync::mpsc::{RecvError, RecvTimeoutError, TryRecvError}, + time::SystemTimeError, +}; + +impl LambdaErrorExt for VarError { + fn error_type(&self) -> &str { + "std::env::VarError" + } +} +impl LambdaErrorExt for ParseError { + fn error_type(&self) -> &str { + "std::string::ParseError" + } +} +impl LambdaErrorExt for RecvTimeoutError { + fn error_type(&self) -> &str { + "std::sync::mpsc::RecvTimeoutError" + } +} +impl LambdaErrorExt for TryRecvError { + fn error_type(&self) -> &str { + "std::sync::mpsc::TryRecvError" + } +} +impl LambdaErrorExt for LayoutErr { + fn error_type(&self) -> &str { + "std::alloc::LayoutErr" + } +} +impl LambdaErrorExt for BorrowError { + fn error_type(&self) -> &str { + "std::cell::BorrowError" + } +} +impl LambdaErrorExt for BorrowMutError { + fn error_type(&self) -> &str { + "std::cell::BorrowMutError" + } +} +impl LambdaErrorExt for DecodeUtf16Error { + fn error_type(&self) -> &str { + "std::char::DecodeUtf16Error" + } +} +impl LambdaErrorExt for ParseCharError { + fn error_type(&self) -> &str { + "std::char::ParseCharError" + } +} +impl LambdaErrorExt for JoinPathsError { + fn error_type(&self) -> &str { + "std::env::JoinPathsError" + } +} +impl LambdaErrorExt for FromBytesWithNulError { + fn error_type(&self) -> &str { + "std::ffi::FromBytesWithNulError" + } +} +impl LambdaErrorExt for IntoStringError { + fn error_type(&self) -> &str { + "std::ffi::IntoStringError" + } +} +impl LambdaErrorExt for NulError { + fn error_type(&self) -> &str { + "std::ffi::NulError" + } +} +impl LambdaErrorExt for AddrParseError { + fn error_type(&self) -> &str { + "std::net::AddrParseError" + } +} +impl LambdaErrorExt for ParseFloatError { + fn error_type(&self) -> &str { + "std::num::ParseFloatError" + } +} +impl LambdaErrorExt for ParseIntError { + fn error_type(&self) -> &str { + "std::num::ParseIntError" + } +} +impl LambdaErrorExt for StripPrefixError { + fn error_type(&self) -> &str { + "std::path::StripPrefixError" + } +} +impl LambdaErrorExt for ParseBoolError { + fn error_type(&self) -> &str { + "std::str::ParseBoolError" + } +} +impl LambdaErrorExt for Utf8Error { + fn error_type(&self) -> &str { + "std::str::Utf8Error" + } +} +impl LambdaErrorExt for FromUtf16Error { + fn error_type(&self) -> &str { + "std::string::FromUtf16Error" + } +} +impl LambdaErrorExt for FromUtf8Error { + fn error_type(&self) -> &str { + "std::string::FromUtf8Error" + } +} +impl LambdaErrorExt for RecvError { + fn error_type(&self) -> &str { + "std::sync::mpsc::RecvError" + } +} +impl LambdaErrorExt for SystemTimeError { + fn error_type(&self) -> &str { + "std::time::SystemTimeError" + } +} +impl From for HandlerError { + fn from(e: VarError) -> Self { + HandlerError::new(e) + } +} +impl From for HandlerError { + fn from(e: ParseError) -> Self { + HandlerError::new(e) + } +} +impl From for HandlerError { + fn from(e: RecvTimeoutError) -> Self { + HandlerError::new(e) + } +} +impl From for HandlerError { + fn from(e: TryRecvError) -> Self { + HandlerError::new(e) + } +} +impl From for HandlerError { + fn from(e: LayoutErr) -> Self { + HandlerError::new(e) + } +} +impl From for HandlerError { + fn from(e: BorrowError) -> Self { + HandlerError::new(e) + } +} +impl From for HandlerError { + fn from(e: BorrowMutError) -> Self { + HandlerError::new(e) + } +} +impl From for HandlerError { + fn from(e: DecodeUtf16Error) -> Self { + HandlerError::new(e) + } +} +impl From for HandlerError { + fn from(e: ParseCharError) -> Self { + HandlerError::new(e) + } +} +impl From for HandlerError { + fn from(e: JoinPathsError) -> Self { + HandlerError::new(e) + } +} +impl From for HandlerError { + fn from(e: FromBytesWithNulError) -> Self { + HandlerError::new(e) + } +} +impl From for HandlerError { + fn from(e: IntoStringError) -> Self { + HandlerError::new(e) + } +} +impl From for HandlerError { + fn from(e: NulError) -> Self { + HandlerError::new(e) + } +} +impl From for HandlerError { + fn from(e: AddrParseError) -> Self { + HandlerError::new(e) + } +} +impl From for HandlerError { + fn from(e: ParseFloatError) -> Self { + HandlerError::new(e) + } +} +impl From for HandlerError { + fn from(e: ParseIntError) -> Self { + HandlerError::new(e) + } +} +impl From for HandlerError { + fn from(e: StripPrefixError) -> Self { + HandlerError::new(e) + } +} +impl From for HandlerError { + fn from(e: ParseBoolError) -> Self { + HandlerError::new(e) + } +} +impl From for HandlerError { + fn from(e: Utf8Error) -> Self { + HandlerError::new(e) + } +} +impl From for HandlerError { + fn from(e: FromUtf16Error) -> Self { + HandlerError::new(e) + } +} +impl From for HandlerError { + fn from(e: FromUtf8Error) -> Self { + HandlerError::new(e) + } +} +impl From for HandlerError { + fn from(e: RecvError) -> Self { + HandlerError::new(e) + } +} +impl From for HandlerError { + fn from(e: SystemTimeError) -> Self { + HandlerError::new(e) + } +} diff --git a/lambda-runtime-errors/src/lib.rs b/lambda-runtime-errors/src/lib.rs new file mode 100644 index 00000000..b4cce3c4 --- /dev/null +++ b/lambda-runtime-errors/src/lib.rs @@ -0,0 +1,187 @@ +//! The Lambda runtime errors crate defines the `LambdaErrorExt` trait +//! that can be used by libriaries to return errors compatible with the +//! AWS Lambda Rust runtime. +mod error_ext_impl; + +pub use crate::error_ext_impl::*; + +use failure::{format_err, Compat, Error, Fail}; +use std::fmt; + +/// The `LambdaErrorExt` trait defines the `error_type()` method used +/// by the AWS Lambda runtime client to generate `ErrorResponse` +/// objects. The value returned by the `error_type()` method is used to +/// populate the `errorType` field in the Lambda response. This crate +/// includes an implementation of this trait for most errors in the +/// standard library. By default, error return their type name. +pub trait LambdaErrorExt { + /// The value for this field should be an alphanumeric unique identifier + /// of the error type. For example `MyCustomError`. + /// + /// # Return + /// An alphanumeric identifier for the error + fn error_type(&self) -> &str; +} + +impl LambdaErrorExt for Error { + fn error_type(&self) -> &str { + self.find_root_cause().name().unwrap_or_else(|| "FailureError") + } +} + +// We implement this trait here so that we can use the Compat type +// in the lambda-runtime crate - heaps of fun between failure and std::error +impl LambdaErrorExt for Compat { + fn error_type(&self) -> &str { + "CompatFailureError" + } +} + +/// `Result` type extension for AWS that makes it easy to generate a `HandlerError` +/// object or a `Compat` from the failure crate using an existing result. +/// This trait should be imported from the `lambda_runtime_core` or `lambda_runtime` +/// crates. +pub trait LambdaResultExt { + /// Takes the incoming `Result` and maps it to a Result that returns an `HandlerError` object. + /// The `HandlerError` type already includes implementations of the `From` trait for most + /// standard library errors. This method is intended to be used when a the `From` trait is not + /// implemented. + /// + /// # Example + /// + /// ```rust,no_run + /// use lambda_runtime_core::{Context, LambdaResultExt, HandlerError, lambda}; + /// use std::error::Error as StdError; + /// + /// fn main() -> Result<(), Box> { + /// lambda!(my_handler); + /// Ok(()) + /// } + /// + /// fn my_handler(_event: Vec, _ctx: Context) -> Result, HandlerError> { + /// let age = "hello"; // this will throw an error when we try to parse it into an int + /// age.parse::().handler_error()?; + /// + /// Ok(vec!()) + /// } + /// ``` + fn handler_error(self) -> Result; + + /// Takes the incoming result and converts it into an `Error` type from the `failure` crate + /// wrapped in a `Compat` object to make it implement the `Error` trait from the standard + /// library. This method makes it easy to write handler functions that return `Compat` + /// directly. + /// + /// # Example + /// + /// ```rust,no_run + /// use lambda_runtime_core::{Context, LambdaResultExt, lambda}; + /// use failure::{Error, Compat}; + /// use std::error::Error as StdError; + /// + /// fn main() -> Result<(), Box> { + /// lambda!(my_handler); + /// Ok(()) + /// } + /// + /// fn my_handler(_event: Vec, _ctx: Context) -> Result, Compat> { + /// let age = "hello"; // this will throw an error when we try to parse it into an int + /// age.parse::().failure_compat()?; + /// Ok(vec!()) + /// } + /// ``` + fn failure_compat(self) -> Result>; +} + +impl LambdaResultExt for Result +where + ERR: Fail + LambdaErrorExt, +{ + fn handler_error(self) -> Result { + self.map_err(HandlerError::new) + } + + fn failure_compat(self) -> Result> { + self.map_err(|err| Error::from(err).compat()) + } +} + +/// The `HandlerError` struct can be use to abstract any `Err` of the handler method `Result`. +/// The `HandlerError` object can be generated `From` any object that supports `Display`, +/// `Send, `Sync`, and `Debug`. This allows handler functions to return any error using +/// the `?` syntax. For example `let _age_num: u8 = e.age.parse()?;` will return the +/// `::Err` from the handler function. +//pub type HandlerError = failure::Error; +#[derive(Debug)] +pub struct HandlerError { + err_type: String, + inner: failure::Error, +} +impl HandlerError { + pub fn new(e: T) -> Self { + let err_type = e.error_type().to_owned(); + HandlerError { + err_type, + inner: failure::Error::from(e), + } + } +} +impl std::error::Error for HandlerError {} +impl fmt::Display for HandlerError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}: {}", self.err_type, self.inner.find_root_cause()) + } +} +impl LambdaErrorExt for HandlerError { + fn error_type(&self) -> &str { + &self.err_type + } +} +impl From<&str> for HandlerError { + fn from(s: &str) -> Self { + HandlerError { + err_type: "UnknownError".to_owned(), + inner: format_err!("{}", s), + } + } +} +impl From for HandlerError { + fn from(e: failure::Error) -> Self { + let error_type = e.error_type(); + HandlerError { + err_type: error_type.to_owned(), + inner: e, + } + } +} +impl From for HandlerError { + fn from(e: serde_json::error::Error) -> Self { + HandlerError { + err_type: "JsonError".to_owned(), + inner: failure::Error::from(e), + } + } +} + +#[cfg(test)] +pub(crate) mod tests { + use super::*; + use failure::Fail; + + #[derive(Fail, Debug)] + #[fail(display = "Custom Error")] + struct CustomError; + + #[test] + fn std_error_type() { + let parsed_int = "hello".parse::(); + let err = HandlerError::from(parsed_int.err().unwrap()); + assert_eq!(err.error_type(), "std::num::ParseIntError"); + } + + #[test] + fn error_type_from_failure() { + let err = HandlerError::from(failure::Error::from(CustomError {})); + assert_eq!(err.error_type(), "lambda_runtime_errors::tests::CustomError"); + } +} diff --git a/lambda-runtime/Cargo.toml b/lambda-runtime/Cargo.toml index ef1cf7ec..18a3cc67 100644 --- a/lambda-runtime/Cargo.toml +++ b/lambda-runtime/Cargo.toml @@ -19,12 +19,11 @@ maintenance = { status = "actively-developed" } serde = "^1" serde_json = "^1" serde_derive = "^1" +tokio = "0.1" log = "^0.4" -hyper = "^0.12" -tokio = "^0.1" -backtrace = "^0.3" -lambda_runtime_client = { path = "../lambda-runtime-client", version = "^0.1" } -chrono = "^0.4" +lambda_runtime_core = { path = "../lambda-runtime-core", version = "^0.1" } +failure = "^0.1" [dev-dependencies] simple_logger = "^1" +simple-error = "^0.1" diff --git a/lambda-runtime/examples/basic.rs b/lambda-runtime/examples/basic.rs index 81f134a5..1a64f628 100644 --- a/lambda-runtime/examples/basic.rs +++ b/lambda-runtime/examples/basic.rs @@ -3,6 +3,7 @@ use std::error::Error; use lambda_runtime::{error::HandlerError, lambda, Context}; use log::{self, error}; use serde_derive::{Deserialize, Serialize}; +use simple_error::bail; use simple_logger; #[derive(Deserialize)] @@ -17,7 +18,7 @@ struct CustomOutput { } fn main() -> Result<(), Box> { - simple_logger::init_with_level(log::Level::Debug).unwrap(); + simple_logger::init_with_level(log::Level::Debug)?; lambda!(my_handler); Ok(()) @@ -26,7 +27,7 @@ fn main() -> Result<(), Box> { fn my_handler(e: CustomEvent, c: Context) -> Result { if e.first_name == "" { error!("Empty first name in request {}", c.aws_request_id); - return Err(c.new_error("Empty first name")); + bail!("Empty first name"); } Ok(CustomOutput { diff --git a/lambda-runtime/examples/custom_error.rs b/lambda-runtime/examples/custom_error.rs new file mode 100644 index 00000000..35f0c0dd --- /dev/null +++ b/lambda-runtime/examples/custom_error.rs @@ -0,0 +1,70 @@ +use lambda_runtime::{error::LambdaErrorExt, lambda, Context}; +use log::error; +use serde_derive::{Deserialize, Serialize}; +use std::{error::Error, fmt}; + +#[derive(Debug)] +struct CustomError { + msg: String, +} +impl CustomError { + fn new(message: &str) -> CustomError { + CustomError { + msg: message.to_owned(), + } + } +} +impl fmt::Display for CustomError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.msg) + } +} +impl Error for CustomError {} +impl From for CustomError { + fn from(i: std::num::ParseIntError) -> Self { + CustomError::new(&format!("{}", i)) + } +} +// the value return by the error_type function is included as the +// `errorType` in the AWS Lambda response +impl LambdaErrorExt for CustomError { + fn error_type(&self) -> &str { + "CustomError" + } +} + +#[derive(Deserialize)] +struct CustomEvent { + #[serde(rename = "firstName")] + first_name: String, + age: String, +} + +#[derive(Serialize)] +struct CustomOutput { + message: String, +} + +fn main() -> Result<(), Box> { + simple_logger::init_with_level(log::Level::Debug)?; + lambda!(my_handler); + + Ok(()) +} + +fn my_handler(e: CustomEvent, c: Context) -> Result { + if e.first_name == "" { + error!("Empty first name in request {}", c.aws_request_id); + // in this case, we explicitly initialize and box our custom error type. + // the HandlerError type is an alias to Box/ + return Err(CustomError::new("Empty first name")); + } + + // For errors simply want to return, because the HandlerError is an alias to any + // generic error type, we can propapgate with the standard "?" syntax. + let _age_num: u8 = e.age.parse()?; + + Ok(CustomOutput { + message: format!("Hello, {}!", e.first_name), + }) +} diff --git a/lambda-runtime/examples/custom_error_failure.rs b/lambda-runtime/examples/custom_error_failure.rs new file mode 100644 index 00000000..8e8c33e8 --- /dev/null +++ b/lambda-runtime/examples/custom_error_failure.rs @@ -0,0 +1,51 @@ +use failure::Fail; +use lambda_runtime::{error::LambdaErrorExt, lambda, Context}; +use log::error; +use serde_derive::{Deserialize, Serialize}; +use std::error::Error as StdError; + +#[derive(Fail, Debug)] +#[fail(display = "Custom Error")] +struct CustomError; +impl LambdaErrorExt for CustomError { + fn error_type(&self) -> &str { + "CustomError" + } +} +impl From for CustomError { + fn from(_i: std::num::ParseIntError) -> Self { + CustomError {} + } +} + +#[derive(Deserialize)] +struct CustomEvent { + #[serde(rename = "firstName")] + first_name: String, + age: String, +} + +#[derive(Serialize)] +struct CustomOutput { + message: String, +} + +fn main() -> Result<(), Box> { + simple_logger::init_with_level(log::Level::Debug)?; + lambda!(my_handler); + + Ok(()) +} + +fn my_handler(e: CustomEvent, c: Context) -> Result { + if e.first_name == "" { + error!("Empty first name in request {}", c.aws_request_id); + return Err(CustomError {}); + } + + let _age_num: u8 = e.age.parse()?; + + Ok(CustomOutput { + message: format!("Hello, {}!", e.first_name), + }) +} diff --git a/lambda-runtime/examples/failure_error.rs b/lambda-runtime/examples/failure_error.rs new file mode 100644 index 00000000..b426ecb2 --- /dev/null +++ b/lambda-runtime/examples/failure_error.rs @@ -0,0 +1,38 @@ +use failure::{format_err, Compat, Error}; +use lambda_runtime::{error::LambdaResultExt, lambda, Context}; +use log::error; +use serde_derive::{Deserialize, Serialize}; +use std::error::Error as StdError; + +#[derive(Deserialize)] +struct CustomEvent { + #[serde(rename = "firstName")] + first_name: String, + age: String, +} + +#[derive(Serialize)] +struct CustomOutput { + message: String, +} + +fn main() -> Result<(), Box> { + simple_logger::init_with_level(log::Level::Debug)?; + lambda!(my_handler); + + Ok(()) +} + +fn my_handler(e: CustomEvent, c: Context) -> Result> { + if e.first_name == "" { + error!("Empty first name in request {}", c.aws_request_id); + let err = format_err!("Invalid First Name"); + return Err(err.compat()); + } + + let _age_num: u8 = e.age.parse().failure_compat()?; + + Ok(CustomOutput { + message: format!("Hello, {}!", e.first_name), + }) +} diff --git a/lambda-runtime/examples/with_custom_runtime.rs b/lambda-runtime/examples/with_custom_runtime.rs index 5faaddf1..d0e6a0b7 100644 --- a/lambda-runtime/examples/with_custom_runtime.rs +++ b/lambda-runtime/examples/with_custom_runtime.rs @@ -1,9 +1,9 @@ -use std::error::Error; - use lambda_runtime::{error::HandlerError, lambda, Context}; use log::{self, error}; use serde_derive::{Deserialize, Serialize}; +use simple_error::bail; use simple_logger; +use std::error::Error; use tokio::runtime::Runtime; #[derive(Deserialize, Clone)] @@ -20,7 +20,7 @@ struct CustomOutput { fn main() -> Result<(), Box> { let rt = Runtime::new()?; - simple_logger::init_with_level(log::Level::Debug).unwrap(); + simple_logger::init_with_level(log::Level::Debug)?; lambda!(my_handler, rt); Ok(()) @@ -29,7 +29,7 @@ fn main() -> Result<(), Box> { fn my_handler(e: CustomEvent, c: Context) -> Result { if e.first_name == "" { error!("Empty first name in request {}", c.aws_request_id); - return Err(c.new_error("Empty first name")); + bail!("Empty first name"); } Ok(CustomOutput { diff --git a/lambda-runtime/src/error.rs b/lambda-runtime/src/error.rs deleted file mode 100644 index 975d7919..00000000 --- a/lambda-runtime/src/error.rs +++ /dev/null @@ -1,199 +0,0 @@ -//! The error module defines the error types that can be returned -//! by custom handlers as well as the runtime itself. -use std::{cmp, env, error::Error, fmt}; - -use backtrace; -use lambda_runtime_client::error; -use serde_json; - -/// The `RuntimeError` object is returned by the custom runtime as it polls -/// for new events and tries to execute the handler function. The error -/// is primarily used by other methods within this crate and should not be relevant -/// to developers building Lambda functions. Handlers are expected to return -/// the `HandlerError` defined in this module. -#[derive(Debug, Clone)] -pub struct RuntimeError { - msg: String, - stack_trace: Option, - /// The request id that generated this error - pub(crate) request_id: Option, - /// Whether the error is recoverable or not. - pub(crate) recoverable: bool, -} - -impl RuntimeError { - /// Creates a new `RuntimeError` that is unrecoverable and it will cause the - /// runtime to panic in order to force a restart of the execution environment. - /// When a new `RuntimeError` is created the stack trace for the error is collected - /// automatically using the `backtrace` crate. - /// - /// # Arguments - /// - /// * `msg` The error message to be attached to the error. - /// - /// # Returns - /// A new `RuntimeError` instance with the `recoverable` property set to `false`. - pub(crate) fn unrecoverable(msg: &str) -> RuntimeError { - let mut new_error = RuntimeError::new(msg); - new_error.recoverable = false; - new_error - } - - /// Creates a new `RuntimeError` with the given properties. The stack trace for the - /// error is collected automatically using the `backtrace` crate. - /// - /// # Arguments - /// - /// * `msg` The error message - /// - /// # Returns - /// A new `RuntimeError` instance. - pub(crate) fn new(msg: &str) -> RuntimeError { - let mut trace: Option = None; - let is_backtrace = env::var("RUST_BACKTRACE"); - if is_backtrace.is_ok() && is_backtrace.unwrap() == "1" { - trace!("Begin backtrace collection"); - trace = Option::from(backtrace::Backtrace::new()); - trace!("Completed backtrace collection"); - } - RuntimeError { - msg: String::from(msg), - stack_trace: trace, - recoverable: true, - request_id: None, - } - } -} - -impl error::RuntimeApiError for RuntimeError { - fn to_response(&self) -> error::ErrorResponse { - let backtrace = format!("{:?}", self.stack_trace); - error::ErrorResponse { - error_message: String::from(self.description()), - error_type: String::from(error::ERROR_TYPE_HANDLED), - stack_trace: Option::from(backtrace.lines().map(|s| s.to_string()).collect::>()), - } - } -} - -impl fmt::Display for RuntimeError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}", self.msg) - } -} - -// This is important for other errors to wrap this one. -impl Error for RuntimeError { - fn description(&self) -> &str { - &self.msg - } - - fn cause(&self) -> Option<&dyn Error> { - // Generic error, underlying cause isn't tracked. - None - } -} - -impl From for RuntimeError { - fn from(e: env::VarError) -> Self { - RuntimeError::unrecoverable(e.description()) - } -} - -impl From for RuntimeError { - fn from(e: serde_json::Error) -> Self { - RuntimeError::unrecoverable(e.description()) - } -} - -impl From for RuntimeError { - fn from(e: error::ApiError) -> Self { - let mut err = RuntimeError::new(e.description()); - err.recoverable = e.recoverable; - err.stack_trace = e.backtrace; - err - } -} - -/// The error type for functions that are used as the `Handler` type. New errors -/// should be instantiated using the `new_error()` method of the `runtime::Context` -/// object passed to the handler function. -/// -/// An implementation of `PartialEq` is provided and based it's comparison on the `msg` -/// field. -#[derive(Debug, Clone)] -pub struct HandlerError { - msg: String, - backtrace: Option, -} - -impl cmp::PartialEq for HandlerError { - fn eq(&self, other: &HandlerError) -> bool { - self.msg == other.msg - } -} - -impl fmt::Display for HandlerError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}", self.msg) - } -} - -// This is important for other errors to wrap this one. -impl Error for HandlerError { - fn description(&self) -> &str { - &self.msg - } - - fn cause(&self) -> Option<&dyn Error> { - // Generic error, underlying cause isn't tracked. - None - } -} - -impl HandlerError { - /// Creates a new handler error. This method is used by the `new_error()` method - /// of the `runtime::Context` object. - /// - /// # Arguments - /// - /// * `msg` The error message for the new error - /// * `trace` A `Backtrace` object to generate the stack trace for the error - /// response. This is provided by the `Context` object. - pub(crate) fn new(msg: &str, trace: Option) -> HandlerError { - HandlerError { - msg: msg.to_string(), - backtrace: trace, - } - } -} - -impl error::RuntimeApiError for HandlerError { - fn to_response(&self) -> error::ErrorResponse { - let backtrace = format!("{:?}", self.backtrace); - error::ErrorResponse { - error_message: String::from(self.description()), - error_type: String::from(error::ERROR_TYPE_HANDLED), - stack_trace: Option::from(backtrace.lines().map(|s| s.to_string()).collect::>()), - } - } -} - -#[cfg(test)] -mod tests { - use super::HandlerError; - - #[test] - fn handler_error_impls_partialeq() { - assert_eq!( - HandlerError { - msg: "test".into(), - backtrace: Default::default() - }, - HandlerError { - msg: "test".into(), - backtrace: Some(Default::default()) - } - ) - } -} diff --git a/lambda-runtime/src/lib.rs b/lambda-runtime/src/lib.rs index 5bf22c1d..b8e0a597 100644 --- a/lambda-runtime/src/lib.rs +++ b/lambda-runtime/src/lib.rs @@ -7,13 +7,9 @@ //! package must be called `bootstrap`. //! //! ```rust,no_run -//! #[macro_use] -//! extern crate serde_derive; -//! #[macro_use] -//! extern crate lambda_runtime; -//! -//! use lambda_runtime::error::HandlerError; -//! +//! use lambda_runtime::{error::HandlerError, lambda, Context}; +//! use simple_error::bail; +//! use serde_derive::{Serialize, Deserialize}; //! //! #[derive(Deserialize, Clone)] //! struct CustomEvent { @@ -30,57 +26,194 @@ //! lambda!(my_handler); //! } //! -//! fn my_handler(e: CustomEvent, ctx: lambda_runtime::Context) -> Result { +//! fn my_handler(e: CustomEvent, ctx: Context) -> Result { //! if e.first_name == "" { -//! return Err(ctx.new_error("Missing first name!")); +//! bail!("Empty first name"); //! } //! Ok(CustomOutput{ //! message: format!("Hello, {}!", e.first_name), //! }) //! } //! ``` -//! -//! You can also provide a closure directly to the `lambda!` macro -//! -//! ```rust,no_run -//! #[macro_use] -//! extern crate serde_derive; -//! #[macro_use] -//! extern crate lambda_runtime; -//! -//! use lambda_runtime::{Context, error::HandlerError}; -//! -//! -//! #[derive(Deserialize, Clone)] -//! struct CustomEvent { -//! first_name: String, -//! last_name: String, -//! } -//! -//! #[derive(Serialize, Clone)] -//! struct CustomOutput { -//! message: String, -//! } -//! -//! fn main() { -//! lambda!( -//! |e: CustomEvent, ctx: Context| { -//! if e.first_name == "" { -//! return Err(ctx.new_error("Missing first name!")); -//! } -//! Ok(CustomOutput{ -//! message: format!("Hello, {}!", e.first_name), -//! }) -//! } -//! ); -//! } -//! ``` -#[macro_use] -extern crate log; +use failure::Fail; +use lambda_runtime_core::{start_with_config, EnvConfigProvider, HandlerError, LambdaErrorExt}; +use serde; +use serde_json; +use std::fmt::Display; +use tokio::runtime::Runtime as TokioRuntime; + +pub use lambda_runtime_core::Context; + +/// The error module exposes the HandlerError type. +pub mod error { + pub use lambda_runtime_core::{HandlerError, LambdaErrorExt, LambdaResultExt}; +} + +/// Functions acting as a handler must conform to this type. +pub trait Handler { + /// Method to execute the handler function + fn run(&mut self, event: Event, ctx: Context) -> Result; +} + +/// Implementation of the `Handler` trait for both function pointers +/// and closures. +impl Handler for Function +where + Function: FnMut(Event, Context) -> Result, + EventError: Fail + LambdaErrorExt + Display + Send + Sync, +{ + fn run(&mut self, event: Event, ctx: Context) -> Result { + (*self)(event, ctx) + } +} + +/// Wraps a typed handler into a closure that complies with the `Handler` trait +/// defined in the `lambda_runtime_core` crate. The closure simply uses `serde_json` +/// to serialize and deserialize the incoming event from a `Vec` and the output +/// to a `Vec`. +fn wrap( + mut h: impl Handler, +) -> impl FnMut(Vec, Context) -> Result, HandlerError> +where + Event: serde::de::DeserializeOwned, + Output: serde::Serialize, + EventError: Fail + LambdaErrorExt + Display + Send + Sync, +{ + move |ev, ctx| { + let event: Event = serde_json::from_slice(&ev)?; + match h.run(event, ctx) { + Ok(out) => { + let out_bytes = serde_json::to_vec(&out)?; + Ok(out_bytes) + } + Err(e) => Err(HandlerError::new(e)), + } + } +} + +/// Creates a new runtime and begins polling for events using Lambda's Runtime APIs. +/// +/// # Arguments +/// +/// * `f` A function pointer that conforms to the `Handler` type. +/// +/// # Panics +/// The function panics if the Lambda environment variables are not set. +pub fn start(f: impl Handler, runtime: Option) +where + Event: serde::de::DeserializeOwned, + Output: serde::Serialize, + EventError: Fail + LambdaErrorExt + Display + Send + Sync, +{ + let wrapped = wrap(f); + start_with_config(wrapped, &EnvConfigProvider::default(), runtime) +} + +/// Initializes the Lambda runtime with the given handler. Optionally this macro can +/// also receive a customized instance of Tokio runtime to drive internal lambda operations +/// to completion +#[macro_export] +macro_rules! lambda { + ($handler:ident) => { + $crate::start($handler, None) + }; + ($handler:ident, $runtime:expr) => { + $crate::start($handler, Some($runtime)) + }; + ($handler:expr) => { + $crate::start($handler, None) + }; + ($handler:expr, $runtime:expr) => { + $crate::start($handler, Some($runtime)) + }; +} + +#[cfg(test)] +pub(crate) mod tests { + use super::*; + use lambda_runtime_core::Context; + use serde_derive::{Deserialize, Serialize}; + use serde_json; + + fn test_context() -> Context { + Context { + memory_limit_in_mb: 128, + function_name: "test_func".to_string(), + function_version: "$LATEST".to_string(), + invoked_function_arn: "arn:aws:lambda".to_string(), + aws_request_id: "123".to_string(), + xray_trace_id: "123".to_string(), + log_stream_name: "logStream".to_string(), + log_group_name: "logGroup".to_string(), + client_context: Option::default(), + identity: Option::default(), + deadline: 0, + } + } + + #[derive(Serialize, Deserialize)] + struct Input { + name: String, + } + + #[derive(Serialize, Deserialize)] + struct Output { + msg: String, + } -mod context; -mod env; -pub mod error; -mod runtime; + #[test] + fn runtime_invokes_handler() { + let handler_ok = |_e: Input, _c: Context| -> Result { + Ok(Output { + msg: "hello".to_owned(), + }) + }; + let mut wrapped_ok = wrap(handler_ok); + let input = Input { + name: "test".to_owned(), + }; + let output = wrapped_ok.run( + serde_json::to_vec(&input).expect("Could not convert input to Vec"), + test_context(), + ); + assert_eq!( + output.is_err(), + false, + "Handler threw an unexpected error: {}", + output.err().unwrap() + ); + let output_obj: Output = serde_json::from_slice(&output.ok().unwrap()).expect("Could not serialize output"); + assert_eq!( + output_obj.msg, + "hello".to_owned(), + "Unexpected output message: {}", + output_obj.msg + ); + } -pub use crate::{context::*, error::HandlerError, runtime::*}; + #[test] + fn runtime_captures_error() { + let handler_ok = |e: Input, _c: Context| -> Result { + let _age = e.name.parse::()?; + Ok(Output { + msg: "hello".to_owned(), + }) + }; + let mut wrapped_ok = wrap(handler_ok); + let input = Input { + name: "test".to_owned(), + }; + let output = wrapped_ok.run( + serde_json::to_vec(&input).expect("Could not convert input to Vec"), + test_context(), + ); + assert_eq!(output.is_err(), true, "Handler did not throw an error"); + let err = output.err().unwrap(); + assert_eq!( + err.error_type(), + "std::num::ParseIntError", + "Unexpected error_type: {}", + err.error_type() + ); + } +}