diff --git a/.gitignore b/.gitignore index 911cf87c..94f3d847 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ /target /.cargo lambda-runtime/libtest.rmeta +Cargo.lock # Built AWS Lambda zipfile lambda.zip diff --git a/Cargo.lock b/Cargo.lock deleted file mode 100644 index 5cd113cd..00000000 --- a/Cargo.lock +++ /dev/null @@ -1,890 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -[[package]] -name = "arc-swap" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "async-stream" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "async-stream-impl 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "async-stream-impl" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "autocfg" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "base64" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "bitflags" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "bytes" -version = "0.5.4" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "cfg-if" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "dtoa" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "fnv" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "fuchsia-zircon" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "fuchsia-zircon-sys" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "futures" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "futures-channel 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-executor 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-io 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-sink 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-task 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-util 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "futures-channel" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "futures-core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-sink 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "futures-core" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "futures-executor" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "futures-core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-task 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-util 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "futures-io" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "futures-macro" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "proc-macro-hack 0.5.11 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "futures-sink" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "futures-task" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "futures-util" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "futures-channel 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-io 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-macro 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-sink 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-task 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "memchr 2.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "pin-utils 0.1.0-alpha.4 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro-hack 0.5.11 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro-nested 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "h2" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "bytes 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)", - "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-sink 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-util 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "http 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "indexmap 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-util 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "hermit-abi" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "http" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "bytes 0.5.4 (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.5 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "http-body" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "bytes 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)", - "http 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "httparse" -version = "1.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "hyper" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "bytes 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-channel 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-util 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "h2 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "http 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "http-body 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "httparse 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "itoa 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", - "pin-project 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", - "tower-service 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "want 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "idna" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-normalization 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "indexmap" -version = "1.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "iovec" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "itoa" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "kernel32-sys" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "lambda" -version = "0.1.0" -dependencies = [ - "async-stream 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "bytes 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "http 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "hyper 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)", - "lambda-attributes 0.1.0", - "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", - "tower-service 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "tracing 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", - "tracing-error 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "tracing-futures 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "lambda-attributes" -version = "0.1.0" -dependencies = [ - "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "lambda_http" -version = "0.2.0-beta.1" -dependencies = [ - "base64 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)", - "http 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "lambda 0.1.0", - "lambda-attributes 0.1.0", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "maplit 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_urlencoded 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "lazy_static" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "libc" -version = "0.2.66" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "log" -version = "0.4.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "maplit" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "matches" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "memchr" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "mio" -version = "0.6.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "iovec 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (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.2 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "mio-named-pipes" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "mio 0.6.21 (registry+https://github.com/rust-lang/crates.io-index)", - "miow 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "mio-uds" -version = "0.6.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "iovec 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", - "mio 0.6.21 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "miow" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", - "ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "miow" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "socket2 0.3.11 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "net2" -version = "0.2.33" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "num_cpus" -version = "1.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "hermit-abi 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "percent-encoding" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "pin-project" -version = "0.4.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "pin-project-internal 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "pin-project-internal" -version = "0.4.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "pin-project-lite" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "pin-utils" -version = "0.1.0-alpha.4" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "proc-macro-hack" -version = "0.5.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "proc-macro-nested" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "proc-macro2" -version = "0.4.30" -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)", -] - -[[package]] -name = "proc-macro2" -version = "1.0.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "quote" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "redox_syscall" -version = "0.1.56" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "ryu" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "serde" -version = "1.0.104" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "serde_derive 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "serde_derive" -version = "1.0.104" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "serde_json" -version = "1.0.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "itoa 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", - "ryu 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "serde_urlencoded" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "dtoa 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", - "itoa 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", - "url 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "sharded-slab" -version = "0.0.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "signal-hook-registry" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "arc-swap 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "slab" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "smallvec" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "socket2" -version = "0.3.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", - "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "syn" -version = "1.0.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "time" -version = "0.1.42" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", - "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "tokio" -version = "0.2.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "bytes 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)", - "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "iovec 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", - "memchr 2.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "mio 0.6.21 (registry+https://github.com/rust-lang/crates.io-index)", - "mio-named-pipes 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "mio-uds 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)", - "num_cpus 1.12.0 (registry+https://github.com/rust-lang/crates.io-index)", - "pin-project-lite 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "signal-hook-registry 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-macros 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "tokio-macros" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "tokio-util" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "bytes 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-sink 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "pin-project-lite 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "tower-service" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "tracing" -version = "0.1.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "tracing-attributes 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", - "tracing-core 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "tracing-attributes" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "tracing-core" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "tracing-error" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "tracing 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", - "tracing-subscriber 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "tracing-futures" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "pin-project 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "tracing 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "tracing-subscriber" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "sharded-slab 0.0.9 (registry+https://github.com/rust-lang/crates.io-index)", - "tracing-core 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "try-lock" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "unicode-bidi" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "unicode-normalization" -version = "0.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "smallvec 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "unicode-xid" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "unicode-xid" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "url" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "idna 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", - "percent-encoding 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "want" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "try-lock 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "winapi" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "winapi" -version = "0.3.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "winapi-build" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "ws2_32-sys" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[metadata] -"checksum arc-swap 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "d7b8a9123b8027467bce0099fe556c628a53c8d83df0507084c31e9ba2e39aff" -"checksum async-stream 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "22068c0c19514942eefcfd4daf8976ef1aad84e61539f95cd200c35202f80af5" -"checksum async-stream-impl 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "25f9db3b38af870bf7e5cc649167533b493928e50744e2c30ae350230b414670" -"checksum autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d" -"checksum base64 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)" = "53d1ccbaf7d9ec9537465a97bf19edc1a4e158ecb49fc16178202238c569cc42" -"checksum bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" -"checksum bytes 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)" = "130aac562c0dd69c56b3b1cc8ffd2e17be31d0b6c25b61c96b76231aa23e39e1" -"checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" -"checksum dtoa 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "4358a9e11b9a09cf52383b451b49a169e8d797b68aa02301ff586d70d9661ea3" -"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.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b6f16056ecbb57525ff698bb955162d0cd03bee84e6241c27ff75c08d8ca5987" -"checksum futures-channel 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "fcae98ca17d102fd8a3603727b9259fcf7fa4239b603d2142926189bc8999b86" -"checksum futures-core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "79564c427afefab1dfb3298535b21eda083ef7935b4f0ecbfcb121f0aec10866" -"checksum futures-executor 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1e274736563f686a837a0568b478bdabfeaec2dca794b5649b04e2fe1627c231" -"checksum futures-io 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e676577d229e70952ab25f3945795ba5b16d63ca794ca9d2c860e5595d20b5ff" -"checksum futures-macro 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "52e7c56c15537adb4f76d0b7a76ad131cb4d2f4f32d3b0bcabcbe1c7c5e87764" -"checksum futures-sink 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "171be33efae63c2d59e6dbba34186fe0d6394fb378069a76dfd80fdcffd43c16" -"checksum futures-task 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0bae52d6b29cf440e298856fec3965ee6fa71b06aa7495178615953fd669e5f9" -"checksum futures-util 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c0d66274fb76985d3c62c886d1da7ac4c0903a8c9f754e8fe0f35a6a6cc39e76" -"checksum h2 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b9433d71e471c1736fd5a61b671fc0b148d7a2992f666c958d03cd8feb3b88d1" -"checksum hermit-abi 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "eff2656d88f158ce120947499e971d743c05dbcbed62e5bd2f38f1698bbc3772" -"checksum http 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b708cc7f06493459026f53b9a61a7a121a5d1ec6238dee58ea4941132b30156b" -"checksum http-body 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "13d5ff830006f7646652e057693569bfe0d51760c0085a071769d142a205111b" -"checksum httparse 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "cd179ae861f0c2e53da70d892f5f3029f9594be0c41dc5269cd371691b1dc2f9" -"checksum hyper 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8bf49cfb32edee45d890537d9057d1b02ed55f53b7b6a30bae83a38c9231749e" -"checksum idna 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "02e2673c30ee86b5b96a9cb52ad15718aa1f966f5ab9ad54a8b95d5ca33120a9" -"checksum indexmap 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0b54058f0a6ff80b6803da8faf8997cde53872b38f4023728f6830b06cd3c0dc" -"checksum iovec 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "b2b3ea6ff95e175473f8ffe6a7eb7c00d054240321b84c57051175fe3c1e075e" -"checksum itoa 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "b8b7a7c0c47db5545ed3fef7468ee7bb5b74691498139e4b3f6a20685dc6dd8e" -"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" -"checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" -"checksum libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)" = "d515b1f41455adea1313a4a2ac8a8a477634fbae63cc6100e3aebb207ce61558" -"checksum log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7" -"checksum maplit 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d" -"checksum matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" -"checksum memchr 2.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3197e20c7edb283f87c071ddfc7a2cca8f8e0b888c242959846a6fce03c72223" -"checksum mio 0.6.21 (registry+https://github.com/rust-lang/crates.io-index)" = "302dec22bcf6bae6dfb69c647187f4b4d0fb6f535521f7bc022430ce8e12008f" -"checksum mio-named-pipes 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "f5e374eff525ce1c5b7687c4cef63943e7686524a387933ad27ca7ec43779cb3" -"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 miow 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "396aa0f2003d7df8395cb93e09871561ccc3e785f0acb369170e8cc74ddf9226" -"checksum net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)" = "42550d9fb7b6684a6d404d9fa7250c2eb2646df731d1c06afc06dcee9e1bcf88" -"checksum num_cpus 1.12.0 (registry+https://github.com/rust-lang/crates.io-index)" = "46203554f085ff89c235cd12f7075f3233af9b11ed7c9e16dfe2560d03313ce6" -"checksum percent-encoding 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" -"checksum pin-project 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "7804a463a8d9572f13453c516a5faea534a2403d7ced2f0c7e100eeff072772c" -"checksum pin-project-internal 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "385322a45f2ecf3410c68d2a549a4a2685e8051d0f278e39743ff4e451cb9b3f" -"checksum pin-project-lite 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "237844750cfbb86f67afe27eee600dfbbcb6188d734139b534cbfbf4f96792ae" -"checksum pin-utils 0.1.0-alpha.4 (registry+https://github.com/rust-lang/crates.io-index)" = "5894c618ce612a3fa23881b152b608bafb8c56cfc22f434a3ba3120b40f7b587" -"checksum proc-macro-hack 0.5.11 (registry+https://github.com/rust-lang/crates.io-index)" = "ecd45702f76d6d3c75a80564378ae228a85f0b59d2f3ed43c91b4a69eb2ebfc5" -"checksum proc-macro-nested 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "369a6ed065f249a159e06c45752c780bda2fb53c995718f9e484d08daa9eb42e" -"checksum proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)" = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759" -"checksum proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)" = "3acb317c6ff86a4e579dfa00fc5e6cca91ecbb4e7eb2df0468805b674eb88548" -"checksum quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "053a8c8bcc71fcce321828dc897a98ab9760bef03a4fc36693c231e5b3216cfe" -"checksum redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)" = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84" -"checksum ryu 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "bfa8506c1de11c9c4e4c38863ccbe02a305c8188e85a05a784c9e11e1c3910c8" -"checksum serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)" = "414115f25f818d7dfccec8ee535d76949ae78584fc4f79a6f45a904bf8ab4449" -"checksum serde_derive 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)" = "128f9e303a5a29922045a830221b8f78ec74a5f544944f3d5984f8ec3895ef64" -"checksum serde_json 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)" = "eab8f15f15d6c41a154c1b128a22f2dfabe350ef53c40953d84e36155c91192b" -"checksum serde_urlencoded 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9ec5d77e2d4c73717816afac02670d5c4f534ea95ed430442cad02e7a6e32c97" -"checksum sharded-slab 0.0.9 (registry+https://github.com/rust-lang/crates.io-index)" = "06d5a3f5166fb5b42a5439f2eee8b9de149e235961e3eb21c5808fc3ea17ff3e" -"checksum signal-hook-registry 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "94f478ede9f64724c5d173d7bb56099ec3e2d9fc2774aac65d34b8b890405f41" -"checksum slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" -"checksum smallvec 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c7cb5678e1615754284ec264d9bb5b4c27d2018577fd90ac0ceb578591ed5ee4" -"checksum socket2 0.3.11 (registry+https://github.com/rust-lang/crates.io-index)" = "e8b74de517221a2cb01a53349cf54182acdc31a074727d3079068448c0676d85" -"checksum syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)" = "af6f3550d8dff9ef7dc34d384ac6f107e5d31c8f57d9f28e0081503f547ac8f5" -"checksum time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "db8dcfca086c1143c9270ac42a2bbd8a7ee477b78ac8e45b19abfb0cbede4b6f" -"checksum tokio 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "8fdd17989496f49cdc57978c96f0c9fe5e4a58a8bddc6813c449a4624f6a030b" -"checksum tokio-macros 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "f4b1e7ed7d5d4c2af3d999904b0eebe76544897cdbfb2b9684bed2174ab20f7c" -"checksum tokio-util 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "571da51182ec208780505a32528fc5512a8fe1443ab960b3f2f3ef093cd16930" -"checksum tower-service 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e987b6bf443f4b5b3b6f38704195592cca41c5bb7aedd3c3693c7081f8289860" -"checksum tracing 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "1721cc8cf7d770cc4257872507180f35a4797272f5962f24c806af9e7faf52ab" -"checksum tracing-attributes 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "7fbad39da2f9af1cae3016339ad7f2c7a9e870f12e8fd04c4fd7ef35b30c0d2b" -"checksum tracing-core 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "0aa83a9a47081cd522c09c81b31aec2c9273424976f922ad61c053b58350b715" -"checksum tracing-error 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b4d7c0b83d4a500748fa5879461652b361edf5c9d51ede2a2ac03875ca185e24" -"checksum tracing-futures 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "ab7bb6f14721aa00656086e9335d363c5c8747bae02ebe32ea2c7dece5689b4c" -"checksum tracing-subscriber 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "1d53c40489aa69c9aed21ff483f26886ca8403df33bdc2d2f87c60c1617826d2" -"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.12 (registry+https://github.com/rust-lang/crates.io-index)" = "5479532badd04e128284890390c1e876ef7a993d0570b3597ae43dfa1d59afa4" -"checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" -"checksum unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" -"checksum url 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "829d4a8476c35c9bf0bbce5a3b23f4106f79728039b726d292bb93bc106787cb" -"checksum want 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" -"checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" -"checksum winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6" -"checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" -"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" -"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" -"checksum ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e" diff --git a/lambda-http/Cargo.toml b/lambda-http/Cargo.toml index 125b1714..e969ef7b 100644 --- a/lambda-http/Cargo.toml +++ b/lambda-http/Cargo.toml @@ -20,8 +20,7 @@ base64 = "0.12" http = "0.2" lambda = { path = "../lambda", version = "0.1" } lambda-attributes = { path = "../lambda-attributes", version = "0.1" } -serde = "^1" -serde_derive = "^1" +serde = { version = "^1", features = ["derive"] } serde_json = "^1" serde_urlencoded = "0.6" diff --git a/lambda-http/src/ext.rs b/lambda-http/src/ext.rs index 3257bccf..9107447e 100644 --- a/lambda-http/src/ext.rs +++ b/lambda-http/src/ext.rs @@ -1,10 +1,9 @@ //! Extension methods for `http::Request` types +use crate::{request::RequestContext, strmap::StrMap, Body}; use serde::{de::value::Error as SerdeError, Deserialize}; use std::{error::Error, fmt}; -use crate::{request::RequestContext, strmap::StrMap, Body}; - /// ALB/API gateway pre-parsed http query string parameters pub(crate) struct QueryStringParameters(pub(crate) StrMap); @@ -68,7 +67,7 @@ impl Error for PayloadError { /// /// ```rust,no_run /// use lambda_http::{handler, lambda::{self, Context}, Body, IntoResponse, Request, Response, RequestExt}; -/// use serde_derive::Deserialize; +/// use serde::Deserialize; /// /// type Error = Box; /// @@ -251,7 +250,7 @@ impl RequestExt for http::Request { #[cfg(test)] mod tests { use crate::{Body, Request, RequestExt}; - use serde_derive::Deserialize; + use serde::Deserialize; #[test] fn requests_can_mock_query_string_parameters_ext() { diff --git a/lambda-http/src/lib.rs b/lambda-http/src/lib.rs index f26fa351..5d6fb32e 100644 --- a/lambda-http/src/lib.rs +++ b/lambda-http/src/lib.rs @@ -122,9 +122,9 @@ pub trait Handler: Sized { /// The type of Response this Handler will return type Response: IntoResponse; /// The type of Future this Handler will return - type Fut: Future> + 'static; + type Fut: Future> + Send + Sync + 'static; /// Function used to execute handler behavior - fn call(&mut self, event: Request, context: Context) -> Self::Fut; + fn call(&self, event: Request, context: Context) -> Self::Fut; } /// Adapts a [`Handler`](trait.Handler.html) to the `lambda::run` interface @@ -135,22 +135,22 @@ pub fn handler(handler: H) -> Adapter { /// An implementation of `Handler` for a given closure return a `Future` representing the computed response impl Handler for F where - F: FnMut(Request, Context) -> Fut, + F: Fn(Request, Context) -> Fut, R: IntoResponse, - Fut: Future> + Send + 'static, + Fut: Future> + Send + Sync + 'static, { type Response = R; type Error = Error; type Fut = Fut; - fn call(&mut self, event: Request, context: Context) -> Self::Fut { - (*self)(event, context) + fn call(&self, event: Request, context: Context) -> Self::Fut { + (self)(event, context) } } #[doc(hidden)] pub struct TransformResponse { is_alb: bool, - fut: Pin>>>, + fut: Pin> + Send + Sync>>, } impl Future for TransformResponse @@ -183,7 +183,7 @@ impl Handler for Adapter { type Response = H::Response; type Error = H::Error; type Fut = H::Fut; - fn call(&mut self, event: Request, context: Context) -> Self::Fut { + fn call(&self, event: Request, context: Context) -> Self::Fut { self.handler.call(event, context) } } @@ -191,7 +191,7 @@ impl Handler for Adapter { impl LambdaHandler, LambdaResponse> for Adapter { type Error = H::Error; type Fut = TransformResponse; - fn call(&mut self, event: LambdaRequest<'_>, context: Context) -> Self::Fut { + fn call(&self, event: LambdaRequest<'_>, context: Context) -> Self::Fut { let is_alb = event.is_alb(); let fut = Box::pin(self.handler.call(event.into(), context)); TransformResponse { is_alb, fut } diff --git a/lambda-http/src/request.rs b/lambda-http/src/request.rs index 347cb29e..3444209f 100644 --- a/lambda-http/src/request.rs +++ b/lambda-http/src/request.rs @@ -3,16 +3,17 @@ //! Typically these are exposed via the `request_context` //! request extension method provided by [lambda_http::RequestExt](../trait.RequestExt.html) //! -use serde::de::{Deserialize, Deserializer, Error as DeError, MapAccess, Visitor}; -use serde_derive::Deserialize; -use serde_json::{error::Error as JsonError, Value}; -use std::{borrow::Cow, collections::HashMap, fmt, io::Read, mem}; - use crate::{ body::Body, ext::{PathParameters, QueryStringParameters, StageVariables}, strmap::StrMap, }; +use serde::{ + de::{Deserializer, Error as DeError, MapAccess, Visitor}, + Deserialize, +}; +use serde_json::{error::Error as JsonError, Value}; +use std::{borrow::Cow, collections::HashMap, fmt, io::Read, mem}; /// Internal representation of an Lambda http event from /// ALB, API Gateway REST and HTTP API proxy event perspectives @@ -101,42 +102,65 @@ impl LambdaRequest<'_> { } } +/// See [context-variable-reference](https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-mapping-template-reference.html) for more detail. #[derive(Deserialize, Debug, Clone)] #[serde(rename_all = "camelCase")] pub struct ApiGatewayV2RequestContext { + /// The API owner's AWS account ID. pub account_id: String, + /// The identifier API Gateway assigns to your API. pub api_id: String, + /// The stringified value of the specified key-value pair of the context map returned from an API Gateway Lambda authorizer function. #[serde(default)] pub authorizer: HashMap, + /// The full domain name used to invoke the API. This should be the same as the incoming Host header. pub domain_name: String, + /// The first label of the $context.domainName. This is often used as a caller/customer identifier. pub domain_prefix: String, + /// The HTTP method used. pub http: Http, + /// The ID that API Gateway assigns to the API request. pub request_id: String, + /// Undocumented, could be resourcePath pub route_key: String, + /// The deployment stage of the API request (for example, Beta or Prod). pub stage: String, + /// Undocumented, could be requestTime pub time: String, + /// Undocumented, could be requestTimeEpoch pub time_epoch: usize, } +/// See [context-variable-reference](https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-mapping-template-reference.html) for more detail. #[derive(Deserialize, Debug, Clone)] #[serde(rename_all = "camelCase")] pub struct ApiGatewayRequestContext { - //pub path: String, + /// The API owner's AWS account ID. pub account_id: String, + /// The identifier that API Gateway assigns to your resource. pub resource_id: String, + /// The deployment stage of the API request (for example, Beta or Prod). pub stage: String, + /// The ID that API Gateway assigns to the API request. pub request_id: String, + /// The path to your resource. For example, for the non-proxy request URI of `https://{rest-api-id.execute-api.{region}.amazonaws.com/{stage}/root/child`, The $context.resourcePath value is /root/child. pub resource_path: String, + /// The HTTP method used. Valid values include: DELETE, GET, HEAD, OPTIONS, PATCH, POST, and PUT. pub http_method: String, + /// The stringified value of the specified key-value pair of the context map returned from an API Gateway Lambda authorizer function. #[serde(default)] pub authorizer: HashMap, + /// The identifier API Gateway assigns to your API. pub api_id: String, + /// Cofnito identity information pub identity: Identity, } +/// Elastic load balancer context information #[derive(Deserialize, Debug, Clone)] #[serde(rename_all = "camelCase")] pub struct AlbRequestContext { + /// Elastic load balancer context information pub elb: Elb, } @@ -166,10 +190,17 @@ pub struct Elb { #[serde(rename_all = "camelCase")] pub struct Http { #[serde(deserialize_with = "deserialize_method")] + /// The HTTP method used. Valid values include: DELETE, GET, HEAD, OPTIONS, PATCH, POST, and PUT. pub method: http::Method, + /// The request path. For example, for a non-proxy request URL of + /// `https://{rest-api-id.execute-api.{region}.amazonaws.com/{stage}/root/child`, + /// the $context.path value is `/{stage}/root/child`. pub path: String, + /// The request protocol, for example, HTTP/1.1. pub protocol: String, + /// The source IP address of the TCP connection making the request to API Gateway. pub source_ip: String, + /// The User-Agent header of the API caller. pub user_agent: String, } @@ -177,17 +208,35 @@ pub struct Http { #[derive(Deserialize, Debug, Default, Clone)] #[serde(rename_all = "camelCase")] pub struct Identity { + /// The source IP address of the TCP connection making the request to API Gateway. pub source_ip: String, + /// The Amazon Cognito identity ID of the caller making the request. + /// Available only if the request was signed with Amazon Cognito credentials. pub cognito_identity_id: Option, + /// The Amazon Cognito identity pool ID of the caller making the request. + /// Available only if the request was signed with Amazon Cognito credentials. pub cognito_identity_pool_id: Option, + /// A comma-separated list of the Amazon Cognito authentication providers used by the caller making the request. + /// Available only if the request was signed with Amazon Cognito credentials. pub cognito_authentication_provider: Option, + /// The Amazon Cognito authentication type of the caller making the request. + /// Available only if the request was signed with Amazon Cognito credentials. pub cognito_authentication_type: Option, + /// The AWS account ID associated with the request. pub account_id: Option, + /// The principal identifier of the caller making the request. pub caller: Option, + /// For API methods that require an API key, this variable is the API key associated with the method request. + /// For methods that don't require an API key, this variable is null. pub api_key: Option, + /// Undocumented. Can be the API key ID associated with an API request that requires an API key. + /// The description of `api_key` and `access_key` may actually be reversed. pub access_key: Option, + /// The principal identifier of the user making the request. Used in Lambda authorizers. pub user: Option, + /// The User-Agent header of the API caller. pub user_agent: Option, + /// The Amazon Resource Name (ARN) of the effective user identified after authentication. pub user_arn: Option, } @@ -358,7 +407,7 @@ impl<'a> From> for http::Request { .expect("failed to build request"); // no builder method that sets headers in batch - mem::replace(req.headers_mut(), headers); + let _ = mem::replace(req.headers_mut(), headers); req } @@ -422,7 +471,7 @@ impl<'a> From> for http::Request { } // no builder method that sets headers in batch - mem::replace(req.headers_mut(), multi_value_headers); + let _ = mem::replace(req.headers_mut(), multi_value_headers); req } @@ -483,7 +532,7 @@ impl<'a> From> for http::Request { } // no builder method that sets headers in batch - mem::replace(req.headers_mut(), multi_value_headers); + let _ = mem::replace(req.headers_mut(), multi_value_headers); req } diff --git a/lambda-http/src/response.rs b/lambda-http/src/response.rs index 61406426..bdeee5c1 100644 --- a/lambda-http/src/response.rs +++ b/lambda-http/src/response.rs @@ -1,16 +1,14 @@ //! Response types +use crate::body::Body; use http::{ header::{HeaderMap, HeaderValue, CONTENT_TYPE}, Response, }; use serde::{ ser::{Error as SerError, SerializeMap}, - Serializer, + Serialize, Serializer, }; -use serde_derive::Serialize; - -use crate::body::Body; /// Representation of API Gateway response #[doc(hidden)] diff --git a/lambda-http/src/strmap.rs b/lambda-http/src/strmap.rs index 10801733..281a1101 100644 --- a/lambda-http/src/strmap.rs +++ b/lambda-http/src/strmap.rs @@ -1,14 +1,13 @@ +use serde::{ + de::{MapAccess, Visitor}, + Deserialize, Deserializer, +}; use std::{ collections::{hash_map::Keys, HashMap}, fmt, sync::Arc, }; -use serde::{ - de::{MapAccess, Visitor}, - Deserialize, Deserializer, -}; - /// A read-only view into a map of string data which may contain multiple values /// /// Internally data is always represented as many valued @@ -76,7 +75,7 @@ impl<'a> Iterator for StrMapIter<'a> { /// internal type used when deserializing StrMaps from /// potentially one or many valued maps -#[derive(serde_derive::Deserialize)] +#[derive(Deserialize)] #[serde(untagged)] enum OneOrMany { One(String), diff --git a/lambda/Cargo.toml b/lambda/Cargo.toml index eb718e80..f8322171 100644 --- a/lambda/Cargo.toml +++ b/lambda/Cargo.toml @@ -2,8 +2,11 @@ name = "lambda" version = "0.1.0" authors = ["David Barsky "] -description = "AWS Lambda Runtime." +description = "AWS Lambda Runtime" edition = "2018" +license = "Apache License 2.0" +repository = "https://github.com/awslabs/aws-lambda-rust-runtime" +readme = "../README.md" [features] # no features by default @@ -20,8 +23,13 @@ tower-service = "0.3" bytes = "0.5" http = "0.2" lambda-attributes = { path = "../lambda-attributes", version = "0.1.0", optional = true} +tracing = { version = "0.1", features = ["log"] } +tracing-futures = "0.2" async-stream = "0.2" -futures = "0.3" -tracing = "0.1.13" -tracing-futures = "0.2.3" -tracing-error = "0.1.2" + +[dev-dependencies] +tracing-subscriber = "0.2" +once_cell = "1.4.0" +simple_logger = "1.6.0" +log = "0.4" +simple-error = "0.2" diff --git a/lambda/examples/README.md b/lambda/examples/README.md new file mode 100644 index 00000000..51a5a10b --- /dev/null +++ b/lambda/examples/README.md @@ -0,0 +1,254 @@ + +## How to compile and run the examples + +1. Create a Lambda function called _RuntimeTest_ in AWS with a custom runtime and no code. + +2. Compile all examples + +``` +cargo build --release --target x86_64-unknown-linux-musl --examples +``` +3. Prepare the package for the example you want to test, e.g. +``` +cp ./target/x86_64-unknown-linux-musl/release/examples/hello ./bootstrap && zip lambda.zip bootstrap && rm bootstrap +``` +4. Upload the package to AWS Lambda +``` +aws lambda update-function-code --region us-east-1 --function-name RuntimeTest --zip-file fileb://lambda.zip +``` +_Feel free to replace the names and IDs with your own values._ + +## basic.rs + +**Deployment**: +```bash +cp ./target/x86_64-unknown-linux-musl/release/examples/basic ./bootstrap && zip lambda.zip bootstrap && rm bootstrap +aws lambda update-function-code --region us-east-1 --function-name RuntimeTest --zip-file fileb://lambda.zip +``` + +**Test event JSON (success)**: +```json +{ "command": "do something" } +``` + +Sample response: +```json +{ + "msg": "Command do something executed.", + "req_id": "67a038e4-dc19-4adf-aa32-5ba09312c6ca" +} +``` + +**Test event JSON (error)**: +```json +{ "foo": "do something" } +``` + +Sample response: +```json +{ + "errorType": "Runtime.ExitError", + "errorMessage": "RequestId: 586366df-f271-4e6e-9c30-b3dcab30f4e8 Error: Runtime exited with error: exit status 1" +} +``` +The runtime could not deserialize our invalid input, but it did not give a detailed explanation why the error occurred in the response. More details appear in the CloudWatch log: +``` +START RequestId: 6e667f61-c5d4-4b07-a60f-cd1ab339c35f Version: $LATEST +Error: Error("missing field `command`", line: 1, column: 22) +END RequestId: 6e667f61-c5d4-4b07-a60f-cd1ab339c35f +REPORT RequestId: 6e667f61-c5d4-4b07-a60f-cd1ab339c35f Duration: 36.34 ms Billed Duration: 100 ms Memory Size: 128 MB Max Memory Used: 10 MB +RequestId: 6e667f61-c5d4-4b07-a60f-cd1ab339c35f Error: Runtime exited with error: exit status 1 +Runtime.ExitError +``` + + See _error-handling.rs_ example for more error handling options. + +## macro.rs + +The most basic example using `#[lambda]` macro to reduce the amount of boilerplate code. + +**Deployment**: +```bash +cp ./target/x86_64-unknown-linux-musl/release/examples/macro ./bootstrap && zip lambda.zip bootstrap && rm bootstrap +aws lambda update-function-code --region us-east-1 --function-name RuntimeTest --zip-file fileb://lambda.zip +``` + +**Test event JSON**: +```json +{ "foo": "bar" } +``` + +Sample response: +```json +{ + "foo": "bar" +} +``` + +## error-handling.rs + +Errors are logged by the runtime only if `log` is initialized by the handler. +These examples use `simple_logger`, but you can use any other provider that works with `log`. +``` +simple_logger::init_with_level(log::Level::Debug)?; +``` + +**Deployment**: +```bash +cp ./target/x86_64-unknown-linux-musl/release/examples/error-handling ./bootstrap && zip lambda.zip bootstrap && rm bootstrap +aws lambda update-function-code --region us-east-1 --function-name RuntimeTest --zip-file fileb://lambda.zip +``` + +The following input/output examples correspond to different `match` arms in the handler of `error-handling.rs`. + +#### Invalid event JSON + +Test input: +```json +{ + "event_type": "WrongType" +} +``` + +Lambda output: +``` +{ + "errorType": "alloc::boxed::Box", + "errorMessage": "unknown variant `WrongType`, expected one of `Response`, `ExternalError`, `SimpleError`, `CustomError`" +} +``` + +CloudWatch records: +``` +START RequestId: b98e07c6-e2ba-4ca6-9968-d0b94729ddba Version: $LATEST +2020-07-21 04:28:52,630 ERROR [lambda] unknown variant `WrongType`, expected one of `Response`, `ExternalError`, `SimpleError`, `CustomError` +END RequestId: b98e07c6-e2ba-4ca6-9968-d0b94729ddba +REPORT RequestId: b98e07c6-e2ba-4ca6-9968-d0b94729ddba Duration: 2.06 ms Billed Duration: 100 ms Memory Size: 128 MB Max Memory Used: 28 MB Init Duration: 33.67 ms +``` + +#### A simple text-only error + +Test event JSON: +```json +{ + "event_type": "SimpleError" +} +``` + +Lambda output: +``` +{ + "errorType": "alloc::boxed::Box", + "errorMessage": "A simple error as requested!" +} +``` + +CloudWatch records: +``` +START RequestId: 77c66dbf-bd60-4f77-8453-682d0bceba91 Version: $LATEST +2020-07-21 04:35:28,044 ERROR [lambda] A simple error as requested! +END RequestId: 77c66dbf-bd60-4f77-8453-682d0bceba91 +REPORT RequestId: 77c66dbf-bd60-4f77-8453-682d0bceba91 Duration: 0.98 ms Billed Duration: 100 ms Memory Size: 128 MB Max Memory Used: 28 MB +``` + +#### A custom error with JSON output for Display trait + +Test event JSON: +```json +{ + "event_type": "CustomError" +} +``` + +Lambda output: +``` +{ + "errorType": "alloc::boxed::Box", + "errorMessage": "{\"is_authenticated\":false,\"msg\":\"A custom error as requested!\",\"req_id\":\"b46b0588-1383-4224-bc7a-42b0d61930c1\"}" +} +``` + +CloudWatch records: +``` +START RequestId: b46b0588-1383-4224-bc7a-42b0d61930c1 Version: $LATEST +2020-07-21 04:39:00,133 ERROR [lambda] {"is_authenticated":false,"msg":"A custom error as requested!","req_id":"b46b0588-1383-4224-bc7a-42b0d61930c1"} +END RequestId: b46b0588-1383-4224-bc7a-42b0d61930c1 +REPORT RequestId: b46b0588-1383-4224-bc7a-42b0d61930c1 Duration: 0.91 ms Billed Duration: 100 ms Memory Size: 128 MB Max Memory Used: 29 MB +``` + +#### A 3rd party error from _std::fs::File::open_ + +Test event JSON: +```json +{ + "event_type": "ExternalError" +} +``` + +Lambda output: +``` +{ + "errorType": "alloc::boxed::Box", + "errorMessage": "No such file or directory (os error 2)" +} +``` + +CloudWatch records: +``` +START RequestId: 893d24e5-cb79-4f6f-bae0-36304c62e9da Version: $LATEST +2020-07-21 04:43:56,254 ERROR [lambda] No such file or directory (os error 2) +END RequestId: 893d24e5-cb79-4f6f-bae0-36304c62e9da +REPORT RequestId: 893d24e5-cb79-4f6f-bae0-36304c62e9da Duration: 1.15 ms Billed Duration: 100 ms Memory Size: 128 MB Max Memory Used: 29 MB +``` + +#### Handler panic + +Test event JSON: +```json +{ + "event_type": "Panic" +} +``` + +Lambda output: +``` +{ + "errorType": "Runtime.ExitError", + "errorMessage": "RequestId: 2d579019-07f7-409a-a6e6-af7725253307 Error: Runtime exited with error: exit status 101" +} +``` + +CloudWatch records: +``` +START RequestId: 2d579019-07f7-409a-a6e6-af7725253307 Version: $LATEST +thread 'main' panicked at 'explicit panic', lambda/examples/error-handling.rs:87:13 +note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace +END RequestId: 2d579019-07f7-409a-a6e6-af7725253307 +REPORT RequestId: 2d579019-07f7-409a-a6e6-af7725253307 Duration: 43.40 ms Billed Duration: 100 ms Memory Size: 128 MB Max Memory Used: 27 MB Init Duration: 23.15 ms +RequestId: 2d579019-07f7-409a-a6e6-af7725253307 Error: Runtime exited with error: exit status 101 +Runtime.ExitError +``` + +#### A response to a successful Lambda execution + +Test event JSON: +```json +{ + "event_type": "Response" +} +``` + +Lambda output: +``` +{ + "msg": "OK", + "req_id": "9752a3ad-6566-44e4-aafd-74db1fd4f361" +} +``` + +CloudWatch records: +``` +START RequestId: 9752a3ad-6566-44e4-aafd-74db1fd4f361 Version: $LATEST +END RequestId: 9752a3ad-6566-44e4-aafd-74db1fd4f361 +REPORT RequestId: 9752a3ad-6566-44e4-aafd-74db1fd4f361 Duration: 0.89 ms Billed Duration: 100 ms Memory Size: 128 MB Max Memory Used: 29 MB +``` \ No newline at end of file diff --git a/lambda/examples/basic.rs b/lambda/examples/basic.rs new file mode 100644 index 00000000..2d2ad02d --- /dev/null +++ b/lambda/examples/basic.rs @@ -0,0 +1,53 @@ +// This example requires the following input to succeed: +// { "command": "do something" } + +use lambda::{handler_fn, Context}; +use serde::{Deserialize, Serialize}; +use simple_logger; + +/// A shorthand for `Box` +/// type required by aws-lambda-rust-runtime. +pub type Error = Box; + +/// This is also a made-up example. Requests come into the runtime as unicode +/// strings in json format, which can map to any structure that implements `serde::Deserialize` +/// The runtime pays no attention to the contents of the request payload. +#[derive(Deserialize)] +struct Request { + command: String, +} + +/// This is a made-up example of what a response structure may look like. +/// There is no restriction on what it can be. The runtime requires responses +/// to be serialized into json. The runtime pays no attention +/// to the contents of the response payload. +#[derive(Serialize)] +struct Response { + req_id: String, + msg: String, +} + +#[tokio::main] +async fn main() -> Result<(), Error> { + // required to enable CloudWatch error logging by the runtime + // can be replaced with any other method of initializing `log` + simple_logger::init_with_level(log::Level::Info)?; + + let func = handler_fn(my_handler); + lambda::run(func).await?; + Ok(()) +} + +pub(crate) async fn my_handler(event: Request, ctx: Context) -> Result { + // extract some useful info from the request + let command = event.command; + + // prepare the response + let resp = Response { + req_id: ctx.request_id, + msg: format!("Command {} executed.", command), + }; + + // return `Response` (it will be serialized to JSON automatically by the runtime) + Ok(resp) +} diff --git a/lambda/examples/error-handling.rs b/lambda/examples/error-handling.rs new file mode 100644 index 00000000..a94579a0 --- /dev/null +++ b/lambda/examples/error-handling.rs @@ -0,0 +1,115 @@ +/// See https://github.com/awslabs/aws-lambda-rust-runtime for more info on Rust runtime for AWS Lambda +use lambda::handler_fn; +use serde::{Deserialize, Serialize}; +use serde_json::{json, Value}; +use std::fs::File; + +/// A shorthand for `Box` type required by aws-lambda-rust-runtime. +pub type Error = Box; + +/// A simple Lambda request structure with just one field +/// that tells the Lambda what is expected of it. +#[derive(Deserialize)] +struct Request { + event_type: EventType, +} + +/// Event types that tell our Lambda what to do do. +#[derive(Deserialize, PartialEq)] +enum EventType { + Response, + ExternalError, + SimpleError, + CustomError, + Panic, +} + +/// A simple Lambda response structure. +#[derive(Serialize)] +struct Response { + req_id: String, + msg: String, +} + +#[derive(Debug, Serialize)] +struct CustomError { + is_authenticated: bool, + req_id: String, + msg: String, +} + +impl std::error::Error for CustomError { + // this implementation required `Debug` and `Display` traits +} + +impl std::fmt::Display for CustomError { + /// Display the error struct as a JSON string + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let err_as_json = json!(self).to_string(); + write!(f, "{}", err_as_json) + } +} + +#[tokio::main] +async fn main() -> Result<(), Error> { + // The runtime logging can be enabled here by initializing `log` with `simple_logger` + // or another compatible crate. The runtime is using `tracing` internally. + // You can comment out the `simple_logger` init line and uncomment the following block to + // use `tracing` in the handler function. + // + simple_logger::init_with_level(log::Level::Info)?; + /* + tracing_subscriber::fmt() + .with_max_level(tracing::Level::INFO) + // this needs to be set to false, otherwise ANSI color codes will + // show up in a confusing manner in CloudWatch logs. + .with_ansi(false) + // disabling time is handy because CloudWatch will add the ingestion time. + .without_time() + .init(); + */ + + // call the actual handler of the request + let func = handler_fn(func); + lambda::run(func).await?; + Ok(()) +} + +/// The actual handler of the Lambda request. +pub(crate) async fn func(event: Value, ctx: lambda::Context) -> Result { + // check what action was requested + match serde_json::from_value::(event)?.event_type { + EventType::SimpleError => { + // generate a simple text message error using `simple_error` crate + return Err(Box::new(simple_error::SimpleError::new("A simple error as requested!"))); + } + EventType::CustomError => { + // generate a custom error using our own structure + let cust_err = CustomError { + is_authenticated: ctx.identity.is_some(), + req_id: ctx.request_id, + msg: "A custom error as requested!".into(), + }; + return Err(Box::new(cust_err)); + } + EventType::ExternalError => { + // try to open a non-existent file to get an error and propagate it with `?` + let _file = File::open("non-existent-file.txt")?; + + // it should never execute past the above line + unreachable!(); + } + EventType::Panic => { + panic!(); + } + EventType::Response => { + // generate and return an OK response in JSON format + let resp = Response { + req_id: ctx.request_id, + msg: "OK".into(), + }; + + return Ok(json!(resp)); + } + } +} diff --git a/lambda/examples/hello-without-macro.rs b/lambda/examples/hello-without-macro.rs deleted file mode 100644 index 2aec334f..00000000 --- a/lambda/examples/hello-without-macro.rs +++ /dev/null @@ -1,15 +0,0 @@ -use lambda::{handler_fn, Context}; -use serde_json::Value; - -type Error = Box; - -#[tokio::main] -async fn main() -> Result<(), Error> { - let func = handler_fn(func); - lambda::run(func).await?; - Ok(()) -} - -async fn func(event: Value, _: Context) -> Result { - Ok(event) -} diff --git a/lambda/examples/hello.rs b/lambda/examples/macro.rs similarity index 61% rename from lambda/examples/hello.rs rename to lambda/examples/macro.rs index 99472874..fff7de45 100644 --- a/lambda/examples/hello.rs +++ b/lambda/examples/macro.rs @@ -3,6 +3,10 @@ use serde_json::Value; type Error = Box; +// #[lambda] attribute removes the need for boilerplate code +// required by `lambda::run(func).await?` as demonstrated in other +// examples. + #[lambda] #[tokio::main] async fn main(event: Value, _: Context) -> Result { diff --git a/lambda/src/client.rs b/lambda/src/client.rs index 64313ebf..82b6e64c 100644 --- a/lambda/src/client.rs +++ b/lambda/src/client.rs @@ -1,77 +1,20 @@ -use crate::{ - requests::{IntoResponse, NextEventResponse}, - Error, -}; -use http::{ - uri::{PathAndQuery, Scheme}, - HeaderValue, Method, Request, Response, StatusCode, Uri, -}; -use hyper::{client::HttpConnector, server::conn::Http, service::service_fn, Body}; -use serde_json::json; -use std::convert::TryFrom; -use tokio::{ - io::{AsyncRead, AsyncWrite}, - select, - sync::oneshot, -}; -use tracing::{error, info, instrument}; - -#[instrument] -async fn hello(req: Request) -> Result, Error> { - Ok(Response::new(Body::from("hello"))) -} - -async fn handle_incoming(req: Request) -> Result, Error> { - let path: Vec<&str> = req - .uri() - .path_and_query() - .unwrap() - .as_str() - .split("/") - .collect::>(); - match &path[1..] { - ["2018-06-01", "runtime", "invocation", "next"] => next_event(&req).await, - ["2018-06-01", "runtime", "invocation", id, "response"] => complete_event(&req, id).await, - ["2018-06-01", "runtime", "invocation", id, "error"] => event_err(&req, id).await, - ["2018-06-01", "runtime", "init", "error"] => unimplemented!(), - _ => unimplemented!(), - } -} - -#[instrument(skip(io, rx))] -async fn handle(io: I, rx: oneshot::Receiver<()>) -> Result<(), hyper::error::Error> -where - I: AsyncRead + AsyncWrite + Unpin + 'static, -{ - let conn = Http::new().serve_connection(io, service_fn(handle_incoming)); - select! { - _ = rx => { - info!("Received cancelation signal"); - return Ok(()) - } - res = conn => { - match res { - Ok(()) => return Ok(()), - Err(e) => { - error!(message = "Got error serving connection", e = %e); - return Err(e); - } - } - } - } -} +use crate::Error; +use http::{uri::Scheme, Request, Response, Uri}; +use hyper::{client::HttpConnector, Body}; +use std::fmt::Debug; #[derive(Debug)] pub(crate) struct Client { - base: Uri, - client: hyper::Client, + pub(crate) base: Uri, + pub(crate) client: hyper::Client, } impl Client where C: hyper::client::connect::Connect + Sync + Send + Clone + 'static, { - pub fn with(base: Uri, client: hyper::Client) -> Self { + pub fn with(base: Uri, connector: C) -> Self { + let client = hyper::Client::builder().build(connector); Self { base, client } } @@ -98,104 +41,134 @@ where pub(crate) async fn call(&self, req: Request) -> Result, Error> { let req = self.set_origin(req)?; let (parts, body) = req.into_parts(); - let body = Body::from(body); let req = Request::from_parts(parts, body); let response = self.client.request(req).await?; Ok(response) } } -async fn next_event(req: &Request) -> Result, Error> { - let path = "/2018-06-01/runtime/invocation/next"; - assert_eq!(req.method(), Method::GET); - assert_eq!(req.uri().path_and_query().unwrap(), &PathAndQuery::from_static(path)); - let body = json!({"message": "hello"}); - - let rsp = NextEventResponse { - request_id: "8476a536-e9f4-11e8-9739-2dfe598c3fcd", - deadline: 1_542_409_706_888, - arn: "arn:aws:lambda:us-east-2:123456789012:function:custom-runtime", - trace_id: "Root=1-5bef4de7-ad49b0e87f6ef6c87fc2e700;Parent=9a9197af755a6419", - body: serde_json::to_vec(&body)?, +#[cfg(test)] +mod endpoint_tests { + use crate::{ + client::Client, + incoming, + requests::{ + EventCompletionRequest, EventErrorRequest, IntoRequest, IntoResponse, NextEventRequest, NextEventResponse, + }, + simulated, + types::Diagnostic, + Error, Runtime, + }; + use http::{uri::PathAndQuery, HeaderValue, Method, Request, Response, StatusCode, Uri}; + use hyper::{server::conn::Http, service::service_fn, Body}; + use serde_json::json; + use std::convert::TryFrom; + use tokio::{ + io::{AsyncRead, AsyncWrite}, + select, + stream::StreamExt, + sync::{self, oneshot}, }; - rsp.into_rsp().map_err(|e| e.into()) -} - -async fn complete_event(req: &Request, id: &str) -> Result, Error> { - assert_eq!(Method::POST, req.method()); - let rsp = Response::builder() - .status(StatusCode::ACCEPTED) - .body(Body::empty()) - .expect("Unable to construct response"); - let expected = format!("/2018-06-01/runtime/invocation/{}/response", id); - assert_eq!(expected, req.uri().path()); + #[cfg(test)] + async fn next_event(req: &Request) -> Result, Error> { + let path = "/2018-06-01/runtime/invocation/next"; + assert_eq!(req.method(), Method::GET); + assert_eq!(req.uri().path_and_query().unwrap(), &PathAndQuery::from_static(path)); + let body = json!({"message": "hello"}); + + let rsp = NextEventResponse { + request_id: "8476a536-e9f4-11e8-9739-2dfe598c3fcd", + deadline: 1_542_409_706_888, + arn: "arn:aws:lambda:us-east-2:123456789012:function:custom-runtime", + trace_id: "Root=1-5bef4de7-ad49b0e87f6ef6c87fc2e700;Parent=9a9197af755a6419", + body: serde_json::to_vec(&body)?, + }; + rsp.into_rsp() + } - Ok(rsp) -} + #[cfg(test)] + async fn complete_event(req: &Request, id: &str) -> Result, Error> { + assert_eq!(Method::POST, req.method()); + let rsp = Response::builder() + .status(StatusCode::ACCEPTED) + .body(Body::empty()) + .expect("Unable to construct response"); -async fn event_err(req: &Request, id: &str) -> Result, Error> { - let expected = format!("/2018-06-01/runtime/invocation/{}/error", id); - assert_eq!(expected, req.uri().path()); + let expected = format!("/2018-06-01/runtime/invocation/{}/response", id); + assert_eq!(expected, req.uri().path()); - assert_eq!(req.method(), Method::POST); - let header = "lambda-runtime-function-error-type"; - let expected = "unhandled"; - assert_eq!(req.headers()[header], HeaderValue::try_from(expected)?); + Ok(rsp) + } - let rsp = Response::builder().status(StatusCode::ACCEPTED).body(Body::empty())?; - Ok(rsp) -} + #[cfg(test)] + async fn event_err(req: &Request, id: &str) -> Result, Error> { + let expected = format!("/2018-06-01/runtime/invocation/{}/error", id); + assert_eq!(expected, req.uri().path()); -fn set_origin(base: Uri, req: Request) -> Result, Error> { - let (mut parts, body) = req.into_parts(); - let (scheme, authority) = { - let scheme = base.scheme().unwrap_or(&Scheme::HTTP); - let authority = base.authority().expect("Authority not found"); - (scheme, authority) - }; - let path = parts.uri.path_and_query().expect("PathAndQuery not found"); + assert_eq!(req.method(), Method::POST); + let header = "lambda-runtime-function-error-type"; + let expected = "unhandled"; + assert_eq!(req.headers()[header], HeaderValue::try_from(expected)?); - let uri = Uri::builder() - .scheme(scheme.clone()) - .authority(authority.clone()) - .path_and_query(path.clone()) - .build() - .expect("Unable to build URI"); + let rsp = Response::builder().status(StatusCode::ACCEPTED).body(Body::empty())?; + Ok(rsp) + } - parts.uri = uri; - Ok(Request::from_parts(parts, body)) -} + #[cfg(test)] + async fn handle_incoming(req: Request) -> Result, Error> { + let path: Vec<&str> = req + .uri() + .path_and_query() + .expect("PathAndQuery not found") + .as_str() + .split('/') + .collect::>(); + match path[1..] { + ["2018-06-01", "runtime", "invocation", "next"] => next_event(&req).await, + ["2018-06-01", "runtime", "invocation", id, "response"] => complete_event(&req, id).await, + ["2018-06-01", "runtime", "invocation", id, "error"] => event_err(&req, id).await, + ["2018-06-01", "runtime", "init", "error"] => unimplemented!(), + _ => unimplemented!(), + } + } -#[cfg(test)] -mod endpoint_tests { - use super::{handle, set_origin}; - use crate::{ - requests::{EventCompletionRequest, EventErrorRequest, IntoRequest, NextEventRequest}, - simulated::SimulatedConnector, - types::Diagnostic, - Error, - }; - use http::{HeaderValue, StatusCode, Uri}; - use std::convert::TryFrom; - use tokio::sync; + #[cfg(test)] + async fn handle(io: I, rx: oneshot::Receiver<()>) -> Result<(), hyper::error::Error> + where + I: AsyncRead + AsyncWrite + Unpin + 'static, + { + let conn = Http::new().serve_connection(io, service_fn(handle_incoming)); + select! { + _ = rx => { + Ok(()) + } + res = conn => { + match res { + Ok(()) => Ok(()), + Err(e) => { + Err(e) + } + } + } + } + } #[tokio::test] - async fn next_event() -> Result<(), Error> { - let (client, server) = crate::simulated::chan(); + async fn test_next_event() -> Result<(), Error> { let base = Uri::from_static("http://localhost:9001"); + let (client, server) = crate::simulated::chan(); let (tx, rx) = sync::oneshot::channel(); let server = tokio::spawn(async { handle(server, rx).await.expect("Unable to handle request"); }); - let conn = SimulatedConnector { inner: client }; - let client = hyper::Client::builder().build(conn); + let conn = simulated::Connector { inner: client }; + let client = Client::with(base, conn); let req = NextEventRequest.into_req()?; - let req = set_origin(base, req)?; - let rsp = client.request(req).await.expect("Unable to send request"); + let rsp = client.call(req).await.expect("Unable to send request"); assert_eq!(rsp.status(), StatusCode::OK); let header = "lambda-runtime-deadline-ms"; @@ -211,7 +184,7 @@ mod endpoint_tests { } #[tokio::test] - async fn ok_response() -> Result<(), Error> { + async fn test_ok_response() -> Result<(), Error> { let (client, server) = crate::simulated::chan(); let (tx, rx) = sync::oneshot::channel(); let base = Uri::from_static("http://localhost:9001"); @@ -220,17 +193,16 @@ mod endpoint_tests { handle(server, rx).await.expect("Unable to handle request"); }); - let conn = SimulatedConnector { inner: client }; - let client = hyper::Client::builder().build(conn); + let conn = simulated::Connector { inner: client }; + let client = Client::with(base, conn); let req = EventCompletionRequest { request_id: "156cb537-e2d4-11e8-9b34-d36013741fb9", body: "done", }; let req = req.into_req()?; - let req = set_origin(base, req)?; - let rsp = client.request(req).await?; + let rsp = client.call(req).await?; assert_eq!(rsp.status(), StatusCode::ACCEPTED); // shutdown server @@ -243,7 +215,7 @@ mod endpoint_tests { } #[tokio::test] - async fn error_response() -> Result<(), Error> { + async fn test_error_response() -> Result<(), Error> { let (client, server) = crate::simulated::chan(); let (tx, rx) = sync::oneshot::channel(); let base = Uri::from_static("http://localhost:9001"); @@ -252,8 +224,8 @@ mod endpoint_tests { handle(server, rx).await.expect("Unable to handle request"); }); - let conn = SimulatedConnector { inner: client }; - let client = hyper::Client::builder().build(conn); + let conn = simulated::Connector { inner: client }; + let client = Client::with(base, conn); let req = EventErrorRequest { request_id: "156cb537-e2d4-11e8-9b34-d36013741fb9", @@ -263,8 +235,7 @@ mod endpoint_tests { }, }; let req = req.into_req()?; - let req = set_origin(base, req)?; - let rsp = client.request(req).await?; + let rsp = client.call(req).await?; assert_eq!(rsp.status(), StatusCode::ACCEPTED); // shutdown server @@ -276,54 +247,38 @@ mod endpoint_tests { } } - // #[tokio::test] - // async fn run_end_to_end() -> Result<(), Error> { - // use serde_json::Value; - - // let (client, server) = crate::simulated::chan(); - - // let (tx, rx) = sync::oneshot::channel(); - // let server = tokio::spawn(async move { handle(server, rx) }); - - // async fn handler(s: Value) -> Result { - // INVOCATION_CTX.with(|_ctx| {}); - // Ok(s) - // } - // let handler = handler_fn(handler); - // let client = tokio::spawn(async move { - // run_simulated(handler, &url).await?; - // Ok::<(), Error>(()) - // }); - // race!(client, server); - // Ok(()) - // } - - // #[tokio::test] - // async fn test_stream_handler() -> Result<(), Error> { - // let (client, server) = crate::simulated::chan(); - // let req = Request::builder() - // .method(Method::GET) - // .uri("http://httpbin.org") - // .body(Body::empty()) - // .expect("Can't build request"); - - // let conn = SimulatedConnector { inner: client }; - // let client = hyper::Client::builder().build(conn); - - // let (tx, rx) = sync::oneshot::channel(); - // let server = tokio::spawn(async { - // handle(server, rx).await.expect("Unable to handle request"); - // }); - - // let rsp = client.request(req).await.expect("Unable to send request"); - // assert_eq!(rsp.status(), http::StatusCode::OK); - - // // shutdown server - // tx.send(()).expect("Receiver has been dropped"); - // match server.await { - // Ok(_) => Ok(()), - // Err(e) if e.is_panic() => return Err::<(), anyhow::Error>(e.into()), - // Err(_) => unreachable!("This branch shouldn't be reachable"), - // } - // } + #[tokio::test] + async fn successful_end_to_end_run() -> Result<(), Error> { + let (client, server) = crate::simulated::chan(); + let (tx, rx) = sync::oneshot::channel(); + let base = Uri::from_static("http://localhost:9001"); + + let server = tokio::spawn(async { + handle(server, rx).await.expect("Unable to handle request"); + }); + let conn = simulated::Connector { inner: client }; + + let runtime = Runtime::builder() + .with_endpoint(base) + .with_connector(conn) + .build() + .expect("Unable to build runtime"); + + async fn func(event: serde_json::Value, _: crate::Context) -> Result { + Ok(event) + } + let f = crate::handler_fn(func); + + let client = &runtime.client; + let incoming = incoming(client).take(1); + runtime.run(incoming, f).await?; + + // shutdown server + tx.send(()).expect("Receiver has been dropped"); + match server.await { + Ok(_) => Ok(()), + Err(e) if e.is_panic() => return Err::<(), Error>(e.into()), + Err(_) => unreachable!("This branch shouldn't be reachable"), + } + } } diff --git a/lambda/src/lib.rs b/lambda/src/lib.rs index 24ef4929..9025717f 100644 --- a/lambda/src/lib.rs +++ b/lambda/src/lib.rs @@ -1,17 +1,17 @@ -#![deny(clippy::all, clippy::pedantic, clippy::nursery, clippy::cargo)] +#![deny(clippy::all, clippy::cargo)] #![warn(missing_docs, nonstandard_style, rust_2018_idioms)] //! The official Rust runtime for AWS Lambda. //! //! There are two mechanisms available for defining a Lambda function: -//! 1. The `#[lambda]` attribute, which generates the boilerplate to +//! 1. The `lambda` attribute maco, which generates the boilerplate to //! to launch and run a Lambda function. //! -//! The `#[lambda]` attribute _must_ be placed on an asynchronous main function. +//! The [`#[lambda]`] attribute _must_ be placed on an asynchronous main function. //! However, as asynchronous main functions are not legal valid Rust //! this means that the main function must also be decorated using a -//! `#[tokio::main]` attribute macro. This is available from -//! the [tokio](https://github.com/tokio-rs/tokio) crate. +//! [`#[tokio::main]`] attribute macro. This is available from +//! the [Tokio] crate. //! //! 2. A type that conforms to the [`Handler`] trait. This type can then be passed //! to the the `lambda::run` function, which launches and runs the Lambda runtime. @@ -33,19 +33,33 @@ //! Ok(event) //! } //! ``` +//! +//! [`Handler`]: trait.Handler.html +//! [`lambda::Context`]: struct.Context.html +//! [`lambda`]: attr.lambda.html +//! [`#[tokio::main]`]: https://docs.rs/tokio/0.2.21/tokio/attr.main.html +//! [Tokio]: https://docs.rs/tokio/ pub use crate::types::Context; use client::Client; -use futures::stream::{Stream, StreamExt}; +use hyper::client::{connect::Connection, HttpConnector}; pub use lambda_attributes::lambda; use serde::{Deserialize, Serialize}; use std::{ convert::{TryFrom, TryInto}, env, fmt, future::Future, + sync::Arc, +}; +use tokio::{ + io::{AsyncRead, AsyncWrite}, + stream::{Stream, StreamExt}, }; +use tower_service::Service; +use tracing::{error, trace}; mod client; mod requests; +#[cfg(test)] mod simulated; /// Types available to a Lambda function. mod types; @@ -88,22 +102,26 @@ impl Config { } } -/// A trait describing an asynchronous function `A` to `B. +/// A trait describing an asynchronous function `A` to `B`. pub trait Handler { /// Errors returned by this handler. type Error; - /// The future response value of this handler. + /// Response of this handler. type Fut: Future>; - /// Process the incoming event and `Context` then return the response asynchronously. - fn call(&mut self, event: A, context: Context) -> Self::Fut; + /// Handle the incoming event. + fn call(&self, event: A, context: Context) -> Self::Fut; } -/// Returns a new `HandlerFn` with the given closure. +/// Returns a new [`HandlerFn`] with the given closure. +/// +/// [`HandlerFn`]: struct.HandlerFn.html pub fn handler_fn(f: F) -> HandlerFn { HandlerFn { f } } -/// A `Handler` implemented by a closure. +/// A [`Handler`] implemented by a closure. +/// +/// [`Handler`]: trait.Handler.html #[derive(Clone, Debug)] pub struct HandlerFn { f: F, @@ -113,15 +131,179 @@ impl Handler for HandlerFn where F: Fn(A, Context) -> Fut, Fut: Future> + Send, - Error: Into + fmt::Debug, + Error: Into> + fmt::Display, { type Error = Error; type Fut = Fut; - fn call(&mut self, req: A, ctx: Context) -> Self::Fut { + fn call(&self, req: A, ctx: Context) -> Self::Fut { (self.f)(req, ctx) } } +#[non_exhaustive] +#[derive(Debug, PartialEq)] +enum BuilderError { + UnsetUri, +} + +struct Runtime = HttpConnector> { + client: Client, +} + +impl Runtime { + pub fn builder() -> RuntimeBuilder { + RuntimeBuilder { + connector: HttpConnector::new(), + uri: None, + } + } +} + +impl Runtime +where + C: Service + Clone + Send + Sync + Unpin + 'static, + >::Future: Unpin + Send, + >::Error: Into>, + >::Response: AsyncRead + AsyncWrite + Connection + Unpin + Send + 'static, +{ + pub async fn run( + &self, + incoming: impl Stream, Error>> + Send, + handler: F, + ) -> Result<(), Error> + where + F: Handler + Send + Sync + 'static, + >::Fut: Future>::Error>> + Send + 'static, + >::Error: fmt::Display + Send + Sync + 'static, + A: for<'de> Deserialize<'de> + Send + Sync + 'static, + B: Serialize + Send + Sync + 'static, + { + let client = &self.client; + let handler = Arc::new(handler); + tokio::pin!(incoming); + while let Some(event) = incoming.next().await { + trace!("New event arrived (run loop)"); + let event = event?; + let (parts, body) = event.into_parts(); + + let ctx: Context = Context::try_from(parts.headers)?; + let body = hyper::body::to_bytes(body).await?; + trace!("{}", std::str::from_utf8(&body)?); // this may be very verbose + let body = serde_json::from_slice(&body)?; + + let handler = Arc::clone(&handler); + let request_id = &ctx.request_id.clone(); + let task = tokio::spawn(async move { handler.call(body, ctx) }); + + let req = match task.await { + Ok(response) => match response.await { + Ok(response) => { + trace!("Ok response from handler (run loop)"); + EventCompletionRequest { + request_id, + body: response, + } + .into_req() + } + Err(err) => { + error!("{}", err); // logs the error in CloudWatch + EventErrorRequest { + request_id, + diagnostic: Diagnostic { + error_type: type_name_of_val(&err).to_owned(), + error_message: format!("{}", err), // returns the error to the caller via Lambda API + }, + } + .into_req() + } + }, + Err(err) if err.is_panic() => { + error!("{:?}", err); // inconsistent with other log record formats - to be reviewed + EventErrorRequest { + request_id, + diagnostic: Diagnostic { + error_type: type_name_of_val(&err).to_owned(), + error_message: format!("Lambda panicked: {}", err), + }, + } + .into_req() + } + Err(_) => unreachable!("tokio::task should not be canceled"), + }; + let req = req?; + client.call(req).await.expect("Unable to send response to Runtime APIs"); + } + Ok(()) + } +} + +struct RuntimeBuilder = hyper::client::HttpConnector> { + connector: C, + uri: Option, +} + +impl RuntimeBuilder +where + C: Service + Clone + Send + Sync + Unpin + 'static, + >::Future: Unpin + Send, + >::Error: Into>, + >::Response: AsyncRead + AsyncWrite + Connection + Unpin + Send + 'static, +{ + pub fn with_connector(self, connector: C2) -> RuntimeBuilder + where + C2: Service + Clone + Send + Sync + Unpin + 'static, + >::Future: Unpin + Send, + >::Error: Into>, + >::Response: AsyncRead + AsyncWrite + Connection + Unpin + Send + 'static, + { + RuntimeBuilder { + connector, + uri: self.uri, + } + } + + pub fn with_endpoint(self, uri: http::Uri) -> Self { + Self { uri: Some(uri), ..self } + } + + pub fn build(self) -> Result, BuilderError> { + let uri = match self.uri { + Some(uri) => uri, + None => return Err(BuilderError::UnsetUri), + }; + let client = Client::with(uri, self.connector); + + Ok(Runtime { client }) + } +} + +#[test] +fn test_builder() { + let runtime = Runtime::builder() + .with_connector(HttpConnector::new()) + .with_endpoint(http::Uri::from_static("http://nomatter.com")) + .build(); + + runtime.unwrap(); +} + +fn incoming(client: &Client) -> impl Stream, Error>> + Send + '_ +where + C: Service + Clone + Send + Sync + Unpin + 'static, + >::Future: Unpin + Send, + >::Error: Into>, + >::Response: AsyncRead + AsyncWrite + Connection + Unpin + Send + 'static, +{ + async_stream::stream! { + loop { + trace!("Waiting for next event (incoming loop)"); + let req = NextEventRequest.into_req().expect("Unable to construct request"); + let res = client.call(req).await; + yield res; + } + } +} + /// Starts the Lambda Rust runtime and begins polling for events on the [Lambda /// Runtime APIs](https://docs.aws.amazon.com/lambda/latest/dg/runtimes-api.html). /// @@ -145,87 +327,24 @@ where /// ``` pub async fn run(handler: F) -> Result<(), Error> where - F: Handler, - >::Error: fmt::Debug, - A: for<'de> Deserialize<'de>, - B: Serialize, + F: Handler + Send + Sync + 'static, + >::Fut: Future>::Error>> + Send + 'static, + >::Error: fmt::Display + Send + Sync + 'static, + A: for<'de> Deserialize<'de> + Send + Sync + 'static, + B: Serialize + Send + Sync + 'static, { - let mut handler = handler; + trace!("Loading config from env"); let config = Config::from_env()?; let uri = config.endpoint.try_into().expect("Unable to convert to URL"); - let client = Client::with(uri, hyper::Client::new()); - let incoming = incoming(&client); - run_inner(&client, incoming, &mut handler).await?; - - Ok(()) -} - -/// Runs the lambda function almost entirely in-memory. This is meant for testing. -pub async fn run_simulated(handler: F, url: &str) -> Result<(), Error> -where - F: Handler, - >::Error: fmt::Debug, - A: for<'de> Deserialize<'de>, - B: Serialize, -{ - let mut handler = handler; - let uri = url.try_into().expect("Unable to convert to URL"); - let client = Client::with(uri, hyper::Client::new()); - let incoming = incoming(&client).take(1); - run_inner(&client, incoming, &mut handler).await?; - - Ok(()) -} - -fn incoming(client: &Client) -> impl Stream, Error>> + '_ { - async_stream::stream! { - loop { - let req = NextEventRequest.into_req().expect("Unable to construct request"); - let res = client.call(req).await; - yield res; - } - } -} - -async fn run_inner( - client: &Client, - incoming: impl Stream, Error>>, - handler: &mut F, -) -> Result<(), Error> -where - F: Handler, - >::Error: fmt::Debug, - A: for<'de> Deserialize<'de>, - B: Serialize, -{ - tokio::pin!(incoming); - - while let Some(event) = incoming.next().await { - let event = event?; - let (parts, body) = event.into_parts(); - - let ctx: Context = Context::try_from(parts.headers)?; - let body = hyper::body::to_bytes(body).await?; - let body = serde_json::from_slice(&body)?; - - let request_id = &ctx.request_id.clone(); - let f = handler.call(body, ctx); - - let req = match f.await { - Ok(res) => EventCompletionRequest { request_id, body: res }.into_req()?, - Err(e) => EventErrorRequest { - request_id, - diagnostic: Diagnostic { - error_message: format!("{:?}", e), - error_type: type_name_of_val(e).to_owned(), - }, - } - .into_req()?, - }; - client.call(req).await?; - } + let runtime = Runtime::builder() + .with_connector(HttpConnector::new()) + .with_endpoint(uri) + .build() + .expect("Unable create runtime"); - Ok(()) + let client = &runtime.client; + let incoming = incoming(client); + runtime.run(incoming, handler).await } fn type_name_of_val(_: T) -> &'static str { diff --git a/lambda/src/requests.rs b/lambda/src/requests.rs index 2d691b94..a89f1689 100644 --- a/lambda/src/requests.rs +++ b/lambda/src/requests.rs @@ -134,7 +134,7 @@ struct InitErrorRequest; impl IntoRequest for InitErrorRequest { fn into_req(self) -> Result, Error> { - let uri = format!("/2018-06-01/runtime/init/error"); + let uri = "/2018-06-01/runtime/init/error".to_string(); let uri = Uri::from_str(&uri)?; let req = Request::builder() diff --git a/lambda/src/simulated.rs b/lambda/src/simulated.rs index b59f0754..a37c63a8 100644 --- a/lambda/src/simulated.rs +++ b/lambda/src/simulated.rs @@ -12,7 +12,7 @@ use std::{ use tokio::io::{AsyncRead, AsyncWrite}; use tower_service::Service; -/// Creates a pair of AsyncReadWrite data streams, where the write end of each member of the pair +/// Creates a pair of `AsyncRead`/`AsyncWrite` data streams, where the write end of each member of the pair /// is the read end of the other member of the pair. This allows us to emulate the behavior of a TcpStream /// but in-memory, deterministically, and with full control over failure injection. pub(crate) fn chan() -> (SimStream, SimStream) { @@ -37,11 +37,11 @@ pub(crate) fn chan() -> (SimStream, SimStream) { } #[derive(Clone)] -pub struct SimulatedConnector { +pub struct Connector { pub inner: SimStream, } -impl Service for SimulatedConnector { +impl Service for Connector { type Response = SimStream; type Error = std::io::Error; type Future = Pin> + Send>>; @@ -104,6 +104,7 @@ pub struct BufferState { } impl BufferState { + /// Creates a new `BufferState`. fn new() -> Self { BufferState { buffer: VecDeque::new(), @@ -208,7 +209,7 @@ mod tests { client.write_all(b"Ping").await.expect("Write should succeed"); // Verify we can read it from side 2 - let mut read_on_server = [0u8; 4]; + let mut read_on_server = [0_u8; 4]; server .read_exact(&mut read_on_server) .await @@ -219,7 +220,7 @@ mod tests { server.write_all(b"Pong").await.expect("Write should succeed"); // Verify we can read it from side 1 - let mut read_on_client = [0u8; 4]; + let mut read_on_client = [0_u8; 4]; client .read_exact(&mut read_on_client) .await