diff --git a/Cargo.lock b/Cargo.lock index 8727db2d5..dda4b8fa2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,10 +1,5 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -[[package]] -name = "adler32" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "aho-corasick" version = "0.7.10" @@ -61,14 +56,6 @@ dependencies = [ "libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "base64" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "base64" version = "0.11.0" @@ -97,16 +84,6 @@ name = "byteorder" version = "1.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "bytes" -version = "0.4.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)", - "iovec 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "cc" version = "1.0.50" @@ -149,97 +126,6 @@ dependencies = [ "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "cookie" -version = "0.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", - "url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "cookie_store" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "cookie 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", - "failure 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", - "idna 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "publicsuffix 1.5.4 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.48 (registry+https://github.com/rust-lang/crates.io-index)", - "time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", - "try_from 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "core-foundation" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "core-foundation-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "core-foundation-sys" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "crc32fast" -version = "1.2.0" -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 = "crossbeam-deque" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "crossbeam-epoch 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-utils 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", - "maybe-uninit 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "crossbeam-epoch" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-utils 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "maybe-uninit 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "memoffset 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", - "scopeguard 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "crossbeam-queue" -version = "0.2.1" -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)", - "crossbeam-utils 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "crossbeam-utils" -version = "0.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "curl" version = "0.4.33" @@ -268,16 +154,6 @@ dependencies = [ "winapi 0.3.8 (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 = "either" -version = "1.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "encoding" version = "0.2.33" @@ -335,14 +211,6 @@ name = "encoding_index_tests" version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "encoding_rs" -version = "0.8.22" -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 = "error-chain" version = "0.10.0" @@ -351,45 +219,6 @@ dependencies = [ "backtrace 0.3.45 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "error-chain" -version = "0.12.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "version_check 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "failure" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "backtrace 0.3.45 (registry+https://github.com/rust-lang/crates.io-index)", - "failure_derive 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "failure_derive" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "proc-macro2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)", - "synstructure 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "flate2" -version = "1.0.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)", - "crc32fast 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)", - "miniz_oxide 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "float-cmp" version = "0.6.0" @@ -403,52 +232,11 @@ name = "fnv" version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "foreign-types" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "foreign-types-shared 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "foreign-types-shared" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "fuchsia-cprng" version = "0.1.1" 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.1.29" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "futures-cpupool" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "num_cpus 1.12.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "getrandom" version = "0.1.14" @@ -459,23 +247,6 @@ dependencies = [ "wasi 0.9.0+wasi-snapshot-preview1 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "h2" -version = "0.1.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "http 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", - "indexmap 1.3.2 (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)", - "string 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "heck" version = "0.3.1" @@ -492,32 +263,6 @@ dependencies = [ "libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "http" -version = "0.1.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "bytes 0.4.12 (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.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "http 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-buf 0.1.1 (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 = "hurl" version = "0.99.13" @@ -526,15 +271,12 @@ dependencies = [ "base64 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", "chrono 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", "clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)", - "cookie 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", "curl 0.4.33 (registry+https://github.com/rust-lang/crates.io-index)", "encoding 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", "float-cmp 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", "libxml 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)", - "percent-encoding 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "proptest 0.9.5 (registry+https://github.com/rust-lang/crates.io-index)", "regex 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "reqwest 0.9.24 (registry+https://github.com/rust-lang/crates.io-index)", "roxmltree 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", "serde-xml-rs 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -545,57 +287,6 @@ dependencies = [ "xmlparser 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "hyper" -version = "0.12.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-cpupool 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", - "h2 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)", - "http 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", - "http-body 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "httparse 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "iovec 0.1.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)", - "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-buf 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-executor 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-reactor 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-tcp 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-threadpool 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-timer 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)", - "want 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "hyper-tls" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "hyper 0.12.35 (registry+https://github.com/rust-lang/crates.io-index)", - "native-tls 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "idna" -version = "0.1.5" -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 = "idna" version = "0.2.0" @@ -606,36 +297,11 @@ dependencies = [ "unicode-normalization 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "indexmap" -version = "1.3.2" -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.67 (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 = "lazy_static" version = "1.4.0" @@ -666,14 +332,6 @@ dependencies = [ "vcpkg 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "lock_api" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "scopeguard 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "log" version = "0.4.8" @@ -687,102 +345,11 @@ name = "matches" version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "maybe-uninit" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "memchr" version = "2.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "memoffset" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "mime" -version = "0.3.16" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "mime_guess" -version = "2.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "mime 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)", - "unicase 2.6.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "miniz_oxide" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "adler32 1.0.4 (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.67 (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 = "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 = "native-tls" -version = "0.2.4" -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)", - "libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "openssl 0.10.28 (registry+https://github.com/rust-lang/crates.io-index)", - "openssl-probe 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "openssl-sys 0.9.54 (registry+https://github.com/rust-lang/crates.io-index)", - "schannel 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)", - "security-framework 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", - "security-framework-sys 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", - "tempfile 3.1.0 (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.67 (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-integer" version = "0.1.42" @@ -800,28 +367,6 @@ dependencies = [ "autocfg 1.0.0 (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.8 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "openssl" -version = "0.10.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "foreign-types 0.3.2 (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.67 (registry+https://github.com/rust-lang/crates.io-index)", - "openssl-sys 0.9.54 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "openssl-probe" version = "0.1.2" @@ -839,35 +384,6 @@ dependencies = [ "vcpkg 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "parking_lot" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "lock_api 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot_core 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "parking_lot_core" -version = "0.6.2" -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)", - "cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)", - "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "smallvec 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "percent-encoding" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "percent-encoding" version = "2.1.0" @@ -896,14 +412,6 @@ dependencies = [ "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "proc-macro2" -version = "1.0.9" -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 = "proptest" version = "0.9.5" @@ -923,18 +431,6 @@ dependencies = [ "tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "publicsuffix" -version = "1.5.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "error-chain 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)", - "idna 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "url 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "quick-error" version = "1.2.3" @@ -948,14 +444,6 @@ dependencies = [ "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "quote" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "proc-macro2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "rand" version = "0.6.5" @@ -1126,39 +614,6 @@ dependencies = [ "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "reqwest" -version = "0.9.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", - "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "cookie 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", - "cookie_store 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "encoding_rs 0.8.22 (registry+https://github.com/rust-lang/crates.io-index)", - "flate2 1.0.13 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "http 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", - "hyper 0.12.35 (registry+https://github.com/rust-lang/crates.io-index)", - "hyper-tls 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "mime 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)", - "mime_guess 2.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "native-tls 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.48 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_urlencoded 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)", - "time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-executor 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-threadpool 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-timer 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)", - "url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)", - "uuid 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)", - "winreg 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "roxmltree" version = "0.7.3" @@ -1172,14 +627,6 @@ name = "rustc-demangle" version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "rustc_version" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "rusty-fork" version = "0.2.2" @@ -1205,51 +652,10 @@ dependencies = [ "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "scopeguard" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "security-framework" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "core-foundation 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "core-foundation-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "security-framework-sys 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "security-framework-sys" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "core-foundation-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "semver" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "semver-parser" -version = "0.7.0" -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-xml-rs" @@ -1262,16 +668,6 @@ dependencies = [ "xml-rs 0.8.0 (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.9 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "serde_json" version = "1.0.48" @@ -1282,30 +678,6 @@ dependencies = [ "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "serde_urlencoded" -version = "0.5.5" -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 1.7.2 (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 = "0.6.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "maybe-uninit 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "smallvec" version = "1.2.0" @@ -1322,14 +694,6 @@ dependencies = [ "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "string" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "strsim" version = "0.8.0" @@ -1374,27 +738,6 @@ dependencies = [ "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "syn" -version = "1.0.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "proc-macro2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.3 (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 = "synstructure" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "proc-macro2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.16 (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 = "tempfile" version = "3.1.0" @@ -1434,155 +777,11 @@ dependencies = [ "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "tokio" -version = "0.1.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "mio 0.6.21 (registry+https://github.com/rust-lang/crates.io-index)", - "num_cpus 1.12.0 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-current-thread 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-executor 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-reactor 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-tcp 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-threadpool 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-timer 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "tokio-buf" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "tokio-current-thread" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-executor 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "tokio-executor" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "crossbeam-utils 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "tokio-io" -version = "0.1.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "tokio-reactor" -version = "0.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "crossbeam-utils 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "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)", - "num_cpus 1.12.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-executor 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-sync 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "tokio-sync" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "tokio-tcp" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "iovec 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "mio 0.6.21 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-reactor 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "tokio-threadpool" -version = "0.1.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "crossbeam-deque 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-queue 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-utils 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "num_cpus 1.12.0 (registry+https://github.com/rust-lang/crates.io-index)", - "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-executor 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "tokio-timer" -version = "0.2.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "crossbeam-utils 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-executor 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 = "try_from" -version = "0.3.2" -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 = "typed-arena" version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "unicase" -version = "2.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "version_check 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "unicode-bidi" version = "0.3.4" @@ -1614,21 +813,6 @@ 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 = "1.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "idna 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", - "percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "url" version = "2.1.1" @@ -1639,14 +823,6 @@ dependencies = [ "percent-encoding 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "uuid" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "vcpkg" version = "0.2.8" @@ -1657,11 +833,6 @@ name = "vec_map" version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "version_check" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "wait-timeout" version = "0.2.0" @@ -1670,26 +841,11 @@ dependencies = [ "libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "want" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "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 = "wasi" version = "0.9.0+wasi-snapshot-preview1" source = "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" @@ -1699,11 +855,6 @@ dependencies = [ "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" @@ -1714,23 +865,6 @@ name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "winreg" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "winapi 0.3.8 (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)", -] - [[package]] name = "xml-rs" version = "0.8.0" @@ -1742,7 +876,6 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" [metadata] -"checksum adler32 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "5d2e7343e7fc9de883d1b0341e0b13970f764c14101234857d2ddafa1cb1cac2" "checksum aho-corasick 0.7.10 (registry+https://github.com/rust-lang/crates.io-index)" = "8716408b8bc624ed7f65d223ddb9ac2d044c0547b6fa4b0d554f3a9540496ada" "checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" "checksum atty 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)" = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" @@ -1750,31 +883,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d" "checksum backtrace 0.3.45 (registry+https://github.com/rust-lang/crates.io-index)" = "ad235dabf00f36301792cfe82499880ba54c6486be094d1047b02bacb67c14e8" "checksum backtrace-sys 0.1.34 (registry+https://github.com/rust-lang/crates.io-index)" = "ca797db0057bae1a7aa2eef3283a874695455cecf08a43bfb8507ee0ebc1ed69" -"checksum base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0b25d992356d2eb0ed82172f5248873db5560c4721f564b13cb5193bda5e668e" "checksum base64 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b41b7ea54a0c9d92199de89e20e58d49f02f8e699814ef3fdf266f6f748d15c7" "checksum bit-set 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e84c238982c4b1e1ee668d136c510c67a13465279c0cb367ea6baf6310620a80" "checksum bit-vec 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f59bbe95d4e52a6398ec21238d31577f2b28a9d86807f06ca59d191d8440d0bb" "checksum bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" "checksum byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de" -"checksum bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)" = "206fdffcfa2df7cbe15601ef46c813fce0965eb3286db6b56c583b814b51c81c" "checksum cc 1.0.50 (registry+https://github.com/rust-lang/crates.io-index)" = "95e28fa049fda1c330bcf9d723be7663a899c4679724b34c81e9f5a326aab8cd" "checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" "checksum chrono 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)" = "80094f509cf8b5ae86a4966a39b3ff66cd7e2a3e594accec3743ff3fabeab5b2" "checksum clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5067f5bb2d80ef5d68b4c87db81601f0b75bca627bc2ef76b141d7b846a3c6d9" "checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" -"checksum cookie 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)" = "888604f00b3db336d2af898ec3c1d5d0ddf5e6d462220f2ededc33a87ac4bbd5" -"checksum cookie_store 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "46750b3f362965f197996c4448e4a0935e791bf7d6631bfce9ee0af3d24c919c" -"checksum core-foundation 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "57d24c7a13c43e870e37c1556b74555437870a04514f7685f5b354e090567171" -"checksum core-foundation-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b3a71ab494c0b5b860bdc8407ae08978052417070c2ced38573a9157ad75b8ac" -"checksum crc32fast 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ba125de2af0df55319f41944744ad91c71113bf74a4646efff39afe1f6842db1" -"checksum crossbeam-deque 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "9f02af974daeee82218205558e51ec8768b48cf524bd01d550abe5573a608285" -"checksum crossbeam-epoch 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "058ed274caafc1f60c4997b5fc07bf7dc7cca454af7c6e81edffe5f33f70dace" -"checksum crossbeam-queue 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c695eeca1e7173472a32221542ae469b3e9aac3a4fc81f7696bcad82029493db" -"checksum crossbeam-utils 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8" "checksum curl 0.4.33 (registry+https://github.com/rust-lang/crates.io-index)" = "78baca05127a115136a9898e266988fc49ca7ea2c839f60fc6e1fc9df1599168" "checksum curl-sys 0.4.36+curl-7.71.1 (registry+https://github.com/rust-lang/crates.io-index)" = "68cad94adeb0c16558429c3c34a607acc9ea58e09a7b66310aabc9788fc5d721" -"checksum dtoa 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "4358a9e11b9a09cf52383b451b49a169e8d797b68aa02301ff586d70d9661ea3" -"checksum either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "bb1f6b1ce1c140482ea30ddd3335fc0024ac7ee112895426e0a629a6c20adfe3" "checksum encoding 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)" = "6b0d943856b990d12d3b55b359144ff341533e516d94098b1d3fc1ac666d36ec" "checksum encoding-index-japanese 1.20141219.5 (registry+https://github.com/rust-lang/crates.io-index)" = "04e8b2ff42e9a05335dbf8b5c6f7567e5591d0d916ccef4e0b1710d32a0d0c91" "checksum encoding-index-korean 1.20141219.5 (registry+https://github.com/rust-lang/crates.io-index)" = "4dc33fb8e6bcba213fe2f14275f0963fd16f0a02c878e3095ecfdf5bee529d81" @@ -1782,73 +902,34 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum encoding-index-singlebyte 1.20141219.5 (registry+https://github.com/rust-lang/crates.io-index)" = "3351d5acffb224af9ca265f435b859c7c01537c0849754d3db3fdf2bfe2ae84a" "checksum encoding-index-tradchinese 1.20141219.5 (registry+https://github.com/rust-lang/crates.io-index)" = "fd0e20d5688ce3cab59eb3ef3a2083a5c77bf496cb798dc6fcdb75f323890c18" "checksum encoding_index_tests 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "a246d82be1c9d791c5dfde9a2bd045fc3cbba3fa2b11ad558f27d01712f00569" -"checksum encoding_rs 0.8.22 (registry+https://github.com/rust-lang/crates.io-index)" = "cd8d03faa7fe0c1431609dfad7bbe827af30f82e1e2ae6f7ee4fca6bd764bc28" "checksum error-chain 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d9435d864e017c3c6afeac1654189b06cdb491cf2ff73dbf0d73b0f292f42ff8" -"checksum error-chain 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d371106cc88ffdfb1eabd7111e432da544f16f3e2d7bf1dfe8bf575f1df045cd" -"checksum failure 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "b8529c2421efa3066a5cbd8063d2244603824daccb6936b079010bb2aa89464b" -"checksum failure_derive 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "030a733c8287d6213886dd487564ff5c8f6aae10278b3588ed177f9d18f8d231" -"checksum flate2 1.0.13 (registry+https://github.com/rust-lang/crates.io-index)" = "6bd6d6f4752952feb71363cffc9ebac9411b75b87c6ab6058c40c8900cf43c0f" "checksum float-cmp 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "da62c4f1b81918835a8c6a484a397775fff5953fe83529afd51b05f5c6a6617d" "checksum fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "2fad85553e09a6f881f739c29f0b00b0f01357c743266d478b68951ce23285f3" -"checksum foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" -"checksum foreign-types-shared 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" "checksum fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" -"checksum fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" -"checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" -"checksum futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)" = "1b980f2816d6ee8673b6517b52cb0e808a180efc92e5c19d02cdda79066703ef" -"checksum futures-cpupool 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "ab90cde24b3319636588d0c35fe03b1333857621051837ed769faefb4c2162e4" "checksum getrandom 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "7abc8dd8451921606d809ba32e95b6111925cd2906060d2dcc29c070220503eb" -"checksum h2 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)" = "a5b34c246847f938a410a03c5458c7fee2274436675e76d8b903c08efc29c462" "checksum heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205" "checksum hermit-abi 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "1010591b26bbfe835e9faeabeb11866061cc7dcebffd56ad7d0942d0e61aefd8" -"checksum http 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)" = "d6ccf5ede3a895d8856620237b2f02972c1bbc78d2965ad7fe8838d4a0ed41f0" -"checksum http-body 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6741c859c1b2463a423a1dbce98d418e6c3c3fc720fb0d45528657320920292d" -"checksum httparse 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "cd179ae861f0c2e53da70d892f5f3029f9594be0c41dc5269cd371691b1dc2f9" -"checksum hyper 0.12.35 (registry+https://github.com/rust-lang/crates.io-index)" = "9dbe6ed1438e1f8ad955a4701e9a944938e9519f6888d12d8558b645e247d5f6" -"checksum hyper-tls 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "3a800d6aa50af4b5850b2b0f659625ce9504df908e9733b635720483be26174f" -"checksum idna 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "38f09e0f0b1fb55fdee1f17470ad800da77af5186a1a76c026b679358b7e844e" "checksum idna 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "02e2673c30ee86b5b96a9cb52ad15718aa1f966f5ab9ad54a8b95d5ca33120a9" -"checksum indexmap 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "076f042c5b7b98f31d205f1249267e12a6518c1481e9dae9764af19b707d2292" -"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.67 (registry+https://github.com/rust-lang/crates.io-index)" = "eb147597cdf94ed43ab7a9038716637d2d1bf2bc571da995d0028dec06bd3018" "checksum libxml 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)" = "fbbe61bd26eaed780d4d81e07ad3f5ce4aae2d61eca61945006875f08cbdf1b4" "checksum libz-sys 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "af67924b8dd885cccea261866c8ce5b74d239d272e154053ff927dae839f5ae9" -"checksum lock_api 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "79b2de95ecb4691949fea4716ca53cdbcfccb2c612e19644a8bad05edcf9f47b" "checksum log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7" "checksum matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" -"checksum maybe-uninit 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00" "checksum memchr 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400" -"checksum memoffset 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "75189eb85871ea5c2e2c15abbdd541185f63b408415e5051f5cac122d8c774b9" -"checksum mime 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)" = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" -"checksum mime_guess 2.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2684d4c2e97d99848d30b324b00c8fcc7e5c897b7cbb5819b09e7c90e8baf212" -"checksum miniz_oxide 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "aa679ff6578b1cddee93d7e82e263b94a575e0bfced07284eb0c037c1d2416a5" -"checksum mio 0.6.21 (registry+https://github.com/rust-lang/crates.io-index)" = "302dec22bcf6bae6dfb69c647187f4b4d0fb6f535521f7bc022430ce8e12008f" -"checksum miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919" -"checksum native-tls 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "2b0d88c06fe90d5ee94048ba40409ef1d9315d86f6f38c2efdaad4fb50c58b2d" -"checksum net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)" = "42550d9fb7b6684a6d404d9fa7250c2eb2646df731d1c06afc06dcee9e1bcf88" "checksum num-integer 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "3f6ea62e9d81a77cd3ee9a2a5b9b609447857f3d358704331e4ef39eb247fcba" "checksum num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "c62be47e61d1842b9170f0fdeec8eba98e60e90e5446449a0545e5152acd7096" -"checksum num_cpus 1.12.0 (registry+https://github.com/rust-lang/crates.io-index)" = "46203554f085ff89c235cd12f7075f3233af9b11ed7c9e16dfe2560d03313ce6" -"checksum openssl 0.10.28 (registry+https://github.com/rust-lang/crates.io-index)" = "973293749822d7dd6370d6da1e523b0d1db19f06c459134c658b2a4261378b52" "checksum openssl-probe 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "77af24da69f9d9341038eba93a073b1fdaaa1b788221b00a69bce9e762cb32de" "checksum openssl-sys 0.9.54 (registry+https://github.com/rust-lang/crates.io-index)" = "1024c0a59774200a555087a6da3f253a9095a5f344e353b212ac4c8b8e450986" -"checksum parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f842b1982eb6c2fe34036a4fbfb06dd185a3f5c8edfaacdf7d1ea10b07de6252" -"checksum parking_lot_core 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b876b1b9e7ac6e1a74a6da34d25c42e17e8862aa409cbbbdcfc8d86c6f3bc62b" -"checksum percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831" "checksum percent-encoding 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" "checksum peresil 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f658886ed52e196e850cfbbfddab9eaa7f6d90dd0929e264c31e5cec07e09e57" "checksum pkg-config 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)" = "05da548ad6865900e60eaba7f589cc0783590a92e940c26953ff81ddbab2d677" "checksum ppv-lite86 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "74490b50b9fbe561ac330df47c08f3f33073d2d00c150f719147d7c54522fa1b" "checksum proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)" = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759" -"checksum proc-macro2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)" = "6c09721c6781493a2a492a96b5a5bf19b65917fe6728884e7c44dd0c60ca3435" "checksum proptest 0.9.5 (registry+https://github.com/rust-lang/crates.io-index)" = "bf6147d103a7c9d7598f4105cf049b15c99e2ecd93179bf024f0fd349be5ada4" -"checksum publicsuffix 1.5.4 (registry+https://github.com/rust-lang/crates.io-index)" = "3bbaa49075179162b49acac1c6aa45fb4dafb5f13cf6794276d77bc7fd95757b" "checksum quick-error 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" "checksum quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)" = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1" -"checksum quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2bdc6c187c65bca4260c9011c9e3132efe4909da44726bad24cf7572ae338d7f" "checksum rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "6d71dacdc3c88c1fde3885a3be3fbab9f35724e6ce99467f7d9c5026132184ca" "checksum rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" "checksum rand_chacha 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "556d3a1ca6600bfcbab7c7c91ccb085ac7fbbcd70e008a98742e7847f4f7bcef" @@ -1868,74 +949,38 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum regex 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "322cf97724bea3ee221b78fe25ac9c46114ebb51747ad5babd51a2fc6a8235a8" "checksum regex-syntax 0.6.16 (registry+https://github.com/rust-lang/crates.io-index)" = "1132f845907680735a84409c3bebc64d1364a5683ffbce899550cd09d5eaefc1" "checksum remove_dir_all 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "4a83fa3702a688b9359eccba92d153ac33fd2e8462f9e0e3fdf155239ea7792e" -"checksum reqwest 0.9.24 (registry+https://github.com/rust-lang/crates.io-index)" = "f88643aea3c1343c804950d7bf983bd2067f5ab59db6d613a08e05572f2714ab" "checksum roxmltree 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "0852407257c1b696a0c66b9db3ffe7769c2744a2fa725c8050e6f3e5a823c02b" "checksum rustc-demangle 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "4c691c0e608126e00913e33f0ccf3727d5fc84573623b8d65b2df340b5201783" -"checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" "checksum rusty-fork 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "3dd93264e10c577503e926bd1430193eeb5d21b059148910082245309b424fae" "checksum ryu 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "bfa8506c1de11c9c4e4c38863ccbe02a305c8188e85a05a784c9e11e1c3910c8" "checksum schannel 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)" = "507a9e6e8ffe0a4e0ebb9a10293e62fdf7657c06f1b8bb07a8fcf697d2abf295" -"checksum scopeguard 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" -"checksum security-framework 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "97bbedbe81904398b6ebb054b3e912f99d55807125790f3198ac990d98def5b0" -"checksum security-framework-sys 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "06fd2f23e31ef68dd2328cc383bd493142e46107a3a0e24f7d734e3f3b80fe4c" -"checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" -"checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" "checksum serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)" = "414115f25f818d7dfccec8ee535d76949ae78584fc4f79a6f45a904bf8ab4449" "checksum serde-xml-rs 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "27d98dfc234faa8532d66c837de56bf4276a259a43dd10ef96feb2fb7ab333b1" -"checksum serde_derive 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)" = "128f9e303a5a29922045a830221b8f78ec74a5f544944f3d5984f8ec3895ef64" "checksum serde_json 1.0.48 (registry+https://github.com/rust-lang/crates.io-index)" = "9371ade75d4c2d6cb154141b9752cf3781ec9c05e0e5cf35060e1e70ee7b9c25" -"checksum serde_urlencoded 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)" = "642dd69105886af2efd227f75a520ec9b44a820d65bc133a9131f7d229fd165a" -"checksum slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" -"checksum smallvec 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)" = "f7b0758c52e15a8b5e3691eae6cc559f08eee9406e548a4477ba4e67770a82b6" "checksum smallvec 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5c2fb2ec9bcd216a5b0d0ccf31ab17b5ed1d627960edff65bbe95d3ce221cefc" "checksum socket2 0.3.12 (registry+https://github.com/rust-lang/crates.io-index)" = "03088793f677dce356f3ccc2edb1b314ad191ab702a5de3faf49304f7e104918" -"checksum string 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d24114bfcceb867ca7f71a0d3fe45d45619ec47a6fbfa98cb14e14250bfa5d6d" "checksum strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" "checksum structopt 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)" = "16c2cdbf9cc375f15d1b4141bc48aeef444806655cd0e904207edc8d68d86ed7" "checksum structopt-derive 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)" = "53010261a84b37689f9ed7d395165029f9cc7abb9f56bbfe86bee2597ed25107" "checksum sxd-document 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "94d82f37be9faf1b10a82c4bd492b74f698e40082f0f40de38ab275f31d42078" "checksum syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)" = "9ca4b3b69a77cbe1ffc9e198781b7acb0c7365a883670e8f1c1bc66fba79a5c5" -"checksum syn 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)" = "123bd9499cfb380418d509322d7a6d52e5315f064fe4b3ad18a53d6b92c07859" -"checksum synstructure 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)" = "67656ea1dc1b41b1451851562ea232ec2e5a80242139f7e679ceccfb5d61f545" "checksum tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9" "checksum textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" "checksum thread_local 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d40c6d1b69745a6ec6fb1ca717914848da4b44ae29d9b3080cbee91d72a69b14" "checksum time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "db8dcfca086c1143c9270ac42a2bbd8a7ee477b78ac8e45b19abfb0cbede4b6f" -"checksum tokio 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)" = "5a09c0b5bb588872ab2f09afa13ee6e9dac11e10a0ec9e8e3ba39a5a5d530af6" -"checksum tokio-buf 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8fb220f46c53859a4b7ec083e41dec9778ff0b1851c0942b211edb89e0ccdc46" -"checksum tokio-current-thread 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "b1de0e32a83f131e002238d7ccde18211c0a5397f60cbfffcb112868c2e0e20e" -"checksum tokio-executor 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "fb2d1b8f4548dbf5e1f7818512e9c406860678f29c300cdf0ebac72d1a3a1671" -"checksum tokio-io 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "57fc868aae093479e3131e3d165c93b1c7474109d13c90ec0dda2a1bbfff0674" -"checksum tokio-reactor 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "09bc590ec4ba8ba87652da2068d150dcada2cfa2e07faae270a5e0409aa51351" -"checksum tokio-sync 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "edfe50152bc8164fcc456dab7891fa9bf8beaf01c5ee7e1dd43a397c3cf87dee" -"checksum tokio-tcp 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "98df18ed66e3b72e742f185882a9e201892407957e45fbff8da17ae7a7c51f72" -"checksum tokio-threadpool 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)" = "df720b6581784c118f0eb4310796b12b1d242a7eb95f716a8367855325c25f89" -"checksum tokio-timer 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)" = "93044f2d313c95ff1cb7809ce9a7a05735b012288a888b62d4434fd58c94f296" -"checksum try-lock 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e604eb7b43c06650e854be16a2a03155743d3752dd1c943f6829e26b7a36e382" -"checksum try_from 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "283d3b89e1368717881a9d51dad843cc435380d8109c9e47d38780a324698d8b" "checksum typed-arena 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a9b2228007eba4120145f785df0f6c92ea538f5a3635a612ecf4e334c8c1446d" -"checksum unicase 2.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6" "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-segmentation 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e83e153d1053cbb5a118eeff7fd5be06ed99153f00dbcd8ae310c5fb2b22edc0" "checksum unicode-width 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "caaa9d531767d1ff2150b9332433f32a24622147e5ebb1f26409d5da67afd479" "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 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dd4e7c0d531266369519a4aa4f399d748bd37043b00bde1e4ff1f60a120b355a" "checksum url 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "829d4a8476c35c9bf0bbce5a3b23f4106f79728039b726d292bb93bc106787cb" -"checksum uuid 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)" = "90dbc611eb48397705a6b0f6e917da23ae517e4d127123d2cf7674206627d32a" "checksum vcpkg 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "3fc439f2794e98976c88a2a2dafce96b930fe8010b0a256b3c2199a773933168" "checksum vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a" -"checksum version_check 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "078775d0255232fb988e6fccf26ddc9d1ac274299aaedcedce21c6f72cc533ce" "checksum wait-timeout 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6" -"checksum want 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b6395efa4784b027708f7451087e647ec73cc74f5d9bc2e418404248d679a230" "checksum wasi 0.9.0+wasi-snapshot-preview1 (registry+https://github.com/rust-lang/crates.io-index)" = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" -"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 winreg 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b2986deb581c4fe11b621998a5e53361efe6b48a151178d0cd9eeffa4dc6acc9" -"checksum ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e" "checksum xml-rs 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "541b12c998c5b56aa2b4e6f18f03664eef9a4fd0a246a55594efae6cc2d964b5" "checksum xmlparser 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8110496c5bcc0d966b0b2da38d5a791aa139eeb0b80e7840a7463c2b806921eb" diff --git a/Cargo.toml b/Cargo.toml index 0fdd6f29e..e26fc0f72 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,7 +16,6 @@ strict = [] [dependencies] clap = "2.33.0" structopt = "0.2.10" -reqwest = "0.9.20" libxml = "0.2.12" regex = "1.1.0" serde_json = "1.0.40" @@ -27,8 +26,6 @@ atty = "0.2.13" url = "2.1.0" sxd-document = "0.3.2" serde = "1.0.104" -percent-encoding = "2.1.0" -cookie = "0.12.0" base64 = "0.11.0" float-cmp = "0.6.0" encoding = "0.2" diff --git a/integration/report/html/index.html b/integration/report/html/index.html index 9d6be9f09..bfae5e44c 100644 --- a/integration/report/html/index.html +++ b/integration/report/html/index.html @@ -1,2 +1,2 @@ -Hurl Report

Hurl Report

Wed, 26 Aug 2020 11:50:20 +0200
filenameduration
tests/assert_base64.hurl0.019s
tests/assert_header.hurl0.023s
tests/assert_json.hurl0.073s
tests/assert_match.hurl0.049s
tests/assert_regex.hurl0.025s
tests/assert_xpath.hurl0.023s
tests/bytes.hurl0.02s
tests/capture_and_assert.hurl0.022s
tests/captures.hurl0.078s
tests/cookies.hurl0.256s
tests/delete.hurl0.033s
tests/empty.hurl0s
tests/encoding.hurl0.059s
tests/error_assert_base64.hurl0.052s
tests/error_assert_file.hurl0.025s
tests/error_assert_header_not_found.hurl0.028s
tests/error_assert_header_value.hurl0.025s
tests/error_assert_http_version.hurl0.025s
tests/error_assert_invalid_predicate_type.hurl0.02s
tests/error_assert_match_utf8.hurl0.022s
tests/error_assert_query_invalid_regex.hurl0.018s
tests/error_assert_query_invalid_xpath.hurl0.021s
tests/error_assert_status.hurl0.019s
tests/error_assert_template_variable_not_found.hurl0.027s
tests/error_assert_value_error.hurl0.03s
tests/error_assert_variable.hurl0.025s
tests/error_file_read_access.hurl0s
tests/error_invalid_jsonpath.hurl0.043s
tests/error_invalid_url.hurl0s
tests/error_invalid_xml.hurl0.036s
tests/error_multipart_form_data.hurl0s
tests/error_predicate.hurl0.06s
tests/error_query_header_not_found.hurl0.033s
tests/error_query_invalid_json.hurl0.022s
tests/error_query_invalid_utf8.hurl0.024s
tests/error_template_variable_not_found.hurl0s
tests/error_template_variable_not_renderable.hurl0.021s
tests/form_params.hurl0.045s
tests/headers.hurl0.14s
tests/hello.hurl0.046s
tests/multipart_form_data.hurl0.024s
tests/no_entry.hurl0s
tests/output.hurl0.052s
tests/patch.hurl0.023s
tests/post_base64.hurl0.027s
tests/post_file.hurl0.02s
tests/post_json.hurl0.294s
tests/post_multilines.hurl0.078s
tests/post_xml.hurl0.068s
tests/predicates-string.hurl0.045s
tests/put.hurl0.024s
tests/querystring_params.hurl0.12s
tests/redirect.hurl0.062s
tests/utf8.hurl0.035s
\ No newline at end of file +Hurl Report

Hurl Report

Thu, 17 Sep 2020 15:58:48 +0200
filenameduration
tests/assert_base64.hurl0.002s
tests/assert_header.hurl0.002s
tests/assert_json.hurl0.008s
tests/assert_match.hurl0.016s
tests/assert_regex.hurl0.004s
tests/assert_xpath.hurl0.002s
tests/bytes.hurl0.001s
tests/capture_and_assert.hurl0.002s
tests/captures.hurl0.008s
tests/cookies.hurl0.017s
tests/delete.hurl0.001s
tests/empty.hurl0s
tests/encoding.hurl0.004s
tests/error_assert_base64.hurl0.002s
tests/error_assert_file.hurl0.002s
tests/error_assert_header_not_found.hurl0.002s
tests/error_assert_header_value.hurl0.002s
tests/error_assert_http_version.hurl0.002s
tests/error_assert_invalid_predicate_type.hurl0.003s
tests/error_assert_match_utf8.hurl0.002s
tests/error_assert_query_invalid_regex.hurl0.002s
tests/error_assert_query_invalid_xpath.hurl0.002s
tests/error_assert_status.hurl0.002s
tests/error_assert_template_variable_not_found.hurl0.001s
tests/error_assert_value_error.hurl0.005s
tests/error_assert_variable.hurl0.004s
tests/error_file_read_access.hurl0s
tests/error_invalid_jsonpath.hurl0.002s
tests/error_invalid_url.hurl0.074s
tests/error_invalid_xml.hurl0.002s
tests/error_multipart_form_data.hurl0s
tests/error_predicate.hurl0.013s
tests/error_query_header_not_found.hurl0.002s
tests/error_query_invalid_json.hurl0.002s
tests/error_query_invalid_utf8.hurl0.002s
tests/error_template_variable_not_found.hurl0s
tests/error_template_variable_not_renderable.hurl0.002s
tests/follow_redirect.hurl0.002s
tests/form_params.hurl0.004s
tests/headers.hurl0.011s
tests/hello.hurl0.003s
tests/multipart_form_data.hurl0.002s
tests/no_entry.hurl0s
tests/output.hurl0.003s
tests/patch.hurl0.002s
tests/post_base64.hurl0.002s
tests/post_file.hurl0.002s
tests/post_json.hurl0.016s
tests/post_multilines.hurl0.005s
tests/post_xml.hurl0.004s
tests/predicates-string.hurl0.004s
tests/put.hurl0.001s
tests/querystring_params.hurl0.008s
tests/redirect.hurl0.003s
tests/utf8.hurl0.002s
\ No newline at end of file diff --git a/integration/report/tests.json b/integration/report/tests.json index e0d17c7f0..c4d41a1c4 100644 --- a/integration/report/tests.json +++ b/integration/report/tests.json @@ -7,16 +7,7 @@ "method": "GET", "url": "http://localhost:8000/assert-base64", "queryString": [], - "headers": [ - { - "name": "User-Agent", - "value": "hurl/0.99.12" - }, - { - "name": "Host", - "value": "localhost" - } - ], + "headers": [], "cookies": [], "body": "" }, @@ -26,20 +17,20 @@ "cookies": [], "headers": [ { - "name": "content-type", + "name": "Content-Type", "value": "text/html; charset=utf-8" }, { - "name": "content-length", + "name": "Content-Length", "value": "19" }, { - "name": "server", + "name": "Server", "value": "Werkzeug/0.16.1 Python/3.5.3" }, { - "name": "date", - "value": "Wed, 26 Aug 2020 09:50:16 GMT" + "name": "Date", + "value": "Thu, 17 Sep 2020 13:58:47 GMT" } ] }, @@ -62,11 +53,11 @@ {}, {} ], - "time": 17 + "time": 1 } ], "success": true, - "time": 19, + "time": 2, "cookies": [] }, { @@ -77,16 +68,7 @@ "method": "GET", "url": "http://localhost:8000/assert-header", "queryString": [], - "headers": [ - { - "name": "User-Agent", - "value": "hurl/0.99.12" - }, - { - "name": "Host", - "value": "localhost" - } - ], + "headers": [], "cookies": [], "body": "" }, @@ -96,49 +78,52 @@ "cookies": [ { "name": "cookie1", - "value": "value1" + "value": "value1", + "path": "/" }, { "name": "cookie2", - "value": "value2" + "value": "value2", + "path": "/" }, { "name": "cookie3", - "value": "value3" + "value": "value3", + "path": "/" } ], "headers": [ { - "name": "content-type", + "name": "Content-Type", "value": "text/html; charset=utf-8" }, { - "name": "header1", + "name": "Header1", "value": "value1" }, { - "name": "set-cookie", + "name": "Set-Cookie", "value": "cookie1=value1; Path=/" }, { - "name": "set-cookie", + "name": "Set-Cookie", "value": "cookie2=value2; Path=/" }, { - "name": "set-cookie", + "name": "Set-Cookie", "value": "cookie3=value3; Path=/" }, { - "name": "content-length", + "name": "Content-Length", "value": "0" }, { - "name": "server", + "name": "Server", "value": "Werkzeug/0.16.1 Python/3.5.3" }, { - "name": "date", - "value": "Wed, 26 Aug 2020 09:50:17 GMT" + "name": "Date", + "value": "Thu, 17 Sep 2020 13:58:47 GMT" } ] }, @@ -170,32 +155,38 @@ {}, {} ], - "time": 21 + "time": 2 } ], "success": true, - "time": 23, + "time": 2, "cookies": [ { + "domain": "localhost", + "include_subdomain": "FALSE", + "path": "/", + "https": "FALSE", + "expires": "0", "name": "cookie1", - "value": "value1", - "domain": "localhost", - "path": "/", - "include_subdomain": false + "value": "value1" }, { + "domain": "localhost", + "include_subdomain": "FALSE", + "path": "/", + "https": "FALSE", + "expires": "0", "name": "cookie2", - "value": "value2", - "domain": "localhost", - "path": "/", - "include_subdomain": false + "value": "value2" }, { - "name": "cookie3", - "value": "value3", "domain": "localhost", + "include_subdomain": "FALSE", "path": "/", - "include_subdomain": false + "https": "FALSE", + "expires": "0", + "name": "cookie3", + "value": "value3" } ] }, @@ -207,16 +198,7 @@ "method": "GET", "url": "http://localhost:8000/assert-json", "queryString": [], - "headers": [ - { - "name": "User-Agent", - "value": "hurl/0.99.12" - }, - { - "name": "Host", - "value": "localhost" - } - ], + "headers": [], "cookies": [], "body": "" }, @@ -226,20 +208,20 @@ "cookies": [], "headers": [ { - "name": "content-type", + "name": "Content-Type", "value": "application/json" }, { - "name": "content-length", + "name": "Content-Length", "value": "146" }, { - "name": "server", + "name": "Server", "value": "Werkzeug/0.16.1 Python/3.5.3" }, { - "name": "date", - "value": "Wed, 26 Aug 2020 09:50:17 GMT" + "name": "Date", + "value": "Thu, 17 Sep 2020 13:58:47 GMT" } ] }, @@ -274,23 +256,14 @@ {}, {} ], - "time": 18 + "time": 1 }, { "request": { "method": "GET", "url": "http://localhost:8000/assert-json/index", "queryString": [], - "headers": [ - { - "name": "User-Agent", - "value": "hurl/0.99.12" - }, - { - "name": "Host", - "value": "localhost" - } - ], + "headers": [], "cookies": [], "body": "" }, @@ -300,20 +273,20 @@ "cookies": [], "headers": [ { - "name": "content-type", + "name": "Content-Type", "value": "text/html; charset=utf-8" }, { - "name": "content-length", + "name": "Content-Length", "value": "1" }, { - "name": "server", + "name": "Server", "value": "Werkzeug/0.16.1 Python/3.5.3" }, { - "name": "date", - "value": "Wed, 26 Aug 2020 09:50:17 GMT" + "name": "Date", + "value": "Thu, 17 Sep 2020 13:58:47 GMT" } ] }, @@ -340,23 +313,14 @@ }, {} ], - "time": 18 + "time": 1 }, { "request": { "method": "GET", "url": "http://localhost:8000/assert-json", "queryString": [], - "headers": [ - { - "name": "User-Agent", - "value": "hurl/0.99.12" - }, - { - "name": "Host", - "value": "localhost" - } - ], + "headers": [], "cookies": [], "body": "" }, @@ -366,20 +330,20 @@ "cookies": [], "headers": [ { - "name": "content-type", + "name": "Content-Type", "value": "application/json" }, { - "name": "content-length", + "name": "Content-Length", "value": "146" }, { - "name": "server", + "name": "Server", "value": "Werkzeug/0.16.1 Python/3.5.3" }, { - "name": "date", - "value": "Wed, 26 Aug 2020 09:50:17 GMT" + "name": "Date", + "value": "Thu, 17 Sep 2020 13:58:47 GMT" } ] }, @@ -405,11 +369,11 @@ {}, {} ], - "time": 24 + "time": 1 } ], "success": true, - "time": 73, + "time": 8, "cookies": [] }, { @@ -420,16 +384,7 @@ "method": "GET", "url": "http://localhost:8000/assert-match", "queryString": [], - "headers": [ - { - "name": "User-Agent", - "value": "hurl/0.99.12" - }, - { - "name": "Host", - "value": "localhost" - } - ], + "headers": [], "cookies": [], "body": "" }, @@ -439,20 +394,20 @@ "cookies": [], "headers": [ { - "name": "content-type", + "name": "Content-Type", "value": "application/json" }, { - "name": "content-length", + "name": "Content-Length", "value": "53" }, { - "name": "server", + "name": "Server", "value": "Werkzeug/0.16.1 Python/3.5.3" }, { - "name": "date", - "value": "Wed, 26 Aug 2020 09:50:17 GMT" + "name": "Date", + "value": "Thu, 17 Sep 2020 13:58:47 GMT" } ] }, @@ -478,11 +433,11 @@ {}, {} ], - "time": 21 + "time": 1 } ], "success": true, - "time": 49, + "time": 16, "cookies": [] }, { @@ -493,16 +448,7 @@ "method": "GET", "url": "http://localhost:8000/assert-regex", "queryString": [], - "headers": [ - { - "name": "User-Agent", - "value": "hurl/0.99.12" - }, - { - "name": "Host", - "value": "localhost" - } - ], + "headers": [], "cookies": [], "body": "" }, @@ -512,20 +458,20 @@ "cookies": [], "headers": [ { - "name": "content-type", + "name": "Content-Type", "value": "text/html; charset=utf-8" }, { - "name": "content-length", + "name": "Content-Length", "value": "12" }, { - "name": "server", + "name": "Server", "value": "Werkzeug/0.16.1 Python/3.5.3" }, { - "name": "date", - "value": "Wed, 26 Aug 2020 09:50:17 GMT" + "name": "Date", + "value": "Thu, 17 Sep 2020 13:58:47 GMT" } ] }, @@ -549,11 +495,11 @@ {}, {} ], - "time": 17 + "time": 1 } ], "success": true, - "time": 25, + "time": 4, "cookies": [] }, { @@ -564,16 +510,7 @@ "method": "GET", "url": "http://localhost:8000/assert-xpath", "queryString": [], - "headers": [ - { - "name": "User-Agent", - "value": "hurl/0.99.12" - }, - { - "name": "Host", - "value": "localhost" - } - ], + "headers": [], "cookies": [], "body": "" }, @@ -583,20 +520,20 @@ "cookies": [], "headers": [ { - "name": "content-type", + "name": "Content-Type", "value": "text/html; charset=utf-8" }, { - "name": "content-length", + "name": "Content-Length", "value": "18" }, { - "name": "server", + "name": "Server", "value": "Werkzeug/0.16.1 Python/3.5.3" }, { - "name": "date", - "value": "Wed, 26 Aug 2020 09:50:17 GMT" + "name": "Date", + "value": "Thu, 17 Sep 2020 13:58:47 GMT" } ] }, @@ -622,11 +559,11 @@ {}, {} ], - "time": 21 + "time": 1 } ], "success": true, - "time": 23, + "time": 2, "cookies": [] }, { @@ -637,16 +574,7 @@ "method": "GET", "url": "http://localhost:8000/bytes", "queryString": [], - "headers": [ - { - "name": "User-Agent", - "value": "hurl/0.99.12" - }, - { - "name": "Host", - "value": "localhost" - } - ], + "headers": [], "cookies": [], "body": "" }, @@ -656,20 +584,20 @@ "cookies": [], "headers": [ { - "name": "content-type", + "name": "Content-Type", "value": "application/octet-stream" }, { - "name": "content-length", + "name": "Content-Length", "value": "1" }, { - "name": "server", + "name": "Server", "value": "Werkzeug/0.16.1 Python/3.5.3" }, { - "name": "date", - "value": "Wed, 26 Aug 2020 09:50:17 GMT" + "name": "Date", + "value": "Thu, 17 Sep 2020 13:58:47 GMT" } ] }, @@ -692,11 +620,11 @@ {}, {} ], - "time": 17 + "time": 1 } ], "success": true, - "time": 20, + "time": 1, "cookies": [] }, { @@ -707,16 +635,7 @@ "method": "GET", "url": "http://localhost:8000/capture-and-assert", "queryString": [], - "headers": [ - { - "name": "User-Agent", - "value": "hurl/0.99.12" - }, - { - "name": "Host", - "value": "localhost" - } - ], + "headers": [], "cookies": [], "body": "" }, @@ -726,20 +645,20 @@ "cookies": [], "headers": [ { - "name": "content-type", + "name": "Content-Type", "value": "text/html; charset=utf-8" }, { - "name": "content-length", + "name": "Content-Length", "value": "12" }, { - "name": "server", + "name": "Server", "value": "Werkzeug/0.16.1 Python/3.5.3" }, { - "name": "date", - "value": "Wed, 26 Aug 2020 09:50:17 GMT" + "name": "Date", + "value": "Thu, 17 Sep 2020 13:58:47 GMT" } ] }, @@ -767,11 +686,11 @@ {}, {} ], - "time": 19 + "time": 1 } ], "success": true, - "time": 22, + "time": 2, "cookies": [] }, { @@ -782,16 +701,7 @@ "method": "GET", "url": "http://localhost:8000/captures", "queryString": [], - "headers": [ - { - "name": "User-Agent", - "value": "hurl/0.99.12" - }, - { - "name": "Host", - "value": "localhost" - } - ], + "headers": [], "cookies": [], "body": "" }, @@ -801,28 +711,28 @@ "cookies": [], "headers": [ { - "name": "content-type", + "name": "Content-Type", "value": "text/html; charset=utf-8" }, { - "name": "header1", + "name": "Header1", "value": "value1" }, { - "name": "header2", + "name": "Header2", "value": "Hello Bob!" }, { - "name": "content-length", + "name": "Content-Length", "value": "0" }, { - "name": "server", + "name": "Server", "value": "Werkzeug/0.16.1 Python/3.5.3" }, { - "name": "date", - "value": "Wed, 26 Aug 2020 09:50:17 GMT" + "name": "Date", + "value": "Thu, 17 Sep 2020 13:58:47 GMT" } ] }, @@ -855,12 +765,12 @@ {}, {} ], - "time": 25 + "time": 1 }, { "request": { "method": "GET", - "url": "http://localhost:8000/captures-check?param1=value1¶m2=Bob", + "url": "http://localhost:8000/captures-check", "queryString": [ { "name": "param1", @@ -871,16 +781,7 @@ "value": "Bob" } ], - "headers": [ - { - "name": "User-Agent", - "value": "hurl/0.99.12" - }, - { - "name": "Host", - "value": "localhost" - } - ], + "headers": [], "cookies": [], "body": "" }, @@ -890,20 +791,20 @@ "cookies": [], "headers": [ { - "name": "content-type", + "name": "Content-Type", "value": "text/html; charset=utf-8" }, { - "name": "content-length", + "name": "Content-Length", "value": "0" }, { - "name": "server", + "name": "Server", "value": "Werkzeug/0.16.1 Python/3.5.3" }, { - "name": "date", - "value": "Wed, 26 Aug 2020 09:50:17 GMT" + "name": "Date", + "value": "Thu, 17 Sep 2020 13:58:48 GMT" } ] }, @@ -925,23 +826,14 @@ }, {} ], - "time": 20 + "time": 1 }, { "request": { "method": "GET", "url": "http://localhost:8000/captures-json", "queryString": [], - "headers": [ - { - "name": "User-Agent", - "value": "hurl/0.99.12" - }, - { - "name": "Host", - "value": "localhost" - } - ], + "headers": [], "cookies": [], "body": "" }, @@ -951,20 +843,20 @@ "cookies": [], "headers": [ { - "name": "content-type", + "name": "Content-Type", "value": "text/html; charset=utf-8" }, { - "name": "content-length", + "name": "Content-Length", "value": "135" }, { - "name": "server", + "name": "Server", "value": "Werkzeug/0.16.1 Python/3.5.3" }, { - "name": "date", - "value": "Wed, 26 Aug 2020 09:50:17 GMT" + "name": "Date", + "value": "Thu, 17 Sep 2020 13:58:48 GMT" } ] }, @@ -1047,11 +939,11 @@ {}, {} ], - "time": 24 + "time": 1 } ], "success": true, - "time": 78, + "time": 8, "cookies": [] }, { @@ -1062,25 +954,11 @@ "method": "GET", "url": "http://localhost:8000/cookies/set-request-cookie1-valueA", "queryString": [], - "headers": [ - { - "name": "User-Agent", - "value": "hurl/0.99.12" - }, - { - "name": "Host", - "value": "localhost" - }, - { - "name": "Cookie", - "value": "cookie1=valueA" - } - ], + "headers": [], "cookies": [ { "name": "cookie1", - "value": "valueA", - "domain": "localhost" + "value": "valueA" } ], "body": "" @@ -1091,20 +969,20 @@ "cookies": [], "headers": [ { - "name": "content-type", + "name": "Content-Type", "value": "text/html; charset=utf-8" }, { - "name": "content-length", + "name": "Content-Length", "value": "0" }, { - "name": "server", + "name": "Server", "value": "Werkzeug/0.16.1 Python/3.5.3" }, { - "name": "date", - "value": "Wed, 26 Aug 2020 09:50:17 GMT" + "name": "Date", + "value": "Thu, 17 Sep 2020 13:58:48 GMT" } ] }, @@ -1126,34 +1004,15 @@ }, {} ], - "time": 38 + "time": 1 }, { "request": { "method": "GET", "url": "http://localhost:8000/cookies/set-session-cookie2-valueA", "queryString": [], - "headers": [ - { - "name": "User-Agent", - "value": "hurl/0.99.12" - }, - { - "name": "Host", - "value": "localhost" - }, - { - "name": "Cookie", - "value": "cookie1=valueA" - } - ], - "cookies": [ - { - "name": "cookie1", - "value": "valueA", - "domain": "localhost" - } - ], + "headers": [], + "cookies": [], "body": "" }, "response": { @@ -1162,29 +1021,30 @@ "cookies": [ { "name": "cookie2", - "value": "valueA" + "value": "valueA", + "path": "/" } ], "headers": [ { - "name": "content-type", + "name": "Content-Type", "value": "text/html; charset=utf-8" }, { - "name": "set-cookie", + "name": "Set-Cookie", "value": "cookie2=valueA; Path=/" }, { - "name": "content-length", + "name": "Content-Length", "value": "0" }, { - "name": "server", + "name": "Server", "value": "Werkzeug/0.16.1 Python/3.5.3" }, { - "name": "date", - "value": "Wed, 26 Aug 2020 09:50:17 GMT" + "name": "Date", + "value": "Thu, 17 Sep 2020 13:58:48 GMT" } ] }, @@ -1207,39 +1067,15 @@ {}, {} ], - "time": 22 + "time": 1 }, { "request": { "method": "GET", "url": "http://localhost:8000/cookies/assert-that-cookie2-is-valueA", "queryString": [], - "headers": [ - { - "name": "User-Agent", - "value": "hurl/0.99.12" - }, - { - "name": "Host", - "value": "localhost" - }, - { - "name": "Cookie", - "value": "cookie1=valueA; cookie2=valueA" - } - ], - "cookies": [ - { - "name": "cookie1", - "value": "valueA", - "domain": "localhost" - }, - { - "name": "cookie2", - "value": "valueA", - "domain": "localhost" - } - ], + "headers": [], + "cookies": [], "body": "" }, "response": { @@ -1248,20 +1084,20 @@ "cookies": [], "headers": [ { - "name": "content-type", + "name": "Content-Type", "value": "text/html; charset=utf-8" }, { - "name": "content-length", + "name": "Content-Length", "value": "0" }, { - "name": "server", + "name": "Server", "value": "Werkzeug/0.16.1 Python/3.5.3" }, { - "name": "date", - "value": "Wed, 26 Aug 2020 09:50:17 GMT" + "name": "Date", + "value": "Thu, 17 Sep 2020 13:58:48 GMT" } ] }, @@ -1283,23 +1119,14 @@ }, {} ], - "time": 22 + "time": 1 }, { "request": { "method": "GET", "url": "http://127.0.0.1:8000/cookies/assert-that-cookie2-is-not-in-session", "queryString": [], - "headers": [ - { - "name": "User-Agent", - "value": "hurl/0.99.12" - }, - { - "name": "Host", - "value": "127.0.0.1" - } - ], + "headers": [], "cookies": [], "body": "" }, @@ -1309,20 +1136,20 @@ "cookies": [], "headers": [ { - "name": "content-type", + "name": "Content-Type", "value": "text/html; charset=utf-8" }, { - "name": "content-length", + "name": "Content-Length", "value": "0" }, { - "name": "server", + "name": "Server", "value": "Werkzeug/0.16.1 Python/3.5.3" }, { - "name": "date", - "value": "Wed, 26 Aug 2020 09:50:17 GMT" + "name": "Date", + "value": "Thu, 17 Sep 2020 13:58:48 GMT" } ] }, @@ -1344,37 +1171,18 @@ }, {} ], - "time": 24 + "time": 1 }, { "request": { "method": "GET", "url": "http://localhost:8000/cookies/set-request-cookie2-valueB", "queryString": [], - "headers": [ - { - "name": "User-Agent", - "value": "hurl/0.99.12" - }, - { - "name": "Host", - "value": "localhost" - }, - { - "name": "Cookie", - "value": "cookie2=valueB; cookie1=valueA" - } - ], + "headers": [], "cookies": [ { "name": "cookie2", - "value": "valueB", - "domain": "localhost" - }, - { - "name": "cookie1", - "value": "valueA", - "domain": "localhost" + "value": "valueB" } ], "body": "" @@ -1385,20 +1193,20 @@ "cookies": [], "headers": [ { - "name": "content-type", + "name": "Content-Type", "value": "text/html; charset=utf-8" }, { - "name": "content-length", + "name": "Content-Length", "value": "0" }, { - "name": "server", + "name": "Server", "value": "Werkzeug/0.16.1 Python/3.5.3" }, { - "name": "date", - "value": "Wed, 26 Aug 2020 09:50:17 GMT" + "name": "Date", + "value": "Thu, 17 Sep 2020 13:58:48 GMT" } ] }, @@ -1420,39 +1228,15 @@ }, {} ], - "time": 18 + "time": 1 }, { "request": { "method": "GET", "url": "http://localhost:8000/cookies/assert-that-cookie2-is-valueB", "queryString": [], - "headers": [ - { - "name": "User-Agent", - "value": "hurl/0.99.12" - }, - { - "name": "Host", - "value": "localhost" - }, - { - "name": "Cookie", - "value": "cookie1=valueA; cookie2=valueB" - } - ], - "cookies": [ - { - "name": "cookie1", - "value": "valueA", - "domain": "localhost" - }, - { - "name": "cookie2", - "value": "valueB", - "domain": "localhost" - } - ], + "headers": [], + "cookies": [], "body": "" }, "response": { @@ -1461,20 +1245,20 @@ "cookies": [], "headers": [ { - "name": "content-type", + "name": "Content-Type", "value": "text/html; charset=utf-8" }, { - "name": "content-length", + "name": "Content-Length", "value": "0" }, { - "name": "server", + "name": "Server", "value": "Werkzeug/0.16.1 Python/3.5.3" }, { - "name": "date", - "value": "Wed, 26 Aug 2020 09:50:17 GMT" + "name": "Date", + "value": "Thu, 17 Sep 2020 13:58:48 GMT" } ] }, @@ -1496,39 +1280,15 @@ }, {} ], - "time": 63 + "time": 1 }, { "request": { "method": "GET", "url": "http://localhost:8000/cookies/delete-cookie2", "queryString": [], - "headers": [ - { - "name": "User-Agent", - "value": "hurl/0.99.12" - }, - { - "name": "Host", - "value": "localhost" - }, - { - "name": "Cookie", - "value": "cookie1=valueA; cookie2=valueB" - } - ], - "cookies": [ - { - "name": "cookie1", - "value": "valueA", - "domain": "localhost" - }, - { - "name": "cookie2", - "value": "valueB", - "domain": "localhost" - } - ], + "headers": [], + "cookies": [], "body": "" }, "response": { @@ -1537,29 +1297,32 @@ "cookies": [ { "name": "cookie2", - "value": "" + "value": "", + "expires": "Thu, 17-Sep-2020 13:58:48 GMT", + "max_age": 0, + "path": "/" } ], "headers": [ { - "name": "content-type", + "name": "Content-Type", "value": "text/html; charset=utf-8" }, { - "name": "set-cookie", - "value": "cookie2=; Expires=Wed, 26-Aug-2020 09:50:17 GMT; Max-Age=0; Path=/" + "name": "Set-Cookie", + "value": "cookie2=; Expires=Thu, 17-Sep-2020 13:58:48 GMT; Max-Age=0; Path=/" }, { - "name": "content-length", + "name": "Content-Length", "value": "0" }, { - "name": "server", + "name": "Server", "value": "Werkzeug/0.16.1 Python/3.5.3" }, { - "name": "date", - "value": "Wed, 26 Aug 2020 09:50:17 GMT" + "name": "Date", + "value": "Thu, 17 Sep 2020 13:58:48 GMT" } ] }, @@ -1582,34 +1345,15 @@ {}, {} ], - "time": 27 + "time": 1 }, { "request": { "method": "GET", "url": "http://localhost:8000/cookies/set", "queryString": [], - "headers": [ - { - "name": "User-Agent", - "value": "hurl/0.99.12" - }, - { - "name": "Host", - "value": "localhost" - }, - { - "name": "Cookie", - "value": "cookie1=valueA" - } - ], - "cookies": [ - { - "name": "cookie1", - "value": "valueA", - "domain": "localhost" - } - ], + "headers": [], + "cookies": [], "body": "" }, "response": { @@ -1618,47 +1362,58 @@ "cookies": [ { "name": "LSID", - "value": "DQAAAKEaem_vYg" + "value": "DQAAAKEaem_vYg", + "expires": "Wed, 13 Jan 2021 22:23:01 GMT", + "path": "/accounts", + "secure": true, + "httponly": true }, { "name": "HSID", "value": "AYQEVnDKrdst", - "domain": "foo.com" + "expires": "Wed, 13 Jan 2021 22:23:01 GMT", + "domain": ".localhost", + "path": "/", + "httponly": true }, { "name": "SSID", "value": "Ap4PGTEq", - "domain": "foo.com" + "expires": "Wed, 13 Jan 2021 22:23:01 GMT", + "domain": ".localhost", + "path": "/", + "secure": true, + "httponly": true } ], "headers": [ { - "name": "content-type", + "name": "Content-Type", "value": "text/html; charset=utf-8" }, { - "name": "set-cookie", + "name": "Set-Cookie", "value": "LSID=DQAAAKEaem_vYg; Expires=Wed, 13 Jan 2021 22:23:01 GMT; Secure; HttpOnly; Path=/accounts" }, { - "name": "set-cookie", - "value": "HSID=AYQEVnDKrdst; Domain=.foo.com; Expires=Wed, 13 Jan 2021 22:23:01 GMT; HttpOnly; Path=/" + "name": "Set-Cookie", + "value": "HSID=AYQEVnDKrdst; Domain=.localhost; Expires=Wed, 13 Jan 2021 22:23:01 GMT; HttpOnly; Path=/" }, { - "name": "set-cookie", - "value": "SSID=Ap4PGTEq; Domain=.foo.com; Expires=Wed, 13 Jan 2021 22:23:01 GMT; Secure; HttpOnly; Path=/" + "name": "Set-Cookie", + "value": "SSID=Ap4PGTEq; Domain=.localhost; Expires=Wed, 13 Jan 2021 22:23:01 GMT; Secure; HttpOnly; Path=/" }, { - "name": "content-length", + "name": "Content-Length", "value": "0" }, { - "name": "server", + "name": "Server", "value": "Werkzeug/0.16.1 Python/3.5.3" }, { - "name": "date", - "value": "Wed, 26 Aug 2020 09:50:17 GMT" + "name": "Date", + "value": "Thu, 17 Sep 2020 13:58:48 GMT" } ] }, @@ -1694,39 +1449,29 @@ {}, {} ], - "time": 28 + "time": 1 } ], "success": true, - "time": 256, + "time": 17, "cookies": [ { + "domain": "localhost", + "include_subdomain": "FALSE", + "path": "/", + "https": "FALSE", + "expires": "0", "name": "cookie1", - "value": "valueA", - "domain": "localhost", + "value": "valueA" + }, + { + "domain": "#HttpOnly_.localhost", + "include_subdomain": "TRUE", "path": "/", - "include_subdomain": true - }, - { - "name": "LSID", - "value": "DQAAAKEaem_vYg", - "domain": "localhost", - "path": "/accounts", - "include_subdomain": false - }, - { + "https": "FALSE", + "expires": "1610576581", "name": "HSID", - "value": "AYQEVnDKrdst", - "domain": "foo.com", - "path": "/", - "include_subdomain": true - }, - { - "name": "SSID", - "value": "Ap4PGTEq", - "domain": "foo.com", - "path": "/", - "include_subdomain": true + "value": "AYQEVnDKrdst" } ] }, @@ -1738,16 +1483,7 @@ "method": "DELETE", "url": "http://localhost:8000/delete", "queryString": [], - "headers": [ - { - "name": "User-Agent", - "value": "hurl/0.99.12" - }, - { - "name": "Host", - "value": "localhost" - } - ], + "headers": [], "cookies": [], "body": "" }, @@ -1757,20 +1493,20 @@ "cookies": [], "headers": [ { - "name": "content-type", + "name": "Content-Type", "value": "text/html; charset=utf-8" }, { - "name": "content-length", + "name": "Content-Length", "value": "0" }, { - "name": "server", + "name": "Server", "value": "Werkzeug/0.16.1 Python/3.5.3" }, { - "name": "date", - "value": "Wed, 26 Aug 2020 09:50:17 GMT" + "name": "Date", + "value": "Thu, 17 Sep 2020 13:58:48 GMT" } ] }, @@ -1792,11 +1528,11 @@ }, {} ], - "time": 32 + "time": 1 } ], "success": true, - "time": 33, + "time": 1, "cookies": [] }, { @@ -1814,16 +1550,7 @@ "method": "GET", "url": "http://localhost:8000/encoding/utf8", "queryString": [], - "headers": [ - { - "name": "User-Agent", - "value": "hurl/0.99.12" - }, - { - "name": "Host", - "value": "localhost" - } - ], + "headers": [], "cookies": [], "body": "" }, @@ -1833,20 +1560,20 @@ "cookies": [], "headers": [ { - "name": "content-type", + "name": "Content-Type", "value": "text/html; charset=utf-8" }, { - "name": "content-length", + "name": "Content-Length", "value": "5" }, { - "name": "server", + "name": "Server", "value": "Werkzeug/0.16.1 Python/3.5.3" }, { - "name": "date", - "value": "Wed, 26 Aug 2020 09:50:17 GMT" + "name": "Date", + "value": "Thu, 17 Sep 2020 13:58:48 GMT" } ] }, @@ -1870,23 +1597,14 @@ {}, {} ], - "time": 24 + "time": 1 }, { "request": { "method": "GET", "url": "http://localhost:8000/encoding/latin1", "queryString": [], - "headers": [ - { - "name": "User-Agent", - "value": "hurl/0.99.12" - }, - { - "name": "Host", - "value": "localhost" - } - ], + "headers": [], "cookies": [], "body": "" }, @@ -1896,20 +1614,20 @@ "cookies": [], "headers": [ { - "name": "content-type", + "name": "Content-Type", "value": "text/html; charset=ISO-8859-1" }, { - "name": "content-length", + "name": "Content-Length", "value": "4" }, { - "name": "server", + "name": "Server", "value": "Werkzeug/0.16.1 Python/3.5.3" }, { - "name": "date", - "value": "Wed, 26 Aug 2020 09:50:17 GMT" + "name": "Date", + "value": "Thu, 17 Sep 2020 13:58:48 GMT" } ] }, @@ -1933,11 +1651,11 @@ {}, {} ], - "time": 30 + "time": 1 } ], "success": true, - "time": 59, + "time": 4, "cookies": [] }, { @@ -1948,16 +1666,7 @@ "method": "GET", "url": "http://localhost:8000/assert-base64", "queryString": [], - "headers": [ - { - "name": "User-Agent", - "value": "hurl/0.99.12" - }, - { - "name": "Host", - "value": "localhost" - } - ], + "headers": [], "cookies": [], "body": "" }, @@ -1967,20 +1676,20 @@ "cookies": [], "headers": [ { - "name": "content-type", + "name": "Content-Type", "value": "text/html; charset=utf-8" }, { - "name": "content-length", + "name": "Content-Length", "value": "19" }, { - "name": "server", + "name": "Server", "value": "Werkzeug/0.16.1 Python/3.5.3" }, { - "name": "date", - "value": "Wed, 26 Aug 2020 09:50:18 GMT" + "name": "Date", + "value": "Thu, 17 Sep 2020 13:58:48 GMT" } ] }, @@ -2003,11 +1712,11 @@ {}, {} ], - "time": 49 + "time": 1 } ], "success": false, - "time": 52, + "time": 2, "cookies": [] }, { @@ -2018,16 +1727,7 @@ "method": "GET", "url": "http://localhost:8000/error-assert-file", "queryString": [], - "headers": [ - { - "name": "User-Agent", - "value": "hurl/0.99.12" - }, - { - "name": "Host", - "value": "localhost" - } - ], + "headers": [], "cookies": [], "body": "" }, @@ -2037,20 +1737,20 @@ "cookies": [], "headers": [ { - "name": "content-type", + "name": "Content-Type", "value": "text/html; charset=utf-8" }, { - "name": "content-length", + "name": "Content-Length", "value": "5" }, { - "name": "server", + "name": "Server", "value": "Werkzeug/0.16.1 Python/3.5.3" }, { - "name": "date", - "value": "Wed, 26 Aug 2020 09:50:18 GMT" + "name": "Date", + "value": "Thu, 17 Sep 2020 13:58:48 GMT" } ] }, @@ -2073,11 +1773,11 @@ {}, {} ], - "time": 22 + "time": 1 } ], "success": false, - "time": 25, + "time": 2, "cookies": [] }, { @@ -2088,16 +1788,7 @@ "method": "GET", "url": "http://localhost:8000/error-assert-header-not-found", "queryString": [], - "headers": [ - { - "name": "User-Agent", - "value": "hurl/0.99.12" - }, - { - "name": "Host", - "value": "localhost" - } - ], + "headers": [], "cookies": [], "body": "" }, @@ -2107,20 +1798,20 @@ "cookies": [], "headers": [ { - "name": "content-type", + "name": "Content-Type", "value": "text/html; charset=utf-8" }, { - "name": "content-length", + "name": "Content-Length", "value": "12" }, { - "name": "server", + "name": "Server", "value": "Werkzeug/0.16.1 Python/3.5.3" }, { - "name": "date", - "value": "Wed, 26 Aug 2020 09:50:18 GMT" + "name": "Date", + "value": "Thu, 17 Sep 2020 13:58:48 GMT" } ] }, @@ -2143,11 +1834,11 @@ {}, {} ], - "time": 24 + "time": 1 } ], "success": false, - "time": 28, + "time": 2, "cookies": [] }, { @@ -2158,16 +1849,7 @@ "method": "GET", "url": "http://localhost:8000/error-assert-header-value", "queryString": [], - "headers": [ - { - "name": "User-Agent", - "value": "hurl/0.99.12" - }, - { - "name": "Host", - "value": "localhost" - } - ], + "headers": [], "cookies": [], "body": "" }, @@ -2177,20 +1859,20 @@ "cookies": [], "headers": [ { - "name": "content-type", + "name": "Content-Type", "value": "text/html; charset=utf-8" }, { - "name": "content-length", + "name": "Content-Length", "value": "12" }, { - "name": "server", + "name": "Server", "value": "Werkzeug/0.16.1 Python/3.5.3" }, { - "name": "date", - "value": "Wed, 26 Aug 2020 09:50:18 GMT" + "name": "Date", + "value": "Thu, 17 Sep 2020 13:58:48 GMT" } ] }, @@ -2213,11 +1895,11 @@ {}, {} ], - "time": 22 + "time": 1 } ], "success": false, - "time": 25, + "time": 2, "cookies": [] }, { @@ -2228,16 +1910,7 @@ "method": "GET", "url": "http://localhost:8000/error-assert/http-version", "queryString": [], - "headers": [ - { - "name": "User-Agent", - "value": "hurl/0.99.12" - }, - { - "name": "Host", - "value": "localhost" - } - ], + "headers": [], "cookies": [], "body": "" }, @@ -2247,20 +1920,20 @@ "cookies": [], "headers": [ { - "name": "content-type", + "name": "Content-Type", "value": "text/html; charset=utf-8" }, { - "name": "content-length", + "name": "Content-Length", "value": "0" }, { - "name": "server", + "name": "Server", "value": "Werkzeug/0.16.1 Python/3.5.3" }, { - "name": "date", - "value": "Wed, 26 Aug 2020 09:50:18 GMT" + "name": "Date", + "value": "Thu, 17 Sep 2020 13:58:48 GMT" } ] }, @@ -2282,11 +1955,11 @@ }, {} ], - "time": 24 + "time": 1 } ], "success": false, - "time": 25, + "time": 2, "cookies": [] }, { @@ -2297,16 +1970,7 @@ "method": "GET", "url": "http://localhost:8000/error-assert-invalid-predicate-type", "queryString": [], - "headers": [ - { - "name": "User-Agent", - "value": "hurl/0.99.12" - }, - { - "name": "Host", - "value": "localhost" - } - ], + "headers": [], "cookies": [], "body": "" }, @@ -2316,20 +1980,20 @@ "cookies": [], "headers": [ { - "name": "content-type", + "name": "Content-Type", "value": "text/html; charset=utf-8" }, { - "name": "content-length", + "name": "Content-Length", "value": "0" }, { - "name": "server", + "name": "Server", "value": "Werkzeug/0.16.1 Python/3.5.3" }, { - "name": "date", - "value": "Wed, 26 Aug 2020 09:50:18 GMT" + "name": "Date", + "value": "Thu, 17 Sep 2020 13:58:48 GMT" } ] }, @@ -2352,11 +2016,11 @@ {}, {} ], - "time": 17 + "time": 1 } ], "success": false, - "time": 20, + "time": 3, "cookies": [] }, { @@ -2367,16 +2031,7 @@ "method": "GET", "url": "http://localhost:8000/error-assert/match-utf8", "queryString": [], - "headers": [ - { - "name": "User-Agent", - "value": "hurl/0.99.12" - }, - { - "name": "Host", - "value": "localhost" - } - ], + "headers": [], "cookies": [], "body": "" }, @@ -2386,20 +2041,20 @@ "cookies": [], "headers": [ { - "name": "content-type", + "name": "Content-Type", "value": "text/html; charset=utf-8" }, { - "name": "content-length", + "name": "Content-Length", "value": "1" }, { - "name": "server", + "name": "Server", "value": "Werkzeug/0.16.1 Python/3.5.3" }, { - "name": "date", - "value": "Wed, 26 Aug 2020 09:50:18 GMT" + "name": "Date", + "value": "Thu, 17 Sep 2020 13:58:48 GMT" } ] }, @@ -2422,11 +2077,11 @@ {}, {} ], - "time": 19 + "time": 1 } ], "success": false, - "time": 22, + "time": 2, "cookies": [] }, { @@ -2437,16 +2092,7 @@ "method": "GET", "url": "http://localhost:8000/error-assert-query-invalid-regex", "queryString": [], - "headers": [ - { - "name": "User-Agent", - "value": "hurl/0.99.12" - }, - { - "name": "Host", - "value": "localhost" - } - ], + "headers": [], "cookies": [], "body": "" }, @@ -2456,20 +2102,20 @@ "cookies": [], "headers": [ { - "name": "content-type", + "name": "Content-Type", "value": "text/html; charset=utf-8" }, { - "name": "content-length", + "name": "Content-Length", "value": "0" }, { - "name": "server", + "name": "Server", "value": "Werkzeug/0.16.1 Python/3.5.3" }, { - "name": "date", - "value": "Wed, 26 Aug 2020 09:50:18 GMT" + "name": "Date", + "value": "Thu, 17 Sep 2020 13:58:48 GMT" } ] }, @@ -2492,11 +2138,11 @@ {}, {} ], - "time": 17 + "time": 1 } ], "success": false, - "time": 18, + "time": 2, "cookies": [] }, { @@ -2507,16 +2153,7 @@ "method": "GET", "url": "http://localhost:8000/utf8", "queryString": [], - "headers": [ - { - "name": "User-Agent", - "value": "hurl/0.99.12" - }, - { - "name": "Host", - "value": "localhost" - } - ], + "headers": [], "cookies": [], "body": "" }, @@ -2526,20 +2163,20 @@ "cookies": [], "headers": [ { - "name": "content-type", + "name": "Content-Type", "value": "text/html; charset=utf-8" }, { - "name": "content-length", + "name": "Content-Length", "value": "18" }, { - "name": "server", + "name": "Server", "value": "Werkzeug/0.16.1 Python/3.5.3" }, { - "name": "date", - "value": "Wed, 26 Aug 2020 09:50:18 GMT" + "name": "Date", + "value": "Thu, 17 Sep 2020 13:58:48 GMT" } ] }, @@ -2562,11 +2199,11 @@ {}, {} ], - "time": 18 + "time": 1 } ], "success": false, - "time": 21, + "time": 2, "cookies": [] }, { @@ -2577,16 +2214,7 @@ "method": "GET", "url": "http://localhost:8000/not_found", "queryString": [], - "headers": [ - { - "name": "User-Agent", - "value": "hurl/0.99.12" - }, - { - "name": "Host", - "value": "localhost" - } - ], + "headers": [], "cookies": [], "body": "" }, @@ -2596,20 +2224,20 @@ "cookies": [], "headers": [ { - "name": "content-type", + "name": "Content-Type", "value": "text/html" }, { - "name": "content-length", + "name": "Content-Length", "value": "232" }, { - "name": "server", + "name": "Server", "value": "Werkzeug/0.16.1 Python/3.5.3" }, { - "name": "date", - "value": "Wed, 26 Aug 2020 09:50:18 GMT" + "name": "Date", + "value": "Thu, 17 Sep 2020 13:58:48 GMT" } ] }, @@ -2631,11 +2259,11 @@ }, {} ], - "time": 17 + "time": 1 } ], "success": false, - "time": 19, + "time": 2, "cookies": [] }, { @@ -2646,16 +2274,7 @@ "method": "GET", "url": "http://localhost:8000/error-assert-template-variable-not-found", "queryString": [], - "headers": [ - { - "name": "User-Agent", - "value": "hurl/0.99.12" - }, - { - "name": "Host", - "value": "localhost" - } - ], + "headers": [], "cookies": [], "body": "" }, @@ -2665,20 +2284,20 @@ "cookies": [], "headers": [ { - "name": "content-type", + "name": "Content-Type", "value": "text/html; charset=utf-8" }, { - "name": "content-length", + "name": "Content-Length", "value": "0" }, { - "name": "server", + "name": "Server", "value": "Werkzeug/0.16.1 Python/3.5.3" }, { - "name": "date", - "value": "Wed, 26 Aug 2020 09:50:18 GMT" + "name": "Date", + "value": "Thu, 17 Sep 2020 13:58:48 GMT" } ] }, @@ -2701,11 +2320,11 @@ {}, {} ], - "time": 26 + "time": 1 } ], "success": false, - "time": 27, + "time": 1, "cookies": [] }, { @@ -2716,16 +2335,7 @@ "method": "GET", "url": "http://localhost:8000/error-assert-value", "queryString": [], - "headers": [ - { - "name": "User-Agent", - "value": "hurl/0.99.12" - }, - { - "name": "Host", - "value": "localhost" - } - ], + "headers": [], "cookies": [], "body": "" }, @@ -2735,20 +2345,20 @@ "cookies": [], "headers": [ { - "name": "content-type", + "name": "Content-Type", "value": "text/html; charset=utf-8" }, { - "name": "content-length", + "name": "Content-Length", "value": "20" }, { - "name": "server", + "name": "Server", "value": "Werkzeug/0.16.1 Python/3.5.3" }, { - "name": "date", - "value": "Wed, 26 Aug 2020 09:50:18 GMT" + "name": "Date", + "value": "Thu, 17 Sep 2020 13:58:48 GMT" } ] }, @@ -2773,11 +2383,11 @@ {}, {} ], - "time": 21 + "time": 1 } ], "success": false, - "time": 30, + "time": 5, "cookies": [] }, { @@ -2788,16 +2398,7 @@ "method": "GET", "url": "http://localhost:8000/error-assert-variable", "queryString": [], - "headers": [ - { - "name": "User-Agent", - "value": "hurl/0.99.12" - }, - { - "name": "Host", - "value": "localhost" - } - ], + "headers": [], "cookies": [], "body": "" }, @@ -2807,20 +2408,20 @@ "cookies": [], "headers": [ { - "name": "content-type", + "name": "Content-Type", "value": "text/html; charset=utf-8" }, { - "name": "content-length", + "name": "Content-Length", "value": "0" }, { - "name": "server", + "name": "Server", "value": "Werkzeug/0.16.1 Python/3.5.3" }, { - "name": "date", - "value": "Wed, 26 Aug 2020 09:50:18 GMT" + "name": "Date", + "value": "Thu, 17 Sep 2020 13:58:48 GMT" } ] }, @@ -2859,11 +2460,11 @@ {}, {} ], - "time": 18 + "time": 1 } ], "success": false, - "time": 25, + "time": 4, "cookies": [] }, { @@ -2887,16 +2488,7 @@ "method": "GET", "url": "http://localhost:8000/error-invalid-jsonpath", "queryString": [], - "headers": [ - { - "name": "User-Agent", - "value": "hurl/0.99.12" - }, - { - "name": "Host", - "value": "localhost" - } - ], + "headers": [], "cookies": [], "body": "" }, @@ -2906,20 +2498,20 @@ "cookies": [], "headers": [ { - "name": "content-type", + "name": "Content-Type", "value": "application/json" }, { - "name": "content-length", + "name": "Content-Length", "value": "60" }, { - "name": "server", + "name": "Server", "value": "Werkzeug/0.16.1 Python/3.5.3" }, { - "name": "date", - "value": "Wed, 26 Aug 2020 09:50:18 GMT" + "name": "Date", + "value": "Thu, 17 Sep 2020 13:58:48 GMT" } ] }, @@ -2942,24 +2534,32 @@ {}, {} ], - "time": 40 + "time": 1 } ], "success": false, - "time": 43, + "time": 2, "cookies": [] }, { "filename": "tests/error_invalid_url.hurl", "entries": [ { + "request": { + "method": "GET", + "url": "unknown", + "queryString": [], + "headers": [], + "cookies": [], + "body": "" + }, "captures": [], "asserts": [], "time": 0 } ], "success": false, - "time": 0, + "time": 74, "cookies": [] }, { @@ -2970,16 +2570,7 @@ "method": "GET", "url": "http://localhost:8000/error-invalid-xml", "queryString": [], - "headers": [ - { - "name": "User-Agent", - "value": "hurl/0.99.12" - }, - { - "name": "Host", - "value": "localhost" - } - ], + "headers": [], "cookies": [], "body": "" }, @@ -2989,20 +2580,20 @@ "cookies": [], "headers": [ { - "name": "content-type", + "name": "Content-Type", "value": "text/html; charset=utf-8" }, { - "name": "content-length", + "name": "Content-Length", "value": "0" }, { - "name": "server", + "name": "Server", "value": "Werkzeug/0.16.1 Python/3.5.3" }, { - "name": "date", - "value": "Wed, 26 Aug 2020 09:50:18 GMT" + "name": "Date", + "value": "Thu, 17 Sep 2020 13:58:48 GMT" } ] }, @@ -3025,11 +2616,11 @@ {}, {} ], - "time": 34 + "time": 1 } ], "success": false, - "time": 36, + "time": 2, "cookies": [] }, { @@ -3053,16 +2644,7 @@ "method": "GET", "url": "http://localhost:8000/predicate/error/type", "queryString": [], - "headers": [ - { - "name": "User-Agent", - "value": "hurl/0.99.12" - }, - { - "name": "Host", - "value": "localhost" - } - ], + "headers": [], "cookies": [], "body": "" }, @@ -3072,20 +2654,20 @@ "cookies": [], "headers": [ { - "name": "content-type", + "name": "Content-Type", "value": "text/html; charset=utf-8" }, { - "name": "content-length", + "name": "Content-Length", "value": "74" }, { - "name": "server", + "name": "Server", "value": "Werkzeug/0.16.1 Python/3.5.3" }, { - "name": "date", - "value": "Wed, 26 Aug 2020 09:50:18 GMT" + "name": "Date", + "value": "Thu, 17 Sep 2020 13:58:48 GMT" } ] }, @@ -3118,11 +2700,11 @@ {}, {} ], - "time": 25 + "time": 1 } ], "success": false, - "time": 60, + "time": 13, "cookies": [] }, { @@ -3133,16 +2715,7 @@ "method": "GET", "url": "http://localhost:8000/error-query-header-not-found", "queryString": [], - "headers": [ - { - "name": "User-Agent", - "value": "hurl/0.99.12" - }, - { - "name": "Host", - "value": "localhost" - } - ], + "headers": [], "cookies": [], "body": "" }, @@ -3152,20 +2725,20 @@ "cookies": [], "headers": [ { - "name": "content-type", + "name": "Content-Type", "value": "text/html; charset=utf-8" }, { - "name": "content-length", + "name": "Content-Length", "value": "12" }, { - "name": "server", + "name": "Server", "value": "Werkzeug/0.16.1 Python/3.5.3" }, { - "name": "date", - "value": "Wed, 26 Aug 2020 09:50:19 GMT" + "name": "Date", + "value": "Thu, 17 Sep 2020 13:58:48 GMT" } ] }, @@ -3188,11 +2761,11 @@ {}, {} ], - "time": 30 + "time": 1 } ], "success": false, - "time": 33, + "time": 2, "cookies": [] }, { @@ -3203,16 +2776,7 @@ "method": "GET", "url": "http://localhost:8000/error-query-invalid-json", "queryString": [], - "headers": [ - { - "name": "User-Agent", - "value": "hurl/0.99.12" - }, - { - "name": "Host", - "value": "localhost" - } - ], + "headers": [], "cookies": [], "body": "" }, @@ -3222,20 +2786,20 @@ "cookies": [], "headers": [ { - "name": "content-type", + "name": "Content-Type", "value": "text/html; charset=utf-8" }, { - "name": "content-length", + "name": "Content-Length", "value": "12" }, { - "name": "server", + "name": "Server", "value": "Werkzeug/0.16.1 Python/3.5.3" }, { - "name": "date", - "value": "Wed, 26 Aug 2020 09:50:19 GMT" + "name": "Date", + "value": "Thu, 17 Sep 2020 13:58:48 GMT" } ] }, @@ -3258,11 +2822,11 @@ {}, {} ], - "time": 19 + "time": 1 } ], "success": false, - "time": 22, + "time": 2, "cookies": [] }, { @@ -3273,16 +2837,7 @@ "method": "GET", "url": "http://localhost:8000/error-query-invalid-utf8", "queryString": [], - "headers": [ - { - "name": "User-Agent", - "value": "hurl/0.99.12" - }, - { - "name": "Host", - "value": "localhost" - } - ], + "headers": [], "cookies": [], "body": "" }, @@ -3292,20 +2847,20 @@ "cookies": [], "headers": [ { - "name": "content-type", + "name": "Content-Type", "value": "text/html; charset=utf-8" }, { - "name": "content-length", + "name": "Content-Length", "value": "1" }, { - "name": "server", + "name": "Server", "value": "Werkzeug/0.16.1 Python/3.5.3" }, { - "name": "date", - "value": "Wed, 26 Aug 2020 09:50:19 GMT" + "name": "Date", + "value": "Thu, 17 Sep 2020 13:58:48 GMT" } ] }, @@ -3328,11 +2883,11 @@ {}, {} ], - "time": 21 + "time": 1 } ], "success": false, - "time": 24, + "time": 2, "cookies": [] }, { @@ -3356,16 +2911,7 @@ "method": "GET", "url": "http://localhost:8000/get-list", "queryString": [], - "headers": [ - { - "name": "User-Agent", - "value": "hurl/0.99.12" - }, - { - "name": "Host", - "value": "localhost" - } - ], + "headers": [], "cookies": [], "body": "" }, @@ -3375,20 +2921,20 @@ "cookies": [], "headers": [ { - "name": "content-type", + "name": "Content-Type", "value": "text/html; charset=utf-8" }, { - "name": "content-length", + "name": "Content-Length", "value": "18" }, { - "name": "server", + "name": "Server", "value": "Werkzeug/0.16.1 Python/3.5.3" }, { - "name": "date", - "value": "Wed, 26 Aug 2020 09:50:19 GMT" + "name": "Date", + "value": "Thu, 17 Sep 2020 13:58:48 GMT" } ] }, @@ -3419,7 +2965,7 @@ }, {} ], - "time": 18 + "time": 1 }, { "captures": [], @@ -3428,7 +2974,72 @@ } ], "success": false, - "time": 21, + "time": 2, + "cookies": [] + }, + { + "filename": "tests/follow_redirect.hurl", + "entries": [ + { + "request": { + "method": "GET", + "url": "http://localhost:8000/follow-redirect", + "queryString": [], + "headers": [], + "cookies": [], + "body": "" + }, + "response": { + "httpVersion": "HTTP/1.0", + "status": 302, + "cookies": [], + "headers": [ + { + "name": "Content-Type", + "value": "text/html; charset=utf-8" + }, + { + "name": "Content-Length", + "value": "287" + }, + { + "name": "Location", + "value": "http://localhost:8000/following-redirect" + }, + { + "name": "Server", + "value": "Werkzeug/0.16.1 Python/3.5.3" + }, + { + "name": "Date", + "value": "Thu, 17 Sep 2020 13:58:48 GMT" + } + ] + }, + "captures": [], + "asserts": [ + { + "source_info": { + "start": { + "line": 2, + "column": 6 + }, + "end": { + "line": 2, + "column": 9 + } + }, + "actual": "1.0", + "expected": "1.0" + }, + {}, + {} + ], + "time": 1 + } + ], + "success": false, + "time": 2, "cookies": [] }, { @@ -3439,22 +3050,9 @@ "method": "POST", "url": "http://localhost:8000/form-params", "queryString": [], - "headers": [ - { - "name": "Content-Type", - "value": "application/x-www-form-urlencoded" - }, - { - "name": "User-Agent", - "value": "hurl/0.99.12" - }, - { - "name": "Host", - "value": "localhost" - } - ], + "headers": [], "cookies": [], - "format_params": [ + "form": [ { "name": "param1", "value": "value1" @@ -3472,7 +3070,7 @@ "value": "a%3db" } ], - "body": "cGFyYW0xPXZhbHVlMSZwYXJhbTI9JnBhcmFtMz1hJTNkYiZwYXJhbTQ9YSUyNTNkYg==" + "body": "" }, "response": { "httpVersion": "HTTP/1.0", @@ -3480,20 +3078,20 @@ "cookies": [], "headers": [ { - "name": "content-type", + "name": "Content-Type", "value": "text/html; charset=utf-8" }, { - "name": "content-length", + "name": "Content-Length", "value": "0" }, { - "name": "server", + "name": "Server", "value": "Werkzeug/0.16.1 Python/3.5.3" }, { - "name": "date", - "value": "Wed, 26 Aug 2020 09:50:19 GMT" + "name": "Date", + "value": "Thu, 17 Sep 2020 13:58:48 GMT" } ] }, @@ -3515,7 +3113,7 @@ }, {} ], - "time": 20 + "time": 2 }, { "request": { @@ -3526,35 +3124,9 @@ { "name": "Content-Type", "value": "application/x-www-form-urlencoded" - }, - { - "name": "User-Agent", - "value": "hurl/0.99.12" - }, - { - "name": "Host", - "value": "localhost" } ], "cookies": [], - "format_params": [ - { - "name": "param1", - "value": "value1" - }, - { - "name": "param2", - "value": "" - }, - { - "name": "param3", - "value": "a=b" - }, - { - "name": "param4", - "value": "a%3db" - } - ], "body": "cGFyYW0xPXZhbHVlMSZwYXJhbTI9JnBhcmFtMz1hJTNkYiZwYXJhbTQ9YSUyNTNkYg==" }, "response": { @@ -3563,20 +3135,20 @@ "cookies": [], "headers": [ { - "name": "content-type", + "name": "Content-Type", "value": "text/html; charset=utf-8" }, { - "name": "content-length", + "name": "Content-Length", "value": "0" }, { - "name": "server", + "name": "Server", "value": "Werkzeug/0.16.1 Python/3.5.3" }, { - "name": "date", - "value": "Wed, 26 Aug 2020 09:50:19 GMT" + "name": "Date", + "value": "Thu, 17 Sep 2020 13:58:48 GMT" } ] }, @@ -3598,11 +3170,11 @@ }, {} ], - "time": 19 + "time": 1 } ], "success": true, - "time": 45, + "time": 4, "cookies": [] }, { @@ -3613,16 +3185,7 @@ "method": "GET", "url": "http://localhost:8000/default-headers", "queryString": [], - "headers": [ - { - "name": "User-Agent", - "value": "hurl/0.99.12" - }, - { - "name": "Host", - "value": "localhost" - } - ], + "headers": [], "cookies": [], "body": "" }, @@ -3632,20 +3195,20 @@ "cookies": [], "headers": [ { - "name": "content-type", + "name": "Content-Type", "value": "text/html; charset=utf-8" }, { - "name": "content-length", + "name": "Content-Length", "value": "0" }, { - "name": "server", + "name": "Server", "value": "Werkzeug/0.16.1 Python/3.5.3" }, { - "name": "date", - "value": "Wed, 26 Aug 2020 09:50:19 GMT" + "name": "Date", + "value": "Thu, 17 Sep 2020 13:58:48 GMT" } ] }, @@ -3667,7 +3230,7 @@ }, {} ], - "time": 22 + "time": 1 }, { "request": { @@ -3681,11 +3244,7 @@ }, { "name": "Host", - "value": "localhost" - }, - { - "name": "Content-Length", - "value": "0" + "value": "localhost:8000" } ], "cookies": [], @@ -3697,20 +3256,20 @@ "cookies": [], "headers": [ { - "name": "content-type", + "name": "Content-Type", "value": "text/html; charset=utf-8" }, { - "name": "content-length", + "name": "Content-Length", "value": "0" }, { - "name": "server", + "name": "Server", "value": "Werkzeug/0.16.1 Python/3.5.3" }, { - "name": "date", - "value": "Wed, 26 Aug 2020 09:50:19 GMT" + "name": "Date", + "value": "Thu, 17 Sep 2020 13:58:48 GMT" } ] }, @@ -3719,11 +3278,11 @@ { "source_info": { "start": { - "line": 8, + "line": 7, "column": 6 }, "end": { - "line": 8, + "line": 7, "column": 9 } }, @@ -3732,7 +3291,7 @@ }, {} ], - "time": 24 + "time": 1 }, { "request": { @@ -3746,11 +3305,7 @@ }, { "name": "Host", - "value": "localhost" - }, - { - "name": "Content-Length", - "value": "0" + "value": "localhost:8000" } ], "cookies": [], @@ -3762,20 +3317,20 @@ "cookies": [], "headers": [ { - "name": "content-type", + "name": "Content-Type", "value": "text/html; charset=utf-8" }, { - "name": "content-length", + "name": "Content-Length", "value": "0" }, { - "name": "server", + "name": "Server", "value": "Werkzeug/0.16.1 Python/3.5.3" }, { - "name": "date", - "value": "Wed, 26 Aug 2020 09:50:19 GMT" + "name": "Date", + "value": "Thu, 17 Sep 2020 13:58:48 GMT" } ] }, @@ -3784,11 +3339,11 @@ { "source_info": { "start": { - "line": 15, + "line": 13, "column": 6 }, "end": { - "line": 15, + "line": 13, "column": 9 } }, @@ -3797,7 +3352,7 @@ }, {} ], - "time": 18 + "time": 1 }, { "request": { @@ -3824,14 +3379,6 @@ { "name": "Color", "value": "Green" - }, - { - "name": "User-Agent", - "value": "hurl/0.99.12" - }, - { - "name": "Host", - "value": "localhost" } ], "cookies": [], @@ -3843,20 +3390,20 @@ "cookies": [], "headers": [ { - "name": "content-type", + "name": "Content-Type", "value": "text/html; charset=utf-8" }, { - "name": "content-length", + "name": "Content-Length", "value": "0" }, { - "name": "server", + "name": "Server", "value": "Werkzeug/0.16.1 Python/3.5.3" }, { - "name": "date", - "value": "Wed, 26 Aug 2020 09:50:19 GMT" + "name": "Date", + "value": "Thu, 17 Sep 2020 13:58:48 GMT" } ] }, @@ -3865,11 +3412,11 @@ { "source_info": { "start": { - "line": 23, + "line": 21, "column": 6 }, "end": { - "line": 23, + "line": 21, "column": 9 } }, @@ -3878,7 +3425,7 @@ }, {} ], - "time": 26 + "time": 1 }, { "request": { @@ -3889,14 +3436,6 @@ { "name": "Beverage", "value": "cafĂ©" - }, - { - "name": "User-Agent", - "value": "hurl/0.99.12" - }, - { - "name": "Host", - "value": "localhost" } ], "cookies": [], @@ -3908,20 +3447,20 @@ "cookies": [], "headers": [ { - "name": "content-type", + "name": "Content-Type", "value": "text/html; charset=utf-8" }, { - "name": "content-length", + "name": "Content-Length", "value": "0" }, { - "name": "server", + "name": "Server", "value": "Werkzeug/0.16.1 Python/3.5.3" }, { - "name": "date", - "value": "Wed, 26 Aug 2020 09:50:19 GMT" + "name": "Date", + "value": "Thu, 17 Sep 2020 13:58:48 GMT" } ] }, @@ -3930,11 +3469,11 @@ { "source_info": { "start": { - "line": 27, + "line": 25, "column": 6 }, "end": { - "line": 27, + "line": 25, "column": 9 } }, @@ -3943,23 +3482,14 @@ }, {} ], - "time": 19 + "time": 1 }, { "request": { "method": "GET", "url": "http://localhost:8000/response-headers", "queryString": [], - "headers": [ - { - "name": "User-Agent", - "value": "hurl/0.99.12" - }, - { - "name": "Host", - "value": "localhost" - } - ], + "headers": [], "cookies": [], "body": "" }, @@ -3969,24 +3499,24 @@ "cookies": [], "headers": [ { - "name": "content-type", + "name": "Content-Type", "value": "text/html; charset=utf-8" }, { - "name": "beverage", + "name": "Beverage", "value": "cafe" }, { - "name": "content-length", + "name": "Content-Length", "value": "0" }, { - "name": "server", + "name": "Server", "value": "Werkzeug/0.16.1 Python/3.5.3" }, { - "name": "date", - "value": "Wed, 26 Aug 2020 09:50:19 GMT" + "name": "Date", + "value": "Thu, 17 Sep 2020 13:58:48 GMT" } ] }, @@ -3995,11 +3525,11 @@ { "source_info": { "start": { - "line": 30, + "line": 28, "column": 6 }, "end": { - "line": 30, + "line": 28, "column": 9 } }, @@ -4009,11 +3539,11 @@ {}, {} ], - "time": 23 + "time": 1 } ], "success": true, - "time": 140, + "time": 11, "cookies": [] }, { @@ -4024,16 +3554,7 @@ "method": "GET", "url": "http://localhost:8000/hello", "queryString": [], - "headers": [ - { - "name": "User-Agent", - "value": "hurl/0.99.12" - }, - { - "name": "Host", - "value": "localhost" - } - ], + "headers": [], "cookies": [], "body": "" }, @@ -4043,20 +3564,20 @@ "cookies": [], "headers": [ { - "name": "content-type", + "name": "Content-Type", "value": "text/html; charset=utf-8" }, { - "name": "content-length", + "name": "Content-Length", "value": "12" }, { - "name": "server", + "name": "Server", "value": "Werkzeug/0.16.1 Python/3.5.3" }, { - "name": "date", - "value": "Wed, 26 Aug 2020 09:50:19 GMT" + "name": "Date", + "value": "Thu, 17 Sep 2020 13:58:48 GMT" } ] }, @@ -4079,23 +3600,14 @@ {}, {} ], - "time": 19 + "time": 1 }, { "request": { "method": "GET", "url": "http://localhost:8000/hello", "queryString": [], - "headers": [ - { - "name": "User-Agent", - "value": "hurl/0.99.12" - }, - { - "name": "Host", - "value": "localhost" - } - ], + "headers": [], "cookies": [], "body": "" }, @@ -4105,20 +3617,20 @@ "cookies": [], "headers": [ { - "name": "content-type", + "name": "Content-Type", "value": "text/html; charset=utf-8" }, { - "name": "content-length", + "name": "Content-Length", "value": "12" }, { - "name": "server", + "name": "Server", "value": "Werkzeug/0.16.1 Python/3.5.3" }, { - "name": "date", - "value": "Wed, 26 Aug 2020 09:50:19 GMT" + "name": "Date", + "value": "Thu, 17 Sep 2020 13:58:48 GMT" } ] }, @@ -4141,11 +3653,11 @@ {}, {} ], - "time": 21 + "time": 1 } ], "success": true, - "time": 46, + "time": 3, "cookies": [] }, { @@ -4156,16 +3668,7 @@ "method": "POST", "url": "http://localhost:8000/multipart-form-data", "queryString": [], - "headers": [ - { - "name": "User-Agent", - "value": "hurl/0.99.12" - }, - { - "name": "Host", - "value": "localhost" - } - ], + "headers": [], "cookies": [], "body": "" }, @@ -4175,20 +3678,20 @@ "cookies": [], "headers": [ { - "name": "content-type", + "name": "Content-Type", "value": "text/html; charset=utf-8" }, { - "name": "content-length", + "name": "Content-Length", "value": "0" }, { - "name": "server", + "name": "Server", "value": "Werkzeug/0.16.1 Python/3.5.3" }, { - "name": "date", - "value": "Wed, 26 Aug 2020 09:50:19 GMT" + "name": "Date", + "value": "Thu, 17 Sep 2020 13:58:48 GMT" } ] }, @@ -4210,11 +3713,11 @@ }, {} ], - "time": 23 + "time": 2 } ], "success": true, - "time": 24, + "time": 2, "cookies": [] }, { @@ -4232,20 +3735,7 @@ "method": "POST", "url": "http://localhost:8000/output/endpoint1", "queryString": [], - "headers": [ - { - "name": "Content-Type", - "value": "application/json" - }, - { - "name": "User-Agent", - "value": "hurl/0.99.12" - }, - { - "name": "Host", - "value": "localhost" - } - ], + "headers": [], "cookies": [], "body": "eyAidXNlciI6ICJib2IiIH0=" }, @@ -4259,15 +3749,15 @@ "value": "DATE1" }, { - "name": "content-type", + "name": "Content-Type", "value": "text/html; charset=utf-8" }, { - "name": "content-length", + "name": "Content-Length", "value": "19" }, { - "name": "server", + "name": "Server", "value": "Werkzeug/0.16.1 Python/3.5.3" } ] @@ -4290,23 +3780,14 @@ }, {} ], - "time": 26 + "time": 1 }, { "request": { "method": "GET", "url": "http://localhost:8000/output/endpoint2", "queryString": [], - "headers": [ - { - "name": "User-Agent", - "value": "hurl/0.99.12" - }, - { - "name": "Host", - "value": "localhost" - } - ], + "headers": [], "cookies": [], "body": "" }, @@ -4320,15 +3801,15 @@ "value": "DATE2" }, { - "name": "content-type", + "name": "Content-Type", "value": "text/html; charset=utf-8" }, { - "name": "content-length", + "name": "Content-Length", "value": "19" }, { - "name": "server", + "name": "Server", "value": "Werkzeug/0.16.1 Python/3.5.3" } ] @@ -4351,11 +3832,11 @@ }, {} ], - "time": 19 + "time": 1 } ], "success": true, - "time": 52, + "time": 3, "cookies": [] }, { @@ -4378,10 +3859,6 @@ { "name": "If-Match", "value": "\"e0023aa4e\"" - }, - { - "name": "User-Agent", - "value": "hurl/0.99.12" } ], "cookies": [], @@ -4393,24 +3870,24 @@ "cookies": [], "headers": [ { - "name": "content-type", + "name": "Content-Type", "value": "text/html; charset=utf-8" }, { - "name": "content-location", + "name": "Content-Location", "value": "/file.txt" }, { - "name": "etag", + "name": "ETag", "value": "\"e0023aa4f\"" }, { - "name": "server", + "name": "Server", "value": "Werkzeug/0.16.1 Python/3.5.3" }, { - "name": "date", - "value": "Wed, 26 Aug 2020 09:50:19 GMT" + "name": "Date", + "value": "Thu, 17 Sep 2020 13:58:48 GMT" } ] }, @@ -4434,11 +3911,11 @@ {}, {} ], - "time": 22 + "time": 1 } ], "success": true, - "time": 23, + "time": 2, "cookies": [] }, { @@ -4449,16 +3926,7 @@ "method": "POST", "url": "http://localhost:8000/post-base64", "queryString": [], - "headers": [ - { - "name": "User-Agent", - "value": "hurl/0.99.12" - }, - { - "name": "Host", - "value": "localhost" - } - ], + "headers": [], "cookies": [], "body": "SGVsbG8gV29ybGQh" }, @@ -4468,20 +3936,20 @@ "cookies": [], "headers": [ { - "name": "content-type", + "name": "Content-Type", "value": "text/html; charset=utf-8" }, { - "name": "content-length", + "name": "Content-Length", "value": "0" }, { - "name": "server", + "name": "Server", "value": "Werkzeug/0.16.1 Python/3.5.3" }, { - "name": "date", - "value": "Wed, 26 Aug 2020 09:50:19 GMT" + "name": "Date", + "value": "Thu, 17 Sep 2020 13:58:48 GMT" } ] }, @@ -4503,11 +3971,11 @@ }, {} ], - "time": 25 + "time": 1 } ], "success": true, - "time": 27, + "time": 2, "cookies": [] }, { @@ -4518,16 +3986,7 @@ "method": "POST", "url": "http://localhost:8000/post-file", "queryString": [], - "headers": [ - { - "name": "User-Agent", - "value": "hurl/0.99.12" - }, - { - "name": "Host", - "value": "localhost" - } - ], + "headers": [], "cookies": [], "body": "SGVsbG8gV29ybGQh" }, @@ -4537,20 +3996,20 @@ "cookies": [], "headers": [ { - "name": "content-type", + "name": "Content-Type", "value": "text/html; charset=utf-8" }, { - "name": "content-length", + "name": "Content-Length", "value": "0" }, { - "name": "server", + "name": "Server", "value": "Werkzeug/0.16.1 Python/3.5.3" }, { - "name": "date", - "value": "Wed, 26 Aug 2020 09:50:19 GMT" + "name": "Date", + "value": "Thu, 17 Sep 2020 13:58:48 GMT" } ] }, @@ -4572,11 +4031,11 @@ }, {} ], - "time": 18 + "time": 1 } ], "success": true, - "time": 20, + "time": 2, "cookies": [] }, { @@ -4587,20 +4046,7 @@ "method": "POST", "url": "http://localhost:8000/post-json", "queryString": [], - "headers": [ - { - "name": "Content-Type", - "value": "application/json" - }, - { - "name": "User-Agent", - "value": "hurl/0.99.12" - }, - { - "name": "Host", - "value": "localhost" - } - ], + "headers": [], "cookies": [], "body": "ewogICAgIm5hbWUiOiAiQm9iIiwKICAgICJwYXNzd29yZCI6ICJzZWNyZXQiCn0=" }, @@ -4610,20 +4056,20 @@ "cookies": [], "headers": [ { - "name": "content-type", + "name": "Content-Type", "value": "text/html; charset=utf-8" }, { - "name": "content-length", + "name": "Content-Length", "value": "0" }, { - "name": "server", + "name": "Server", "value": "Werkzeug/0.16.1 Python/3.5.3" }, { - "name": "date", - "value": "Wed, 26 Aug 2020 09:50:19 GMT" + "name": "Date", + "value": "Thu, 17 Sep 2020 13:58:48 GMT" } ] }, @@ -4645,27 +4091,14 @@ }, {} ], - "time": 37 + "time": 1 }, { "request": { "method": "POST", "url": "http://localhost:8000/post-json-array", "queryString": [], - "headers": [ - { - "name": "Content-Type", - "value": "application/json" - }, - { - "name": "User-Agent", - "value": "hurl/0.99.12" - }, - { - "name": "Host", - "value": "localhost" - } - ], + "headers": [], "cookies": [], "body": "WzEsMiwzXQ==" }, @@ -4675,20 +4108,20 @@ "cookies": [], "headers": [ { - "name": "content-type", + "name": "Content-Type", "value": "text/html; charset=utf-8" }, { - "name": "content-length", + "name": "Content-Length", "value": "0" }, { - "name": "server", + "name": "Server", "value": "Werkzeug/0.16.1 Python/3.5.3" }, { - "name": "date", - "value": "Wed, 26 Aug 2020 09:50:19 GMT" + "name": "Date", + "value": "Thu, 17 Sep 2020 13:58:48 GMT" } ] }, @@ -4710,27 +4143,14 @@ }, {} ], - "time": 32 + "time": 1 }, { "request": { "method": "POST", "url": "http://localhost:8000/post-json-string", "queryString": [], - "headers": [ - { - "name": "Content-Type", - "value": "application/json" - }, - { - "name": "User-Agent", - "value": "hurl/0.99.12" - }, - { - "name": "Host", - "value": "localhost" - } - ], + "headers": [], "cookies": [], "body": "IkhlbGxvIg==" }, @@ -4740,20 +4160,20 @@ "cookies": [], "headers": [ { - "name": "content-type", + "name": "Content-Type", "value": "text/html; charset=utf-8" }, { - "name": "content-length", + "name": "Content-Length", "value": "0" }, { - "name": "server", + "name": "Server", "value": "Werkzeug/0.16.1 Python/3.5.3" }, { - "name": "date", - "value": "Wed, 26 Aug 2020 09:50:19 GMT" + "name": "Date", + "value": "Thu, 17 Sep 2020 13:58:48 GMT" } ] }, @@ -4775,27 +4195,14 @@ }, {} ], - "time": 41 + "time": 1 }, { "request": { "method": "POST", "url": "http://localhost:8000/post-json-number", "queryString": [], - "headers": [ - { - "name": "Content-Type", - "value": "application/json" - }, - { - "name": "User-Agent", - "value": "hurl/0.99.12" - }, - { - "name": "Host", - "value": "localhost" - } - ], + "headers": [], "cookies": [], "body": "MTAw" }, @@ -4805,20 +4212,20 @@ "cookies": [], "headers": [ { - "name": "content-type", + "name": "Content-Type", "value": "text/html; charset=utf-8" }, { - "name": "content-length", + "name": "Content-Length", "value": "0" }, { - "name": "server", + "name": "Server", "value": "Werkzeug/0.16.1 Python/3.5.3" }, { - "name": "date", - "value": "Wed, 26 Aug 2020 09:50:19 GMT" + "name": "Date", + "value": "Thu, 17 Sep 2020 13:58:48 GMT" } ] }, @@ -4840,27 +4247,14 @@ }, {} ], - "time": 35 + "time": 1 }, { "request": { "method": "POST", "url": "http://localhost:8000/post-json-numbers", "queryString": [], - "headers": [ - { - "name": "Content-Type", - "value": "application/json" - }, - { - "name": "User-Agent", - "value": "hurl/0.99.12" - }, - { - "name": "Host", - "value": "localhost" - } - ], + "headers": [], "cookies": [], "body": "ewogICAgIm5hdHVyYWwiOiAxMDAsCiAgICAibmVnYXRpdmUiOiAtMSwKICAgICJmbG9hdCI6ICIzLjMzMzMzMzMzMzMzMzMzMyIsCiAgICAiZXhwb25lbnQiOiAxMDBlMTAwCn0=" }, @@ -4870,20 +4264,20 @@ "cookies": [], "headers": [ { - "name": "content-type", + "name": "Content-Type", "value": "text/html; charset=utf-8" }, { - "name": "content-length", + "name": "Content-Length", "value": "0" }, { - "name": "server", + "name": "Server", "value": "Werkzeug/0.16.1 Python/3.5.3" }, { - "name": "date", - "value": "Wed, 26 Aug 2020 09:50:19 GMT" + "name": "Date", + "value": "Thu, 17 Sep 2020 13:58:48 GMT" } ] }, @@ -4905,27 +4299,14 @@ }, {} ], - "time": 18 + "time": 1 }, { "request": { "method": "POST", "url": "http://localhost:8000/post-json-boolean", "queryString": [], - "headers": [ - { - "name": "Content-Type", - "value": "application/json" - }, - { - "name": "User-Agent", - "value": "hurl/0.99.12" - }, - { - "name": "Host", - "value": "localhost" - } - ], + "headers": [], "cookies": [], "body": "dHJ1ZQ==" }, @@ -4935,20 +4316,20 @@ "cookies": [], "headers": [ { - "name": "content-type", + "name": "Content-Type", "value": "text/html; charset=utf-8" }, { - "name": "content-length", + "name": "Content-Length", "value": "0" }, { - "name": "server", + "name": "Server", "value": "Werkzeug/0.16.1 Python/3.5.3" }, { - "name": "date", - "value": "Wed, 26 Aug 2020 09:50:20 GMT" + "name": "Date", + "value": "Thu, 17 Sep 2020 13:58:48 GMT" } ] }, @@ -4970,23 +4351,14 @@ }, {} ], - "time": 30 + "time": 1 }, { "request": { "method": "GET", "url": "http://localhost:8000/get-name", "queryString": [], - "headers": [ - { - "name": "User-Agent", - "value": "hurl/0.99.12" - }, - { - "name": "Host", - "value": "localhost" - } - ], + "headers": [], "cookies": [], "body": "" }, @@ -4996,20 +4368,20 @@ "cookies": [], "headers": [ { - "name": "content-type", + "name": "Content-Type", "value": "text/html; charset=utf-8" }, { - "name": "content-length", + "name": "Content-Length", "value": "3" }, { - "name": "server", + "name": "Server", "value": "Werkzeug/0.16.1 Python/3.5.3" }, { - "name": "date", - "value": "Wed, 26 Aug 2020 09:50:20 GMT" + "name": "Date", + "value": "Thu, 17 Sep 2020 13:58:48 GMT" } ] }, @@ -5036,27 +4408,14 @@ }, {} ], - "time": 43 + "time": 1 }, { "request": { "method": "POST", "url": "http://localhost:8000/post-json", "queryString": [], - "headers": [ - { - "name": "Content-Type", - "value": "application/json" - }, - { - "name": "User-Agent", - "value": "hurl/0.99.12" - }, - { - "name": "Host", - "value": "localhost" - } - ], + "headers": [], "cookies": [], "body": "ewogICAgIm5hbWUiOiAiQm9iIiwKICAgICJwYXNzd29yZCI6ICJzZWNyZXQiCn0=" }, @@ -5066,30 +4425,30 @@ "cookies": [], "headers": [ { - "name": "content-type", + "name": "Content-Type", "value": "text/html; charset=utf-8" }, { - "name": "content-length", + "name": "Content-Length", "value": "0" }, { - "name": "server", + "name": "Server", "value": "Werkzeug/0.16.1 Python/3.5.3" }, { - "name": "date", - "value": "Wed, 26 Aug 2020 09:50:20 GMT" + "name": "Date", + "value": "Thu, 17 Sep 2020 13:58:48 GMT" } ] }, "captures": [], "asserts": [], - "time": 32 + "time": 1 } ], "success": true, - "time": 294, + "time": 16, "cookies": [] }, { @@ -5100,16 +4459,7 @@ "method": "POST", "url": "http://localhost:8000/post-multilines", "queryString": [], - "headers": [ - { - "name": "User-Agent", - "value": "hurl/0.99.12" - }, - { - "name": "Host", - "value": "localhost" - } - ], + "headers": [], "cookies": [], "body": "bmFtZSxhZ2UKYm9iLDEwCmJpbGwsMjIK" }, @@ -5119,20 +4469,20 @@ "cookies": [], "headers": [ { - "name": "content-type", + "name": "Content-Type", "value": "text/html; charset=utf-8" }, { - "name": "content-length", + "name": "Content-Length", "value": "0" }, { - "name": "server", + "name": "Server", "value": "Werkzeug/0.16.1 Python/3.5.3" }, { - "name": "date", - "value": "Wed, 26 Aug 2020 09:50:20 GMT" + "name": "Date", + "value": "Thu, 17 Sep 2020 13:58:48 GMT" } ] }, @@ -5154,23 +4504,14 @@ }, {} ], - "time": 18 + "time": 1 }, { "request": { "method": "GET", "url": "http://localhost:8000/get-bob-age", "queryString": [], - "headers": [ - { - "name": "User-Agent", - "value": "hurl/0.99.12" - }, - { - "name": "Host", - "value": "localhost" - } - ], + "headers": [], "cookies": [], "body": "" }, @@ -5180,20 +4521,20 @@ "cookies": [], "headers": [ { - "name": "content-type", + "name": "Content-Type", "value": "text/html; charset=utf-8" }, { - "name": "content-length", + "name": "Content-Length", "value": "2" }, { - "name": "server", + "name": "Server", "value": "Werkzeug/0.16.1 Python/3.5.3" }, { - "name": "date", - "value": "Wed, 26 Aug 2020 09:50:20 GMT" + "name": "Date", + "value": "Thu, 17 Sep 2020 13:58:48 GMT" } ] }, @@ -5220,23 +4561,14 @@ }, {} ], - "time": 25 + "time": 1 }, { "request": { "method": "POST", "url": "http://localhost:8000/post-multilines", "queryString": [], - "headers": [ - { - "name": "User-Agent", - "value": "hurl/0.99.12" - }, - { - "name": "Host", - "value": "localhost" - } - ], + "headers": [], "cookies": [], "body": "bmFtZSxhZ2UKYm9iLDEwCmJpbGwsMjIK" }, @@ -5246,20 +4578,20 @@ "cookies": [], "headers": [ { - "name": "content-type", + "name": "Content-Type", "value": "text/html; charset=utf-8" }, { - "name": "content-length", + "name": "Content-Length", "value": "0" }, { - "name": "server", + "name": "Server", "value": "Werkzeug/0.16.1 Python/3.5.3" }, { - "name": "date", - "value": "Wed, 26 Aug 2020 09:50:20 GMT" + "name": "Date", + "value": "Thu, 17 Sep 2020 13:58:48 GMT" } ] }, @@ -5281,11 +4613,11 @@ }, {} ], - "time": 26 + "time": 1 } ], "success": true, - "time": 78, + "time": 5, "cookies": [] }, { @@ -5296,16 +4628,7 @@ "method": "POST", "url": "http://localhost:8000/post-xml", "queryString": [], - "headers": [ - { - "name": "User-Agent", - "value": "hurl/0.99.12" - }, - { - "name": "Host", - "value": "localhost" - } - ], + "headers": [], "cookies": [], "body": "PD94bWwgdmVyc2lvbj0iMS4wIj8+Cjxkcmluaz5jYWbDqTwvZHJpbms+" }, @@ -5315,20 +4638,20 @@ "cookies": [], "headers": [ { - "name": "content-type", + "name": "Content-Type", "value": "text/html; charset=utf-8" }, { - "name": "content-length", + "name": "Content-Length", "value": "0" }, { - "name": "server", + "name": "Server", "value": "Werkzeug/0.16.1 Python/3.5.3" }, { - "name": "date", - "value": "Wed, 26 Aug 2020 09:50:20 GMT" + "name": "Date", + "value": "Thu, 17 Sep 2020 13:58:48 GMT" } ] }, @@ -5350,23 +4673,14 @@ }, {} ], - "time": 31 + "time": 2 }, { "request": { "method": "POST", "url": "http://localhost:8000/post-xml-no-prolog", "queryString": [], - "headers": [ - { - "name": "User-Agent", - "value": "hurl/0.99.12" - }, - { - "name": "Host", - "value": "localhost" - } - ], + "headers": [], "cookies": [], "body": "PGRyaW5rPmNhZsOpPC9kcmluaz4=" }, @@ -5376,20 +4690,20 @@ "cookies": [], "headers": [ { - "name": "content-type", + "name": "Content-Type", "value": "text/html; charset=utf-8" }, { - "name": "content-length", + "name": "Content-Length", "value": "0" }, { - "name": "server", + "name": "Server", "value": "Werkzeug/0.16.1 Python/3.5.3" }, { - "name": "date", - "value": "Wed, 26 Aug 2020 09:50:20 GMT" + "name": "Date", + "value": "Thu, 17 Sep 2020 13:58:48 GMT" } ] }, @@ -5411,11 +4725,11 @@ }, {} ], - "time": 32 + "time": 1 } ], "success": true, - "time": 68, + "time": 4, "cookies": [] }, { @@ -5426,16 +4740,7 @@ "method": "GET", "url": "http://localhost:8000/predicates-string", "queryString": [], - "headers": [ - { - "name": "User-Agent", - "value": "hurl/0.99.12" - }, - { - "name": "Host", - "value": "localhost" - } - ], + "headers": [], "cookies": [], "body": "" }, @@ -5445,20 +4750,20 @@ "cookies": [], "headers": [ { - "name": "content-type", + "name": "Content-Type", "value": "text/html; charset=utf-8" }, { - "name": "content-length", + "name": "Content-Length", "value": "12" }, { - "name": "server", + "name": "Server", "value": "Werkzeug/0.16.1 Python/3.5.3" }, { - "name": "date", - "value": "Wed, 26 Aug 2020 09:50:20 GMT" + "name": "Date", + "value": "Thu, 17 Sep 2020 13:58:48 GMT" } ] }, @@ -5484,23 +4789,14 @@ {}, {} ], - "time": 28 + "time": 1 }, { "request": { "method": "GET", "url": "http://localhost:8000/predicates-string-empty", "queryString": [], - "headers": [ - { - "name": "User-Agent", - "value": "hurl/0.99.12" - }, - { - "name": "Host", - "value": "localhost" - } - ], + "headers": [], "cookies": [], "body": "" }, @@ -5510,20 +4806,20 @@ "cookies": [], "headers": [ { - "name": "content-type", + "name": "Content-Type", "value": "text/html; charset=utf-8" }, { - "name": "content-length", + "name": "Content-Length", "value": "0" }, { - "name": "server", + "name": "Server", "value": "Werkzeug/0.16.1 Python/3.5.3" }, { - "name": "date", - "value": "Wed, 26 Aug 2020 09:50:20 GMT" + "name": "Date", + "value": "Thu, 17 Sep 2020 13:58:48 GMT" } ] }, @@ -5547,11 +4843,11 @@ {}, {} ], - "time": 13 + "time": 1 } ], "success": true, - "time": 45, + "time": 4, "cookies": [] }, { @@ -5562,16 +4858,7 @@ "method": "PUT", "url": "http://localhost:8000/put", "queryString": [], - "headers": [ - { - "name": "User-Agent", - "value": "hurl/0.99.12" - }, - { - "name": "Host", - "value": "localhost" - } - ], + "headers": [], "cookies": [], "body": "" }, @@ -5581,20 +4868,20 @@ "cookies": [], "headers": [ { - "name": "content-type", + "name": "Content-Type", "value": "text/html; charset=utf-8" }, { - "name": "content-length", + "name": "Content-Length", "value": "0" }, { - "name": "server", + "name": "Server", "value": "Werkzeug/0.16.1 Python/3.5.3" }, { - "name": "date", - "value": "Wed, 26 Aug 2020 09:50:20 GMT" + "name": "Date", + "value": "Thu, 17 Sep 2020 13:58:48 GMT" } ] }, @@ -5616,11 +4903,11 @@ }, {} ], - "time": 23 + "time": 1 } ], "success": true, - "time": 24, + "time": 1, "cookies": [] }, { @@ -5629,7 +4916,7 @@ { "request": { "method": "GET", - "url": "http://localhost:8000/querystring-params?param1=value1¶m2=¶m3=a%3Db¶m4=1,2,3", + "url": "http://localhost:8000/querystring-params", "queryString": [ { "name": "param1", @@ -5648,16 +4935,7 @@ "value": "1,2,3" } ], - "headers": [ - { - "name": "User-Agent", - "value": "hurl/0.99.12" - }, - { - "name": "Host", - "value": "localhost" - } - ], + "headers": [], "cookies": [], "body": "" }, @@ -5667,20 +4945,20 @@ "cookies": [], "headers": [ { - "name": "content-type", + "name": "Content-Type", "value": "text/html; charset=utf-8" }, { - "name": "content-length", + "name": "Content-Length", "value": "0" }, { - "name": "server", + "name": "Server", "value": "Werkzeug/0.16.1 Python/3.5.3" }, { - "name": "date", - "value": "Wed, 26 Aug 2020 09:50:20 GMT" + "name": "Date", + "value": "Thu, 17 Sep 2020 13:58:48 GMT" } ] }, @@ -5702,23 +4980,14 @@ }, {} ], - "time": 38 + "time": 1 }, { "request": { "method": "GET", "url": "http://localhost:8000/querystring-params?param1=value1¶m2=¶m3=a%3db¶m4=1,2,3", "queryString": [], - "headers": [ - { - "name": "User-Agent", - "value": "hurl/0.99.12" - }, - { - "name": "Host", - "value": "localhost" - } - ], + "headers": [], "cookies": [], "body": "" }, @@ -5728,20 +4997,20 @@ "cookies": [], "headers": [ { - "name": "content-type", + "name": "Content-Type", "value": "text/html; charset=utf-8" }, { - "name": "content-length", + "name": "Content-Length", "value": "0" }, { - "name": "server", + "name": "Server", "value": "Werkzeug/0.16.1 Python/3.5.3" }, { - "name": "date", - "value": "Wed, 26 Aug 2020 09:50:20 GMT" + "name": "Date", + "value": "Thu, 17 Sep 2020 13:58:48 GMT" } ] }, @@ -5763,12 +5032,12 @@ }, {} ], - "time": 22 + "time": 1 }, { "request": { "method": "GET", - "url": "http://localhost:8000/querystring-params?param1=value1&¶m2=¶m3=a%3Db¶m4=1,2,3", + "url": "http://localhost:8000/querystring-params?param1=value1", "queryString": [ { "name": "param2", @@ -5783,16 +5052,7 @@ "value": "1,2,3" } ], - "headers": [ - { - "name": "User-Agent", - "value": "hurl/0.99.12" - }, - { - "name": "Host", - "value": "localhost" - } - ], + "headers": [], "cookies": [], "body": "" }, @@ -5802,20 +5062,20 @@ "cookies": [], "headers": [ { - "name": "content-type", + "name": "Content-Type", "value": "text/html; charset=utf-8" }, { - "name": "content-length", + "name": "Content-Length", "value": "0" }, { - "name": "server", + "name": "Server", "value": "Werkzeug/0.16.1 Python/3.5.3" }, { - "name": "date", - "value": "Wed, 26 Aug 2020 09:50:20 GMT" + "name": "Date", + "value": "Thu, 17 Sep 2020 13:58:48 GMT" } ] }, @@ -5837,28 +5097,19 @@ }, {} ], - "time": 33 + "time": 1 }, { "request": { "method": "GET", - "url": "http://localhost:8000/querystring-params-encoded?value1=/&value2=%2F&&value3=%2F", + "url": "http://localhost:8000/querystring-params-encoded?value1=/&value2=%2F", "queryString": [ { "name": "value3", "value": "/" } ], - "headers": [ - { - "name": "User-Agent", - "value": "hurl/0.99.12" - }, - { - "name": "Host", - "value": "localhost" - } - ], + "headers": [], "cookies": [], "body": "" }, @@ -5868,20 +5119,20 @@ "cookies": [], "headers": [ { - "name": "content-type", + "name": "Content-Type", "value": "text/html; charset=utf-8" }, { - "name": "content-length", + "name": "Content-Length", "value": "0" }, { - "name": "server", + "name": "Server", "value": "Werkzeug/0.16.1 Python/3.5.3" }, { - "name": "date", - "value": "Wed, 26 Aug 2020 09:50:20 GMT" + "name": "Date", + "value": "Thu, 17 Sep 2020 13:58:48 GMT" } ] }, @@ -5903,11 +5154,11 @@ }, {} ], - "time": 21 + "time": 1 } ], "success": true, - "time": 120, + "time": 8, "cookies": [] }, { @@ -5918,16 +5169,7 @@ "method": "GET", "url": "http://localhost:8000/redirect", "queryString": [], - "headers": [ - { - "name": "User-Agent", - "value": "hurl/0.99.12" - }, - { - "name": "Host", - "value": "localhost" - } - ], + "headers": [], "cookies": [], "body": "" }, @@ -5937,24 +5179,24 @@ "cookies": [], "headers": [ { - "name": "content-type", + "name": "Content-Type", "value": "text/html; charset=utf-8" }, { - "name": "content-length", + "name": "Content-Length", "value": "271" }, { - "name": "location", + "name": "Location", "value": "http://localhost:8000/redirected" }, { - "name": "server", + "name": "Server", "value": "Werkzeug/0.16.1 Python/3.5.3" }, { - "name": "date", - "value": "Wed, 26 Aug 2020 09:50:20 GMT" + "name": "Date", + "value": "Thu, 17 Sep 2020 13:58:48 GMT" } ] }, @@ -5977,23 +5219,14 @@ {}, {} ], - "time": 19 + "time": 1 }, { "request": { "method": "GET", "url": "http://localhost:8000/redirected", "queryString": [], - "headers": [ - { - "name": "User-Agent", - "value": "hurl/0.99.12" - }, - { - "name": "Host", - "value": "localhost" - } - ], + "headers": [], "cookies": [], "body": "" }, @@ -6003,20 +5236,20 @@ "cookies": [], "headers": [ { - "name": "content-type", + "name": "Content-Type", "value": "text/html; charset=utf-8" }, { - "name": "content-length", + "name": "Content-Length", "value": "0" }, { - "name": "server", + "name": "Server", "value": "Werkzeug/0.16.1 Python/3.5.3" }, { - "name": "date", - "value": "Wed, 26 Aug 2020 09:50:20 GMT" + "name": "Date", + "value": "Thu, 17 Sep 2020 13:58:48 GMT" } ] }, @@ -6038,11 +5271,11 @@ }, {} ], - "time": 39 + "time": 1 } ], "success": true, - "time": 62, + "time": 3, "cookies": [] }, { @@ -6053,16 +5286,7 @@ "method": "GET", "url": "http://localhost:8000/utf8", "queryString": [], - "headers": [ - { - "name": "User-Agent", - "value": "hurl/0.99.12" - }, - { - "name": "Host", - "value": "localhost" - } - ], + "headers": [], "cookies": [], "body": "" }, @@ -6072,20 +5296,20 @@ "cookies": [], "headers": [ { - "name": "content-type", + "name": "Content-Type", "value": "text/html; charset=utf-8" }, { - "name": "content-length", + "name": "Content-Length", "value": "18" }, { - "name": "server", + "name": "Server", "value": "Werkzeug/0.16.1 Python/3.5.3" }, { - "name": "date", - "value": "Wed, 26 Aug 2020 09:50:20 GMT" + "name": "Date", + "value": "Thu, 17 Sep 2020 13:58:48 GMT" } ] }, @@ -6109,11 +5333,11 @@ {}, {} ], - "time": 32 + "time": 1 } ], "success": true, - "time": 35, + "time": 2, "cookies": [] } ] \ No newline at end of file diff --git a/integration/tests/error_assert_match_utf8.err b/integration/tests/error_assert_match_utf8.err index 6c8792a24..f37cfe486 100644 --- a/integration/tests/error_assert_match_utf8.err +++ b/integration/tests/error_assert_match_utf8.err @@ -1,7 +1,7 @@ -error: Invalid Utf8 +error: Invalid Decoding --> tests/error_assert_match_utf8.hurl:4:1 | 4 | body matches ".*" - | ^^^^ The http response is not a valid utf8 string + | ^^^^ The body can not be decoded with charset 'utf-8' | diff --git a/integration/tests/error_invalid_url.err b/integration/tests/error_invalid_url.err index ff3ef628c..ad6e1de0c 100644 --- a/integration/tests/error_invalid_url.err +++ b/integration/tests/error_invalid_url.err @@ -1,7 +1,7 @@ -error: Invalid url +error: Http Connection --> tests/error_invalid_url.hurl:1:5 | 1 | GET unknown - | ^^^^^^^ Invalid url + | ^^^^^^^ can not connect to unknown | diff --git a/integration/tests/error_multipart_form_data.err b/integration/tests/error_multipart_form_data.err index fed6852c2..86be79fa3 100644 --- a/integration/tests/error_multipart_form_data.err +++ b/integration/tests/error_multipart_form_data.err @@ -2,6 +2,6 @@ error: File ReadAccess --> tests/error_multipart_form_data.hurl:4:15 | 4 | upload1: file,unknown; - | ^^^^^^^ File unknown can not be read + | ^^^^^^^ File tests/unknown can not be read | diff --git a/integration/tests/error_query_invalid_utf8.err b/integration/tests/error_query_invalid_utf8.err index 674abdaf4..6d3baa4d0 100644 --- a/integration/tests/error_query_invalid_utf8.err +++ b/integration/tests/error_query_invalid_utf8.err @@ -1,7 +1,7 @@ -error: Invalid Utf8 +error: Invalid Decoding --> tests/error_query_invalid_utf8.hurl:4:1 | 4 | jsonpath "$.errors" countEquals 2 - | ^^^^^^^^^^^^^^^^^^^ The http response is not a valid utf8 string + | ^^^^^^^^^^^^^^^^^^^ The body can not be decoded with charset 'utf-8' | diff --git a/integration/tests/headers.hurl b/integration/tests/headers.hurl index ce4314f0a..40f633427 100644 --- a/integration/tests/headers.hurl +++ b/integration/tests/headers.hurl @@ -3,15 +3,13 @@ HTTP/1.0 200 GET http://localhost:8000/default-headers User-Agent: hurl/1.0 -Host: localhost # comment -Content-Length: 0 +Host: localhost:8000 # comment HTTP/1.0 200 GET http://localhost:8000/default-headers User-Agent: hurl/1.0 -Host: localhost # comment -Content-Length: 0 +Host: localhost:8000 # comment HTTP/1.0 200 GET http://localhost:8000/custom-headers diff --git a/integration/tests/headers.py b/integration/tests/headers.py index d2c81a537..d48508212 100644 --- a/integration/tests/headers.py +++ b/integration/tests/headers.py @@ -6,8 +6,8 @@ from tests import app def default_headers(): print('> host:' + request.headers['Host'] + "'") assert 'hurl' in request.headers['User-Agent'] - assert request.headers['Host'] == 'localhost' - assert int(request.headers['Content-Length']) == 0 + assert request.headers['Host'] == 'localhost:8000' + assert 'Content-Length' not in request.headers return '' diff --git a/integration/tests/hello.py b/integration/tests/hello.py index 8dd79aed4..99d13c8a5 100644 --- a/integration/tests/hello.py +++ b/integration/tests/hello.py @@ -1,6 +1,10 @@ from tests import app +from flask import request @app.route("/hello") def hello(): + assert 'Content-Type' not in request.headers + assert 'Content-Length' not in request.headers + assert len(request.data) == 0 return 'Hello World!' diff --git a/src/bin/hurl.rs b/src/bin/hurl.rs index 323b23169..79e649b90 100644 --- a/src/bin/hurl.rs +++ b/src/bin/hurl.rs @@ -15,7 +15,7 @@ * limitations under the License. * */ -//use hurl::parser::error::ParseError; + use std::collections::HashMap; use std::env; use std::fs; @@ -30,11 +30,11 @@ use clap::{AppSettings, ArgMatches}; use hurl::cli; use hurl::core::common::FormatError; use hurl::html; -use hurl::http; +use hurl::http::libcurl; use hurl::parser; use hurl::runner; use hurl::runner::core::*; -use hurl::runner::{log_deserialize}; +use hurl::runner::log_deserialize; use hurl::format; @@ -46,11 +46,11 @@ pub struct CLIOptions { pub insecure: bool, pub variables: HashMap, pub to_entry: Option, - pub redirect: http::client::Redirect, - pub http_proxy: Option, - pub https_proxy: Option, - pub all_proxy: Option, - pub noproxy_hosts: Vec, + pub follow_location: bool, + pub max_redirect: Option, + pub proxy: Option, + pub no_proxy: Option, + pub cookie_input_file: Option, } @@ -58,12 +58,9 @@ fn execute(filename: &str, contents: String, current_dir: &Path, file_root: Option, - cookies: Vec, cli_options: CLIOptions, logger: format::logger::Logger, ) -> HurlResult { - let mut cookiejar = http::cookie::CookieJar::init(cookies); - match parser::parse_hurl_file(contents.as_str()) { Err(e) => { let error = hurl::format::error::Error { @@ -79,26 +76,23 @@ fn execute(filename: &str, std::process::exit(2); } Ok(hurl_file) => { - logger.verbose(format!("Fail fast: {}", cli_options.fail_fast).as_str()); - logger.verbose(format!("variables: {:?}", cli_options.variables).as_str()); - if let Some(proxy) = cli_options.http_proxy.clone() { - logger.verbose(format!("http_proxy: {}", proxy).as_str()); + logger.verbose(format!("fail fast: {}", cli_options.fail_fast).as_str()); + logger.verbose(format!("insecure: {}", cli_options.insecure).as_str()); + logger.verbose(format!("follow redirect: {}", cli_options.follow_location).as_str()); + if let Some(n) = cli_options.max_redirect { + logger.verbose(format!("max redirect: {}", n).as_str()); } - if let Some(proxy) = cli_options.https_proxy.clone() { - logger.verbose(format!("https_proxy: {}", proxy).as_str()); - } - if let Some(proxy) = cli_options.all_proxy.clone() { - logger.verbose(format!("all_proxy: {}", proxy).as_str()); - } - if !cli_options.noproxy_hosts.is_empty() { - logger.verbose(format!("noproxy: {}", cli_options.noproxy_hosts.join(", ")).as_str()); + if let Some(proxy) = cli_options.proxy.clone() { + logger.verbose(format!("proxy: {}", proxy).as_str()); + } + + if !cli_options.variables.is_empty() { + logger.verbose("variables:"); + for (name, value) in cli_options.variables.clone() { + logger.verbose(format!(" {}={}", name, value).as_str()); + } } - match cli_options.redirect { - http::client::Redirect::None {} => {} - http::client::Redirect::Limited(n) => logger.verbose(format!("follow redirect (max: {})", n).as_str()), - http::client::Redirect::Unlimited {} => logger.verbose("follow redirect"), - }; if let Some(to_entry) = cli_options.to_entry { if to_entry < hurl_file.entries.len() { @@ -108,16 +102,24 @@ fn execute(filename: &str, } } - let noproxy_hosts = cli_options.noproxy_hosts.clone(); - let redirect = cli_options.redirect.clone(); - let client = http::client::Client::init(http::client::ClientOptions { - noproxy_hosts, - insecure: cli_options.insecure, - redirect, - http_proxy: cli_options.http_proxy.clone(), - https_proxy: cli_options.https_proxy.clone(), - all_proxy: cli_options.all_proxy.clone(), - }); + let follow_location = cli_options.follow_location; + let verbose = cli_options.verbose; + let insecure = cli_options.insecure; + let max_redirect = cli_options.max_redirect; + let proxy = cli_options.proxy; + let no_proxy = cli_options.no_proxy; + let cookie_input_file = cli_options.cookie_input_file; + let options = libcurl::client::ClientOptions { + follow_location, + max_redirect, + cookie_input_file, + proxy, + no_proxy, + verbose, + insecure, + }; + let mut client = libcurl::client::Client::init(options); + let context_dir = match file_root { None => { @@ -140,9 +142,8 @@ fn execute(filename: &str, to_entry: cli_options.to_entry, }; runner::file::run(hurl_file, - client, + &mut client, filename.to_string(), - &mut cookiejar, context_dir, options, logger, @@ -163,19 +164,6 @@ fn output_color(matches: ArgMatches) -> bool { } -fn noproxy_host(matches: ArgMatches) -> Vec { - match matches.value_of("noproxy") { - Some(value) => { - value.split(',').map(|e| e.trim().to_string()).collect() - } - _ => if let Ok(v) = std::env::var("no_proxy") { - v.split(',').map(|e| e.trim().to_string()).collect() - } else { - vec![] - } - } -} - fn to_entry(matches: ArgMatches, logger: format::logger::Logger) -> Option { match matches.value_of("to_entry") { Some(value) => { @@ -352,7 +340,7 @@ fn app() -> clap::App<'static, 'static> { ) .arg( - clap::Arg::with_name("redirect") + clap::Arg::with_name("follow_location") .short("L") .long("location") .help("Follow redirects"), @@ -361,7 +349,6 @@ fn app() -> clap::App<'static, 'static> { clap::Arg::with_name("max_redirects") .long("max-redirs") .value_name("NUM") - .default_value("50") .allow_hyphen_values(true) .help("Maximum number of redirects allowed"), ) @@ -431,12 +418,22 @@ fn parse_options(matches: ArgMatches, logger: format::logger::Logger) -> Result< let fail_fast = !matches.is_present("fail_at_end"); let variables = variables(matches.clone(), logger.clone()); let to_entry = to_entry(matches.clone(), logger); - let redirect = cli::options::redirect(matches.is_present("redirect"), matches.value_of("max_redirects").unwrap_or_default())?; - let http_proxy = cli::options::proxy(matches.value_of("proxy"), env::var("http_proxy").ok())?; - let https_proxy = cli::options::proxy(matches.value_of("proxy"), env::var("https_proxy").ok())?; - let all_proxy = cli::options::proxy(matches.value_of("proxy"), env::var("all_proxy").ok())?; - let noproxy_hosts = noproxy_host(matches.clone()); + let proxy = matches.value_of("proxy").map(|x| x.to_string()); + let no_proxy = matches.value_of("proxy").map(|x| x.to_string()); let insecure = matches.is_present("insecure"); + let follow_location = matches.is_present("follow_location"); + let cookie_input_file = matches.value_of("cookie_input_file").map(|x| x.to_string()); + let max_redirect = match matches.value_of("max_redirects") { + None => Some(50), + Some("-1") => None, + Some(s) => { + match s.parse::() { + Ok(x) => Some(x), + Err(_) => return Err(cli::Error{ message: "max_redirs option can not be parsed".to_string() }) + } + } + }; + Ok(CLIOptions { verbose, color, @@ -444,11 +441,11 @@ fn parse_options(matches: ArgMatches, logger: format::logger::Logger) -> Result< insecure, variables, to_entry, - redirect, - http_proxy, - https_proxy, - all_proxy, - noproxy_hosts, + follow_location, + max_redirect, + proxy, + no_proxy, + cookie_input_file, }) } @@ -485,12 +482,6 @@ fn main() -> Result<(), cli::Error> { color: output_color(matches.clone()), }; - let cookies = match matches.value_of("cookies_input_file") { - Some(filename) => unwrap_or_exit(cli::options::cookies(filename), logger.clone()), - None => vec![], - }; - - let (mut hurl_results, json_file) = json_file(matches.clone(), logger.clone()); let html_report = html_report(matches.clone(), logger.clone()); @@ -536,7 +527,6 @@ fn main() -> Result<(), cli::Error> { contents, current_dir, file_root.clone(), - cookies.clone(), cli_options.clone(), logger.clone(), ); @@ -661,7 +651,7 @@ fn write_cookies_file(file_path: PathBuf, hurl_results: Vec, logger: } Some(result) => { for cookie in result.cookies.clone() { - s.push_str(cookie.to_netscape().as_str()); + s.push_str(cookie.to_string().as_str()); s.push('\n'); } } diff --git a/src/cli/options.rs b/src/cli/options.rs index 015ac3820..81a9aee52 100644 --- a/src/cli/options.rs +++ b/src/cli/options.rs @@ -15,9 +15,6 @@ * limitations under the License. * */ -use std::fs; - -use crate::http; use super::Error; @@ -32,34 +29,6 @@ pub fn cookies_output_file(filename: String, n: usize) -> Result Result, Error> { - let path = std::path::Path::new(filename); - if !path.exists() { - return Err(Error { - message: format!("file {} does not exist", filename) - }); - } - let s = fs::read_to_string(filename).expect("Something went wrong reading the file"); - let lines: Vec<&str> = regex::Regex::new(r"\n|\r\n") - .unwrap() - .split(&s) - .collect(); - - let mut cookies = vec![]; - for line in lines { - if line.starts_with('#') || line.is_empty() { - continue; - } - if let Some(cookie) = http::cookie::Cookie::from_netscape(line) { - cookies.push(cookie); - } else { - return Err(Error { - message: format!("Cookie {} can not be parsed", line) - }); - }; - } - Ok(cookies) -} pub fn output_color(color_present: bool, no_color_present: bool, stdout: bool) -> bool { if color_present { @@ -71,55 +40,8 @@ pub fn output_color(color_present: bool, no_color_present: bool, stdout: bool) - } } -pub fn redirect(redirect_present: bool, max_redirect: &str) -> Result { - if redirect_present { - if max_redirect == "-1" { - Ok(http::client::Redirect::Unlimited) - } else if let Ok(n) = max_redirect.parse::() { - Ok(http::client::Redirect::Limited(n)) - } else { - Err(Error { message: "Invalid value for option --max-redirs".to_string() }) - } - } else { - Ok(http::client::Redirect::None) - } -} -pub fn validate_proxy(url: String) -> Result { - // validate proxy value at parsing - // use code from reqwest for the timebeing - let url = if url.starts_with("http") { - url - } else { - format!("http://{}", url) - }; - match reqwest::Proxy::http(url.as_str()) { - Ok(_) => Ok(url), - Err(_) => Err(Error { message: format!("Invalid proxy url <{}>", url) }) - } -} - -pub fn proxy(option_value: Option<&str>, env_value: Option) -> Result, Error> { - match option_value { - Some(url) => if url.is_empty() { - Ok(None) - } else { - let url = validate_proxy(url.to_string())?; - Ok(Some(url)) - }, - None => match env_value { - Some(url) => if url.is_empty() { - Ok(None) - } else { - let url = validate_proxy(url)?; - Ok(Some(url)) - }, - None => Ok(None), - } - } -} - #[cfg(test)] mod tests { @@ -131,20 +53,5 @@ mod tests { assert_eq!(output_color(false, false, true), true); } - #[test] - fn test_redirect() { - assert_eq!(redirect(false, "10").unwrap(), http::client::Redirect::None); - assert_eq!(redirect(true, "10").unwrap(), http::client::Redirect::Limited(10)); - assert_eq!(redirect(true, "-1").unwrap(), http::client::Redirect::Unlimited); - assert_eq!(redirect(true, "A").err().unwrap().message, "Invalid value for option --max-redirs"); - } - #[test] - fn test_http_proxy() { - assert_eq!(proxy(None, None).unwrap(), None); - assert_eq!(proxy(Some("http://localhost:8001"), None).unwrap(), Some("http://localhost:8001".to_string())); - assert_eq!(proxy(Some("http://localhost:8001"), Some("http://localhost:8002".to_string())).unwrap(), Some("http://localhost:8001".to_string())); - assert_eq!(proxy(Some(""), Some("http://localhost:8002".to_string())).unwrap(), None); - assert_eq!(proxy(None, Some("http://localhost:8002".to_string())).unwrap(), Some("http://localhost:8002".to_string())); - } } diff --git a/src/core/common.rs b/src/core/common.rs index 1940fddda..853adbf94 100644 --- a/src/core/common.rs +++ b/src/core/common.rs @@ -17,7 +17,7 @@ */ use std::fmt; -use serde::{Deserialize, Serialize}; +use serde::Serialize; use serde::ser::Serializer; #[derive(Clone, Debug, PartialEq, Eq)] @@ -31,7 +31,7 @@ pub enum DeprecatedValue { ListInt(Vec), } -#[derive(Clone, Debug, PartialEq, Eq, Deserialize)] +#[derive(Clone, Debug, PartialEq, Eq)] //#[derive(Clone, Debug, PartialEq, PartialOrd)] pub enum Value { Bool(bool), @@ -130,13 +130,13 @@ impl Value { } -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Eq)] pub struct Pos { pub line: usize, pub column: usize, } -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Eq)] pub struct SourceInfo { pub start: Pos, pub end: Pos, diff --git a/src/http/client.rs b/src/http/client.rs deleted file mode 100644 index cfaf2d610..000000000 --- a/src/http/client.rs +++ /dev/null @@ -1,216 +0,0 @@ -/* - * hurl (https://hurl.dev) - * Copyright (C) 2020 Orange - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - - -use std::path::Path; - -use super::core::*; -use super::request::*; -use super::response::*; - -pub struct Client { - _inner_client: reqwest::Client, - options: ClientOptions, -} - -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct ClientOptions { - pub noproxy_hosts: Vec, - pub insecure: bool, - pub redirect: Redirect, - pub http_proxy: Option, - pub https_proxy: Option, - pub all_proxy: Option, -} - -#[derive(Clone, Debug, PartialEq, Eq)] -pub enum Redirect { - None, - Limited(usize), - Unlimited, -} - - -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct HttpError { - pub url: String, - pub message: String, -} - -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct Proxy { - pub protocol: Option, - pub host: String, - pub port: Option, -} - -#[derive(Clone, Debug, PartialEq, Eq)] -pub enum ProxyProtocol { - Http, - Https, -} - - -pub fn get_redirect_policy(redirect: Redirect) -> reqwest::RedirectPolicy { - match redirect { - Redirect::None => reqwest::RedirectPolicy::none(), - Redirect::Limited(max) => reqwest::RedirectPolicy::limited(max), - Redirect::Unlimited {} => reqwest::RedirectPolicy::custom(|attempt| { attempt.follow() }), - } -} - -impl Client { - pub fn init(options: ClientOptions) -> Client { - let client_builder = reqwest::Client::builder() - .redirect(get_redirect_policy(options.redirect.clone())) - .use_sys_proxy() - .danger_accept_invalid_hostnames(options.insecure) - .danger_accept_invalid_certs(options.insecure) - .cookie_store(false); - Client { - _inner_client: client_builder.build().unwrap(), - options, - } - } - - pub fn execute(&self, request: &Request) -> Result { - let mut headers = reqwest::header::HeaderMap::new(); - for header in request.clone().headers() { - headers.append( - reqwest::header::HeaderName::from_lowercase( - header.name.to_lowercase().as_str().as_bytes(), - ) - .unwrap(), - reqwest::header::HeaderValue::from_str(header.value.as_str()).unwrap(), - ); - } - - - let client_builder = reqwest::Client::builder() - .redirect(get_redirect_policy(self.options.redirect.clone())) - .danger_accept_invalid_hostnames(self.options.insecure) - .danger_accept_invalid_certs(self.options.insecure) - .cookie_store(false); - - - let client_builder = if let Some(url) = self.options.http_proxy.clone() { - let proxy = reqwest::Proxy::http(url.as_str()).unwrap(); - client_builder.proxy(proxy) - } else { - client_builder - }; - let client_builder = if let Some(url) = self.options.https_proxy.clone() { - let proxy = reqwest::Proxy::https(url.as_str()).unwrap(); - client_builder.proxy(proxy) - } else { - client_builder - }; - let client_builder = if let Some(url) = self.options.all_proxy.clone() { - let proxy = reqwest::Proxy::all(url.as_str()).unwrap(); - client_builder.proxy(proxy) - } else { - client_builder - }; - - let client_builder = if self.options.noproxy_hosts.contains(&request.url.host.clone()) { - client_builder.no_proxy() - } else { - client_builder - }; - - let client = client_builder.build().unwrap(); - - - let req = if request.multipart.is_empty() { - client - .request( - request.clone().method.to_reqwest(), - reqwest::Url::parse(request.clone().url().as_str()).unwrap(), - ) - .headers(headers) - .body(request.clone().body) - .build() - .unwrap() - } else { - let mut form = reqwest::multipart::Form::new(); - for param in request.multipart.clone() { - match param { - MultipartParam::TextParam { name, value } => { - form = form.text(name, value) - } - MultipartParam::FileParam { name, filename, content_type } => { - if let Some(content_type) = content_type { - let path = Path::new(filename.as_str()); - let part = reqwest::multipart::Part::file(path).unwrap() - .mime_str(content_type.as_str()) - .unwrap(); - form = form.part(name, part); - } else { - form = form.file(name, filename).unwrap(); - } - } - } - } - client - .request( - request.clone().method.to_reqwest(), - reqwest::Url::parse(request.clone().url().as_str()).unwrap(), - ) - .headers(headers) - .multipart(form) - .build() - .unwrap() - }; - - match client.execute(req) { - Ok(mut resp) => { - let mut headers = vec![]; - //eprintln!(">>> response headers {:?}", resp.headers().clone()); - for (name, value) in resp.headers() { - headers.push(Header { - name: name.as_str().to_string(), - value: value.to_str().unwrap().to_string(), - }) - } - - let version = match resp.version() { - reqwest::Version::HTTP_10 => Version::Http10, - reqwest::Version::HTTP_11 => Version::Http11, - reqwest::Version::HTTP_2 => Version::Http2, - v => panic!("Version {:?} not supported!", v), - }; - let mut buf: Vec = vec![]; - resp.copy_to(&mut buf).unwrap(); // TODO Test error case - resp.content_length(); // dirty hack to prevent error "connection closed before message completed"? - - Ok(Response { - version, - status: resp.status().as_u16(), - headers, - body: buf, - }) - } - Err(e) => { - Err(HttpError { - message: format!("{:?}", e.to_string()), - url: request.clone().url(), - }) - } - } - } -} diff --git a/src/http/cookie.rs b/src/http/cookie.rs deleted file mode 100644 index 8cf767d41..000000000 --- a/src/http/cookie.rs +++ /dev/null @@ -1,472 +0,0 @@ -/* - * hurl (https://hurl.dev) - * Copyright (C) 2020 Orange - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ -use core::fmt; - -use chrono::NaiveDateTime; -use cookie::Cookie as ExternalCookie; - -use super::core::*; - -//use std::collections::HashMap; - -// cookies -// keep cookies same name different domains -// send the most specific?? send the 2 of them? -// more flexible to keep list of cookies internally - -pub type Domain = String; -pub type Name = String; - -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct ResponseCookie { - pub name: String, - pub value: String, - pub max_age: Option, - pub domain: Option, - pub path: Option, - pub secure: Option, - pub http_only: Option, - pub expires: Option, - pub same_site: Option, - -} - -pub struct ParseCookieError {} - -impl std::str::FromStr for ResponseCookie { - type Err = ParseCookieError; - - fn from_str(s: &str) -> Result { - let c = ExternalCookie::parse(s).unwrap(); - let name = c.name().to_string(); - let value = c.value().to_string(); - let max_age = match c.max_age() { - None => None, - Some(d) => Some(d.num_seconds()) - }; - let domain = match c.domain() { - None => None, - Some(v) => Some(v.to_string()) - }; - let path = match c.path() { - None => None, - Some(v) => Some(v.to_string()) - }; - let secure = match c.secure() { - None => None, - Some(value) => Some(value) - }; - let http_only = match c.http_only() { - None => None, - Some(value) => Some(value) - }; - let expires = match c.expires() { - None => None, - Some(time) => Some(time.rfc822().to_string()) - }; - let same_site = match c.same_site() { - None => None, - Some(s) => Some(s.to_string()) - }; - Ok(ResponseCookie { name, value, max_age, domain, path, secure, expires, http_only, same_site }) - } -} - -impl fmt::Display for ResponseCookie { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let max_age = match self.clone().max_age { - None => String::from(""), - Some(v) => format!("; Max-Age:{}", v) - }; - let domain = match self.clone().domain { - None => String::from(""), - Some(v) => format!("; Domain:{}", v) - }; - let path = match self.clone().path { - None => String::from(""), - Some(v) => format!("; Path:{}", v) - }; - write!(f, "{}={}{}{}{}", - self.name, - self.value, - max_age, - domain, - path) - } -} - -impl fmt::Display for Cookie { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}={}; domain={}; path={}", - self.name, - self.value, - self.domain, - self.path, - ) - } -} - -impl ResponseCookie { - - -// pub fn to_header(&self) -> Header { -// return Header { -// name: String::from("Cookie"), -// value: format!("{}={}", self.name, self.value), -// }; -// //format!("Cookie: {}", self.to_string()); -// } - - - pub fn encode_cookie(header_name: String, header_value: String) -> Header { - let name = String::from("Cookie"); - let value = format!("{}={};", header_name, header_value); - Header { name, value } - } -} - - -#[derive(Clone, Debug, PartialEq)] -pub struct CookieJar { - inner: Vec -} - -impl CookieJar { - pub fn init(cookies: Vec) -> CookieJar { - CookieJar { inner: cookies } - } - - pub fn cookies(self) -> Vec { - self.inner - } - - - pub fn get_cookies(self, domain: String, path: String) -> Vec { - self.inner - .iter() - .filter(|c| c.is_usable(domain.clone(), path.clone())) - .map(|c| ResponseCookie { - name: c.clone().name, - value: c.clone().value, - max_age: None, - domain: Some(c.domain.clone()), - path: Some(c.path.clone()), - secure: Some(c.secure), - http_only: None, - expires: None, - same_site: None, - }) - .collect() - } - - pub fn update_cookies(&mut self, default_domain: String, _default_path: String, cookie: ResponseCookie) { - match cookie.max_age { - Some(0) => { - //eprintln!("delete cookie {:?}", cookie); - self.inner.retain(|c| c.name != cookie.name); - } - _ => { - - // replace value if same name+domain - let domain = match cookie.clone().domain { - None => default_domain, - Some(d) => d, - }; - let path = match cookie.clone().path { - None => String::from("/"), // do not use default path for the time-beingdefault_path, - Some(p) => p, - }; - - // find existing cookie - for c in self.inner.iter_mut() { - if c.name == cookie.name && c.domain == domain { - c.value = cookie.value; - return; - } - } - - let secure = if let Some(v) = cookie.secure { v } else { false }; - - // push new cookie - self.inner.push(Cookie { - name: cookie.clone().name, - value: cookie.clone().value, - domain, - path, - subdomains: cookie.domain.is_some(), - secure, - expires: None, - }); - } - } - } -} - -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct Cookie { - pub name: String, - pub value: String, - pub domain: String, - pub path: String, - pub subdomains: bool, - pub secure: bool, - pub expires: Option, -} - -impl Cookie { - fn is_usable(&self, domain: String, path: String) -> bool { - - // domain - if !is_subdomain(self.clone().domain, domain.clone()) { - return false; - } - if !self.subdomains && domain != self.clone().domain { - return false; - } - - // path - if !is_subpath(self.clone().path, path) { - return false; - } - true - } -} - -fn is_subdomain(domain: String, subdomain: String) -> bool { - if domain.as_str() == "" { - return false; - } - - let mut domain_segments: Vec<&str> = domain.split('.').collect(); - if domain_segments.get(0).unwrap() == &"" { - domain_segments.remove(0); - } - domain_segments.reverse(); - - let mut subdomain_segments: Vec<&str> = subdomain.split('.').collect(); - if subdomain_segments.get(0).unwrap() == &"" { - subdomain_segments.remove(0); - } - subdomain_segments.reverse(); - if domain_segments.len() > subdomain_segments.len() { - return false; - } - - for i in 0..domain_segments.len() { - if domain_segments.get(i).unwrap() != subdomain_segments.get(i).unwrap() { - return false; - } - } - - true -} - -fn is_subpath(path: String, subpath: String) -> bool { - if path.as_str() == "" { - return false; - } - - let mut path_segments: Vec<&str> = path.split('/').collect(); - if path_segments.get(0).unwrap() == &"" { - path_segments.remove(0); - } - path_segments.reverse(); - if path_segments.get(0).unwrap() == &"" { - path_segments.remove(0); - } - - let mut subpath_segments: Vec<&str> = subpath.split('/').collect(); - if subpath_segments.get(0).unwrap() == &"" { - subpath_segments.remove(0); - } - subpath_segments.reverse(); - if path_segments.len() > subpath_segments.len() { - return false; - } - - - for i in 0..path_segments.len() { - if path_segments.get(i).unwrap() != subpath_segments.get(i).unwrap() { - return false; - } - } - - true -} - -impl Cookie { - pub fn to_netscape(&self) -> String { - let domain_name = self.domain.to_string(); - let include_domains = if self.subdomains { "TRUE" } else { "FALSE" }.to_string(); - let path = self.path.clone(); - let https_only = if self.secure { "TRUE" } else { "FALSE" }.to_string(); - let expires = if let Some(expires) = self.expires { - expires.timestamp().to_string() - } else { - "0".to_string() - }; - let name = self.name.clone(); - let value = self.value.clone(); - format!("{}\t{}\t{}\t{}\t{}\t{}\t{}", - domain_name, - include_domains, - path, - https_only, - expires, - name, - value - ) - } - - pub fn from_netscape(s: &str) -> Option { - let tokens = s.split('\t').collect::>(); - if tokens.len() != 7 { return None; } - - let domain = (*tokens.get(0).unwrap()).to_string(); - let subdomains = (*tokens.get(1).unwrap()).to_string().as_str() == "TRUE"; - let path = (*tokens.get(2).unwrap()).to_string(); - let secure = (*tokens.get(3).unwrap()).to_string().as_str() == "TRUE"; - let expires = None; - let name = (*tokens.get(5).unwrap()).to_string(); - let value = (*tokens.get(6).unwrap()).to_string(); - - Some(Cookie { name, value, domain, path, subdomains, secure, expires }) - } -} - - -#[cfg(test)] -mod tests { - use super::*; - - fn cookie_lsid() -> Cookie { - Cookie { - name: String::from("LSID"), - value: String::from("DQAAAK…Eaem_vYg"), - domain: String::from("docs.foo.com"), - path: String::from("/accounts"), - subdomains: false, - secure: false, - expires: None, - } - } - - fn cookie_hsid() -> Cookie { - Cookie { - name: String::from("HSID"), - value: String::from("AYQEVn…DKrdst"), - domain: String::from(".foo.com"), - path: String::from("/"), - subdomains: true, - secure: false, - expires: None, - } - } - - fn cookie_ssid() -> Cookie { - Cookie { - name: String::from("SSID"), - value: String::from("Ap4P…GTEq"), - domain: String::from("foo.com"), - path: String::from("/"), - subdomains: true, - secure: false, - expires: None, - } - } - - fn sample_cookiejar() -> CookieJar { - CookieJar { - inner: vec![ - cookie_lsid(), - cookie_hsid(), - cookie_ssid(), - ] - } - } - - #[test] - fn test_is_usable() { - let domain = String::from("example.org"); - let path = String::from("/"); - assert_eq!(cookie_lsid().is_usable(domain.clone(), path.clone()), false); - assert_eq!(cookie_hsid().is_usable(domain.clone(), path.clone()), false); - assert_eq!(cookie_ssid().is_usable(domain, path.clone()), false); - - let domain = String::from("foo.com"); - let path = String::from("/"); - assert_eq!(cookie_lsid().is_usable(domain.clone(), path.clone()), false); - assert_eq!(cookie_hsid().is_usable(domain.clone(), path.clone()), true); - assert_eq!(cookie_ssid().is_usable(domain, path.clone()), true); - - let domain = String::from("foo.com"); - let path = String::from("/accounts"); - assert_eq!(cookie_lsid().is_usable(domain.clone(), path.clone()), false); - assert_eq!(cookie_hsid().is_usable(domain.clone(), path.clone()), true); - assert_eq!(cookie_ssid().is_usable(domain, path), true); - - let domain = String::from("docs.foo.com"); - let path = String::from("/accounts"); - assert_eq!(cookie_lsid().is_usable(domain.clone(), path.clone()), true); - assert_eq!(cookie_hsid().is_usable(domain.clone(), path.clone()), true); - assert_eq!(cookie_ssid().is_usable(domain, path), true); - } - - #[test] - fn test_get_cookies() { - let domain = String::from("docs.foo.com"); - let path = String::from("/accounts"); - assert_eq!(sample_cookiejar().get_cookies(domain, path).len(), 3); - - let domain = String::from("toto.docs.foo.com"); - let path = String::from("/accounts"); - assert_eq!(sample_cookiejar().get_cookies(domain, path).len(), 2); - } - - #[test] - fn test_is_subdomain() { - assert_eq!(is_subdomain(String::from("foo.example.org"), String::from("example.org")), false); - assert_eq!(is_subdomain(String::from("example.org"), String::from("toto.org")), false); - - assert_eq!(is_subdomain(String::from("example.org"), String::from("example.org")), true); - assert_eq!(is_subdomain(String::from("example.org"), String::from("foo.example.org")), true); - assert_eq!(is_subdomain(String::from(".example.org"), String::from("foo.example.org")), true); - } - - #[test] - fn test_is_subpath() { - assert_eq!(is_subpath(String::from("/toto"), String::from("/toto")), true); - assert_eq!(is_subpath(String::from("/"), String::from("/toto")), true); - assert_eq!(is_subpath(String::from("/to"), String::from("/toto")), false); - } - - #[test] - fn test_from_netscape() { - assert_eq!(Cookie::from_netscape("localhost\tFALSE\t/\tFALSE\t0\tcookie2\tvalueA").unwrap(), - Cookie { - name: "cookie2".to_string(), - value: "valueA".to_string(), - domain: "localhost".to_string(), - path: "/".to_string(), - subdomains: false, - secure: false, - expires: None, - } - ); - } -} diff --git a/src/http/core.rs b/src/http/core.rs deleted file mode 100644 index 041508911..000000000 --- a/src/http/core.rs +++ /dev/null @@ -1,128 +0,0 @@ -/* - * hurl (https://hurl.dev) - * Copyright (C) 2020 Orange - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ -extern crate reqwest; - -use std::fmt; - -use serde::{Deserialize, Serialize}; - -pub enum Encoding { - Utf8, - Latin1, -} - -impl fmt::Display for Encoding { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", match self { - Encoding::Utf8 => "utf8", - Encoding::Latin1 => "iso8859-1" - }) - } -} - -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub struct Url { - pub scheme: String, - pub host: String, - pub port: Option, - pub path: String, - pub query_string: String, -} - - -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub struct Header { - pub name: String, - pub value: String, -} - -pub fn get_header_value(headers: Vec
, name: &str) -> Option { - for header in headers { - if header.name.as_str() == name { - return Some(header.value); - } - } - None -} - -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub struct Param { - pub name: String, - pub value: String, -} - -pub fn encode_form_params(params: Vec) -> Vec { - params - .iter() - //.map(|p| format!("{}={}", p.name, utf8_percent_encode(p.value.as_str(), FRAGMENT))) - .map(|p| format!("{}={}", p.name, url_encode(p.value.clone()))) - .collect::>() - .join("&") - .into_bytes() -} - - -fn url_encode(s: String) -> String { - const MAX_CHAR_VAL: u32 = std::char::MAX as u32; - let mut buff = [0; 4]; - s.chars() - .map(|ch| { - match ch as u32 { - 0..=47 | 58..=64 | 91..=96 | 123..=MAX_CHAR_VAL => { - ch.encode_utf8(&mut buff); - buff[0..ch.len_utf8()].iter().map(|&byte| format!("%{:x}", byte)).collect::() - } - _ => ch.to_string(), - } - }) - .collect::() -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_encode_form_params() { - assert_eq!( - encode_form_params(vec![ - Param { - name: String::from("param1"), - value: String::from("value1"), - }, - Param { - name: String::from("param2"), - value: String::from(""), - } - ]), - vec![ - 112, 97, 114, 97, 109, 49, 61, 118, 97, 108, 117, 101, 49, 38, 112, 97, 114, 97, 109, - 50, 61 - ] - ); - assert_eq!( - std::str::from_utf8(&encode_form_params(vec![ - Param { name: String::from("param1"), value: String::from("value1") }, - Param { name: String::from("param2"), value: String::from("") }, - Param { name: String::from("param3"), value: String::from("a=b") }, - Param { name: String::from("param4"), value: String::from("a%3db") }, - ])).unwrap(), - "param1=value1¶m2=¶m3=a%3db¶m4=a%253db" - ); - } -} diff --git a/src/http/export.rs b/src/http/export.rs deleted file mode 100644 index 2f0347d1b..000000000 --- a/src/http/export.rs +++ /dev/null @@ -1,119 +0,0 @@ -/* - * hurl (https://hurl.dev) - * Copyright (C) 2020 Orange - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ -use serde::ser::{Serializer, SerializeStruct}; -use serde::Serialize; - -use super::cookie::*; -use super::request::*; -use super::response::*; - -impl Serialize for Request { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - // 3 is the number of fields in the struct. - let mut state = serializer.serialize_struct("??", 3)?; - state.serialize_field("method", &self.clone().method.to_text())?; - state.serialize_field("url", &self.clone().url())?; - state.serialize_field("queryString", &self.clone().querystring)?; - state.serialize_field("headers", &self.clone().headers())?; - state.serialize_field("cookies", &self.clone().cookies)?; - - if let Some(params) = self.clone().form_params() { - state.serialize_field("format_params", ¶ms)?; - } - state.serialize_field("body", &base64::encode(&self.body))?; - - state.end() - } -} - -impl Serialize for Response { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - // 3 is the number of fields in the struct. - let mut state = serializer.serialize_struct("??", 3)?; - state.serialize_field("httpVersion", &self.clone().version)?; - state.serialize_field("status", &self.clone().status)?; - state.serialize_field("cookies", &self.clone().cookies())?; - state.serialize_field("headers", &self.clone().headers)?; - - // WIP - Serialize response body only for json for the timebeing - let content_type = self.get_header("content_type", true); - if let Some(value) = content_type.first() { - if value.as_str() == "application/json; charset=UTF-8" { - let s = String::from_utf8(self.body.clone()).expect("Found invalid UTF-8"); - let result: Result = serde_json::from_str(s.as_str()); - if let Ok(v) = result { - state.serialize_field("json", &v)?; - } - } - } - - state.end() - } -} - -impl Serialize for ResponseCookie { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - // 3 is the number of fields in the struct. - let mut state = serializer.serialize_struct("??", 3)?; - state.serialize_field("name", &self.clone().name)?; - state.serialize_field("value", &self.clone().value)?; - if let Some(value) = self.clone().domain { - state.serialize_field("domain", &value)?; - } - state.end() - } -} - -impl Serialize for Cookie { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - let mut state = serializer.serialize_struct("InternalCookie", 3)?; - state.serialize_field("name", &self.clone().name)?; - state.serialize_field("value", &self.clone().value)?; - state.serialize_field("domain", &self.clone().domain)?; - state.serialize_field("path", &self.clone().path)?; - state.serialize_field("include_subdomain", &self.clone().subdomains)?; - state.end() - } -} - - -impl Serialize for Version { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - match self { - Version::Http10 => serializer.serialize_str("HTTP/1.0"), - Version::Http11 => serializer.serialize_str("HTTP/1.1"), - Version::Http2 => serializer.serialize_str("HTTP/2"), - } - } -} - diff --git a/src/http/import.rs b/src/http/import.rs deleted file mode 100644 index 434e9d168..000000000 --- a/src/http/import.rs +++ /dev/null @@ -1,391 +0,0 @@ -/* - * hurl (https://hurl.dev) - * Copyright (C) 2020 Orange - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ -extern crate url as external_url; - -use chrono::DateTime; - -use super::cookie::*; -use super::core::*; -use super::request::*; -use super::response::*; - -type ParseError = String; - -pub fn parse_request(value: serde_json::Value) -> Result { - if let serde_json::Value::Object(map) = value { - let method = match map.get("method") { - Some(serde_json::Value::String(s)) => parse_method(s.clone())?, - _ => return Err("expecting a string for the method".to_string()), - }; - let url = match map.get("url") { - Some(serde_json::Value::String(s)) => parse_url(s.clone())?, - _ => return Err("expecting a string for the url".to_string()), - }; - - let headers = match map.get("headers") { - Some(serde_json::Value::Array(values)) => { - let mut headers = vec![]; - for value in values { - let header = parse_header(value.clone())?; - headers.push(header); - } - headers - } - _ => vec![], - }; - - let cookies = match map.get("cookies") { - Some(serde_json::Value::Array(values)) => { - let mut headers = vec![]; - for value in values { - let header = parse_response_cookie(value.clone())?; - headers.push(header); - } - headers - } - _ => vec![], - }; - - let multipart = vec![]; - - Ok(Request { - method, - url, - querystring: vec![], - headers, - cookies, - body: vec![], - multipart, - }) - } else { - Err("expecting an object for the request".to_string()) - } -} - -pub fn parse_response(value: serde_json::Value) -> Result { - if let serde_json::Value::Object(map) = value { - let status = match map.get("status") { - Some(serde_json::Value::Number(x)) => if let Some(x) = x.as_u64() { - x as u16 - } else { - return Err("expecting a integer for the status".to_string()); - }, - _ => return Err("expecting a number for the status".to_string()), - }; - - let version = match map.get("httpVersion") { - Some(serde_json::Value::String(s)) => parse_version(s.clone())?, - _ => return Err("expecting a string for the version".to_string()), - }; - - let headers = match map.get("headers") { - Some(serde_json::Value::Array(values)) => { - let mut headers = vec![]; - for value in values { - let header = parse_header(value.clone())?; - headers.push(header); - } - headers - } - _ => vec![], - }; - - Ok(Response { - version, - status, - headers, - body: vec![], - }) - } else { - Err("expecting an object for the response".to_string()) - } -} - -fn parse_method(s: String) -> Result { - match s.as_str() { - "GET" => Ok(Method::Get), - "HEAD" => Ok(Method::Head), - "POST" => Ok(Method::Post), - "PUT" => Ok(Method::Put), - "DELETE" => Ok(Method::Delete), - "CONNECT" => Ok(Method::Connect), - "OPTIONS" => Ok(Method::Options), - "TRACE" => Ok(Method::Trace), - "PATCH" => Ok(Method::Patch), - _ => Err(format!("Invalid method <{}>", s)) - } -} - -fn parse_url(s: String) -> Result { - match external_url::Url::parse(s.as_str()) { - Err(_) => Err(format!("Invalid url <{}>", s)), - Ok(u) => Ok(Url { - scheme: u.scheme().to_string(), - host: u.host_str().unwrap().to_string(), - port: u.port(), - path: u.path().to_string(), - query_string: if let Some(s) = u.query() { s.to_string() } else { "".to_string() }, - }) - } -} - -fn parse_header(value: serde_json::Value) -> Result { - if let serde_json::Value::Object(map) = value { - let name = match map.get("name") { - Some(serde_json::Value::String(s)) => s.to_string(), - _ => return Err("expecting a string for the header name".to_string()), - }; - let value = match map.get("value") { - Some(serde_json::Value::String(s)) => s.to_string(), - _ => return Err("expecting a string for the header value".to_string()), - }; - Ok(Header { name, value }) - } else { - Err("Expecting object for one header".to_string()) - } -} - -pub fn parse_response_cookie(value: serde_json::Value) -> Result { - if let serde_json::Value::Object(map) = value { - let name = match map.get("name") { - Some(serde_json::Value::String(s)) => s.to_string(), - _ => return Err("expecting a string for the cookie name".to_string()), - }; - let value = match map.get("value") { - Some(serde_json::Value::String(s)) => s.to_string(), - _ => return Err("expecting a string for the cookie value".to_string()), - }; - let domain = match map.get("domain") { - None => None, - Some(serde_json::Value::String(s)) => Some(s.to_string()), - _ => return Err("expecting a string for the cookie domain".to_string()), - }; - let path = match map.get("path") { - None => None, - Some(serde_json::Value::String(s)) => Some(s.to_string()), - _ => return Err("expecting a string for the cookie path".to_string()), - }; - let expires = match map.get("expires") { - None => None, - Some(serde_json::Value::String(s)) => Some(s.to_string()), - _ => return Err("expecting a string for the cookie expires".to_string()), - }; - let secure = match map.get("secure") { - None => None, - Some(serde_json::Value::Bool(value)) => Some(*value), - _ => return Err("expecting a bool for the cookie secure flag".to_string()), - }; - let http_only = match map.get("http_only") { - None => None, - Some(serde_json::Value::Bool(value)) => Some(*value), - _ => return Err("expecting a bool for the cookie http_only flag".to_string()), - }; - let same_site = match map.get("same_site") { - None => None, - Some(serde_json::Value::String(s)) => Some(s.to_string()), - _ => return Err("expecting a string for the cookie same_site".to_string()), - }; - Ok(ResponseCookie { name, value, max_age: None, domain, path, secure, http_only, expires, same_site }) - } else { - Err("Expecting object for one cookie".to_string()) - } -} - -pub fn parse_cookie(value: serde_json::Value) -> Result { - if let serde_json::Value::Object(map) = value { - let name = match map.get("name") { - Some(serde_json::Value::String(s)) => s.to_string(), - _ => return Err("expecting a string for the cookie name".to_string()), - }; - let value = match map.get("value") { - Some(serde_json::Value::String(s)) => s.to_string(), - _ => return Err("expecting a string for the cookie value".to_string()), - }; - let domain = match map.get("domain") { - Some(serde_json::Value::String(s)) => s.to_string(), - _ => return Err("expecting a string for the cookie domain".to_string()), - }; - let path = match map.get("path") { - Some(serde_json::Value::String(s)) => s.to_string(), - _ => return Err("expecting a string for the cookie path".to_string()), - }; - let subdomains = match map.get("include_subdomain") { - Some(serde_json::Value::Bool(v)) => *v, - _ => return Err("expecting a bool for the include_subdomain".to_string()), - }; - let secure = match map.get("secure") { - None => false, - Some(serde_json::Value::Bool(value)) => *value, - _ => return Err("expecting a bool for the secure flag".to_string()), - }; - let expires = match map.get("expired") { - None => None, - Some(serde_json::Value::String(v)) => { - match DateTime::parse_from_rfc3339(v.as_str()) { - Ok(v) => Some(v.naive_utc()), - Err(_) => return Err("expecting a String (date) for the expired fieldate can be parsed".to_string()), - } - } - _ => return Err("expecting a String (date) for the expired field".to_string()), - }; - - Ok(Cookie { name, value, domain, path, subdomains, secure, expires }) - } else { - Err("Expecting object for one cookie".to_string()) - } -} - -fn parse_version(s: String) -> Result { - match s.as_str() { - "HTTP/1.0" => Ok(Version::Http10), - "HTTP/1.1" => Ok(Version::Http11), - "HTTP/2" => Ok(Version::Http2), - _ => Err("Expecting version HTTP/1.0, HTTP/1.2 or HTTP/2".to_string()) - } -} - - -#[cfg(test)] -mod tests { - use super::*; - use super::super::request::tests::*; - - #[test] - fn test_parse_request() { - let v: serde_json::Value = serde_json::from_str(r#"{ - "method": "GET", - "url": "http://localhost:8000/hello", - "headers": [] -}"#).unwrap(); - assert_eq!(parse_request(v).unwrap(), hello_http_request()); - - let v: serde_json::Value = serde_json::from_str(r#"{ - "method": "GET", - "url": "http://localhost:8000/querystring-params?param1=value1¶m2=a%20b", - "headers": [] -}"#).unwrap(); - assert_eq!(parse_request(v).unwrap(), Request { - method: Method::Get, - url: Url { - scheme: "http".to_string(), - host: "localhost".to_string(), - port: Some(8000), - path: "/querystring-params".to_string(), - query_string: "param1=value1¶m2=a%20b".to_string(), - }, - querystring: vec![], - headers: vec![], - cookies: vec![], - body: vec![], - multipart: vec![], - }); - - - let v: serde_json::Value = serde_json::from_str(r#"{ - "method": "GET", - "url": "http://localhost/custom", - "headers": [ - {"name": "User-Agent", "value": "iPhone"}, - {"name": "Foo", "value": "Bar"} - ], - "cookies": [ - {"name": "theme", "value": "light"}, - {"name": "sessionToken", "value": "abc123"} - ] -}"#).unwrap(); - assert_eq!(parse_request(v).unwrap(), custom_http_request()); - } - - #[test] - fn test_parse_response() { - let v: serde_json::Value = serde_json::from_str(r#"{ - "status": 200, - "httpVersion": "HTTP/1.0", - "headers": [ - {"name": "Content-Type", "value": "text/html; charset=utf-8" }, - {"name": "Content-Length", "value": "12" } - - ] -}"#).unwrap(); - assert_eq!(parse_response(v).unwrap(), Response { - version: Version::Http10, - status: 200, - headers: vec![ - Header { name: String::from("Content-Type"), value: String::from("text/html; charset=utf-8") }, - Header { name: String::from("Content-Length"), value: String::from("12") }, - ], - body: vec![], - }); - } - - #[test] - fn test_parse_method() { - assert_eq!(parse_method("GET".to_string()).unwrap(), Method::Get); - - let error = parse_method("x".to_string()).err().unwrap(); - assert_eq!(error, "Invalid method "); - } - - #[test] - fn test_parse_url() { - assert_eq!( - parse_url("http://localhost:8000/query?param1=value1".to_string()).unwrap(), - Url { - scheme: "http".to_string(), - host: "localhost".to_string(), - port: Some(8000), - path: "/query".to_string(), - query_string: "param1=value1".to_string(), - }); - } - - #[test] - fn test_parse_header() { - let v: serde_json::Value = serde_json::from_str(r#"{ - "name": "name1", - "value": "value1" -}"#).unwrap(); - assert_eq!(parse_header(v).unwrap(), Header { name: "name1".to_string(), value: "value1".to_string() }); - } - - #[test] - fn test_parse_response_cookie() { - let v: serde_json::Value = serde_json::from_str(r#"{ - "name": "name1", - "value": "value1" -}"#).unwrap(); - assert_eq!(parse_response_cookie(v).unwrap(), - ResponseCookie { - name: "name1".to_string(), - value: "value1".to_string(), - max_age: None, - domain: None, - path: None, - secure: None, - http_only: None, - expires: None, - same_site: None, - } - ); - } - - #[test] - fn test_parse_version() { - assert_eq!(parse_version("HTTP/1.0".to_string()).unwrap(), Version::Http10); - } -} diff --git a/src/http/libcurl/client.rs b/src/http/libcurl/client.rs index d56d0c62d..2d51196ef 100644 --- a/src/http/libcurl/client.rs +++ b/src/http/libcurl/client.rs @@ -22,6 +22,8 @@ use curl::easy; use super::core::*; use std::io::Read; +use encoding::{Encoding, DecoderTrap}; +use encoding::all::ISO_8859_1; #[derive(Debug)] @@ -34,20 +36,23 @@ pub struct Client { pub follow_location: bool, pub redirect_count: usize, pub max_redirect: Option, + pub verbose: bool, } #[derive(Debug, Clone)] pub struct ClientOptions { pub follow_location: bool, pub max_redirect: Option, - pub cookie_file: Option, - pub cookie_jar: Option, + pub cookie_input_file: Option, pub proxy: Option, + pub no_proxy: Option, pub verbose: bool, + pub insecure: bool, } impl Client { + /// /// Init HTTP hurl client /// @@ -56,27 +61,34 @@ impl Client { // Activate cookie storage // with or without persistence (empty string) - h.cookie_file(options.cookie_file.unwrap_or_else(|| "".to_string()).as_str()).unwrap(); - - - if let Some(cookie_jar) = options.cookie_jar { - h.cookie_jar(cookie_jar.as_str()).unwrap(); - } + h.cookie_file(options.cookie_input_file.unwrap_or_else(|| "".to_string()).as_str()).unwrap(); if let Some(proxy) = options.proxy { h.proxy(proxy.as_str()).unwrap(); } - + if let Some(s) = options.no_proxy { + h.noproxy(s.as_str()).unwrap(); + } h.verbose(options.verbose).unwrap(); + h.ssl_verify_host(options.insecure).unwrap(); + h.ssl_verify_peer(options.insecure).unwrap(); Client { handle: Box::new(h), follow_location: options.follow_location, max_redirect: options.max_redirect, redirect_count: 0, + verbose: options.verbose, } } + /// + /// reset HTTP hurl client + /// + pub fn reset(&mut self) { + self.handle.reset(); + self.handle.verbose(self.verbose).unwrap(); + } /// /// Execute an http request @@ -93,7 +105,8 @@ impl Client { let b = request.body.clone(); let mut data: &[u8] = b.as_ref(); self.set_body(data); - self.set_headers(&request.headers, data.is_empty()); + self.set_headers(&request); + self.handle.debug_function(|info_type, data| match info_type { @@ -106,7 +119,9 @@ impl Client { } } easy::InfoType::HeaderIn => { - eprint!("< {}", str::from_utf8(data).unwrap()); + if let Some(s) = decode_header(data) { + eprint!("< {}", s); + } } _ => {} } @@ -123,9 +138,8 @@ impl Client { } transfer.header_function(|h| { - match str::from_utf8(h) { - Ok(s) => lines.push(s.to_string()), - Err(e) => println!("Error decoding header {:?}", e), + if let Some(s) = decode_header(h) { + lines.push(s) } true }).unwrap(); @@ -140,6 +154,7 @@ impl Client { 5 => return Err(HttpError::CouldNotResolveProxyName), 6 => return Err(HttpError::CouldNotResolveHost), 7 => return Err(HttpError::FailToConnect), + 60 => return Err(HttpError::SSLCertificate), _ => panic!("{:#?}", e), } } @@ -161,6 +176,7 @@ impl Client { multipart: vec![], cookies: vec![], body: vec![], + content_type: None, }; let redirect_count = redirect_count + 1; @@ -173,6 +189,7 @@ impl Client { return self.execute(&request, redirect_count); } self.redirect_count = redirect_count; + self.reset(); Ok(Response { version, @@ -191,6 +208,8 @@ impl Client { } else { let url = if url.ends_with('?') { url.to_string() + } else if url.contains('?') { + format!("{}&", url) } else { format!("{}?", url) }; @@ -218,32 +237,44 @@ impl Client { } } + /// /// set request headers /// - fn set_headers(&mut self, headers: &[Header], default_content_type: bool) { + fn set_headers(&mut self, request: &Request) { let mut list = easy::List::new(); - for header in headers.to_owned() { + for header in request.headers.clone() { list.append(format!("{}: {}", header.name, header.value).as_str()).unwrap(); } - if get_header_values(headers.to_vec(), "Content-Type".to_string()).is_empty() && !default_content_type { - list.append("Content-Type:").unwrap(); + if get_header_values(request.headers.clone(), "Content-Type".to_string()).is_empty() { + if let Some(s) = request.content_type.clone() { + list.append(format!("Content-Type: {}", s).as_str()).unwrap(); + } else { + list.append("Content-Type:").unwrap(); // remove header Content-Type + } } - list.append(format!("User-Agent: hurl/{}", clap::crate_version!()).as_str()).unwrap(); +// if request.form.is_empty() && request.multipart.is_empty() && request.body.is_empty() { +// list.append("Content-Length:").unwrap(); +// } + + if get_header_values(request.headers.clone(), "User-Agent".to_string()).is_empty() { + list.append(format!("User-Agent: hurl/{}", clap::crate_version!()).as_str()).unwrap(); + } self.handle.http_headers(list).unwrap(); } + /// /// set request cookies /// - fn set_cookies(&mut self, cookies: &[RequestCookie]) { - for cookie in cookies { - self.handle.cookie(cookie.to_string().as_str()).unwrap(); - } + fn set_cookies(&mut self, _cookies: &[RequestCookie]) { + //for cookie in cookies { + //self.handle.cookie(cookie.to_string().as_str()).unwrap(); // added in the store beforehand + //} } @@ -393,6 +424,17 @@ impl Client { } cookies } + + /// + /// Add cookie to Cookiejar + /// TODO check domain/path,... + pub fn add_cookie(&mut self, cookie: Cookie) { + if self.verbose { + eprintln!("* add to cookie store: {}", cookie); + //self.handle.cookie_list(format!("Set-Cookie: {}={}", cookie.name, cookie.value).as_str()).unwrap(); + } + self.handle.cookie_list(cookie.to_string().as_str()).unwrap(); + } } @@ -438,6 +480,25 @@ fn split_lines(data: &[u8]) -> Vec { } +/// +/// Decode optionally header value as text with utf8 or iso-8859-1 encoding +/// +pub fn decode_header(data: &[u8]) -> Option { + match str::from_utf8(data) { + Ok(s) => Some(s.to_string()), + Err(_) => { + match ISO_8859_1.decode(data, DecoderTrap::Strict) { + Ok(s) => Some(s), + Err(_) => { + println!("Error decoding header both utf8 and iso-8859-1 {:?}", data); + None + } + } + } + } +} + + #[cfg(test)] mod tests { use super::*; diff --git a/src/http/libcurl/core.rs b/src/http/libcurl/core.rs index 4974e95a0..91d5fa244 100644 --- a/src/http/libcurl/core.rs +++ b/src/http/libcurl/core.rs @@ -28,8 +28,28 @@ pub struct Request { pub multipart: Vec, pub cookies: Vec, pub body: Vec, + pub content_type: Option, } + +//impl Request { +// +// /// +// /// Get implicit content-type from request +// /// Note that for multipart, the content-type is not returned because it is generated at runtime by the client +// /// +// pub fn content_type(&self) -> Option { +// if self.form.is_empty() { +// Some("application/x-www-form-urlencoded".to_string()) +// // } else if self..mform.is_empty() { +// // Some("application/x-www-form-urlencoded".to_string()) +// } else { +// None +// } +// } +//} + + #[derive(Clone, Debug, PartialEq, Eq)] pub struct Response { pub version: Version, @@ -76,24 +96,56 @@ pub enum Version { Http2, } +impl fmt::Display for Version { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let value = match self { + Version::Http10 => "1.0", + Version::Http11 => "1.1", + Version::Http2 => "2", + }; + write!(f, "{}", value) + } +} + #[derive(Clone, Debug, PartialEq, Eq)] pub struct Header { pub name: String, pub value: String, } +impl fmt::Display for Header { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}: {}", self.name, self.value) + } +} + #[derive(Clone, Debug, PartialEq, Eq)] pub struct Param { pub name: String, pub value: String, } +impl fmt::Display for Param { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}: {}", self.name, self.value) + } +} + #[derive(Clone, Debug, PartialEq, Eq)] pub enum MultipartParam { Param(Param), FileParam(FileParam), } +impl fmt::Display for MultipartParam { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + MultipartParam::Param(param) => write!(f, "{}", param.to_string()), + MultipartParam::FileParam(param) => write!(f, "{}", param.to_string()), + } + } +} + #[derive(Clone, Debug, PartialEq, Eq)] pub struct FileParam { pub name: String, @@ -102,6 +154,12 @@ pub struct FileParam { pub content_type: String, } +impl fmt::Display for FileParam { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}: file,{}; {}", self.name, self.filename, self.content_type) + } +} + #[derive(Clone, Debug, PartialEq, Eq)] pub struct RequestCookie { @@ -120,6 +178,12 @@ pub struct Cookie { pub value: String, } +impl fmt::Display for Cookie { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}\t{}\t{}\t{}\t{}\t{}\t{}", self.domain, self.include_subdomain, self.path, self.https, self.expires, self.name, self.value) + } +} + impl fmt::Display for RequestCookie { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { @@ -135,6 +199,7 @@ pub enum HttpError { FailToConnect, TooManyRedirect, CouldNotParseResponse, + SSLCertificate, } @@ -162,6 +227,7 @@ pub fn get_header_values(headers: Vec
, expected_name: String) -> Vec Request { + Request { + method: Method::Get, + url: "http://localhost:8000/querystring-params".to_string(), + querystring: vec![ + Param { name: String::from("param1"), value: String::from("value1") }, + Param { name: String::from("param2"), value: String::from("a b") }, + ], + headers: vec![], + cookies: vec![], + body: vec![], + multipart: vec![], + form: vec![], + content_type: None, } } @@ -226,7 +312,112 @@ pub mod tests { cookies: vec![], body: "param1=value1¶m2=¶m3=a%3db¶m4=a%253db".to_string().into_bytes(), multipart: vec![], - form: vec![] + form: vec![], + content_type: Some("multipart/form-data".to_string()), + } + } + + pub fn hello_http_response() -> Response { + Response { + version: Version::Http10, + status: 200, + headers: vec![ + Header { name: String::from("Content-Type"), value: String::from("text/html; charset=utf-8") }, + Header { name: String::from("Content-Length"), value: String::from("12") }, + ], + body: String::into_bytes(String::from("Hello World!")), + } + } + + pub fn html_http_response() -> Response { + Response { + version: Version::Http10, + status: 200, + headers: vec![ + Header { name: String::from("Content-Type"), value: String::from("text/html; charset=utf-8") }, + ], + body: String::into_bytes(String::from("
")), + } + } + + pub fn xml_invalid_response() -> Response { + Response { + version: Version::Http10, + status: 200, + headers: vec![ + Header { name: String::from("Content-Type"), value: String::from("text/html; charset=utf-8") }, + Header { name: String::from("Content-Length"), value: String::from("12") }, + ], + body: String::into_bytes(r#" +xxx +"#.to_string()), + } + } + + pub fn xml_two_users_http_response() -> Response { + Response { + version: Version::Http10, + status: 200, + headers: vec![ + Header { name: String::from("Content-Type"), value: String::from("text/html; charset=utf-8") }, + Header { name: String::from("Content-Length"), value: String::from("12") }, + ], + body: String::into_bytes(r#" + + + Bob + Bill + +"#.to_string()), + } + } + + pub fn xml_three_users_http_response() -> Response { + Response { + version: Version::Http10, + status: 200, + headers: vec![ + Header { name: String::from("Content-Type"), value: String::from("text/html; charset=utf-8") }, + Header { name: String::from("Content-Length"), value: String::from("12") }, + ], + body: String::into_bytes(r#" + + + Bob + Bill + Bruce + +"#.to_string()), + } + } + + pub fn json_http_response() -> Response { + Response { + version: Version::Http10, + status: 0, + headers: vec![], + body: String::into_bytes(r#" +{ + "success":false, + "errors": [ + { "id": "error1"}, + {"id": "error2"} + ], + "duration": 1.5 +} +"#.to_string()), + } + } + + pub fn bytes_http_response() -> Response { + Response { + version: Version::Http10, + status: 200, + headers: vec![ + Header { name: String::from("Content-Type"), value: String::from("application/octet-stream") }, + Header { name: String::from("Content-Length"), value: String::from("1") }, + ], + body: vec![255], } } } \ No newline at end of file diff --git a/src/http/mod.rs b/src/http/mod.rs index cb94bad5d..bc639b207 100644 --- a/src/http/mod.rs +++ b/src/http/mod.rs @@ -15,11 +15,5 @@ * limitations under the License. * */ -pub mod client; -pub mod core; -pub mod cookie; -pub mod request; -pub mod response; -pub mod import; -pub mod export; + pub mod libcurl; \ No newline at end of file diff --git a/src/http/request.rs b/src/http/request.rs deleted file mode 100644 index eaba2900e..000000000 --- a/src/http/request.rs +++ /dev/null @@ -1,413 +0,0 @@ -/* - * hurl (https://hurl.dev) - * Copyright (C) 2020 Orange - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ -use std::fmt; - -use percent_encoding::{AsciiSet, CONTROLS, percent_decode, utf8_percent_encode}; -use serde::{Deserialize, Serialize}; - -use super::cookie::*; -use super::core::*; - -const FRAGMENT: &AsciiSet = &CONTROLS - .add(b' ') - .add(b'"') - .add(b':') - .add(b'/') - .add(b'<') - .add(b'>') - .add(b'+') - .add(b'=') - .add(b'?') - .add(b'%') - .add(b'`'); - - -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct Request { - pub method: Method, - pub url: Url, - pub querystring: Vec, - pub headers: Vec
, - pub cookies: Vec, - pub body: Vec, - pub multipart: Vec, - -} - -#[derive(Clone, Debug, PartialEq, Eq)] -pub enum MultipartParam { - TextParam { name: String, value: String }, - FileParam { name: String, filename: String, content_type: Option }, -} - - -fn has_header(headers: &[Header], name: String) -> bool { - for header in headers { - if header.name.as_str() == name { - return true; - } - } - false -} - -pub enum RequestEncoding { - Utf8, - Latin1, -} - -impl fmt::Display for RequestEncoding { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", match self { - RequestEncoding::Utf8 => "utf8", - RequestEncoding::Latin1 => "iso8859-1" - }) - } -} - -impl Request { - pub fn host(self) -> String { - self.url.host - } - - pub fn url(self) -> String { - let port = match self.url.port { - None => String::from(""), - Some(p) => format!(":{}", p) - }; - let querystring = self.url.query_string.clone(); - - // add params - let querystring = if self.querystring.is_empty() { - querystring - } else { - let mut buf = querystring.clone(); - if !querystring.is_empty() { - buf.push('&'); - } - for param in self.querystring { - if !buf.is_empty() { - buf.push('&'); - } - let encoded = utf8_percent_encode(param.value.as_str(), FRAGMENT).to_string(); - buf.push_str(format!("{}={}", param.name, encoded).as_str()); - } - buf - }; - let querystring = if querystring.is_empty() { - "".to_string() - } else { - format!("?{}", querystring) - }; - return format!("{}://{}{}{}{}", - self.url.scheme, - self.url.host, - port, - self.url.path, - querystring - ); - } - - pub fn headers(self) -> Vec
{ - let mut headers: Vec
= self.headers.clone(); - let user_agent = format!("hurl/{}", clap::crate_version!()); - let default_headers = vec![ - (String::from("User-Agent"), user_agent), - (String::from("Host"), self.url.clone().host) - ]; - - for (name, value) in default_headers { - if !has_header(&self.headers, name.clone()) { - headers.push(Header { name, value }); - } - } - - if !self.cookies.is_empty() { - headers.push(Header { - name: String::from("Cookie"), - value: self.cookies - .iter() - .map(|c| format!("{}={}", c.name, c.value)) - .collect::>() - .join("; "), - }); - } - headers - } - - pub fn content_type(self) -> Option { - for Header { name, value } in self.headers { - if name.as_str() == "Content-Type" { - return Some(value); - } - } - None - } - - pub fn add_session_cookies(&mut self, cookies: Vec) { - //eprintln!("add session cookies {:?}", cookies); - - for cookie in cookies { - - // TBC: both request and session cookies should have a domain => should not be an Option - let session_domain = cookie.clone().domain.unwrap(); - match self.clone().get_cookie(cookie.clone().name) { - Some(ResponseCookie { domain: Some(domain), .. }) => { - if session_domain != domain { - self.cookies.push(cookie.clone()); - } - } - _ => { - self.cookies.push(cookie.clone()); - } - } - } - } - - - pub fn get_cookie(self, name: String) -> Option { - for cookie in self.cookies { - if cookie.name == name { - return Some(cookie); - } - } - None - } - - - pub fn form_params(self) -> Option> { - if self.clone().content_type() != Some(String::from("application/x-www-form-urlencoded")) { - return None; - } - let decoded = percent_decode(&self.body); - let params = match decoded.decode_utf8() { - Ok(v) => { - let params: Vec<&str> = v.split('&').collect(); - params.iter().map(|s| Param::parse(s)).collect() - } - _ => vec![] - }; - Some(params) - } - - - pub fn encoding(&self) -> Encoding { - if let Some(v) = self.get_header("content-type", true) { - if v.contains("charset=ISO-8859-1") { - return Encoding::Latin1; - } - } - Encoding::Utf8 - } - - pub fn get_header(&self, name: &str, case_sensitive: bool) -> Option { - for header in self.headers.clone() { - if header.name == name - || !case_sensitive && header.name.to_lowercase() == name.to_lowercase() - { - return Some(header.value); - } - } - None - } -} - -impl Param { - fn parse(s: &str) -> Param { - match s.find('=') { - None => Param { name: s.to_string(), value: String::from("") }, - Some(i) => { - let (name, value) = s.split_at(i); - Param { name: name.to_string(), value: value[1..].to_string() } - } - } - } -} - - -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub enum Method { - Get, - Head, - Post, - Put, - Delete, - Connect, - Options, - Trace, - Patch, -} - -impl Method { - pub fn to_reqwest(&self) -> reqwest::Method { - match self { - Method::Get => reqwest::Method::GET, - Method::Head => reqwest::Method::HEAD, - Method::Post => reqwest::Method::POST, - Method::Put => reqwest::Method::PUT, - Method::Delete => reqwest::Method::DELETE, - Method::Connect => reqwest::Method::CONNECT, - Method::Options => reqwest::Method::OPTIONS, - Method::Trace => reqwest::Method::TRACE, - Method::Patch => reqwest::Method::PATCH, - } - } -} - - -#[cfg(test)] -pub mod tests { - use super::*; - - pub fn hello_http_request() -> Request { - Request { - method: Method::Get, - url: Url { - scheme: "http".to_string(), - host: "localhost".to_string(), - port: Some(8000), - path: "/hello".to_string(), - query_string: "".to_string(), - }, - querystring: vec![], - headers: vec![], - cookies: vec![], - body: vec![], - multipart: vec![], - } - } - - // GET http://localhost:8000/querystring-params?param1=value1¶m2 - pub fn query_http_request() -> Request { - Request { - method: Method::Get, - url: Url { - scheme: "http".to_string(), - host: "localhost".to_string(), - port: Some(8000), - path: "/querystring-params".to_string(), - query_string: "".to_string(), - }, - querystring: vec![ - Param { name: String::from("param1"), value: String::from("value1") }, - Param { name: String::from("param2"), value: String::from("a b") }, - ], - headers: vec![], - cookies: vec![], - body: vec![], - multipart: vec![], - } - } - - - pub fn custom_http_request() -> Request { - Request { - method: Method::Get, - url: Url { - scheme: "http".to_string(), - host: "localhost".to_string(), - port: None, - path: "/custom".to_string(), - query_string: "".to_string(), - }, - querystring: vec![], - headers: vec![ - Header { name: String::from("User-Agent"), value: String::from("iPhone") }, - Header { name: String::from("Foo"), value: String::from("Bar") }, - ], - cookies: vec![ - ResponseCookie { - name: String::from("theme"), - value: String::from("light"), - max_age: None, - domain: None, - path: None, - secure: None, - http_only: None, - expires: None, - same_site: None, - }, - ResponseCookie { - name: String::from("sessionToken"), - value: String::from("abc123"), - max_age: None, - domain: None, - path: None, - secure: None, - http_only: None, - expires: None, - same_site: None, - } - ], - body: vec![], - multipart: vec![], - } - } - - - pub fn form_http_request() -> Request { - Request { - method: Method::Post, - url: Url { - scheme: "http".to_string(), - host: "localhost".to_string(), - port: None, - path: "/form-params".to_string(), - query_string: "".to_string(), - }, - querystring: vec![], - headers: vec![ - Header { name: String::from("Content-Type"), value: String::from("application/x-www-form-urlencoded") }, - ], - cookies: vec![], - body: "param1=value1¶m2=¶m3=a%3db¶m4=a%253db".to_string().into_bytes(), - multipart: vec![], - } - } - - #[test] - pub fn test_headers() { - assert_eq!(hello_http_request().headers(), vec![ - Header { name: String::from("User-Agent"), value: format!("hurl/{}", clap::crate_version!()) }, - Header { name: String::from("Host"), value: String::from("localhost") } - ]); - - assert_eq!(custom_http_request().headers(), vec![ - Header { name: String::from("User-Agent"), value: String::from("iPhone") }, - Header { name: String::from("Foo"), value: String::from("Bar") }, - Header { name: String::from("Host"), value: String::from("localhost") }, - Header { name: String::from("Cookie"), value: String::from("theme=light; sessionToken=abc123") }, - ]); - } - - #[test] - pub fn test_url() { - assert_eq!(hello_http_request().url(), String::from("http://localhost:8000/hello")); - assert_eq!(query_http_request().url(), String::from("http://localhost:8000/querystring-params?param1=value1¶m2=a%20b")); - } - - #[test] - pub fn test_form_params() { - assert_eq!(hello_http_request().form_params(), None); - assert_eq!(form_http_request().form_params().unwrap(), vec![ - Param { name: String::from("param1"), value: String::from("value1") }, - Param { name: String::from("param2"), value: String::from("") }, - Param { name: String::from("param3"), value: String::from("a=b") }, - Param { name: String::from("param4"), value: String::from("a%3db") }, - ]); - } -} diff --git a/src/http/response.rs b/src/http/response.rs deleted file mode 100644 index 126593b53..000000000 --- a/src/http/response.rs +++ /dev/null @@ -1,268 +0,0 @@ -/* - * hurl (https://hurl.dev) - * Copyright (C) 2020 Orange - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ -use std::fmt; - -use super::cookie::*; -use super::core::*; - -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct Response { - pub version: Version, - pub status: u16, - pub headers: Vec
, - pub body: Vec, -} - -#[derive(Clone, Debug, PartialEq, Eq)] -pub enum Version { - Http10, - Http11, - Http2, -} - -impl fmt::Display for Version { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let s = match self { - Version::Http10 => "1.0", - Version::Http11 => "1.1", - Version::Http2 => "2", - }; - write!(f, "{}", s) - } -} - -impl Response { - pub fn get_header(&self, name: &str, case_sensitive: bool) -> Vec { - let mut values = vec![]; - for header in self.headers.clone() { - if header.name == name - || !case_sensitive && header.name.to_lowercase() == name.to_lowercase() - { - values.push(header.value); - } - } - values - } - - pub fn get_cookie(&self, name: &str) -> Option { - for cookie in self.cookies() { - if cookie.name.as_str() == name - { - return Some(cookie); - } - } - None - } - - pub fn cookies(&self) -> Vec { - let mut cookies = vec![]; - for Header { name, value } in self.clone().headers { - if name.to_lowercase() == "set-cookie" { - let c = cookie::Cookie::parse(value.as_str()).unwrap(); -// eprintln!(">>> parse set-cookie header"); -// eprintln!(">>> c = {:?}", c); -// -// let fields = value.split(";").collect::>(); -// let name_value = fields.get(0).unwrap().split("=").collect::>(); -// let name = name_value.get(0).unwrap().to_string(); -// let value = name_value.get(1).unwrap().to_string(); - let name = c.name().to_string(); - let value = c.value().to_string(); - let max_age = match c.max_age() { - None => None, - Some(d) => Some(d.num_seconds()) - }; - let expires = match c.expires() { - None => None, - Some(time) => Some(time.rfc822().to_string()) - }; - let domain = match c.domain() { - None => None, - Some(v) => Some(v.to_string()) - }; - let path = match c.path() { - None => None, - Some(v) => Some(v.to_string()) - }; - let secure = if let Some(value) = c.secure() { - Some(value) - } else { - None - }; - let http_only = if let Some(value) = c.http_only() { - Some(value) - } else { - None - }; - let same_site = match c.same_site() { - None => None, - Some(v) => Some(v.to_string()) - }; - cookies.push(ResponseCookie { name, value, max_age, expires, domain, path, secure, http_only, same_site }); - } - } - cookies - } -} - -impl Response { - pub fn content_type(&self) -> Option { - let values = self.get_header("content-type", true); - if let Some(value) = values.first() { - Some(value.clone()) - } else { - None - } - } - - pub fn encoding(&self) -> Encoding { - if let Some(value) = self.content_type() { - if value.contains("charset=ISO-8859-1") { - return Encoding::Latin1; - } - } - Encoding::Utf8 - } - - pub fn has_utf8_body(&self) -> bool { - if let Some(value) = self.content_type() { - value.contains("charset=utf-8") - } else { - false - } - } - - - pub fn is_html(&self) -> bool { - if let Some(value) = self.content_type() { - value.contains("html") - } else { - false - } - } -} - - -#[cfg(test)] -pub mod tests { - use super::*; - - pub fn hello_http_response() -> Response { - Response { - version: Version::Http10, - status: 200, - headers: vec![ - Header { name: String::from("Content-Type"), value: String::from("text/html; charset=utf-8") }, - Header { name: String::from("Content-Length"), value: String::from("12") }, - ], - body: String::into_bytes(String::from("Hello World!")), - } - } - - pub fn html_http_response() -> Response { - Response { - version: Version::Http10, - status: 200, - headers: vec![ - Header { name: String::from("Content-Type"), value: String::from("text/html; charset=utf-8") }, - ], - body: String::into_bytes(String::from("
")), - } - } - - pub fn xml_invalid_response() -> Response { - Response { - version: Version::Http10, - status: 200, - headers: vec![ - Header { name: String::from("Content-Type"), value: String::from("text/html; charset=utf-8") }, - Header { name: String::from("Content-Length"), value: String::from("12") }, - ], - body: String::into_bytes(r#" -xxx -"#.to_string()), - } - } - - pub fn xml_two_users_http_response() -> Response { - Response { - version: Version::Http10, - status: 200, - headers: vec![ - Header { name: String::from("Content-Type"), value: String::from("text/html; charset=utf-8") }, - Header { name: String::from("Content-Length"), value: String::from("12") }, - ], - body: String::into_bytes(r#" - - - Bob - Bill - -"#.to_string()), - } - } - - pub fn xml_three_users_http_response() -> Response { - Response { - version: Version::Http10, - status: 200, - headers: vec![ - Header { name: String::from("Content-Type"), value: String::from("text/html; charset=utf-8") }, - Header { name: String::from("Content-Length"), value: String::from("12") }, - ], - body: String::into_bytes(r#" - - - Bob - Bill - Bruce - -"#.to_string()), - } - } - - pub fn json_http_response() -> Response { - Response { - version: Version::Http10, - status: 0, - headers: vec![], - body: String::into_bytes(r#" -{ - "success":false, - "errors": [ - { "id": "error1"}, - {"id": "error2"} - ], - "duration": 1.5 -} -"#.to_string()), - } - } - - pub fn bytes_http_response() -> Response { - Response { - version: Version::Http10, - status: 200, - headers: vec![ - Header { name: String::from("Content-Type"), value: String::from("application/octet-stream") }, - Header { name: String::from("Content-Length"), value: String::from("1") }, - ], - body: vec![255], - } - } -} diff --git a/src/runner/assert.rs b/src/runner/assert.rs index 44dc9a729..37e6b1313 100644 --- a/src/runner/assert.rs +++ b/src/runner/assert.rs @@ -19,7 +19,7 @@ use std::collections::HashMap; use crate::core::common::Value; -use crate::http; +use crate::http::libcurl; use super::core::{Error, RunnerError}; use super::core::*; @@ -107,7 +107,7 @@ impl AssertResult { } impl Assert { - pub fn eval(self, http_response: http::response::Response, variables: &HashMap) -> AssertResult { + pub fn eval(self, http_response: libcurl::core::Response, variables: &HashMap) -> AssertResult { let actual = self.query.eval(variables, http_response); let source_info = self.predicate.clone().predicate_func.source_info; let predicate_result = match actual.clone() { @@ -162,7 +162,7 @@ pub mod tests { fn test_eval() { let variables = HashMap::new(); assert_eq!( - assert_count_user().eval(http::response::tests::xml_three_users_http_response(), &variables), + assert_count_user().eval(libcurl::core::tests::xml_three_users_http_response(), &variables), AssertResult::Explicit { actual: Ok(Some(Value::Nodeset(3))), source_info: SourceInfo::init(1, 14, 1, 27), diff --git a/src/runner/capture.rs b/src/runner/capture.rs index 2bfbfb7ec..d91e21c3f 100644 --- a/src/runner/capture.rs +++ b/src/runner/capture.rs @@ -20,14 +20,15 @@ use std::collections::HashMap; use regex::Regex; use crate::core::common::Value; -use crate::http; +use crate::http::libcurl; use super::core::{CaptureResult, Error}; use super::core::RunnerError; use super::super::core::ast::*; impl Capture { - pub fn eval(self, variables: &HashMap, http_response: http::response::Response) -> Result { + + pub fn eval(self, variables: &HashMap, http_response: libcurl::core::Response) -> Result { let name = self.name.value; let value = self.query.clone().eval(variables, http_response)?; let value = match value { @@ -183,7 +184,7 @@ pub mod tests { }, }; - let error = capture.eval(&variables, http::response::tests::xml_three_users_http_response()).err().unwrap(); + let error = capture.eval(&variables, libcurl::core::tests::xml_three_users_http_response()).err().unwrap(); assert_eq!(error.source_info.start, Pos { line: 1, column: 7 }); assert_eq!(error.inner, RunnerError::QueryInvalidXpathEval) } @@ -238,13 +239,13 @@ pub mod tests { #[test] fn test_capture() { let variables = HashMap::new(); - assert_eq!(user_count_capture().eval(&variables, http::response::tests::xml_three_users_http_response()).unwrap(), + assert_eq!(user_count_capture().eval(&variables, libcurl::core::tests::xml_three_users_http_response()).unwrap(), CaptureResult { name: "UserCount".to_string(), value: Value::from_f64(3.0), }); - assert_eq!(duration_capture().eval(&variables, http::response::tests::json_http_response()).unwrap(), + assert_eq!(duration_capture().eval(&variables, libcurl::core::tests::json_http_response()).unwrap(), CaptureResult { name: "duration".to_string(), value: Value::from_f64(1.5), diff --git a/src/runner/cookie.rs b/src/runner/cookie.rs index 46cecadb1..eb054e8ba 100644 --- a/src/runner/cookie.rs +++ b/src/runner/cookie.rs @@ -99,7 +99,6 @@ impl ResponseCookie { /// pub fn max_age(&self) -> Option { for attr in self.attributes.clone() { - eprintln!("{:#?}", attr); if attr.name.as_str() == "Max-Age" { if let Some(v) = attr.value { if let Ok(v2) = v.as_str().parse::() { diff --git a/src/runner/core.rs b/src/runner/core.rs index 2c4cac84a..a362226c7 100644 --- a/src/runner/core.rs +++ b/src/runner/core.rs @@ -17,11 +17,9 @@ */ use std::collections::HashMap; -use serde::{Deserialize, Serialize}; use crate::core::common::{FormatError, SourceInfo, Value}; -use crate::http; -use crate::http::cookie::Cookie; +use crate::http::libcurl; #[derive(Clone, Debug, PartialEq, Eq)] pub struct RunnerOptions { @@ -31,15 +29,13 @@ pub struct RunnerOptions { } -//region resultlet noproxy_hosts = noproxy_host(matches.clone()); - #[derive(Clone, Debug, PartialEq, Eq)] pub struct HurlResult { pub filename: String, pub entries: Vec, pub time_in_ms: u128, pub success: bool, - pub cookies: Vec, + pub cookies: Vec, } impl HurlResult { @@ -54,8 +50,8 @@ impl HurlResult { #[derive(Clone, Debug, PartialEq, Eq)] pub struct EntryResult { - pub request: Option, - pub response: Option, + pub request: Option, + pub response: Option, //pub captures: Vec<(String, Value)>, pub captures: Vec, pub asserts: Vec, @@ -85,25 +81,20 @@ pub type PredicateResult = Result<(), Error>; // region error -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Eq)] pub struct Error { pub source_info: SourceInfo, pub inner: RunnerError, pub assert: bool, } -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Eq)] pub enum RunnerError { TemplateVariableNotDefined { name: String }, VariableNotDefined { name: String }, InvalidURL(String), HttpConnection { url: String, message: String }, FileReadAccess { value: String }, - - // Capture - //CaptureNonScalarUnsupported, - //??CaptureError {}, - InvalidUtf8, InvalidDecoding { charset: String }, InvalidCharset { charset: String }, @@ -157,7 +148,6 @@ impl FormatError for Error { RunnerError::AssertVersion { .. } => "Assert Http Version".to_string(), RunnerError::AssertStatus { .. } => "Assert Status".to_string(), RunnerError::QueryInvalidJson { .. } => "Invalid Json".to_string(), - RunnerError::InvalidUtf8 { .. } => "Invalid Utf8".to_string(), RunnerError::QueryInvalidJsonpathExpression { .. } => "Invalid jsonpath".to_string(), RunnerError::PredicateType { .. } => "Assert - Inconsistent predicate type".to_string(), RunnerError::SubqueryInvalidInput { .. } => "Subquery error".to_string(), @@ -174,7 +164,7 @@ impl FormatError for Error { match &self.inner { RunnerError::InvalidURL(url) => format!("Invalid url <{}>", url), RunnerError::TemplateVariableNotDefined { name } => format!("You must set the variable {}", name), - RunnerError::HttpConnection { url, message } => format!("can not connect to {} ({})", url, message), + RunnerError::HttpConnection { url, .. } => format!("can not connect to {}", url), RunnerError::AssertVersion { actual, .. } => format!("actual value is <{}>", actual), RunnerError::AssertStatus { actual, .. } => format!("actual value is <{}>", actual), RunnerError::PredicateValue(value) => format!("actual value is <{}>", value.to_string()), @@ -187,7 +177,6 @@ impl FormatError for Error { RunnerError::AssertHeaderValueError { actual } => format!("actual value is <{}>", actual), RunnerError::AssertBodyValueError { actual, .. } => format!("actual value is <{}>", actual), RunnerError::QueryInvalidJson { .. } => "The http response is not a valid json".to_string(), - RunnerError::InvalidUtf8 { .. } => "The http response is not a valid utf8 string".to_string(), RunnerError::QueryInvalidJsonpathExpression { value } => format!("the jsonpath expression '{}' is not valid", value), RunnerError::PredicateType { .. } => "predicate type inconsistent with value return by query".to_string(), RunnerError::SubqueryInvalidInput => "Type from query result and subquery do not match".to_string(), diff --git a/src/runner/entry.rs b/src/runner/entry.rs index 1e1aa6cb6..f473cde9b 100644 --- a/src/runner/entry.rs +++ b/src/runner/entry.rs @@ -18,21 +18,18 @@ use std::collections::HashMap; use std::time::Instant; -use encoding::{DecoderTrap, Encoding}; -use encoding::all::ISO_8859_1; use crate::core::ast::*; use crate::core::common::SourceInfo; use crate::core::common::Value; -use crate::http; -use crate::http::cookie::CookieJar; +use crate::http::libcurl; + use super::core::*; use super::core::{Error, RunnerError}; use crate::format::logger::Logger; - /// Run an entry with the hurl http client /// /// # Examples @@ -42,26 +39,23 @@ use crate::format::logger::Logger; /// use hurl::runner; /// /// // Create an http client -/// let client = http::client::Client::init(http::client::ClientOptions { -/// noproxy_hosts: vec![], -/// insecure: false, -/// redirect: http::client::Redirect::None, -/// http_proxy: None, -/// https_proxy: None, -/// all_proxy: None -/// }); +//// let client = http::client::Client::init(http::client::ClientOptions { +//// noproxy_hosts: vec![], +//// insecure: false, +//// redirect: http::client::Redirect::None, +//// http_proxy: None, +//// https_proxy: None, +//// all_proxy: None +//// }); /// ``` -pub fn run(entry: Entry, http_client: &http::client::Client, +pub fn run(entry: Entry, + http_client: &mut libcurl::client::Client, entry_index: usize, variables: &mut HashMap, - cookiejar: &mut CookieJar, context_dir: String, logger: &Logger, ) -> EntryResult { - - //let mut entry_log_builder = EntryLogBuilder::init(); - - let mut http_request = match entry.clone().request.eval(variables, context_dir.clone()) { + let http_request = match entry.clone().request.eval(variables, context_dir.clone()) { Ok(r) => r, Err(error) => { return EntryResult { @@ -75,27 +69,44 @@ pub fn run(entry: Entry, http_client: &http::client::Client, }; } }; - let cookies = cookiejar.clone().get_cookies(http_request.clone().host(), String::from("/")); - http_request.add_session_cookies(cookies); logger.verbose("------------------------------------------------------------------------------"); logger.verbose(format!("executing entry {}", entry_index + 1).as_str()); - for line in http_request.verbose_output() { - logger.send(line); - } - if let Some(params) = http_request.clone().form_params() { - logger.verbose("Form Params"); - for param in params { - logger.verbose(format!(" {}={}", param.name, param.value).as_str()); + + // Temporary - add cookie from request to the cookie store + // should be set explicitly + // url should be valid at the point + // do not use cookie from request + use url::Url; + if let Ok(url) = Url::parse(http_request.url.as_str()) { + for c in http_request.cookies.clone() { + let cookie = libcurl::core::Cookie { + domain: url.host_str().unwrap().to_string(), + include_subdomain: "FALSE".to_string(), + path: "/".to_string(), + https: "FALSE".to_string(), + expires: "0".to_string(), + name: c.name, + value: c.value, + }; + http_client.add_cookie(cookie); } - }; + } + + logger.verbose(""); + logger.verbose("Cookie store:"); + for cookie in http_client.get_cookie_storage() { + logger.verbose(cookie.to_string().as_str()); + } + logger.verbose(""); + log_request(logger, &http_request); let start = Instant::now(); - let http_response = match http_client.execute(&http_request) { + let http_response = match http_client.execute(&http_request, 0) { Ok(response) => response, - Err(e) => { + Err(_) => { return EntryResult { - request: Some(http_request), + request: Some(http_request.clone()), response: None, captures: vec![], asserts: vec![], @@ -106,8 +117,8 @@ pub fn run(entry: Entry, http_client: &http::client::Client, end: entry.clone().request.url.source_info.end, }, inner: RunnerError::HttpConnection { - message: e.message, - url: e.url, + message: "".to_string(), + url: http_request.url, }, assert: false, }], @@ -115,15 +126,10 @@ pub fn run(entry: Entry, http_client: &http::client::Client, }; } }; + let time_in_ms = start.elapsed().as_millis(); - for line in http_response.verbose_output() { - logger.receive(line); - } + logger.verbose(format!("Response Time: {}ms", time_in_ms).as_str()); - - //entry_log_builder.response(http_response.clone(), verbose); - - //hurl_log.entries.push(log_builder.build()); let captures = match entry.response.clone() { None => vec![], Some(response) => match response.eval_captures(http_response.clone(), variables) { @@ -146,57 +152,25 @@ pub fn run(entry: Entry, http_client: &http::client::Client, variables.insert(capture_result.name, capture_result.value); } - let asserts = match entry.response { None => vec![], Some(response) => response.eval_asserts(variables, http_response.clone(), context_dir) }; - let errors = asserts .iter() .filter_map(|assert| assert.clone().error()) .map(|Error { source_info, inner, .. }| Error { source_info, inner, assert: true }) .collect(); - - // update cookies - // for the domain - let domain = http_request.clone().host(); - //let mut cookies = cookie_store.get_cookies(host); - - // TEMPORARY also update store from request cookie - // TODO - DO BE REMOVED - add explicit directive in hurl file to interract with cookiejar - for cookie in http_request.clone().cookies { - cookiejar.update_cookies( - domain.clone(), - http_request.clone().url.path, - cookie, - ); - } - - - for cookie in http_response.cookies() { - cookiejar.update_cookies( - domain.clone(), - http_request.clone().url.path, - cookie, - ); - } - - if !captures.is_empty() { logger.verbose("Captures"); for capture in captures.clone() { - logger.verbose(format!("{}: {:?}", capture.name, capture.value).as_str()); - } - } - if !cookiejar.clone().cookies().is_empty() { - logger.verbose("CookieJar"); - for cookie in cookiejar.clone().cookies() { - logger.verbose(cookie.to_string().as_str()); + logger.verbose(format!("{}: {}", capture.name, capture.value).as_str()); } } + logger.verbose(""); + EntryResult { request: Some(http_request), response: Some(http_response), @@ -208,76 +182,39 @@ pub fn run(entry: Entry, http_client: &http::client::Client, } - - -// cookies -// for all domains - -// but only pass cookies for one domain for the request - -fn decode_bytes(bytes: Vec, encoding: http::core::Encoding) -> Result { - match encoding { - http::core::Encoding::Utf8 {} => match String::from_utf8(bytes) { - Ok(s) => Ok(s), - Err(_) => Err(RunnerError::InvalidDecoding { charset: encoding.to_string() }), - }, - http::core::Encoding::Latin1 {} => match ISO_8859_1.decode(&bytes, DecoderTrap::Strict) { - Ok(s) => Ok(s), - Err(_) => Err(RunnerError::InvalidDecoding { charset: encoding.to_string() }), - }, +pub fn log_request(logger: &Logger, request: &libcurl::core::Request) { + logger.verbose("Request"); + logger.verbose(format!("{} {}", request.method, request.url).as_str()); + for header in request.headers.clone() { + logger.verbose(header.to_string().as_str()); } -} - - -impl http::request::Request { - pub fn verbose_output(&self) -> Vec { - let mut lines = vec![]; - lines.push(format!("{} {}", self.clone().method.to_text(), self.clone().url())); - for header in self.clone().headers() { - lines.push(header.to_string()); + if !request.querystring.is_empty() { + logger.verbose("[QueryStringParams]"); + for param in request.querystring.clone() { + logger.verbose(param.to_string().as_str()); } - lines.push("".to_string()); - if !self.body.is_empty() { - let body = match decode_bytes(self.body.clone(), self.encoding()) { - Ok(s) => s, - Err(_) => format!("{:x?}", self.body) - }; - let body_lines: Vec<&str> = regex::Regex::new(r"\n|\r\n") - .unwrap() - .split(&body) - .collect(); - for line in body_lines { - lines.push(line.to_string()); - } - } - lines } -} - -impl http::response::Response { - pub fn verbose_output(&self) -> Vec { - let mut lines = vec![]; - lines.push(format!("HTTP/{} {}", self.version.to_text(), self.status)); - for header in self.clone().headers { - lines.push(header.to_string()); + if !request.form.is_empty() { + logger.verbose("[FormParams]"); + for param in request.form.clone() { + logger.verbose(param.to_string().as_str()); } - lines.push("".to_string()); - if !self.body.is_empty() { - - //let body = body_text(self.clone().body, get_header_value(self.clone().headers, "content-type")); - //let body = substring(body.as_str(), 0, limit_body); - let body = match decode_bytes(self.body.clone(), self.encoding()) { - Ok(s) => s, - Err(_) => format!("{:x?}", self.body) - }; - let body_lines: Vec<&str> = regex::Regex::new(r"\n|\r\n") - .unwrap() - .split(&body) - .collect(); - for line in body_lines { - lines.push(line.to_string()); - } - } - lines } -} + if !request.multipart.is_empty() { + logger.verbose("[MultipartFormData]"); + for param in request.multipart.clone() { + logger.verbose(param.to_string().as_str()); + } + } + if !request.cookies.is_empty() { + logger.verbose("[Cookies]"); + for cookie in request.cookies.clone() { + logger.verbose(cookie.to_string().as_str()); + } + } + if let Some(s) = request.content_type.clone() { + logger.verbose(""); + logger.verbose(format!("implicit content-type={}", s).as_str()); + } + logger.verbose(""); +} \ No newline at end of file diff --git a/src/runner/file.rs b/src/runner/file.rs index ae0dd38ce..6874266b9 100644 --- a/src/runner/file.rs +++ b/src/runner/file.rs @@ -20,7 +20,8 @@ use std::time::Instant; use crate::core::ast::*; use crate::core::common::Value; -use crate::http; +use crate::http::libcurl; + use super::core::*; use super::super::format; @@ -34,7 +35,7 @@ use crate::core::common::FormatError; /// # Example /// /// ``` -/// use hurl::http; +/// use hurl::http::libcurl; /// use hurl::runner; /// use hurl::format; /// @@ -47,15 +48,16 @@ use crate::core::common::FormatError; /// let hurl_file = hurl::parser::parse_hurl_file(s).unwrap(); /// /// // Create an http client -/// let mut cookie_store = http::cookie::CookieJar::init(vec![]); -/// let client = http::client::Client::init(http::client::ClientOptions { -/// noproxy_hosts: vec![], +/// let options = libcurl::client::ClientOptions { +/// follow_location: false, +/// max_redirect: None, +/// cookie_input_file: None, +/// proxy: None, +/// no_proxy: None, +/// verbose: false, /// insecure: false, -/// redirect: http::client::Redirect::None, -/// http_proxy: None, -/// https_proxy: None, -/// all_proxy: None -/// }); +/// }; +/// let mut client = libcurl::client::Client::init(options); /// /// // Define runner options /// let variables = std::collections::HashMap::new(); @@ -79,9 +81,8 @@ use crate::core::common::FormatError; /// let context_dir = "current_dir".to_string(); /// let hurl_results = runner::file::run( /// hurl_file, -/// client, +/// &mut client, /// filename, -/// &mut cookie_store, /// context_dir, /// options, /// logger @@ -91,9 +92,8 @@ use crate::core::common::FormatError; /// ``` pub fn run( hurl_file: HurlFile, - http_client: http::client::Client, + http_client: &mut libcurl::client::Client, filename: String, - cookiejar: &mut http::cookie::CookieJar, context_dir: String, options: RunnerOptions, logger: format::logger::Logger, @@ -113,7 +113,7 @@ pub fn run( let start = Instant::now(); for (entry_index, entry) in hurl_file.entries.iter().take(n).cloned().enumerate().collect::>() { - let entry_result = entry::run(entry, &http_client, entry_index, &mut variables, cookiejar, context_dir.clone(), &logger); + let entry_result = entry::run(entry, http_client, entry_index, &mut variables, context_dir.clone(), &logger); entries.push(entry_result.clone()); for e in entry_result.errors.clone() { let error = format::error::Error { @@ -135,7 +135,7 @@ pub fn run( let time_in_ms = start.elapsed().as_millis(); let success = entries.iter().flat_map(|e| e.errors.clone()).next().is_none(); - let cookies = cookiejar.clone().cookies(); + let cookies = http_client.get_cookie_storage(); HurlResult { filename, entries, diff --git a/src/runner/log_deserialize.rs b/src/runner/log_deserialize.rs index aaf843860..401d55402 100644 --- a/src/runner/log_deserialize.rs +++ b/src/runner/log_deserialize.rs @@ -17,7 +17,6 @@ */ -use crate::http; use crate::http::libcurl::core::*; use super::cookie::*; @@ -77,14 +76,14 @@ fn parse_entry_result(value: serde_json::Value) -> Result { let request = match value.get("request") { None => None, Some(v) => { - let r = http::import::parse_request(v.clone())?; + let r = parse_request(v.clone())?; Some(r) } }; let response = match value.get("response") { None => None, Some(v) => { - let r = http::import::parse_response(v.clone())?; + let r = parse_response(v.clone())?; Some(r) } }; @@ -162,6 +161,7 @@ pub fn parse_request(value: serde_json::Value) -> Result { // TODO let multipart = vec![]; let body = vec![]; + let content_type = None; Ok(Request { method, @@ -172,6 +172,7 @@ pub fn parse_request(value: serde_json::Value) -> Result { body, multipart, form, + content_type, }) } else { Err("expecting an object for the request".to_string()) @@ -376,6 +377,7 @@ mod tests { body: vec![], form: vec![], multipart: vec![], + content_type: None, }); diff --git a/src/runner/log_serialize.rs b/src/runner/log_serialize.rs index 39cb89918..2f9b6d69b 100644 --- a/src/runner/log_serialize.rs +++ b/src/runner/log_serialize.rs @@ -64,8 +64,8 @@ impl Serialize for AssertResult { S: Serializer, { let mut state = serializer.serialize_struct("??", 3)?; - if let AssertResult::Version { source_info, actual, expected } = self { - state.serialize_field("source_info", source_info)?; + if let AssertResult::Version { actual, expected,.. } = self { + //state.serialize_field("source_info", source_info)?; state.serialize_field("actual", actual)?; state.serialize_field("expected", expected)?; }; @@ -123,16 +123,6 @@ impl Serialize for Response { state.serialize_field("headers", &self.clone().headers)?; // TODO Serialize body -// let content_type = self.get_header("content_type", true); -// if let Some(value) = content_type.first() { -// if value.as_str() == "application/json; charset=UTF-8" { -// let s = String::from_utf8(self.body.clone()).expect("Found invalid UTF-8"); -// let result: Result = serde_json::from_str(s.as_str()); -// if let Ok(v) = result { -// state.serialize_field("json", &v)?; -// } -// } -// } state.end() } } @@ -173,22 +163,6 @@ impl Serialize for RequestCookie { } } -//impl Serialize for Cookie { -// fn serialize(&self, serializer: S) -> Result -// where -// S: Serializer, -// { -// let mut state = serializer.serialize_struct("InternalCookie", 3)?; -// state.serialize_field("name", &self.clone().name)?; -// state.serialize_field("value", &self.clone().value)?; -// state.serialize_field("domain", &self.clone().domain)?; -// state.serialize_field("path", &self.clone().path)?; -// state.serialize_field("include_subdomain", &self.clone().subdomains)?; -// state.end() -// } -//} - - impl Serialize for Version { fn serialize(&self, serializer: S) -> Result where @@ -235,3 +209,20 @@ impl Serialize for ResponseCookie { } } + +impl Serialize for Cookie { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let mut state = serializer.serialize_struct("Cookie", 3)?; + state.serialize_field("domain", &self.clone().domain)?; + state.serialize_field("include_subdomain", &self.clone().include_subdomain)?; + state.serialize_field("path", &self.clone().path)?; + state.serialize_field("https", &self.clone().https)?; + state.serialize_field("expires", &self.clone().expires)?; + state.serialize_field("name", &self.clone().name)?; + state.serialize_field("value", &self.clone().value)?; + state.end() + } +} \ No newline at end of file diff --git a/src/runner/mod.rs b/src/runner/mod.rs index 8acd59b86..2ab04b319 100644 --- a/src/runner/mod.rs +++ b/src/runner/mod.rs @@ -34,12 +34,12 @@ mod http_response; mod json; pub mod log_serialize; pub mod log_deserialize; +mod multipart; mod predicate; mod query; pub mod request; mod response; mod template; -mod text; mod xpath; mod expr; diff --git a/src/runner/multipart.rs b/src/runner/multipart.rs new file mode 100644 index 000000000..c8c3bce3d --- /dev/null +++ b/src/runner/multipart.rs @@ -0,0 +1,223 @@ +/* + * hurl (https://hurl.dev) + * Copyright (C) 2020 Orange + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +extern crate libxml; + + +use std::collections::HashMap; +use std::ffi::OsStr; +use std::fs::File; +#[allow(unused)] +use std::io::prelude::*; +use std::io::Read; +use std::path::Path; + +use crate::core::ast::*; +use crate::core::common::Value; +use crate::http::libcurl; + +use super::core::{Error, RunnerError}; + + + +impl MultipartParam { + pub fn eval(self, + variables: &HashMap, + context_dir: String, + ) -> Result { + match self { + MultipartParam::Param(KeyValue { key, value, .. }) => { + let name = key.value; + let value = value.eval(variables)?; + Ok(libcurl::core::MultipartParam::Param(libcurl::core::Param { name, value })) + } + MultipartParam::FileParam(param) => { + let file_param = param.eval(context_dir)?; + Ok(libcurl::core::MultipartParam::FileParam(file_param)) + } + } + } +} + +impl FileParam { + pub fn eval(self, context_dir: String) -> Result { + let name = self.key.value; + + let filename = self.value.filename.clone(); + let path = Path::new(filename.value.as_str()); + let absolute_filename = if path.is_absolute() { + filename.value.clone() + } else { + Path::new(context_dir.as_str()).join(filename.value.clone()).to_str().unwrap().to_string() + }; + + let data = match File::open(absolute_filename.clone()) { + Ok(mut f) => { + let mut bytes = Vec::new(); + match f.read_to_end(&mut bytes) { + Ok(_) => bytes, + Err(_) => return Err(Error { + source_info: filename.source_info, + inner: RunnerError::FileReadAccess { value: absolute_filename }, + assert: false, + }) + } + } + Err(_) => return Err(Error { + source_info: filename.source_info, + inner: RunnerError::FileReadAccess { value: absolute_filename }, + assert: false, + }) + }; + + if !Path::new(&absolute_filename).exists() { + return Err(Error { + source_info: filename.source_info, + inner: RunnerError::FileReadAccess { value: filename.value.clone() }, + assert: false, + }); + } + + let content_type = self.value.content_type(); + Ok(libcurl::core::FileParam { + name, + filename: filename.value, + data, + content_type, + }) + } +} + + +impl FileValue { + pub fn content_type(&self) -> String { + match self.content_type.clone() { + None => match Path::new(self.filename.value.as_str()).extension().and_then(OsStr::to_str) { + Some("gif") => "image/gif".to_string(), + Some("jpg") => "image/jpeg".to_string(), + Some("jpeg") => "image/jpeg".to_string(), + Some("png") => "image/png".to_string(), + Some("svg") => "image/svg+xml".to_string(), + Some("txt") => "text/plain".to_string(), + Some("htm") => "text/html".to_string(), + Some("html") => "text/html".to_string(), + Some("pdf") => "application/pdf".to_string(), + Some("xml") => "application/xml".to_string(), + _ => "application/octet-stream".to_string(), + } + Some(content_type) => content_type + } + } +} + + +#[cfg(test)] +mod tests { + use crate::core::common::SourceInfo; + + use super::*; + + pub fn whitespace() -> Whitespace { + Whitespace { + value: String::from(" "), + source_info: SourceInfo::init(0, 0, 0, 0), + } + } + + + #[test] + pub fn test_eval_file_param() { + let line_terminator = LineTerminator { + space0: whitespace(), + comment: None, + newline: whitespace(), + }; + assert_eq!(FileParam { + line_terminators: vec![], + space0: whitespace(), + key: EncodedString { + value: "upload1".to_string(), + encoded: "upload1".to_string(), + quotes: false, + source_info: SourceInfo::init(0, 0, 0, 0), + }, + space1: whitespace(), + space2: whitespace(), + value: FileValue { + space0: whitespace(), + filename: Filename { value: "hello.txt".to_string(), source_info: SourceInfo::init(0, 0, 0, 0) }, + space1: whitespace(), + space2: whitespace(), + content_type: None, + }, + line_terminator0: line_terminator, + }.eval("integration/tests".to_string()).unwrap(), + libcurl::core::FileParam { + name: "upload1".to_string(), + filename: "hello.txt".to_string(), + data: b"Hello World!".to_vec(), + content_type: "text/plain".to_string(), + }); + } + + #[test] + pub fn test_file_value_content_type() { + assert_eq!(FileValue { + space0: whitespace(), + filename: Filename { + value: "hello.txt".to_string(), + source_info: SourceInfo::init(0, 0, 0, 0), + }, + space1: whitespace(), + space2: whitespace(), + content_type: None, + }.content_type(), "text/plain".to_string()); + + assert_eq!(FileValue { + space0: whitespace(), + filename: Filename { + value: "hello.html".to_string(), + source_info: SourceInfo::init(0, 0, 0, 0), + }, + space1: whitespace(), + space2: whitespace(), + content_type: None, + }.content_type(), "text/html".to_string()); + + assert_eq!(FileValue { + space0: whitespace(), + filename: Filename { + value: "hello.txt".to_string(), + source_info: SourceInfo::init(0, 0, 0, 0), + }, + space1: whitespace(), + space2: whitespace(), + content_type: Some("text/html".to_string()), + }.content_type(), "text/html".to_string()); + + assert_eq!(FileValue { + space0: whitespace(), + filename: Filename { + value: "hello".to_string(), + source_info: SourceInfo::init(0, 0, 0, 0), + }, + space1: whitespace(), + space2: whitespace(), + content_type: None, + }.content_type(), "application/octet-stream".to_string()); + } +} diff --git a/src/runner/query.rs b/src/runner/query.rs index dd39c2885..0089fd213 100644 --- a/src/runner/query.rs +++ b/src/runner/query.rs @@ -15,108 +15,51 @@ * limitations under the License. * */ + use std::collections::HashMap; -use encoding::{DecoderTrap, Encoding}; -use encoding::all::ISO_8859_1; use regex::Regex; -#[cfg(test)] -use crate::core::common::{Pos, SourceInfo}; use crate::core::common::Value; -//use crate::core::jsonpath; -use crate::http; -use crate::http::cookie::ResponseCookie; +use crate::http::libcurl; use crate::jsonpath; +use super::cookie; use super::core::{Error, RunnerError}; -//use super::http; use super::super::core::ast::*; use super::xpath; -// QueryResult -// success => just the value is kept -// error => thrown within asserts / logged within Captures - -// => hurl report => do you need more information than just the value? -// each assert returned by an entry -// contains a queryLog which contains a query result - -// json -// 1.0 and 1 are different? -// 1.01 and 1.010 different on ast => but same on value - - -// value not found -// Option - -// source info for the input? - -// value not found/ does not exist => add error -// depends on the query type -// jsonpath will return an empty list? no match to be clarified -// header return really nothing - -// check that header does not exist? -// only for assert specific not found error to work with predicate exist -// => does not make sense for capture - -// implicit assert - - -// status qnd body never fails! -// semantic not in the type system -// in the test methods availibility - - -// error source_info for invalid response content -// source info => all the query!! not just the expression! => should come before invalid expression - -// error: Invalid XML response -// --> test.hurl:10:2 -// 10 | xpath //person countEquals 10 -// | ^^^^^^^^^^^^^^ - -// error: Invalid XML response -// --> test.hurl:10:7 -// 10 | xpath //person countEquals 10 -// | ^^^^^^^^ - -// also use for implicit assert => query header => distinguihed between the 2 header quqry (implicit vs explicit) -// error: Header not Found -// --> test.hurl:10:2 -// 10 | Custom: XXX -// | ^^^^^^ -// an enum is always a value? - - pub type QueryResult = Result, Error>; -impl http::response::Response { - // utf8 except if content-type include another charset - pub fn text(&self) -> Result { - if let Some(v) = self.content_type() { - if v.contains("charset=ISO-8859-1") { - match ISO_8859_1.decode(&self.body, DecoderTrap::Strict) { - Ok(s) => return Ok(s), - Err(_) => return Err(RunnerError::InvalidDecoding { charset: "iso8859-1".to_string() }), - } + +impl libcurl::core::Response { + pub fn is_html(&self) -> bool { + for header in self.headers.clone() { + if header.name.to_lowercase() == "content-type" { + return header.value.contains("html"); } } - match String::from_utf8(self.body.clone()) { - Ok(s) => Ok(s), - Err(_) => Err(RunnerError::InvalidUtf8 {}), + false + } + + pub fn get_cookie(&self, name: String) -> Option { + for cookie in self.cookies() { + if cookie.name == name { + return Some(cookie); + } } + None } } + impl Query { - pub fn eval(self, variables: &HashMap, http_response: http::response::Response) -> QueryResult { + pub fn eval(self, variables: &HashMap, http_response: libcurl::core::Response) -> QueryResult { match self.value { QueryValue::Status {} => Ok(Some(Value::Integer(i64::from(http_response.status)))), QueryValue::Header { name, .. } => { let header_name = name.eval(variables)?; - let values = http_response.get_header(header_name.as_str(), false); + let values = http_response.get_header(header_name); if values.is_empty() { Ok(None) } else if values.len() == 1 { @@ -129,7 +72,7 @@ impl Query { } QueryValue::Cookie { expr: CookiePath { name, attribute }, .. } => { let cookie_name = name.eval(variables)?; - match http_response.get_cookie(cookie_name.as_str()) { + match http_response.get_cookie(cookie_name) { None => Ok(None), Some(cookie) => { let attribute_name = if let Some(attribute) = attribute { @@ -263,16 +206,16 @@ impl Query { } impl CookieAttributeName { - pub fn eval(self, cookie: ResponseCookie) -> Option { + pub fn eval(self, cookie: cookie::ResponseCookie) -> Option { match self { CookieAttributeName::Value(_) => Some(Value::String(cookie.value)), - CookieAttributeName::Expires(_) => cookie.expires.map(Value::String), - CookieAttributeName::MaxAge(_) => cookie.max_age.map(Value::Integer), - CookieAttributeName::Domain(_) => cookie.domain.map(Value::String), - CookieAttributeName::Path(_) => cookie.path.map(Value::String), - CookieAttributeName::Secure(_) => cookie.secure.map(Value::Bool), - CookieAttributeName::HttpOnly(_) => cookie.http_only.map(Value::Bool), - CookieAttributeName::SameSite(_) => cookie.same_site.map(Value::String), + CookieAttributeName::Expires(_) => cookie.expires().map(Value::String), + CookieAttributeName::MaxAge(_) => cookie.max_age().map(Value::Integer), + CookieAttributeName::Domain(_) => cookie.domain().map(Value::String), + CookieAttributeName::Path(_) => cookie.path().map(Value::String), + CookieAttributeName::Secure(_) => if cookie.has_secure() { Some(Value::Bool(true)) } else { None }, + CookieAttributeName::HttpOnly(_) => if cookie.has_httponly() { Some(Value::Bool(true)) } else { None }, + CookieAttributeName::SameSite(_) => cookie.samesite().map(Value::String), } } } @@ -280,7 +223,7 @@ impl CookieAttributeName { #[cfg(test)] pub mod tests { - use crate::http::cookie::ResponseCookie; + use crate::core::common::{Pos, SourceInfo}; use super::*; @@ -354,9 +297,9 @@ pub mod tests { } } - pub fn json_http_response() -> http::response::Response { - http::response::Response { - version: http::response::Version::Http10, + pub fn json_http_response() -> libcurl::core::Response { + libcurl::core::Response { + version: libcurl::core::Version::Http10, status: 0, headers: vec![], body: String::into_bytes(r#" @@ -493,7 +436,7 @@ pub mod tests { fn test_query_status() { let variables = HashMap::new(); assert_eq!( - Query { source_info: SourceInfo::init(0, 0, 0, 0), value: QueryValue::Status {} }.eval(&variables, http::response::tests::hello_http_response()).unwrap().unwrap(), + Query { source_info: SourceInfo::init(0, 0, 0, 0), value: QueryValue::Status {} }.eval(&variables, libcurl::core::tests::hello_http_response()).unwrap().unwrap(), Value::Integer(200) ); } @@ -521,7 +464,7 @@ pub mod tests { // let error = query_header.eval(http::hello_http_response()).err().unwrap(); // assert_eq!(error.source_info.start, Pos { line: 1, column: 8 }); // assert_eq!(error.inner, RunnerError::QueryHeaderNotFound); - assert_eq!(query_header.eval(&variables, http::response::tests::hello_http_response()).unwrap(), None); + assert_eq!(query_header.eval(&variables, libcurl::core::tests::hello_http_response()).unwrap(), None); } #[test] @@ -545,7 +488,7 @@ pub mod tests { }, }; assert_eq!( - query_header.eval(&variables, http::response::tests::hello_http_response()).unwrap().unwrap(), + query_header.eval(&variables, libcurl::core::tests::hello_http_response()).unwrap().unwrap(), Value::String(String::from("text/html; charset=utf-8")) ); } @@ -557,11 +500,11 @@ pub mod tests { value: String::from(""), source_info: SourceInfo::init(0, 0, 0, 0), }; - let response = http::response::Response { - version: http::response::Version::Http10, + let response = libcurl::core::Response { + version: libcurl::core::Version::Http10, status: 0, headers: vec![ - http::core::Header { + libcurl::core::Header { name: "Set-Cookie".to_string(), value: "LSID=DQAAAKEaem_vYg; Path=/accounts; Expires=Wed, 13 Jan 2021 22:23:01 GMT; Secure; HttpOnly".to_string(), } @@ -667,18 +610,27 @@ pub mod tests { #[test] fn test_eval_cookie_attribute_name() { - -// "LSID=DQAAAKEaem_vYg; Path=/accounts; Expires=Wed, 13 Jan 2021 22:23:01 GMT; Secure; HttpOnly" - let cookie = ResponseCookie { + let cookie = cookie::ResponseCookie { name: "LSID".to_string(), value: "DQAAAKEaem_vYg".to_string(), - domain: None, - path: Some("/accounts".to_string()), - max_age: None, - expires: Some("Wed, 13 Jan 2021 22:23:01 GMT".to_string()), - secure: Some(true), - http_only: Some(true), - same_site: None, + attributes: vec![ + cookie::CookieAttribute { + name: "Path".to_string(), + value: Some("/accounts".to_string()), + }, + cookie::CookieAttribute { + name: "Expires".to_string(), + value: Some("Wed, 13 Jan 2021 22:23:01 GMT".to_string()), + }, + cookie::CookieAttribute { + name: "Secure".to_string(), + value: None, + }, + cookie::CookieAttribute { + name: "HttpOnly".to_string(), + value: None, + } + ], }; assert_eq!(CookieAttributeName::Value("_".to_string()).eval(cookie.clone()).unwrap(), Value::String("DQAAAKEaem_vYg".to_string())); assert_eq!(CookieAttributeName::Domain("_".to_string()).eval(cookie.clone()), None); @@ -687,7 +639,7 @@ pub mod tests { assert_eq!(CookieAttributeName::Expires("_".to_string()).eval(cookie.clone()).unwrap(), Value::String("Wed, 13 Jan 2021 22:23:01 GMT".to_string())); assert_eq!(CookieAttributeName::Secure("_".to_string()).eval(cookie.clone()).unwrap(), Value::Bool(true)); assert_eq!(CookieAttributeName::HttpOnly("_".to_string()).eval(cookie.clone()).unwrap(), Value::Bool(true)); - assert_eq!(CookieAttributeName::SameSite("_".to_string()).eval(cookie.clone()), None); + assert_eq!(CookieAttributeName::SameSite("_".to_string()).eval(cookie), None); } #[test] @@ -697,29 +649,29 @@ pub mod tests { Query { source_info: SourceInfo::init(0, 0, 0, 0), value: QueryValue::Body {}, - }.eval(&variables, http::response::tests::hello_http_response()).unwrap().unwrap(), + }.eval(&variables, libcurl::core::tests::hello_http_response()).unwrap().unwrap(), Value::String(String::from("Hello World!")) ); let error = Query { source_info: SourceInfo::init(1, 1, 1, 2), value: QueryValue::Body {}, - }.eval(&variables, http::response::tests::bytes_http_response()).err().unwrap(); + }.eval(&variables, libcurl::core::tests::bytes_http_response()).err().unwrap(); assert_eq!(error.source_info, SourceInfo::init(1, 1, 1, 2)); - assert_eq!(error.inner, RunnerError::InvalidUtf8 {}); + assert_eq!(error.inner, RunnerError::InvalidDecoding { charset: "utf-8".to_string()}); } #[test] fn test_query_invalid_utf8() { let variables = HashMap::new(); - let http_response = http::response::Response { - version: http::response::Version::Http10, + let http_response = libcurl::core::Response { + version: libcurl::core::Version::Http10, status: 0, headers: vec![], body: vec![200], }; let error = xpath_users().eval(&variables, http_response).err().unwrap(); assert_eq!(error.source_info.start, Pos { line: 1, column: 1 }); - assert_eq!(error.inner, RunnerError::InvalidUtf8); + assert_eq!(error.inner, RunnerError::InvalidDecoding { charset: "utf-8".to_string() }); } #[test] @@ -745,7 +697,7 @@ pub mod tests { }, }, }; - let error = query.eval(&variables, http::response::tests::xml_two_users_http_response()).err().unwrap(); + let error = query.eval(&variables, libcurl::core::tests::xml_two_users_http_response()).err().unwrap(); assert_eq!(error.inner, RunnerError::QueryInvalidXpathEval); assert_eq!(error.source_info.start, Pos { line: 1, column: 7 }); } @@ -754,8 +706,8 @@ pub mod tests { fn test_query_xpath() { let variables = HashMap::new(); - assert_eq!(xpath_users().eval(&variables, http::response::tests::xml_two_users_http_response()).unwrap().unwrap(), Value::Nodeset(2)); - assert_eq!(xpath_count_user_query().eval(&variables, http::response::tests::xml_two_users_http_response()).unwrap().unwrap(), Value::Float(2, 0)); + assert_eq!(xpath_users().eval(&variables, libcurl::core::tests::xml_two_users_http_response()).unwrap().unwrap(), Value::Nodeset(2)); + assert_eq!(xpath_count_user_query().eval(&variables, libcurl::core::tests::xml_two_users_http_response()).unwrap().unwrap(), Value::Float(2, 0)); } #[cfg(test)] @@ -786,7 +738,7 @@ pub mod tests { #[test] fn test_query_xpath_with_html() { let variables = HashMap::new(); - assert_eq!(xpath_html_charset().eval(&variables, http::response::tests::html_http_response()).unwrap().unwrap(), Value::String(String::from("UTF-8"))); + assert_eq!(xpath_html_charset().eval(&variables, libcurl::core::tests::html_http_response()).unwrap().unwrap(), Value::String(String::from("UTF-8"))); } #[test] @@ -823,8 +775,8 @@ pub mod tests { #[test] fn test_query_invalid_json() { let variables = HashMap::new(); - let http_response = http::response::Response { - version: http::response::Version::Http10, + let http_response = libcurl::core::Response { + version: libcurl::core::Version::Http10, status: 0, headers: vec![], body: String::into_bytes(String::from("xxx")), @@ -837,8 +789,8 @@ pub mod tests { #[test] fn test_query_json_not_found() { let variables = HashMap::new(); - let http_response = http::response::Response { - version: http::response::Version::Http10, + let http_response = libcurl::core::Response { + version: libcurl::core::Version::Http10, status: 0, headers: vec![], body: String::into_bytes(String::from("{}")), @@ -867,11 +819,11 @@ pub mod tests { fn test_query_regex() { let variables = HashMap::new(); assert_eq!( - regex_name().eval(&variables, http::response::tests::hello_http_response()).unwrap().unwrap(), + regex_name().eval(&variables, libcurl::core::tests::hello_http_response()).unwrap().unwrap(), Value::String("World".to_string()) ); - let error = regex_invalid().eval(&variables, http::response::tests::hello_http_response()).err().unwrap(); + let error = regex_invalid().eval(&variables, libcurl::core::tests::hello_http_response()).err().unwrap(); assert_eq!(error.source_info, SourceInfo::init(1, 7, 1, 10)); assert_eq!(error.inner, RunnerError::InvalidRegex()); } diff --git a/src/runner/request.rs b/src/runner/request.rs index 62aadb02c..e20ab50c7 100644 --- a/src/runner/request.rs +++ b/src/runner/request.rs @@ -20,158 +20,99 @@ extern crate serde_json; extern crate url as external_url; use std::collections::HashMap; -use std::path::Path; +#[allow(unused)] +use std::io::prelude::*; use crate::core::ast::*; use crate::core::common::Value; -use crate::http; +use crate::http::libcurl; -use super::core::{Error, RunnerError}; +use super::core::Error; impl Request { pub fn eval(self, variables: &HashMap, context_dir: String, - ) -> Result { + ) -> Result { let method = self.method.clone().eval(); - let url = self.clone().url.eval(&variables)?; - let mut querystring: Vec = vec![]; - // query string from url - // parse url string - let (url, params) = match external_url::Url::parse(url.as_str()) { - Err(_) => { - return Err(Error { - source_info: self.clone().url.source_info, - inner: RunnerError::InvalidURL(url), - assert: false, - }); - } - Ok(u) => { - let url = http::core::Url { - scheme: u.scheme().to_string(), - host: u.host_str().unwrap().to_string(), - port: u.port(), - path: u.path().to_string(), - query_string: if let Some(s) = u.query() { s.to_string() } else { "".to_string() }, - }; - let params: Vec = vec![]; - (url, params) - } - }; - for param in params { - querystring.push(param); - } - for param in self.clone().querystring_params() { - let name = param.key.value; - let value = param.value.eval(variables)?; - querystring.push(http::core::Param { name, value }); - } + let url = self.clone().url.eval(&variables)?; + // headers - let mut headers: Vec = vec![]; + let mut headers: Vec = vec![]; for header in self.clone().headers { let name = header.key.value; let value = header.value.eval(variables)?; - headers.push(http::core::Header { + headers.push(libcurl::core::Header { name, value, }); } - - // add cookies - //let host = url.host.as_str(); - let mut cookies = vec![]; - // TODO cookie from header - for cookie in self.clone().cookies() { - let cookie = http::cookie::ResponseCookie { - name: cookie.clone().name.value, - value: cookie.clone().value.value, - max_age: None, - domain: Some(url.clone().host), - path: None, - secure: None, - http_only: None, - expires: None, - same_site: None, - }; - //headers.push(cookie.to_header()); - cookies.push(cookie); + let mut querystring: Vec = vec![]; + for param in self.clone().querystring_params() { + let name = param.key.value; + let value = param.value.eval(variables)?; + querystring.push(libcurl::core::Param { name, value }); } + let mut form: Vec = vec![]; + for param in self.clone().form_params() { + let name = param.key.value; + let value = param.value.eval(variables)?; + form.push(libcurl::core::Param { name, value }); + } +// if !self.clone().form_params().is_empty() { +// headers.push(libcurl::core::Header { +// name: String::from("Content-Type"), +// value: String::from("application/x-www-form-urlencoded"), +// }); +// } - if !self.clone().form_params().is_empty() { - headers.push(http::core::Header { - name: String::from("Content-Type"), - value: String::from("application/x-www-form-urlencoded"), - }); + let mut cookies = vec![]; + for cookie in self.clone().cookies() { + let cookie = libcurl::core::RequestCookie { + name: cookie.clone().name.value, + value: cookie.clone().value.value, + }; + cookies.push(cookie); } let bytes = match self.clone().body { Some(body) => body.eval(variables, context_dir.clone())?, - None => { - if !self.clone().form_params().is_empty() { - let mut params = vec![]; - for param in self.clone().form_params() { - let name = param.key.value; - let value = param.value.eval(variables)?; - params.push(http::core::Param { - name, - value, - }); - } - - http::core::encode_form_params(params) - } else { - vec![] - } - } + None => vec![] }; + let mut multipart = vec![]; for multipart_param in self.clone().multipart_form_data() { - match multipart_param { - MultipartParam::FileParam(FileParam { key, value: FileValue { filename, content_type, .. }, .. }) => { - let name = key.value; - - let path = Path::new(filename.value.as_str()); - let absolute_filename = if path.is_absolute() { - filename.clone().value - } else { - Path::new(context_dir.as_str()).join(filename.value.clone()).to_str().unwrap().to_string() - }; - - if !Path::new(&absolute_filename).exists() { - return Err(Error { - source_info: filename.source_info, - inner: RunnerError::FileReadAccess { value: filename.value.clone() }, - assert: false, - }); - } - multipart.push(http::request::MultipartParam::FileParam { name, filename: absolute_filename, content_type }); - } - MultipartParam::Param(KeyValue { key, value, .. }) => { - let name = key.value; - let value = value.eval(variables)?; - multipart.push(http::request::MultipartParam::TextParam { name, value }); - } - } + let param = multipart_param.eval(variables, context_dir.clone())?; + multipart.push(param); } + let content_type = if !form.is_empty() { + Some("application/x-www-form-urlencoded".to_string()) + } else if !multipart.is_empty() { + Some("multipart/form-data".to_string()) + } else if let Some(Body { value:Bytes::Json {..}, ..}) = self.body { + Some("application/json".to_string()) + } else { + None + }; // add implicit content type - if self.content_type().is_none() { - if let Some(body) = self.body { - if let Bytes::Json { .. } = body.value { - headers.push(http::core::Header { - name: String::from("Content-Type"), - value: String::from("application/json"), - }); - } - } - } +// if self.content_type().is_none() { +// if let Some(body) = self.body { +// if let Bytes::Json { .. } = body.value { +// headers.push(libcurl::core::Header { +// name: String::from("Content-Type"), +// value: String::from("application/json"), +// }); +// } +// } +// } - Ok(http::request::Request { + Ok(libcurl::core::Request { method, url, querystring, @@ -179,6 +120,8 @@ impl Request { cookies, body: bytes, multipart, + form, + content_type }) } @@ -193,33 +136,33 @@ impl Request { } impl Method { - fn eval(self) -> http::request::Method { + fn eval(self) -> libcurl::core::Method { match self { - Method::Get => http::request::Method::Get, - Method::Head => http::request::Method::Head, - Method::Post => http::request::Method::Post, - Method::Put => http::request::Method::Put, - Method::Delete => http::request::Method::Delete, - Method::Connect => http::request::Method::Connect, - Method::Options => http::request::Method::Options, - Method::Trace => http::request::Method::Trace, - Method::Patch => http::request::Method::Patch, + Method::Get => libcurl::core::Method::Get, + Method::Head => libcurl::core::Method::Head, + Method::Post => libcurl::core::Method::Post, + Method::Put => libcurl::core::Method::Put, + Method::Delete => libcurl::core::Method::Delete, + Method::Connect => libcurl::core::Method::Connect, + Method::Options => libcurl::core::Method::Options, + Method::Trace => libcurl::core::Method::Trace, + Method::Patch => libcurl::core::Method::Patch, } } } -pub fn split_url(url: String) -> (String, Vec) { +pub fn split_url(url: String) -> (String, Vec) { match url.find('?') { None => (url, vec![]), Some(index) => { let (url, params) = url.split_at(index); - let params: Vec = params[1..].split('&') + let params: Vec = params[1..].split('&') .map(|s| { match s.find('=') { - None => http::core::Param { name: s.to_string(), value: String::from("") }, + None => libcurl::core::Param { name: s.to_string(), value: String::from("") }, Some(index) => { let (name, value) = s.split_at(index); - http::core::Param { name: name.to_string(), value: value[1..].to_string() } + libcurl::core::Param { name: name.to_string(), value: value[1..].to_string() } } } }) @@ -230,56 +173,41 @@ pub fn split_url(url: String) -> (String, Vec) { } } -pub fn eval_url(s: String) -> Result { - match url::Url::parse(s.as_str()) { - Err(_) => Err(RunnerError::InvalidURL(s)), - Ok(u) => Ok(http::core::Url { - scheme: u.scheme().to_string(), - host: u.host_str().unwrap().to_string(), - port: u.port(), - path: u.path().to_string(), - - query_string: match u.query() { - None => "".to_string(), - Some(s) => s.to_string() - }, - }) - } -} - #[cfg(test)] mod tests { use crate::core::common::SourceInfo; use super::*; + use super::super::core::RunnerError; - pub fn hello_request() -> Request { - -// GET {{base_url}}/hello - let whitespace = Whitespace { + pub fn whitespace() -> Whitespace { + Whitespace { value: String::from(" "), source_info: SourceInfo::init(0, 0, 0, 0), - }; + } + } + + pub fn hello_request() -> Request { let line_terminator = LineTerminator { - space0: whitespace.clone(), + space0: whitespace(), comment: None, - newline: whitespace.clone(), + newline: whitespace(), }; Request { line_terminators: vec![], - space0: whitespace.clone(), + space0: whitespace(), method: Method::Get, - space1: whitespace.clone(), + space1: whitespace(), url: Template { elements: vec![ TemplateElement::Expression(Expr { - space0: whitespace.clone(), + space0: whitespace(), variable: Variable { name: String::from("base_url"), source_info: SourceInfo::init(1, 7, 1, 15), }, - space1: whitespace.clone(), + space1: whitespace(), }), TemplateElement::String { value: String::from("/hello"), @@ -298,41 +226,33 @@ mod tests { } pub fn simple_key_value(key: EncodedString, value: Template) -> KeyValue { - let whitespace = Whitespace { - value: String::from(" "), - source_info: SourceInfo::init(0, 0, 0, 0), - }; let line_terminator = LineTerminator { - space0: whitespace.clone(), + space0: whitespace(), comment: None, - newline: whitespace.clone(), + newline: whitespace(), }; KeyValue { line_terminators: vec![], - space0: whitespace.clone(), + space0: whitespace(), key, - space1: whitespace.clone(), - space2: whitespace.clone(), + space1: whitespace(), + space2: whitespace(), value, - line_terminator0: line_terminator.clone(), + line_terminator0: line_terminator, } } pub fn query_request() -> Request { - let whitespace = Whitespace { - value: String::from(" "), - source_info: SourceInfo::init(0, 0, 0, 0), - }; let line_terminator = LineTerminator { - space0: whitespace.clone(), + space0: whitespace(), comment: None, - newline: whitespace.clone(), + newline: whitespace(), }; Request { line_terminators: vec![], - space0: whitespace.clone(), + space0: whitespace(), method: Method::Get, - space1: whitespace.clone(), + space1: whitespace(), url: Template { elements: vec![ TemplateElement::String { @@ -348,8 +268,8 @@ mod tests { sections: vec![ Section { line_terminators: vec![], - space0: whitespace.clone(), - line_terminator0: line_terminator.clone(), + space0: whitespace(), + line_terminator0: line_terminator, value: SectionValue::QueryParams(vec![ simple_key_value( EncodedString { @@ -362,12 +282,12 @@ mod tests { quotes: false, elements: vec![ TemplateElement::Expression(Expr { - space0: whitespace.clone(), + space0: whitespace(), variable: Variable { name: String::from("param1"), source_info: SourceInfo::init(1, 7, 1, 15), }, - space1: whitespace.clone(), + space1: whitespace(), }) ], source_info: SourceInfo::init(0, 0, 0, 0), @@ -392,7 +312,6 @@ mod tests { }, ) ]), - source_info: SourceInfo::init(0, 0, 0, 0), }, ], @@ -412,40 +331,16 @@ mod tests { #[test] pub fn test_hello_request() { let mut variables = HashMap::new(); - // let cookies = HashMap::new(); variables.insert(String::from("base_url"), Value::String(String::from("http://localhost:8000"))); let http_request = hello_request().eval(&variables, "current_dir".to_string()).unwrap(); - assert_eq!(http_request, http::request::tests::hello_http_request()); + assert_eq!(http_request, libcurl::core::tests::hello_http_request()); } #[test] pub fn test_query_request() { let mut variables = HashMap::new(); - //let cookies = HashMap::new(); variables.insert(String::from("param1"), Value::String(String::from("value1"))); let http_request = query_request().eval(&variables, "current_dir".to_string()).unwrap(); - assert_eq!(http_request, http::request::tests::query_http_request()); - } - - #[test] - pub fn test_split_url() { - assert_eq!( - split_url(String::from("http://localhost:8000/hello")), - (String::from("http://localhost:8000/hello"), vec![]) - ); - assert_eq!( - split_url(String::from("http://localhost:8000/hello?param1=value1")), - (String::from("http://localhost:8000/hello"), vec![http::core::Param { name: String::from("param1"), value: String::from("value1") }]) - ); - } - - #[test] - pub fn test_eval_url() { - assert_eq!(eval_url(String::from("xxx")).err().unwrap(), RunnerError::InvalidURL(String::from("xxx"))); - - let url = eval_url(String::from("http://localhost:8000/querystring-params?param1=value1")).unwrap(); - assert_eq!(url.host, "localhost"); - assert_eq!(url.port, Some(8000)); -// assert_eq!(url.querystring.unwrap(), String::from("param1=value1")); + assert_eq!(http_request, libcurl::core::tests::query_http_request()); } } diff --git a/src/runner/response.rs b/src/runner/response.rs index 3403a366d..347582640 100644 --- a/src/runner/response.rs +++ b/src/runner/response.rs @@ -17,38 +17,33 @@ */ use std::collections::HashMap; -use encoding::{DecoderTrap, Encoding}; -use encoding::all::ISO_8859_1; use crate::core::common::{Pos, SourceInfo}; use crate::core::common::Value; -use crate::http; +use crate::http::libcurl; use crate::runner::core::RunnerError; use super::core::*; use super::core::Error; use super::super::core::ast::*; -pub fn decode_bytes(bytes: Vec, encoding: http::core::Encoding) -> Result { - match encoding { - http::core::Encoding::Utf8 {} => match String::from_utf8(bytes) { - Ok(s) => Ok(s), - Err(_) => Err(RunnerError::InvalidDecoding { charset: encoding.to_string() }), - }, - http::core::Encoding::Latin1 {} => match ISO_8859_1.decode(&bytes, DecoderTrap::Strict) { - Ok(s) => Ok(s), - Err(_) => Err(RunnerError::InvalidDecoding { charset: encoding.to_string() }), - }, +impl libcurl::core::Response { + pub fn get_header(&self, name: String) -> Vec { + self.headers + .iter() + .filter(|&h| h.name.to_lowercase() == name.to_lowercase()) + .map(|h| h.value.clone()) + .collect() } } impl Response { - pub fn eval_asserts(self, variables: &HashMap, http_response: http::response::Response, context_dir: String) -> Vec { + pub fn eval_asserts(self, variables: &HashMap, http_response: libcurl::core::Response, context_dir: String) -> Vec { let mut asserts = vec![]; let version = self.clone().version; asserts.push(AssertResult::Version { - actual: http_response.version.to_text(), + actual: http_response.version.to_string(), expected: version.value.as_str().to_string(), source_info: version.source_info, }); @@ -71,7 +66,7 @@ impl Response { } Ok(expected) => { let header_name = header.key.value.clone(); - let actuals = http_response.get_header(header_name.as_str(), false); + let actuals = http_response.get_header(header_name); if actuals.is_empty() { asserts.push(AssertResult::Header { actual: Err(Error { @@ -119,7 +114,7 @@ impl Response { Ok(s) => Ok(Value::String(s)), Err(e) => Err(e), }; - let actual = match decode_bytes(http_response.body.clone(), http_response.encoding()) { + let actual = match http_response.text() { Ok(s) => Ok(Value::String(s)), Err(e) => Err(Error { source_info: SourceInfo { @@ -138,7 +133,7 @@ impl Response { } Bytes::Xml { value } => { let expected = Ok(Value::String(value)); - let actual = match decode_bytes(http_response.body.clone(), http_response.encoding()) { + let actual = match http_response.text() { Ok(s) => Ok(Value::String(s)), Err(e) => Err(Error { source_info: SourceInfo { @@ -160,7 +155,7 @@ impl Response { Ok(s) => Ok(Value::String(s)), Err(e) => Err(e), }; - let actual = match decode_bytes(http_response.body.clone(), http_response.encoding()) { + let actual = match http_response.text() { Ok(s) => Ok(Value::String(s)), Err(e) => Err(Error { source_info: SourceInfo { @@ -205,7 +200,7 @@ impl Response { asserts } - pub fn eval_captures(self, http_response: http::response::Response, variables: &HashMap) -> Result, Error> { + pub fn eval_captures(self, http_response: libcurl::core::Response, variables: &HashMap) -> Result, Error> { let mut captures = vec![]; for capture in self.captures() { let capture_result = capture.eval(variables, http_response.clone())?; @@ -273,7 +268,7 @@ mod tests { let variables = HashMap::new(); let context_dir = "undefined".to_string(); assert_eq!( - user_response().eval_asserts(&variables, http::response::tests::xml_two_users_http_response(), context_dir), + user_response().eval_asserts(&variables, libcurl::core::tests::xml_two_users_http_response(), context_dir), vec![ AssertResult::Version { actual: String::from("1.0"), @@ -306,7 +301,7 @@ mod tests { pub fn test_eval_captures() { let variables = HashMap::new(); assert_eq!( - user_response().eval_captures(http::response::tests::xml_two_users_http_response(), &variables).unwrap(), + user_response().eval_captures(libcurl::core::tests::xml_two_users_http_response(), &variables).unwrap(), vec![ CaptureResult { name: "UserCount".to_string(), diff --git a/src/runner/text.rs b/src/runner/text.rs deleted file mode 100644 index f8b829d11..000000000 --- a/src/runner/text.rs +++ /dev/null @@ -1,184 +0,0 @@ -/* - * hurl (https://hurl.dev) - * Copyright (C) 2020 Orange - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ -use std::fmt; - -use crate::http::core::*; -use crate::http::request::*; -use crate::http::response::*; - -impl Request { - pub fn to_text(&self) -> String { - let mut s = format!("{} {}\n", - self.clone().method.to_text(), - self.clone().url() - ); - for header in self.clone().headers() { - s.push_str(header.to_text().as_str()); - } - s.push_str("\n"); - - let body = match self.clone().form_params() { - None => body_text(self.clone().body, self.clone().content_type()), - Some(params) => { - let mut buf = String::from("[Form Params]"); - for param in params { - buf.push_str(format!("\n{}={}", param.name, param.value).as_str()) - } - buf - } - }; - s.push_str(body.as_str()); - s.push_str("\n"); - s - } -} - -impl Response { - pub fn to_text(&self, limit_body: usize) -> String { - let mut s = format!("HTTP/{} {}\n", self.version.to_text(), self.status); - for header in self.headers.clone() { - s.push_str(header.to_text().as_str()); - } - s.push_str(""); - - // shoudl use number of char, not a number of bytes!! - //let limit_body = 400; // TODO should be explicitly pass as a command-line argument - if !self.body.is_empty() { - let body = body_text(self.clone().body, get_header_value(self.clone().headers, "content-type")); - s.push_str(substring(body.as_str(), 0, limit_body)); - } - s - } -} - -impl Method { - pub fn to_text(&self) -> String { - match self { - Method::Get => String::from("GET"), - Method::Head => String::from("HEAD"), - Method::Post => String::from("POST"), - Method::Put => String::from("PUT"), - Method::Delete => String::from("DELETE"), - Method::Connect => String::from("CONNECT"), - Method::Options => String::from("OPTIONS"), - Method::Trace => String::from("TRACE"), - Method::Patch => String::from("PATCH"), - } - } -} - -impl Version { - pub fn to_text(&self) -> String { - match self { - Version::Http10 => String::from("1.0"), - Version::Http11 => String::from("1.1"), - Version::Http2 => String::from("2"), - } - } -} - -impl Header { - fn to_text(&self) -> String { - return format!("{}: {}\n", self.name, self.value); - } -} - -impl fmt::Display for Header { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}: {}", self.name, self.value) - } -} - -fn body_text(bytes: Vec, content_type: Option) -> String { - match content_type { - Some(content_type) => - if is_text(content_type.as_str()) { - match String::from_utf8(bytes.clone()) { - Ok(v) => v, - Err(_) => format!("{:?}", bytes) - } - } else { - format!("{:?}", bytes) - } - _ => { - if bytes.is_empty() { - String::from("") - } else { - format!("{:?}", bytes) - } - } - } -} - -fn is_text(content_type: &str) -> bool { - for s in &[ - "application/json", - "text/html", - "charset=utf-8", - "application/x-www-form-urlencoded" - ] { - if content_type.contains(s) { - return true; - } - } - false -} - -fn substring(s: &str, start: usize, len: usize) -> &str { - let mut char_pos = 0; - let mut byte_start = 0; - let mut it = s.chars(); - loop { - if char_pos == start { break; } - if let Some(c) = it.next() { - char_pos += 1; - byte_start += c.len_utf8(); - } else { break; } - } - char_pos = 0; - let mut byte_end = byte_start; - loop { - if char_pos == len { break; } - if let Some(c) = it.next() { - char_pos += 1; - byte_end += c.len_utf8(); - } else { break; } - } - &s[byte_start..byte_end] -} - - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_is_text() { - assert_eq!(is_text("application/json"), true); - assert_eq!(is_text("application/json;charset=utf-8"), true); - } - - #[test] - fn test_substring() { - assert_eq!(substring("", 0, 0), ""); - assert_eq!(substring("hello world!", 0, 5), "hello"); - assert_eq!(substring("hello world!", 0, 15), "hello world!"); - } -} - - diff --git a/tests/http.rs b/tests/http.rs deleted file mode 100644 index 8808b3bf2..000000000 --- a/tests/http.rs +++ /dev/null @@ -1,298 +0,0 @@ -/* - * hurl (https://hurl.dev) - * Copyright (C) 2020 Orange - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ -extern crate hurl; - -use hurl::http; - -fn default_client_options() -> http::client::ClientOptions { - http::client::ClientOptions { - noproxy_hosts: vec![], - insecure: true, - redirect: http::client::Redirect::None, - http_proxy: None, - https_proxy: None, - all_proxy: None, - } -} - -#[test] -fn test_hello() { - let client = http::client::Client::init(default_client_options()); - - let request = http::request::Request { - method: http::request::Method::Get, - url: http::core::Url { - scheme: "http".to_string(), - host: "localhost".to_string(), - port: Some(8000), - path: "/hello".to_string(), - query_string: "".to_string(), // querystring: None - }, //"http://localhost:8000/hello".to_string(), - //querystring_params: vec![], - querystring: vec![], - headers: vec![ - http::core::Header { - name: String::from("User-Agent"), - value: String::from("hurl/0.1.1"), - }, - http::core::Header { - name: String::from("Host"), - value: String::from("TBD"), - }, - ], - cookies: vec![], - body: vec![], - multipart: vec![], - }; - - let result = client.execute(&request); - println!("{:?}", result); - let response = result.unwrap(); - assert_eq!(response.status, 200); - assert_eq!(response.body.len(), 12); - assert_eq!( - String::from_utf8(response.body).unwrap(), - "Hello World!".to_string() - ); -} - -//#[test] -//fn test_text_utf8() { -// let client = Client::init(ClientOptions {}); -// -// let request = Request { -// method: Method::Get, -// url: "http://localhost:5000/cafe".to_string(), -// headers: vec![], -// body: vec![], -// }; -// let response = client.execute(&request).unwrap(); -// assert_eq!(response.status, 200); -// assert_eq!(response.body.len(), 5); -// assert_eq!( -// String::from_utf8(response.body).unwrap(), -// "cafĂ©".to_string() -// ); -//} - -#[cfg(test)] -fn hello_request() -> http::request::Request { - http::request::Request { - method: http::request::Method::Get, - url: http::core::Url { - scheme: "http".to_string(), - host: "localhost".to_string(), - port: Some(8000), - path: "/hello".to_string(), - query_string: "".to_string(), // querystring: None - }, //"http://localhost:8000/hello".to_string(), - querystring: vec![], - headers: vec![], - cookies: vec![], - body: vec![], - multipart: vec![], - } -} - -#[test] -fn test_multiple_calls() { - let client = http::client::Client::init(default_client_options()); - let response = client.execute(&hello_request()).unwrap(); - assert_eq!(response.status, 200); - let response = client.execute(&hello_request()).unwrap(); - assert_eq!(response.status, 200); -} - -#[test] -fn test_response_headers() { - let client = http::client::Client::init(default_client_options()); - let response = client.execute(&hello_request()).unwrap(); - println!("{:?}", response); - assert_eq!(response.status, 200); - assert_eq!( - response - .get_header("Content-Length", false) - .first() - .unwrap(), - "12" - ); -} - -#[test] -fn test_send_cookie() { - let client = http::client::Client::init(default_client_options()); - let request = http::request::Request { - method: http::request::Method::Get, - url: http::core::Url { - scheme: "http".to_string(), - host: "localhost".to_string(), - port: Some(8000), - path: "/cookies/set-request-cookie1-valueA".to_string(), - query_string: "".to_string(), // querystring: None - }, //"http://localhost:8000/send-cookie".to_string(), - querystring: vec![], - headers: vec![http::core::Header { - name: "Cookie".to_string(), - value: "cookie1=valueA;".to_string(), - }], - cookies: vec![], - body: vec![], - multipart: vec![], - }; - let response = client.execute(&request).unwrap(); - assert_eq!(response.status, 200); - - let _client = http::client::Client::init(default_client_options()); - // let _cookie_header = http::cookie::Cookie { - // name: "Cookie1".to_string(), - // value: "valueA;".to_string(), - // max_age: None, - // domain: None, - // path: None, - // }.to_header(); - /* - let request = Request { - method: Method::Get, - url: "http://localhost:5000/send-cookie1-value1".to_string(), - headers: vec![cookie_header], - body: vec![], - }; - let response = client.execute(&request).unwrap(); - assert_eq!(response.status, 200); - */ -} - -#[test] -fn test_redirect() { - let client = http::client::Client::init(default_client_options()); - - let request = http::request::Request { - method: http::request::Method::Get, - url: http::core::Url { - scheme: "http".to_string(), - host: "localhost".to_string(), - port: Some(8000), - path: "/redirect".to_string(), - query_string: "".to_string(), // querystring: None - }, // "http://localhost:8000/redirect".to_string(), - querystring: vec![], - headers: vec![], - cookies: vec![], - body: vec![], - multipart: vec![], - }; - let response = client.execute(&request).unwrap(); - assert_eq!(response.status, 302); - assert_eq!( - response.get_header("location", true).first().unwrap(), - "http://localhost:8000/redirected" - ); -} - -#[test] -fn test_querystring_param() { - let client = http::client::Client::init(default_client_options()); - - let request = http::request::Request { - method: http::request::Method::Get, - url: http::core::Url { - scheme: "http".to_string(), - host: "localhost".to_string(), - port: Some(8000), - path: "/querystring-params".to_string(), - query_string: "".to_string(), // querystring: Some(String::from("param1=value1¶m2¶m3=a%3db")) - }, - querystring: vec![ - http::core::Param { - name: String::from("param1"), - value: String::from("value1"), - }, - http::core::Param { - name: String::from("param2"), - value: String::from(""), - }, - http::core::Param { - name: String::from("param3"), - value: String::from("a=b"), - }, - http::core::Param { - name: String::from("param4"), - value: String::from("1,2,3"), - }, - ], - headers: vec![], - cookies: vec![], - body: vec![], - multipart: vec![], - }; - let response = client.execute(&request).unwrap(); - assert_eq!(response.status, 200); -} - -#[test] -// curl -H 'Host:localhost:5000' -H 'content-type:application/x-www-form-urlencoded' -X POST 'http://localhost:5000/form-params' --data-binary 'param1=value1¶m2=' -fn test_form_param() { - let client = http::client::Client::init(default_client_options()); - - let request = http::request::Request { - method: http::request::Method::Post, - url: http::core::Url { - scheme: "http".to_string(), - host: "localhost".to_string(), - port: Some(8000), - path: "/form-params".to_string(), - query_string: "".to_string(), - }, // "http://localhost:8000/form-params".to_string(), - querystring: vec![], - headers: vec![http::core::Header { - name: "Content-Type".to_string(), - value: "application/x-www-form-urlencoded".to_string(), - }], - cookies: vec![], - body: "param1=value1¶m2=¶m3=a%3db¶m4=a%253db" - .to_string() - .into_bytes(), - multipart: vec![], - }; - let response = client.execute(&request).unwrap(); - assert_eq!(response.status, 200); - - /* - let client = Client::init(ClientOptions {}); // TO BE FIXED connection ended before message read => sync wait?? - let request = Request { - method: Method::Post, - url: "http://localhost:5000/form-params".to_string(), - headers: vec![Header { - name: "Content-Type".to_string(), - value: "application/x-www-form-urlencoded".to_string(), - }], - body: encode_form_params(vec![ - Param { - name: "param1".to_string(), - value: "value1".to_string(), - }, - Param { - name: "param2".to_string(), - value: "".to_string(), - }, - ]), - }; - let response = client.execute(&request).unwrap(); - assert_eq!(response.status, 200); - */ -} diff --git a/tests/libcurl.rs b/tests/libcurl.rs index cdfbb3610..7ee89b462 100644 --- a/tests/libcurl.rs +++ b/tests/libcurl.rs @@ -59,10 +59,11 @@ fn default_client() -> libcurl::client::Client { let options = ClientOptions { follow_location: false, max_redirect: None, - cookie_file: None, - cookie_jar: None, + cookie_input_file: None, proxy: None, - verbose: false, + no_proxy: None, + verbose: true, + insecure: false, }; libcurl::client::Client::init(options) } @@ -76,7 +77,8 @@ fn default_get_request(url: String) -> Request { form: vec![], multipart: vec![], cookies: vec![], - body: vec![] + body: vec![], + content_type: None, } } @@ -113,7 +115,8 @@ fn test_put() { form: vec![], multipart: vec![], cookies: vec![], - body: vec![] + body: vec![], + content_type: None, }; let response = client.execute(&request, 0).unwrap(); assert_eq!(response.status, 200); @@ -137,7 +140,8 @@ fn test_patch() { form: vec![], multipart: vec![], cookies: vec![], - body: vec![] + body: vec![], + content_type: None, }; let response = client.execute(&request, 0).unwrap(); assert_eq!(response.status, 204); @@ -166,7 +170,8 @@ fn test_custom_headers() { form: vec![], multipart: vec![], cookies: vec![], - body: vec![] + body: vec![], + content_type: None, }; let response = client.execute(&request, 0).unwrap(); assert_eq!(response.status, 200); @@ -194,7 +199,8 @@ fn test_querystring_params() { form: vec![], multipart: vec![], cookies: vec![], - body: vec![] + body: vec![], + content_type: None, }; let response = client.execute(&request, 0).unwrap(); assert_eq!(response.status, 200); @@ -222,7 +228,8 @@ fn test_form_params() { ], multipart: vec![], cookies: vec![], - body: vec![] + body: vec![], + content_type: Some("application/x-www-form-urlencoded".to_string()), }; let response = client.execute(&request, 0).unwrap(); assert_eq!(response.status, 200); @@ -255,10 +262,11 @@ fn test_follow_location() { let options = ClientOptions { follow_location: true, max_redirect: None, - cookie_file: None, - cookie_jar: None, + cookie_input_file: None, proxy: None, + no_proxy: None, verbose: false, + insecure: false }; let mut client = libcurl::client::Client::init(options); let response = client.execute(&request, 0).unwrap(); @@ -281,10 +289,11 @@ fn test_max_redirect() { let options = ClientOptions { follow_location: true, max_redirect: Some(10), - cookie_file: None, - cookie_jar: None, + cookie_input_file: None, proxy: None, + no_proxy: None, verbose: false, + insecure: false, }; let mut client = libcurl::client::Client::init(options); let request = default_get_request("http://localhost:8000/redirect".to_string()); @@ -336,13 +345,20 @@ fn test_multipart_form_data() { }), ], cookies: vec![], - body: vec![] + body: vec![], + content_type: Some("multipart/form-data".to_string()), }; let response = client.execute(&request, 0).unwrap(); assert_eq!(response.status, 200); assert!(response.body.is_empty()); + // make sure you can reuse client for other request + let request = default_get_request("http://localhost:8000/hello".to_string()); + let response = client.execute(&request, 0).unwrap(); + assert_eq!(response.status, 200); + assert_eq!(response.body, b"Hello World!".to_vec()); + } // endregion @@ -362,6 +378,7 @@ fn test_post_bytes() { multipart: vec![], cookies: vec![], body: b"Hello World!".to_vec(), + content_type: None }; let response = client.execute(&request, 0).unwrap(); assert_eq!(response.status, 200); @@ -393,10 +410,11 @@ fn test_error_fail_to_connect() { let options = ClientOptions { follow_location: false, max_redirect: None, - cookie_file: None, - cookie_jar: None, + cookie_input_file: None, proxy: Some("localhost:9999".to_string()), + no_proxy: None, verbose: true, + insecure: false, }; let mut client = libcurl::client::Client::init(options); let request = default_get_request("http://localhost:8000/hello".to_string()); @@ -411,10 +429,11 @@ fn test_error_could_not_resolve_proxy_name() { let options = ClientOptions { follow_location: false, max_redirect: None, - cookie_file: None, - cookie_jar: None, + cookie_input_file: None, proxy: Some("unknown".to_string()), + no_proxy: None, verbose: false, + insecure: false, }; let mut client = libcurl::client::Client::init(options); let request = default_get_request("http://localhost:8000/hello".to_string()); @@ -439,8 +458,22 @@ fn test_cookie() { cookies: vec![ RequestCookie { name: "cookie1".to_string(), value: "valueA".to_string() } ], - body: vec![] + body: vec![], + content_type: None, }; + + // set cookie from the request cookie (temporary) + let request_cookie = request.cookies.get(0).unwrap(); + let cookie = Cookie { + domain: "localhost".to_string(), + include_subdomain: "FALSE".to_string(), + path: "/".to_string(), + https: "FALSE".to_string(), + expires: "0".to_string(), + name: request_cookie.name.clone(), + value: request_cookie.value.clone(), + }; + client.add_cookie(cookie); let response = client.execute(&request, 0).unwrap(); assert_eq!(response.status, 200); assert!(response.body.is_empty()); @@ -449,10 +482,10 @@ fn test_cookie() { // For the time-being setting a cookie on a request // update the cookie store as well // The same cookie does not need to be set explicitly on further requests - let request = default_get_request("http://localhost:8000/cookies/set-request-cookie1-valueA".to_string()); - let response = client.execute(&request, 0).unwrap(); - assert_eq!(response.status, 200); - assert!(response.body.is_empty()); + // let request = default_get_request("http://localhost:8000/cookies/set-request-cookie1-valueA".to_string()); + // let response = client.execute(&request, 0).unwrap(); + // assert_eq!(response.status, 200); + // assert!(response.body.is_empty()); } @@ -474,6 +507,7 @@ fn test_cookie_storage() { name: "cookie2".to_string(), value: "valueA".to_string(), }); + let request = default_get_request("http://localhost:8000/cookies/assert-that-cookie2-is-valueA".to_string()); let response = client.execute(&request, 0).unwrap(); assert_eq!(response.status, 200); @@ -491,10 +525,11 @@ fn test_cookie_file() { let options = ClientOptions { follow_location: false, max_redirect: None, - cookie_file: Some(temp_file.to_string()), - cookie_jar: None, + cookie_input_file: Some(temp_file.to_string()), proxy: None, + no_proxy: None, verbose: false, + insecure: false, }; let mut client = libcurl::client::Client::init(options); let request = default_get_request("http://localhost:8000/cookies/assert-that-cookie2-is-valueA".to_string()); @@ -514,10 +549,11 @@ fn test_proxy() { let options = ClientOptions { follow_location: false, max_redirect: None, - cookie_file: None, - cookie_jar: None, + cookie_input_file: None, proxy: Some("localhost:8080".to_string()), + no_proxy: None, verbose: false, + insecure: false, }; let mut client = libcurl::client::Client::init(options); let request = default_get_request("http://localhost:8000/hello".to_string()); diff --git a/tests/runner.rs b/tests/runner.rs index 03658a8f9..fd6f41496 100644 --- a/tests/runner.rs +++ b/tests/runner.rs @@ -21,7 +21,7 @@ use hurl::core::ast; use hurl::core::common::{Pos, SourceInfo}; use hurl::runner; use hurl::format; -use hurl::http; +use hurl::http::libcurl; use std::collections::HashMap; use hurl::core::ast::{Template, TemplateElement, EncodedString}; use hurl::runner::core::RunnerOptions; @@ -30,7 +30,6 @@ use hurl::runner::core::RunnerOptions; // can be used for debugging #[test] fn test_hurl_file() { - let mut cookie_store = http::cookie::CookieJar::init(vec![]); //let filename = "integration/tests/post_json.hurl"; //let filename = "integration/tests/error_assert_match_utf8.hurl"; let filename = "integration/tests/error_template_variable_not_renderable.hurl"; @@ -38,14 +37,16 @@ fn test_hurl_file() { let content = std::fs::read_to_string(filename).expect("Something went wrong reading the file"); let hurl_file = hurl::parser::parse_hurl_file(content.as_str()).unwrap(); let variables = HashMap::new(); - let client = http::client::Client::init(http::client::ClientOptions { - noproxy_hosts: vec![], + let options = libcurl::client::ClientOptions { + follow_location: false, + max_redirect: None, + cookie_input_file: None, + proxy: None, + no_proxy: None, + verbose: false, insecure: false, - redirect: http::client::Redirect::None, - http_proxy: None, - https_proxy: None, - all_proxy: None - }); + }; + let mut client = libcurl::client::Client::init(options); let mut lines: Vec<&str> = regex::Regex::new(r"\n|\r\n") .unwrap() .split(&content) @@ -68,10 +69,9 @@ fn test_hurl_file() { let _hurl_log = runner::file::run( hurl_file, - client, + &mut client, //&mut variables, filename.to_string(), - &mut cookie_store, "current_dir".to_string(), options, logger @@ -144,15 +144,16 @@ fn hello_request() -> ast::Request { #[test] fn test_hello() { - let mut cookie_store = http::cookie::CookieJar::init(vec![]); - let client = http::client::Client::init(http::client::ClientOptions { - noproxy_hosts: vec![], + let options = libcurl::client::ClientOptions { + follow_location: false, + max_redirect: None, + cookie_input_file: None, + proxy: None, + no_proxy: None, + verbose: false, insecure: false, - redirect: http::client::Redirect::None, - http_proxy: None, - https_proxy: None, - all_proxy: None - }); + }; + let mut client = libcurl::client::Client::init(options); let source_info = SourceInfo { start: Pos { line: 1, column: 1 }, end: Pos { line: 1, column: 1 }, @@ -207,9 +208,8 @@ fn test_hello() { }; let _hurl_log = runner::file::run( hurl_file, - client, + &mut client, String::from("filename"), - &mut cookie_store, "current_dir".to_string(), options, logger