Merge pull request #53 from deps-rs/fut03

This commit is contained in:
Rob Ede 2020-10-01 15:40:56 +01:00 committed by GitHub
commit 0fa8e46425
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
21 changed files with 1026 additions and 1066 deletions

718
Cargo.lock generated
View file

@ -12,6 +12,12 @@ version = "1.0.32"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6b602bfe940d21c130f3895acd65221e8a61270debe89d628b9cb4e3ccb8569b" checksum = "6b602bfe940d21c130f3895acd65221e8a61270debe89d628b9cb4e3ccb8569b"
[[package]]
name = "arc-swap"
version = "0.4.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4d25d88fd6b8041580a654f9d0c581a047baee2b3efee13275f2fc392fc75034"
[[package]] [[package]]
name = "autocfg" name = "autocfg"
version = "1.0.1" version = "1.0.1"
@ -74,22 +80,17 @@ checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de"
[[package]] [[package]]
name = "bytes" name = "bytes"
version = "0.4.12" version = "0.5.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "206fdffcfa2df7cbe15601ef46c813fce0965eb3286db6b56c583b814b51c81c" checksum = "0e4cec68f03f32e44924783795810fa50a7035d8c8ebe78580ad7e6c703fba38"
dependencies = [
"byteorder",
"either",
"iovec",
]
[[package]] [[package]]
name = "cadence" name = "cadence"
version = "0.13.2" version = "0.21.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "99612ce0a00efdaf3d81a5e8e17f0eed55a10e862033183c847a0365983af88c" checksum = "e45b9cdf75cdddb0877f8af74c345d06b0c8a924c5115d2467d94d7e4bdf9180"
dependencies = [ dependencies = [
"crossbeam", "crossbeam-channel",
] ]
[[package]] [[package]]
@ -121,9 +122,9 @@ checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
[[package]] [[package]]
name = "chrono" name = "chrono"
version = "0.4.18" version = "0.4.19"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d021fddb7bd3e734370acfa4a83f34095571d8570c039f1420d77540f68d5772" checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73"
dependencies = [ dependencies = [
"libc", "libc",
"num-integer", "num-integer",
@ -133,15 +134,6 @@ dependencies = [
"winapi 0.3.9", "winapi 0.3.9",
] ]
[[package]]
name = "cloudabi"
version = "0.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f"
dependencies = [
"bitflags",
]
[[package]] [[package]]
name = "core-foundation" name = "core-foundation"
version = "0.7.0" version = "0.7.0"
@ -175,44 +167,11 @@ dependencies = [
] ]
[[package]] [[package]]
name = "crossbeam" name = "crossbeam-channel"
version = "0.2.12" version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bd66663db5a988098a89599d4857919b3acf7f61402e61365acfd3919857b9be" checksum = "b153fe7cbef478c567df0f972e02e6d736db11affe43dfc9c56a9374d1adfb87"
[[package]]
name = "crossbeam-deque"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9f02af974daeee82218205558e51ec8768b48cf524bd01d550abe5573a608285"
dependencies = [ dependencies = [
"crossbeam-epoch",
"crossbeam-utils",
"maybe-uninit",
]
[[package]]
name = "crossbeam-epoch"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "058ed274caafc1f60c4997b5fc07bf7dc7cca454af7c6e81edffe5f33f70dace"
dependencies = [
"autocfg",
"cfg-if",
"crossbeam-utils",
"lazy_static",
"maybe-uninit",
"memoffset",
"scopeguard",
]
[[package]]
name = "crossbeam-queue"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "774ba60a54c213d409d5353bda12d49cd68d14e45036a285234c8d6f91f92570"
dependencies = [
"cfg-if",
"crossbeam-utils", "crossbeam-utils",
"maybe-uninit", "maybe-uninit",
] ]
@ -239,9 +198,9 @@ dependencies = [
[[package]] [[package]]
name = "derive_more" name = "derive_more"
version = "0.99.10" version = "0.99.11"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1dcfabdab475c16a93d669dddfc393027803e347d09663f524447f642fbb84ba" checksum = "41cb0e6161ad61ed084a36ba71fbba9e3ac5aee3606fb607fe08da6acbcf3d8c"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -257,12 +216,6 @@ dependencies = [
"generic-array", "generic-array",
] ]
[[package]]
name = "either"
version = "1.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457"
[[package]] [[package]]
name = "fake-simd" name = "fake-simd"
version = "0.1.2" version = "0.1.2"
@ -308,18 +261,97 @@ checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7"
[[package]] [[package]]
name = "futures" name = "futures"
version = "0.1.29" version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b980f2816d6ee8673b6517b52cb0e808a180efc92e5c19d02cdda79066703ef" checksum = "1e05b85ec287aac0dc34db7d4a569323df697f9c55b99b15d6b4ef8cde49f613"
dependencies = [
"futures-channel",
"futures-core",
"futures-executor",
"futures-io",
"futures-sink",
"futures-task",
"futures-util",
]
[[package]] [[package]]
name = "futures-cpupool" name = "futures-channel"
version = "0.1.8" version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab90cde24b3319636588d0c35fe03b1333857621051837ed769faefb4c2162e4" checksum = "f366ad74c28cca6ba456d95e6422883cfb4b252a83bed929c83abfdbbf2967d5"
dependencies = [ dependencies = [
"futures", "futures-core",
"num_cpus", "futures-sink",
]
[[package]]
name = "futures-core"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "59f5fff90fd5d971f936ad674802482ba441b6f09ba5e15fd8b39145582ca399"
[[package]]
name = "futures-executor"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "10d6bb888be1153d3abeb9006b11b02cf5e9b209fda28693c31ae1e4e012e314"
dependencies = [
"futures-core",
"futures-task",
"futures-util",
]
[[package]]
name = "futures-io"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "de27142b013a8e869c14957e6d2edeef89e97c289e69d042ee3a49acd8b51789"
[[package]]
name = "futures-macro"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d0b5a30a4328ab5473878237c447333c093297bded83a4983d10f4deea240d39"
dependencies = [
"proc-macro-hack",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "futures-sink"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f2032893cb734c7a05d85ce0cc8b8c4075278e93b24b66f9de99d6eb0fa8acc"
[[package]]
name = "futures-task"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bdb66b5f09e22019b1ab0830f7785bcea8e7a42148683f99214f73f8ec21a626"
dependencies = [
"once_cell",
]
[[package]]
name = "futures-util"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8764574ff08b701a084482c3c7031349104b07ac897393010494beaa18ce32c6"
dependencies = [
"futures-channel",
"futures-core",
"futures-io",
"futures-macro",
"futures-sink",
"futures-task",
"memchr",
"pin-project",
"pin-utils",
"proc-macro-hack",
"proc-macro-nested",
"slab",
] ]
[[package]] [[package]]
@ -365,20 +397,21 @@ checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574"
[[package]] [[package]]
name = "h2" name = "h2"
version = "0.1.26" version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a5b34c246847f938a410a03c5458c7fee2274436675e76d8b903c08efc29c462" checksum = "993f9e0baeed60001cf565546b0d3dbe6a6ad23f2bd31644a133c641eccf6d53"
dependencies = [ dependencies = [
"byteorder",
"bytes", "bytes",
"fnv", "fnv",
"futures", "futures-core",
"futures-sink",
"futures-util",
"http", "http",
"indexmap", "indexmap",
"log",
"slab", "slab",
"string", "tokio",
"tokio-io", "tokio-util",
"tracing",
] ]
[[package]] [[package]]
@ -416,9 +449,9 @@ dependencies = [
[[package]] [[package]]
name = "http" name = "http"
version = "0.1.21" version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d6ccf5ede3a895d8856620237b2f02972c1bbc78d2965ad7fe8838d4a0ed41f0" checksum = "28d569972648b2c512421b5f2a405ad6ac9666547189d0c5477a3f200f3e02f9"
dependencies = [ dependencies = [
"bytes", "bytes",
"fnv", "fnv",
@ -427,14 +460,12 @@ dependencies = [
[[package]] [[package]]
name = "http-body" name = "http-body"
version = "0.1.0" version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6741c859c1b2463a423a1dbce98d418e6c3c3fc720fb0d45528657320920292d" checksum = "13d5ff830006f7646652e057693569bfe0d51760c0085a071769d142a205111b"
dependencies = [ dependencies = [
"bytes", "bytes",
"futures",
"http", "http",
"tokio-buf",
] ]
[[package]] [[package]]
@ -444,46 +475,46 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cd179ae861f0c2e53da70d892f5f3029f9594be0c41dc5269cd371691b1dc2f9" checksum = "cd179ae861f0c2e53da70d892f5f3029f9594be0c41dc5269cd371691b1dc2f9"
[[package]] [[package]]
name = "hyper" name = "httpdate"
version = "0.12.35" version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9dbe6ed1438e1f8ad955a4701e9a944938e9519f6888d12d8558b645e247d5f6" checksum = "494b4d60369511e7dea41cf646832512a94e542f68bb9c49e54518e0f468eb47"
[[package]]
name = "hyper"
version = "0.13.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2f3afcfae8af5ad0576a31e768415edb627824129e8e5a29b8bfccb2f234e835"
dependencies = [ dependencies = [
"bytes", "bytes",
"futures", "futures-channel",
"futures-cpupool", "futures-core",
"futures-util",
"h2", "h2",
"http", "http",
"http-body", "http-body",
"httparse", "httparse",
"iovec", "httpdate",
"itoa", "itoa",
"log", "pin-project",
"net2", "socket2",
"rustc_version",
"time",
"tokio", "tokio",
"tokio-buf", "tower-service",
"tokio-executor", "tracing",
"tokio-io",
"tokio-reactor",
"tokio-tcp",
"tokio-threadpool",
"tokio-timer",
"want", "want",
] ]
[[package]] [[package]]
name = "hyper-tls" name = "hyper-tls"
version = "0.3.2" version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3a800d6aa50af4b5850b2b0f659625ce9504df908e9733b635720483be26174f" checksum = "d979acc56dcb5b8dddba3917601745e877576475aa046df3226eabdecef78eed"
dependencies = [ dependencies = [
"bytes", "bytes",
"futures",
"hyper", "hyper",
"native-tls", "native-tls",
"tokio-io", "tokio",
"tokio-tls",
] ]
[[package]] [[package]]
@ -600,15 +631,6 @@ version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8dd5a6d5999d9907cda8ed67bbd137d3af8085216c2ac62de5be860bd41f304a" checksum = "8dd5a6d5999d9907cda8ed67bbd137d3af8085216c2ac62de5be860bd41f304a"
[[package]]
name = "lock_api"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c4da24a77a3d8a6d4862d95f72e6fdb9c09a643ecdb402d754004a557f2bec75"
dependencies = [
"scopeguard",
]
[[package]] [[package]]
name = "log" name = "log"
version = "0.4.11" version = "0.4.11"
@ -673,13 +695,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00" checksum = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00"
[[package]] [[package]]
name = "memoffset" name = "memchr"
version = "0.5.6" version = "2.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "043175f069eda7b85febe4a74abbaeff828d9f8b448515d3151a14a3542811aa" checksum = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400"
dependencies = [
"autocfg",
]
[[package]] [[package]]
name = "mio" name = "mio"
@ -694,12 +713,24 @@ dependencies = [
"kernel32-sys", "kernel32-sys",
"libc", "libc",
"log", "log",
"miow", "miow 0.2.1",
"net2", "net2",
"slab", "slab",
"winapi 0.2.8", "winapi 0.2.8",
] ]
[[package]]
name = "mio-named-pipes"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0840c1c50fd55e521b247f949c241c9997709f23bd7f023b9762cd561e935656"
dependencies = [
"log",
"mio",
"miow 0.3.5",
"winapi 0.3.9",
]
[[package]] [[package]]
name = "mio-uds" name = "mio-uds"
version = "0.6.8" version = "0.6.8"
@ -723,6 +754,16 @@ dependencies = [
"ws2_32-sys", "ws2_32-sys",
] ]
[[package]]
name = "miow"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "07b88fb9795d4d36d62a012dfbf49a8f5cf12751f36d31a9dbe66d528e58979e"
dependencies = [
"socket2",
"winapi 0.3.9",
]
[[package]] [[package]]
name = "native-tls" name = "native-tls"
version = "0.2.4" version = "0.2.4"
@ -835,32 +876,6 @@ dependencies = [
"ttf-parser", "ttf-parser",
] ]
[[package]]
name = "parking_lot"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f842b1982eb6c2fe34036a4fbfb06dd185a3f5c8edfaacdf7d1ea10b07de6252"
dependencies = [
"lock_api",
"parking_lot_core",
"rustc_version",
]
[[package]]
name = "parking_lot_core"
version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b876b1b9e7ac6e1a74a6da34d25c42e17e8862aa409cbbbdcfc8d86c6f3bc62b"
dependencies = [
"cfg-if",
"cloudabi",
"libc",
"redox_syscall",
"rustc_version",
"smallvec",
"winapi 0.3.9",
]
[[package]] [[package]]
name = "percent-encoding" name = "percent-encoding"
version = "2.1.0" version = "2.1.0"
@ -910,6 +925,38 @@ dependencies = [
"sha-1", "sha-1",
] ]
[[package]]
name = "pin-project"
version = "0.4.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f48fad7cfbff853437be7cf54d7b993af21f53be7f0988cbfe4a51535aa77205"
dependencies = [
"pin-project-internal",
]
[[package]]
name = "pin-project-internal"
version = "0.4.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24c6d293bdd3ca5a1697997854c6cf7855e43fb6a0ba1c47af57a5bcafd158ae"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "pin-project-lite"
version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4fe74897791e156a0cd8cce0db31b9b2198e67877316bf3086c3acd187f719f0"
[[package]]
name = "pin-utils"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
[[package]] [[package]]
name = "pkg-config" name = "pkg-config"
version = "0.3.18" version = "0.3.18"
@ -931,6 +978,18 @@ version = "0.2.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c36fa947111f5c62a733b652544dd0016a43ce89619538a8ef92724a6f501a20" checksum = "c36fa947111f5c62a733b652544dd0016a43ce89619538a8ef92724a6f501a20"
[[package]]
name = "proc-macro-hack"
version = "0.5.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "99c605b9a0adc77b7211c6b1f722dcb613d68d66859a44f3d485a6da332b0598"
[[package]]
name = "proc-macro-nested"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eba180dafb9038b050a4c280019bbedf9f2467b61e5d892dcad585bb57aadc5a"
[[package]] [[package]]
name = "proc-macro2" name = "proc-macro2"
version = "1.0.23" version = "1.0.23"
@ -998,9 +1057,9 @@ checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce"
[[package]] [[package]]
name = "relative-path" name = "relative-path"
version = "0.3.7" version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e614f96449605730b4f7ad2c019e88c1652d730634b4eba07b810801856635e3" checksum = "65aff7c83039e88c1c0b4bedf8dfa93d6ec84d5fc2945b37c1fa4186f46c5f94"
dependencies = [ dependencies = [
"serde", "serde",
] ]
@ -1016,18 +1075,9 @@ dependencies = [
[[package]] [[package]]
name = "route-recognizer" name = "route-recognizer"
version = "0.1.13" version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ea509065eb0b3c446acdd0102f0d46567dc30902dc0be91d6552035d92b0f4f8" checksum = "56770675ebc04927ded3e60633437841581c285dc6236109ea25fbf3beb7b59e"
[[package]]
name = "rustc_version"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
dependencies = [
"semver 0.9.0",
]
[[package]] [[package]]
name = "rustsec" name = "rustsec"
@ -1097,18 +1147,6 @@ dependencies = [
"winapi 0.3.9", "winapi 0.3.9",
] ]
[[package]]
name = "scoped-tls"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "332ffa32bf586782a3efaeb58f127980944bbc8c4d6913a86107ac2a5ab24b28"
[[package]]
name = "scopeguard"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
[[package]] [[package]]
name = "security-framework" name = "security-framework"
version = "0.4.4" version = "0.4.4"
@ -1196,9 +1234,9 @@ dependencies = [
[[package]] [[package]]
name = "serde_json" name = "serde_json"
version = "1.0.57" version = "1.0.58"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "164eacbdb13512ec2745fb09d51fd5b22b0d65ed294a1dcf7285a360c80a675c" checksum = "a230ea9107ca2220eea9d46de97eddcb04cd00e92d13dda78e478dd33fa82bd4"
dependencies = [ dependencies = [
"itoa", "itoa",
"ryu", "ryu",
@ -1232,6 +1270,7 @@ dependencies = [
"lru-cache", "lru-cache",
"maud", "maud",
"once_cell", "once_cell",
"pin-project",
"relative-path", "relative-path",
"route-recognizer", "route-recognizer",
"rustsec", "rustsec",
@ -1241,10 +1280,18 @@ dependencies = [
"serde_json", "serde_json",
"slog", "slog",
"slog-json", "slog-json",
"tokio-core", "tokio",
"tokio-service",
"toml", "toml",
"try_future", ]
[[package]]
name = "signal-hook-registry"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a3e12110bc539e657a646068aaf5eb5b63af9d0c1f7b29c97113fad80e15f035"
dependencies = [
"arc-swap",
"libc",
] ]
[[package]] [[package]]
@ -1271,15 +1318,6 @@ dependencies = [
"slog", "slog",
] ]
[[package]]
name = "smallvec"
version = "0.6.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f7b0758c52e15a8b5e3691eae6cc559f08eee9406e548a4477ba4e67770a82b6"
dependencies = [
"maybe-uninit",
]
[[package]] [[package]]
name = "smol_str" name = "smol_str"
version = "0.1.17" version = "0.1.17"
@ -1290,12 +1328,15 @@ dependencies = [
] ]
[[package]] [[package]]
name = "string" name = "socket2"
version = "0.2.1" version = "0.3.15"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d24114bfcceb867ca7f71a0d3fe45d45619ec47a6fbfa98cb14e14250bfa5d6d" checksum = "b1fa70dc5c8104ec096f4fe7ede7a221d35ae13dcd19ba1ad9a81d2cab9a1c44"
dependencies = [ dependencies = [
"bytes", "cfg-if",
"libc",
"redox_syscall",
"winapi 0.3.9",
] ]
[[package]] [[package]]
@ -1362,223 +1403,61 @@ checksum = "238ce071d267c5710f9d31451efec16c5ee22de34df17cc05e56cbc92e967117"
[[package]] [[package]]
name = "tokio" name = "tokio"
version = "0.1.22" version = "0.2.22"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a09c0b5bb588872ab2f09afa13ee6e9dac11e10a0ec9e8e3ba39a5a5d530af6" checksum = "5d34ca54d84bf2b5b4d7d31e901a8464f7b60ac145a284fba25ceb801f2ddccd"
dependencies = [ dependencies = [
"bytes", "bytes",
"futures",
"mio",
"num_cpus",
"tokio-codec",
"tokio-current-thread",
"tokio-executor",
"tokio-fs",
"tokio-io",
"tokio-reactor",
"tokio-sync",
"tokio-tcp",
"tokio-threadpool",
"tokio-timer",
"tokio-udp",
"tokio-uds",
]
[[package]]
name = "tokio-buf"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8fb220f46c53859a4b7ec083e41dec9778ff0b1851c0942b211edb89e0ccdc46"
dependencies = [
"bytes",
"either",
"futures",
]
[[package]]
name = "tokio-codec"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "25b2998660ba0e70d18684de5d06b70b70a3a747469af9dea7618cc59e75976b"
dependencies = [
"bytes",
"futures",
"tokio-io",
]
[[package]]
name = "tokio-core"
version = "0.1.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aeeffbbb94209023feaef3c196a41cbcdafa06b4a6f893f68779bb5e53796f71"
dependencies = [
"bytes",
"futures",
"iovec",
"log",
"mio",
"scoped-tls",
"tokio",
"tokio-executor",
"tokio-io",
"tokio-reactor",
"tokio-timer",
]
[[package]]
name = "tokio-current-thread"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1de0e32a83f131e002238d7ccde18211c0a5397f60cbfffcb112868c2e0e20e"
dependencies = [
"futures",
"tokio-executor",
]
[[package]]
name = "tokio-executor"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fb2d1b8f4548dbf5e1f7818512e9c406860678f29c300cdf0ebac72d1a3a1671"
dependencies = [
"crossbeam-utils",
"futures",
]
[[package]]
name = "tokio-fs"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "297a1206e0ca6302a0eed35b700d292b275256f596e2f3fea7729d5e629b6ff4"
dependencies = [
"futures",
"tokio-io",
"tokio-threadpool",
]
[[package]]
name = "tokio-io"
version = "0.1.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57fc868aae093479e3131e3d165c93b1c7474109d13c90ec0dda2a1bbfff0674"
dependencies = [
"bytes",
"futures",
"log",
]
[[package]]
name = "tokio-reactor"
version = "0.1.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09bc590ec4ba8ba87652da2068d150dcada2cfa2e07faae270a5e0409aa51351"
dependencies = [
"crossbeam-utils",
"futures",
"lazy_static",
"log",
"mio",
"num_cpus",
"parking_lot",
"slab",
"tokio-executor",
"tokio-io",
"tokio-sync",
]
[[package]]
name = "tokio-service"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24da22d077e0f15f55162bdbdc661228c1581892f52074fb242678d015b45162"
dependencies = [
"futures",
]
[[package]]
name = "tokio-sync"
version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "edfe50152bc8164fcc456dab7891fa9bf8beaf01c5ee7e1dd43a397c3cf87dee"
dependencies = [
"fnv", "fnv",
"futures", "futures-core",
]
[[package]]
name = "tokio-tcp"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "98df18ed66e3b72e742f185882a9e201892407957e45fbff8da17ae7a7c51f72"
dependencies = [
"bytes",
"futures",
"iovec", "iovec",
"mio",
"tokio-io",
"tokio-reactor",
]
[[package]]
name = "tokio-threadpool"
version = "0.1.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df720b6581784c118f0eb4310796b12b1d242a7eb95f716a8367855325c25f89"
dependencies = [
"crossbeam-deque",
"crossbeam-queue",
"crossbeam-utils",
"futures",
"lazy_static", "lazy_static",
"log",
"num_cpus",
"slab",
"tokio-executor",
]
[[package]]
name = "tokio-timer"
version = "0.2.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "93044f2d313c95ff1cb7809ce9a7a05735b012288a888b62d4434fd58c94f296"
dependencies = [
"crossbeam-utils",
"futures",
"slab",
"tokio-executor",
]
[[package]]
name = "tokio-udp"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2a0b10e610b39c38b031a2fcab08e4b82f16ece36504988dcbd81dbba650d82"
dependencies = [
"bytes",
"futures",
"log",
"mio",
"tokio-codec",
"tokio-io",
"tokio-reactor",
]
[[package]]
name = "tokio-uds"
version = "0.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab57a4ac4111c8c9dbcf70779f6fc8bc35ae4b2454809febac840ad19bd7e4e0"
dependencies = [
"bytes",
"futures",
"iovec",
"libc", "libc",
"log", "memchr",
"mio", "mio",
"mio-named-pipes",
"mio-uds", "mio-uds",
"tokio-codec", "num_cpus",
"tokio-io", "pin-project-lite",
"tokio-reactor", "signal-hook-registry",
"slab",
"tokio-macros",
"winapi 0.3.9",
]
[[package]]
name = "tokio-macros"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0c3acc6aa564495a0f2e1d59fab677cd7f81a19994cfc7f3ad0e64301560389"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "tokio-tls"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a70f4fcd7b3b24fb194f837560168208f669ca8cb70d0c4b862944452396343"
dependencies = [
"native-tls",
"tokio",
]
[[package]]
name = "tokio-util"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be8242891f2b6cbef26a2d7e8605133c2c554cd35b3e4948ea892d6d68436499"
dependencies = [
"bytes",
"futures-core",
"futures-sink",
"log",
"pin-project-lite",
"tokio",
] ]
[[package]] [[package]]
@ -1590,21 +1469,39 @@ dependencies = [
"serde", "serde",
] ]
[[package]]
name = "tower-service"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e987b6bf443f4b5b3b6f38704195592cca41c5bb7aedd3c3693c7081f8289860"
[[package]]
name = "tracing"
version = "0.1.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b0987850db3733619253fe60e17cb59b82d37c7e6c0236bb81e4d6b87c879f27"
dependencies = [
"cfg-if",
"log",
"pin-project-lite",
"tracing-core",
]
[[package]]
name = "tracing-core"
version = "0.1.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f50de3927f93d202783f4513cda820ab47ef17f624b03c096e86ef00c67e6b5f"
dependencies = [
"lazy_static",
]
[[package]] [[package]]
name = "try-lock" name = "try-lock"
version = "0.2.3" version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642"
[[package]]
name = "try_future"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "30454186ee38f29e06f386c6e9f773b7c33e85430db6f90e4597419b4c5baad7"
dependencies = [
"futures",
]
[[package]] [[package]]
name = "ttf-parser" name = "ttf-parser"
version = "0.6.2" version = "0.6.2"
@ -1666,11 +1563,10 @@ checksum = "6454029bf181f092ad1b853286f23e2c507d8e8194d01d92da4a55c274a5508c"
[[package]] [[package]]
name = "want" name = "want"
version = "0.2.0" version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6395efa4784b027708f7451087e647ec73cc74f5d9bc2e418404248d679a230" checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0"
dependencies = [ dependencies = [
"futures",
"log", "log",
"try-lock", "try-lock",
] ]

View file

@ -14,28 +14,26 @@ members = [
badge = { path = "./libs/badge" } badge = { path = "./libs/badge" }
anyhow = "1" anyhow = "1"
cadence = "0.13" cadence = "0.21"
derive_more = "0.99" derive_more = "0.99"
futures = "0.1" futures = "0.3"
hyper = "0.12" hyper = "0.13"
hyper-tls = "0.3" hyper-tls = "0.4"
indexmap = { version = "1", features = ["serde-1"] } indexmap = { version = "1", features = ["serde-1"] }
lru-cache = "0.1" lru-cache = "0.1" # TODO: replace unmaintained crate
maud = "0.22" maud = "0.22"
once_cell = "1.4" once_cell = "1.4"
relative-path = { version = "0.3.7", features = ["serde"] } pin-project = "0.4"
route-recognizer = "0.1" relative-path = { version = "1.3", features = ["serde"] }
route-recognizer = "0.2"
rustsec = "0.21" rustsec = "0.21"
semver = { version = "0.11", features = ["serde"] } semver = { version = "0.11", features = ["serde"] }
serde = { version = "1", features = ["derive"] } serde = { version = "1", features = ["derive"] }
serde_json = "1" serde_json = "1"
slog = "2" slog = "2"
slog-json = "2" slog-json = "2"
tokio-core = "0.1" tokio = { version = "0.2", features = ["full"] }
tokio-service = "0.1"
toml = "0.5" toml = "0.5"
try_future = "0.1"
[build-dependencies] [build-dependencies]
sass-rs = "0.2" sass-rs = "0.2"

2
rust-toolchain Normal file
View file

@ -0,0 +1,2 @@
nightly-2020-09-27

View file

@ -1,66 +1,55 @@
use anyhow::{anyhow, ensure, Error}; use anyhow::Error;
use futures::stream::futures_unordered; use futures::StreamExt;
use futures::{Future, Poll, Stream};
use crate::models::crates::{AnalyzedDependencies, CrateDeps}; use crate::models::crates::{AnalyzedDependencies, CrateDeps};
use crate::{engine::machines::analyzer::DependencyAnalyzer, Engine};
use super::super::machines::analyzer::DependencyAnalyzer; pub async fn analyze_dependencies(
use super::super::Engine; engine: Engine,
deps: CrateDeps,
) -> Result<AnalyzedDependencies, Error> {
let advisory_db = engine.fetch_advisory_db().await?;
pub struct AnalyzeDependenciesFuture { let mut analyzer = DependencyAnalyzer::new(&deps, Some(advisory_db));
inner: Box<dyn Future<Item = AnalyzedDependencies, Error = Error> + Send>,
}
impl AnalyzeDependenciesFuture { let main_deps =
pub fn new(engine: Engine, deps: CrateDeps) -> Self { deps.main.into_iter().filter_map(
let future = |(name, dep)| {
engine.fetch_advisory_db().and_then(move |advisory_db| { if dep.is_external() {
let analyzer = DependencyAnalyzer::new(&deps, Some(advisory_db)); Some(name)
} else {
None
}
},
);
let dev_deps =
deps.dev.into_iter().filter_map(
|(name, dep)| {
if dep.is_external() {
Some(name)
} else {
None
}
},
);
let build_deps =
deps.build.into_iter().filter_map(
|(name, dep)| {
if dep.is_external() {
Some(name)
} else {
None
}
},
);
let main_deps = deps.main.into_iter().filter_map(|(name, dep)| { let deps_iter = main_deps.chain(dev_deps).chain(build_deps);
if dep.is_external() { let mut releases = engine.fetch_releases(deps_iter);
Some(name)
} else {
None
}
});
let dev_deps = deps.dev.into_iter().filter_map(|(name, dep)| {
if dep.is_external() {
Some(name)
} else {
None
}
});
let build_deps = deps.build.into_iter().filter_map(|(name, dep)| {
if dep.is_external() {
Some(name)
} else {
None
}
});
let release_futures = for release in releases.next().await {
engine.fetch_releases(main_deps.chain(dev_deps).chain(build_deps)); let release = release?;
analyzer.process(release)
futures_unordered(release_futures)
.fold(analyzer, |mut analyzer, releases| {
analyzer.process(releases);
Ok(analyzer) as Result<_, Error>
})
.map(|analyzer| analyzer.finalize())
});
AnalyzeDependenciesFuture {
inner: Box::new(future),
}
} }
}
impl Future for AnalyzeDependenciesFuture { Ok(analyzer.finalize())
type Item = AnalyzedDependencies;
type Error = Error;
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
self.inner.poll()
}
} }

View file

@ -1,8 +1,8 @@
use std::mem; use std::{future::Future, mem, pin::Pin, task::Context, task::Poll};
use anyhow::{anyhow, ensure, Error}; use anyhow::Error;
use futures::stream::FuturesOrdered; use futures::{ready, Stream};
use futures::{try_ready, Async, Future, Poll, Stream}; use futures::{stream::FuturesOrdered, FutureExt};
use relative_path::RelativePathBuf; use relative_path::RelativePathBuf;
use crate::models::repo::RepoPath; use crate::models::repo::RepoPath;
@ -11,24 +11,28 @@ use super::super::machines::crawler::ManifestCrawler;
pub use super::super::machines::crawler::ManifestCrawlerOutput; pub use super::super::machines::crawler::ManifestCrawlerOutput;
use super::super::Engine; use super::super::Engine;
#[pin_project::pin_project]
pub struct CrawlManifestFuture { pub struct CrawlManifestFuture {
repo_path: RepoPath, repo_path: RepoPath,
engine: Engine, engine: Engine,
crawler: ManifestCrawler, crawler: ManifestCrawler,
futures: #[pin]
FuturesOrdered<Box<dyn Future<Item = (RelativePathBuf, String), Error = Error> + Send>>, futures: FuturesOrdered<
Pin<Box<dyn Future<Output = Result<(RelativePathBuf, String), Error>> + Send>>,
>,
} }
impl CrawlManifestFuture { impl CrawlManifestFuture {
pub fn new(engine: &Engine, repo_path: RepoPath, entry_point: RelativePathBuf) -> Self { pub fn new(engine: &Engine, repo_path: RepoPath, entry_point: RelativePathBuf) -> Self {
let future: Box<dyn Future<Item = _, Error = _> + Send> = Box::new(
engine
.retrieve_manifest_at_path(&repo_path, &entry_point)
.map(move |contents| (entry_point, contents)),
);
let engine = engine.clone(); let engine = engine.clone();
let crawler = ManifestCrawler::new(); let crawler = ManifestCrawler::new();
let mut futures = FuturesOrdered::new(); let mut futures = FuturesOrdered::new();
let future: Pin<Box<dyn Future<Output = _> + Send>> = Box::pin(
engine
.retrieve_manifest_at_path(&repo_path, &entry_point)
.map(move |contents| contents.map(|c| (entry_point, c))),
);
futures.push(future); futures.push(future);
CrawlManifestFuture { CrawlManifestFuture {
@ -41,27 +45,33 @@ impl CrawlManifestFuture {
} }
impl Future for CrawlManifestFuture { impl Future for CrawlManifestFuture {
type Item = ManifestCrawlerOutput; type Output = Result<ManifestCrawlerOutput, Error>;
type Error = Error;
fn poll(&mut self) -> Poll<Self::Item, Self::Error> { fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
match try_ready!(self.futures.poll()) { let this = self.as_mut().project();
match ready!(this.futures.poll_next(cx)) {
None => { None => {
let crawler = mem::replace(&mut self.crawler, ManifestCrawler::new()); let crawler = mem::replace(&mut self.crawler, ManifestCrawler::new());
Ok(Async::Ready(crawler.finalize())) Poll::Ready(Ok(crawler.finalize()))
} }
Some((path, raw_manifest)) => {
Some(Ok((path, raw_manifest))) => {
let output = self.crawler.step(path, raw_manifest)?; let output = self.crawler.step(path, raw_manifest)?;
for path in output.paths_of_interest.into_iter() { for path in output.paths_of_interest.into_iter() {
let future: Box<dyn Future<Item = _, Error = _> + Send> = Box::new( let future: Pin<Box<dyn Future<Output = _> + Send>> = Box::pin(
self.engine self.engine
.retrieve_manifest_at_path(&self.repo_path, &path) .retrieve_manifest_at_path(&self.repo_path, &path)
.map(move |contents| (path, contents)), .map(move |contents| contents.map(|c| ((path, c)))),
); );
self.futures.push(future); self.futures.push(future);
} }
self.poll()
self.poll(cx)
} }
Some(Err(err)) => Poll::Ready(Err(err.into())),
} }
} }
} }

View file

@ -1,5 +1,5 @@
mod analyze; mod analyze;
mod crawl; mod crawl;
pub use self::analyze::AnalyzeDependenciesFuture; pub use self::analyze::analyze_dependencies;
pub use self::crawl::CrawlManifestFuture; pub use self::crawl::CrawlManifestFuture;

View file

@ -1,6 +1,6 @@
use std::collections::HashMap; use std::collections::HashMap;
use anyhow::{anyhow, ensure, Error}; use anyhow::Error;
use indexmap::IndexMap; use indexmap::IndexMap;
use relative_path::RelativePathBuf; use relative_path::RelativePathBuf;

View file

@ -1,51 +1,57 @@
use std::collections::HashSet; use std::{
use std::sync::Arc; collections::HashSet,
use std::time::{Duration, Instant}; panic::RefUnwindSafe,
sync::{Arc, Mutex},
task::{Context, Poll},
time::{Duration, Instant},
};
use anyhow::{anyhow, ensure, Error}; use anyhow::{anyhow, Error};
use cadence::prelude::*;
use cadence::{MetricSink, NopMetricSink, StatsdClient}; use cadence::{MetricSink, NopMetricSink, StatsdClient};
use futures::future::join_all; use futures::{future::try_join_all, stream::FuturesUnordered, Future, FutureExt, Stream};
use futures::{future, Future}; use hyper::{
use hyper::client::{HttpConnector, ResponseFuture}; client::{HttpConnector, ResponseFuture},
use hyper::{Body, Client, Request, Response}; service::Service,
Body, Client, Request, Response,
};
use hyper_tls::HttpsConnector; use hyper_tls::HttpsConnector;
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
use relative_path::{RelativePath, RelativePathBuf}; use relative_path::{RelativePath, RelativePathBuf};
use rustsec::database::Database; use rustsec::database::Database;
use semver::VersionReq; use semver::VersionReq;
use slog::Logger; use slog::Logger;
use tokio_service::Service;
use crate::utils::cache::Cache;
use crate::models::crates::{AnalyzedDependencies, CrateName, CratePath, CrateRelease};
use crate::models::repo::{RepoPath, Repository};
use crate::interactors::crates::{GetPopularCrates, QueryCrate}; use crate::interactors::crates::{GetPopularCrates, QueryCrate};
use crate::interactors::github::GetPopularRepos; use crate::interactors::github::GetPopularRepos;
use crate::interactors::rustsec::FetchAdvisoryDatabase; use crate::interactors::rustsec::FetchAdvisoryDatabase;
use crate::interactors::RetrieveFileAtPath; use crate::interactors::RetrieveFileAtPath;
use crate::models::crates::{AnalyzedDependencies, CrateName, CratePath, CrateRelease};
use crate::models::repo::{RepoPath, Repository};
use crate::utils::cache::Cache;
mod fut; mod fut;
mod machines; mod machines;
use self::fut::AnalyzeDependenciesFuture; use self::fut::analyze_dependencies;
use self::fut::CrawlManifestFuture; use self::fut::CrawlManifestFuture;
type HttpClient = Client<HttpsConnector<HttpConnector>>; type HttpClient = Client<HttpsConnector<HttpConnector>>;
// type HttpClient = Client<HttpConnector>;
// workaround for hyper 0.12 not implementing Service for Client // workaround for hyper 0.12 not implementing Service for Client
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
struct ServiceHttpClient(HttpClient); struct ServiceHttpClient(HttpClient);
impl Service for ServiceHttpClient { impl Service<Request<Body>> for ServiceHttpClient {
type Request = Request<Body>;
type Response = Response<Body>; type Response = Response<Body>;
type Error = hyper::Error; type Error = hyper::Error;
type Future = ResponseFuture; type Future = ResponseFuture;
fn call(&self, req: Self::Request) -> Self::Future { fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
self.0.poll_ready(cx).map_err(|err| err.into())
}
fn call(&mut self, req: Request<Body>) -> Self::Future {
self.0.request(req) self.0.request(req)
} }
} }
@ -55,15 +61,16 @@ pub struct Engine {
client: HttpClient, client: HttpClient,
logger: Logger, logger: Logger,
metrics: StatsdClient, metrics: StatsdClient,
query_crate: Arc<Cache<QueryCrate<ServiceHttpClient>>>, // TODO: use futures aware mutex
get_popular_crates: Arc<Cache<GetPopularCrates<ServiceHttpClient>>>, query_crate: Arc<Mutex<Cache<QueryCrate<ServiceHttpClient>, CrateName>>>,
get_popular_repos: Arc<Cache<GetPopularRepos<ServiceHttpClient>>>, get_popular_crates: Arc<Mutex<Cache<GetPopularCrates<ServiceHttpClient>, ()>>>,
retrieve_file_at_path: Arc<RetrieveFileAtPath<ServiceHttpClient>>, get_popular_repos: Arc<Mutex<Cache<GetPopularRepos<ServiceHttpClient>, ()>>>,
fetch_advisory_db: Arc<Cache<FetchAdvisoryDatabase<ServiceHttpClient>>>, retrieve_file_at_path: Arc<Mutex<RetrieveFileAtPath<ServiceHttpClient>>>,
fetch_advisory_db: Arc<Mutex<Cache<FetchAdvisoryDatabase<ServiceHttpClient>, ()>>>,
} }
impl Engine { impl Engine {
pub fn new(client: Client<HttpsConnector<HttpConnector>>, logger: Logger) -> Engine { pub fn new(client: HttpClient, logger: Logger) -> Engine {
let metrics = StatsdClient::from_sink("engine", NopMetricSink); let metrics = StatsdClient::from_sink("engine", NopMetricSink);
let service_client = ServiceHttpClient(client.clone()); let service_client = ServiceHttpClient(client.clone());
@ -93,19 +100,20 @@ impl Engine {
client: client.clone(), client: client.clone(),
logger, logger,
metrics, metrics,
query_crate: Arc::new(query_crate), query_crate: Arc::new(Mutex::new(query_crate)),
get_popular_crates: Arc::new(get_popular_crates), get_popular_crates: Arc::new(Mutex::new(get_popular_crates)),
get_popular_repos: Arc::new(get_popular_repos), get_popular_repos: Arc::new(Mutex::new(get_popular_repos)),
retrieve_file_at_path: Arc::new(RetrieveFileAtPath(service_client)), retrieve_file_at_path: Arc::new(Mutex::new(RetrieveFileAtPath(service_client))),
fetch_advisory_db: Arc::new(fetch_advisory_db), fetch_advisory_db: Arc::new(Mutex::new(fetch_advisory_db)),
} }
} }
pub fn set_metrics<M: MetricSink + Send + Sync + 'static>(&mut self, sink: M) { pub fn set_metrics<M: MetricSink + Send + Sync + RefUnwindSafe + 'static>(&mut self, sink: M) {
self.metrics = StatsdClient::from_sink("engine", sink); self.metrics = StatsdClient::from_sink("engine", sink);
} }
} }
#[derive(Debug)]
pub struct AnalyzeDependenciesOutcome { pub struct AnalyzeDependenciesOutcome {
pub crates: Vec<(CrateName, AnalyzedDependencies)>, pub crates: Vec<(CrateName, AnalyzedDependencies)>,
pub duration: Duration, pub duration: Duration,
@ -132,141 +140,150 @@ impl AnalyzeDependenciesOutcome {
} }
impl Engine { impl Engine {
pub fn get_popular_repos(&self) -> impl Future<Item = Vec<Repository>, Error = Error> + Send { pub async fn get_popular_repos(&self) -> Result<Vec<Repository>, Error> {
self.get_popular_repos.call(()).from_err().map(|repos| { let repos = self.get_popular_repos.lock().unwrap().call(());
repos let repos = repos.await?;
.iter()
.filter(|repo| !POPULAR_REPO_BLOCK_LIST.contains(&repo.path)) let filtered_repos = repos
.cloned() .iter()
.collect() .filter(|repo| !POPULAR_REPO_BLOCK_LIST.contains(&repo.path))
}) .cloned()
.collect();
Ok(filtered_repos)
} }
pub fn get_popular_crates(&self) -> impl Future<Item = Vec<CratePath>, Error = Error> + Send { pub async fn get_popular_crates(&self) -> Result<Vec<CratePath>, Error> {
self.get_popular_crates let crates = self.get_popular_crates.lock().unwrap().call(());
.call(()) let crates = crates.await?;
.from_err() Ok(crates.clone())
.map(|crates| crates.clone())
} }
pub fn analyze_repo_dependencies( pub async fn analyze_repo_dependencies(
&self, &self,
repo_path: RepoPath, repo_path: RepoPath,
) -> impl Future<Item = AnalyzeDependenciesOutcome, Error = Error> + Send { ) -> Result<AnalyzeDependenciesOutcome, Error> {
let start = Instant::now(); let start = Instant::now();
let entry_point = RelativePath::new("/").to_relative_path_buf(); let entry_point = RelativePath::new("/").to_relative_path_buf();
let manifest_future = CrawlManifestFuture::new(self, repo_path.clone(), entry_point);
let engine = self.clone(); let engine = self.clone();
manifest_future.and_then(move |manifest_output| {
let engine_for_analyze = engine.clone();
let futures = manifest_output
.crates
.into_iter()
.map(move |(crate_name, deps)| {
let analyzed_deps_future =
AnalyzeDependenciesFuture::new(engine_for_analyze.clone(), deps);
analyzed_deps_future.map(move |analyzed_deps| (crate_name, analyzed_deps)) let manifest_future = CrawlManifestFuture::new(self, repo_path.clone(), entry_point);
}); let manifest_output = manifest_future.await?;
join_all(futures).and_then(move |crates| { let engine_for_analyze = engine.clone();
let duration = start.elapsed(); let futures = manifest_output
engine .crates
.metrics .into_iter()
.time_duration_with_tags("analyze_duration", duration) .map(|(crate_name, deps)| async {
.with_tag("repo_site", repo_path.site.as_ref()) let analyzed_deps = analyze_dependencies(engine_for_analyze.clone(), deps).await?;
.with_tag("repo_qual", repo_path.qual.as_ref()) Ok::<_, Error>((crate_name, analyzed_deps))
.with_tag("repo_name", repo_path.name.as_ref())
.send()?;
Ok(AnalyzeDependenciesOutcome { crates, duration })
}) })
}) .collect::<Vec<_>>();
let crates = try_join_all(futures).await?;
let duration = start.elapsed();
// engine
// .metrics
// .time_duration_with_tags("analyze_duration", duration)
// .with_tag("repo_site", repo_path.site.as_ref())
// .with_tag("repo_qual", repo_path.qual.as_ref())
// .with_tag("repo_name", repo_path.name.as_ref())
// .send()?;
Ok(AnalyzeDependenciesOutcome { crates, duration })
} }
pub fn analyze_crate_dependencies( pub async fn analyze_crate_dependencies(
&self, &self,
crate_path: CratePath, crate_path: CratePath,
) -> impl Future<Item = AnalyzeDependenciesOutcome, Error = Error> + Send { ) -> Result<AnalyzeDependenciesOutcome, Error> {
let start = Instant::now(); let start = Instant::now();
let query_future = self.query_crate.call(crate_path.name.clone()).from_err(); let query_response = self
.query_crate
.lock()
.unwrap()
.call(crate_path.name.clone());
let query_response = query_response.await?;
let engine = self.clone(); let engine = self.clone();
query_future.and_then(move |query_response| {
match query_response
.releases
.iter()
.find(|release| release.version == crate_path.version)
{
None => future::Either::A(future::err(anyhow!(
"could not find crate release with version {}",
crate_path.version
))),
Some(release) => {
let analyzed_deps_future =
AnalyzeDependenciesFuture::new(engine.clone(), release.deps.clone());
future::Either::B(analyzed_deps_future.map(move |analyzed_deps| { match query_response
let crates = vec![(crate_path.name, analyzed_deps)].into_iter().collect(); .releases
let duration = start.elapsed(); .iter()
.find(|release| release.version == crate_path.version)
{
None => Err(anyhow!(
"could not find crate release with version {}",
crate_path.version
)),
AnalyzeDependenciesOutcome { crates, duration } Some(release) => {
})) let analyzed_deps =
} analyze_dependencies(engine.clone(), release.deps.clone()).await?;
let crates = vec![(crate_path.name, analyzed_deps)];
let duration = start.elapsed();
Ok(AnalyzeDependenciesOutcome { crates, duration })
} }
}) }
} }
pub fn find_latest_crate_release( pub async fn find_latest_crate_release(
&self, &self,
name: CrateName, name: CrateName,
req: VersionReq, req: VersionReq,
) -> impl Future<Item = Option<CrateRelease>, Error = Error> + Send { ) -> Result<Option<CrateRelease>, Error> {
self.query_crate let query_response = self.query_crate.lock().unwrap().call(name);
.call(name) let query_response = query_response.await?;
.from_err()
.map(move |query_response| { let latest = query_response
query_response .releases
.releases .iter()
.iter() .filter(|release| req.matches(&release.version))
.filter(|release| req.matches(&release.version)) .max_by(|r1, r2| r1.version.cmp(&r2.version))
.max_by(|r1, r2| r1.version.cmp(&r2.version)) .cloned();
.cloned()
}) Ok(latest)
} }
fn fetch_releases<I: IntoIterator<Item = CrateName>>( fn fetch_releases<I: IntoIterator<Item = CrateName>>(
&self, &self,
names: I, names: I,
) -> impl Iterator<Item = impl Future<Item = Vec<CrateRelease>, Error = Error>> { ) -> impl Stream<Item = Result<Vec<CrateRelease>, Error>> {
let engine = self.clone(); let engine = self.clone();
names.into_iter().map(move |name| {
engine names
.query_crate .into_iter()
.call(name) .map(|name| {
.from_err() engine
.map(|resp| resp.releases.clone()) .query_crate
}) .lock()
.unwrap()
.call(name)
.map(|resp| resp.map(|r| r.releases.clone()))
})
.collect::<FuturesUnordered<_>>()
} }
fn retrieve_manifest_at_path( fn retrieve_manifest_at_path(
&self, &self,
repo_path: &RepoPath, repo_path: &RepoPath,
path: &RelativePathBuf, path: &RelativePathBuf,
) -> impl Future<Item = String, Error = Error> + Send { ) -> impl Future<Output = Result<String, Error>> {
let manifest_path = path.join(RelativePath::new("Cargo.toml")); let manifest_path = path.join(RelativePath::new("Cargo.toml"));
self.retrieve_file_at_path self.retrieve_file_at_path
.lock()
.unwrap()
.call((repo_path.clone(), manifest_path)) .call((repo_path.clone(), manifest_path))
} }
fn fetch_advisory_db(&self) -> impl Future<Item = Arc<Database>, Error = Error> + Send { fn fetch_advisory_db(&self) -> impl Future<Output = Result<Arc<Database>, Error>> {
self.fetch_advisory_db self.fetch_advisory_db.lock().unwrap().call(())
.call(())
.from_err()
.map(|db| db.clone())
} }
} }

View file

@ -1,4 +1,4 @@
use anyhow::{anyhow, ensure, Error}; use anyhow::Error;
use hyper::Uri; use hyper::Uri;
use relative_path::RelativePathBuf; use relative_path::RelativePathBuf;
@ -8,12 +8,14 @@ const BITBUCKET_USER_CONTENT_BASE_URI: &'static str = "https://bitbucket.org";
pub fn get_manifest_uri(repo_path: &RepoPath, path: &RelativePathBuf) -> Result<Uri, Error> { pub fn get_manifest_uri(repo_path: &RepoPath, path: &RelativePathBuf) -> Result<Uri, Error> {
let path_str: &str = path.as_ref(); let path_str: &str = path.as_ref();
Ok(format!(
let url = format!(
"{}/{}/{}/raw/HEAD/{}", "{}/{}/{}/raw/HEAD/{}",
BITBUCKET_USER_CONTENT_BASE_URI, BITBUCKET_USER_CONTENT_BASE_URI,
repo_path.qual.as_ref(), repo_path.qual.as_ref(),
repo_path.name.as_ref(), repo_path.name.as_ref(),
path_str path_str
) );
.parse::<Uri>()?)
Ok(url.parse::<Uri>()?)
} }

View file

@ -1,11 +1,17 @@
use std::str; use std::pin::Pin;
use std::{future::Future, task::Poll};
use std::{str, task::Context};
use anyhow::{anyhow, ensure, Error}; use anyhow::{anyhow, Error};
use futures::{future, Future, IntoFuture, Stream}; use futures::{
use hyper::{Body, Error as HyperError, Method, Request, Response, Uri, header::USER_AGENT}; future::{err, ok, ready},
TryFutureExt,
};
use hyper::{
body, header::USER_AGENT, service::Service, Body, Error as HyperError, Request, Response, Uri,
};
use semver::{Version, VersionReq}; use semver::{Version, VersionReq};
use serde::Deserialize; use serde::Deserialize;
use tokio_service::Service;
use crate::models::crates::{CrateDep, CrateDeps, CrateName, CratePath, CrateRelease}; use crate::models::crates::{CrateDep, CrateDeps, CrateName, CratePath, CrateRelease};
@ -65,6 +71,7 @@ fn convert_pkgs(
Ok(QueryCrateResponse { releases: releases }) Ok(QueryCrateResponse { releases: releases })
} }
#[derive(Debug, Clone)]
pub struct QueryCrateResponse { pub struct QueryCrateResponse {
pub releases: Vec<CrateRelease>, pub releases: Vec<CrateRelease>,
} }
@ -72,19 +79,20 @@ pub struct QueryCrateResponse {
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct QueryCrate<S>(pub S); pub struct QueryCrate<S>(pub S);
impl<S> Service for QueryCrate<S> impl<S> Service<CrateName> for QueryCrate<S>
where where
S: Service<Request = Request<Body>, Response = Response<Body>, Error = HyperError> S: Service<Request<Body>, Response = Response<Body>, Error = HyperError> + Clone,
+ Clone
+ 'static,
S::Future: Send + 'static, S::Future: Send + 'static,
{ {
type Request = CrateName;
type Response = QueryCrateResponse; type Response = QueryCrateResponse;
type Error = Error; type Error = Error;
type Future = Box<dyn Future<Item = Self::Response, Error = Self::Error> + Send>; type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>> + Send>>;
fn call(&self, crate_name: CrateName) -> Self::Future { fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
self.0.poll_ready(cx).map_err(|err| err.into())
}
fn call(&mut self, crate_name: CrateName) -> Self::Future {
let lower_name = crate_name.as_ref().to_lowercase(); let lower_name = crate_name.as_ref().to_lowercase();
let path = match lower_name.len() { let path = match lower_name.len() {
@ -94,35 +102,41 @@ where
_ => format!("{}/{}/{}", &lower_name[0..2], &lower_name[2..4], lower_name), _ => format!("{}/{}/{}", &lower_name[0..2], &lower_name[2..4], lower_name),
}; };
let uri = let uri = format!("{}/master/{}", CRATES_INDEX_BASE_URI, path);
try_future_box!(format!("{}/master/{}", CRATES_INDEX_BASE_URI, path).parse::<Uri>()); let uri = uri.parse::<Uri>().expect("TODO: MAP ERROR PROPERLY");
let request = Request::get(uri.clone()) let request = Request::get(uri.clone())
.header(USER_AGENT, "deps.rs") .header(USER_AGENT, "deps.rs")
.body(Body::empty()).unwrap(); .body(Body::empty())
.unwrap();
Box::new(self.0.call(request).from_err().and_then(move |response| { Box::pin(
let status = response.status(); self.0
if !status.is_success() { .call(request)
try_future!(Err(anyhow!("Status code {} for URI {}", status, uri))); .map_err(|err| err.into())
} .and_then(move |response| {
let status = response.status();
if !status.is_success() {
return err(anyhow!("Status code {} for URI {}", status, uri));
}
let body_future = response.into_body().concat2().from_err(); ok(response)
let decode_future = body_future.and_then(move |body| { })
let string_body = str::from_utf8(body.as_ref())?; .and_then(|response| body::to_bytes(response.into_body()).err_into())
let packages = string_body .and_then(|body| ready(String::from_utf8(body.to_vec())).err_into())
.lines() .and_then(|string_body| {
.map(|s| s.trim()) ready(
.filter(|s| !s.is_empty()) string_body
.map(|s| serde_json::from_str::<RegistryPackage>(s)) .lines()
.collect::<Result<_, _>>()?; .map(|s| s.trim())
Ok(packages) .filter(|s| !s.is_empty())
}); .map(serde_json::from_str)
.collect::<Result<_, _>>(),
decode_future )
.and_then(move |pkgs| convert_pkgs(&crate_name, pkgs)) .err_into()
.into() })
})) .and_then(move |pkgs| ready(convert_pkgs(&crate_name, pkgs))),
)
} }
} }
@ -154,44 +168,46 @@ fn convert_summary(response: SummaryResponse) -> Result<Vec<CratePath>, Error> {
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct GetPopularCrates<S>(pub S); pub struct GetPopularCrates<S>(pub S);
impl<S> Service for GetPopularCrates<S> impl<S> Service<()> for GetPopularCrates<S>
where where
S: Service<Request = Request<Body>, Response = Response<Body>, Error = HyperError> S: Service<Request<Body>, Response = Response<Body>, Error = HyperError> + Clone,
+ Clone
+ 'static,
S::Future: Send + 'static, S::Future: Send + 'static,
{ {
type Request = ();
type Response = Vec<CratePath>; type Response = Vec<CratePath>;
type Error = Error; type Error = Error;
type Future = Box<dyn Future<Item = Self::Response, Error = Self::Error> + Send>; type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>> + Send>>;
fn call(&self, _req: ()) -> Self::Future { fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
let service = self.0.clone(); self.0.poll_ready(cx).map_err(|err| err.into())
}
let uri = format!("{}/summary", CRATES_API_BASE_URI) fn call(&mut self, _req: ()) -> Self::Future {
.parse::<Uri>() let mut service = self.0.clone();
.unwrap();
let uri = format!("{}/summary", CRATES_API_BASE_URI);
let uri = uri.parse::<Uri>().unwrap();
let request = Request::get(uri.clone()) let request = Request::get(uri.clone())
.header(USER_AGENT, "deps.rs") .header(USER_AGENT, "deps.rs")
.body(Body::empty()).unwrap(); .body(Body::empty())
.unwrap();
Box::new(service.call(request).from_err().and_then(move |response| { Box::pin(
let status = response.status(); service
if !status.is_success() { .call(request)
future::Either::A(future::err(anyhow!( .map_err(|err| err.into())
"Status code {} for URI {}", .and_then(move |response| {
status, let status = response.status();
uri if !status.is_success() {
))) err(anyhow!("Status code {} for URI {}", status, uri))
} else { } else {
let body_future = response.into_body().concat2().from_err(); ok(response)
let decode_future = body_future.and_then(|body| { }
let summary = serde_json::from_slice::<SummaryResponse>(&body)?; })
convert_summary(summary) .and_then(|response| body::to_bytes(response.into_body()).err_into())
}); .and_then(|bytes| {
future::Either::B(decode_future) ready(serde_json::from_slice::<SummaryResponse>(&bytes)).err_into()
} })
})) .and_then(|summary| ready(convert_summary(summary)).err_into()),
)
} }
} }

View file

@ -1,10 +1,15 @@
use anyhow::{anyhow, ensure, Error}; use std::{future::Future, pin::Pin, task::Context, task::Poll};
use futures::{Future, Stream};
use hyper::header::USER_AGENT; use anyhow::{anyhow, Error};
use hyper::{Body, Error as HyperError, Method, Request, Response, Uri}; use futures::{
future::{err, ok, ready},
TryFutureExt,
};
use hyper::{
body, header::USER_AGENT, service::Service, Body, Error as HyperError, Request, Response, Uri,
};
use relative_path::RelativePathBuf; use relative_path::RelativePathBuf;
use serde::Deserialize; use serde::Deserialize;
use tokio_service::Service;
use crate::models::repo::{RepoPath, Repository}; use crate::models::repo::{RepoPath, Repository};
@ -43,57 +48,63 @@ struct GithubOwner {
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct GetPopularRepos<S>(pub S); pub struct GetPopularRepos<S>(pub S);
impl<S> Service for GetPopularRepos<S> impl<S> Service<()> for GetPopularRepos<S>
where where
S: Service<Request = Request<Body>, Response = Response<Body>, Error = HyperError> S: Service<Request<Body>, Response = Response<Body>, Error = HyperError> + Clone,
+ Clone
+ 'static,
S::Future: Send + 'static, S::Future: Send + 'static,
{ {
type Request = ();
type Response = Vec<Repository>; type Response = Vec<Repository>;
type Error = Error; type Error = Error;
type Future = Box<dyn Future<Item = Self::Response, Error = Self::Error> + Send>; type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>> + Send>>;
fn call(&self, _req: ()) -> Self::Future { fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
let uri = try_future_box!(format!( self.0.poll_ready(cx).map_err(|err| err.into())
}
fn call(&mut self, _req: ()) -> Self::Future {
let uri = format!(
"{}/search/repositories?q=language:rust&sort=stars", "{}/search/repositories?q=language:rust&sort=stars",
GITHUB_API_BASE_URI GITHUB_API_BASE_URI
) )
.parse::<Uri>()); .parse::<Uri>()
.expect("TODO: handle error properly");
let mut request = Request::get(uri); let request = Request::get(uri)
request.header(USER_AGENT, "deps.rs"); .header(USER_AGENT, "deps.rs")
let request = request.body(Body::empty()).unwrap(); .body(Body::empty())
.unwrap();
Box::new(self.0.call(request).from_err().and_then(|response| { Box::pin(
let status = response.status(); self.0
if !status.is_success() { .call(request)
try_future!(Err(anyhow!( .err_into()
"Status code {} for popular repo search", .and_then(|response| {
status let status = response.status();
))); if !status.is_success() {
} return err(anyhow!("Status code {} for popular repo search", status));
}
let body_future = response.into_body().concat2().from_err(); ok(response)
let decode_future = body_future
.and_then(|body| serde_json::from_slice(body.as_ref()).map_err(|err| err.into()));
decode_future
.and_then(|search_response: GithubSearchResponse| {
search_response
.items
.into_iter()
.map(|item| {
let path =
RepoPath::from_parts("github", &item.owner.login, &item.name)?;
Ok(Repository {
path,
description: item.description,
})
})
.collect::<Result<Vec<_>, _>>()
}) })
.into() .and_then(|response| body::to_bytes(response.into_body()).err_into())
})) .and_then(|bytes| ready(serde_json::from_slice(bytes.as_ref())).err_into())
.and_then(|search_response: GithubSearchResponse| {
ready(
search_response
.items
.into_iter()
.map(|item| {
let path =
RepoPath::from_parts("github", &item.owner.login, &item.name)?;
Ok(Repository {
path,
description: item.description,
})
})
.collect::<Result<Vec<_>, Error>>(),
)
}),
)
} }
} }

View file

@ -1,4 +1,4 @@
use anyhow::{anyhow, ensure, Error}; use anyhow::Error;
use hyper::Uri; use hyper::Uri;
use relative_path::RelativePathBuf; use relative_path::RelativePathBuf;

View file

@ -1,8 +1,15 @@
use anyhow::{anyhow, ensure, Error}; use std::{future::Future, task::Poll};
use futures::{Future, Stream}; use std::{pin::Pin, task::Context};
use hyper::{Body, Error as HyperError, Method, Request, Response, header::USER_AGENT};
use anyhow::{anyhow, Error};
use futures::{
future::{err, ok, ready},
TryFutureExt,
};
use hyper::{
body, header::USER_AGENT, service::Service, Body, Error as HyperError, Request, Response,
};
use relative_path::RelativePathBuf; use relative_path::RelativePathBuf;
use tokio_service::Service;
use crate::models::repo::{RepoPath, RepoSite}; use crate::models::repo::{RepoPath, RepoSite};
@ -15,41 +22,52 @@ pub mod rustsec;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct RetrieveFileAtPath<S>(pub S); pub struct RetrieveFileAtPath<S>(pub S);
impl<S> Service for RetrieveFileAtPath<S> impl<S> Service<(RepoPath, RelativePathBuf)> for RetrieveFileAtPath<S>
where where
S: Service<Request = Request<Body>, Response = Response<Body>, Error = HyperError> S: Service<Request<Body>, Response = Response<Body>, Error = HyperError> + Clone,
+ Clone
+ 'static,
S::Future: Send + 'static, S::Future: Send + 'static,
{ {
type Request = (RepoPath, RelativePathBuf);
type Response = String; type Response = String;
type Error = Error; type Error = Error;
type Future = Box<dyn Future<Item = Self::Response, Error = Self::Error> + Send>; type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>> + Send>>;
fn call(&self, req: Self::Request) -> Self::Future { fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
self.0.poll_ready(cx).map_err(|err| err.into())
}
fn call(&mut self, req: (RepoPath, RelativePathBuf)) -> Self::Future {
let (repo_path, path) = req; let (repo_path, path) = req;
let uri = match &repo_path.site { let uri = match &repo_path.site {
&RepoSite::Github => try_future_box!(github::get_manifest_uri(&repo_path, &path)), RepoSite::Github => github::get_manifest_uri(&repo_path, &path),
&RepoSite::Gitlab => try_future_box!(gitlab::get_manifest_uri(&repo_path, &path)), RepoSite::Gitlab => gitlab::get_manifest_uri(&repo_path, &path),
&RepoSite::Bitbucket => try_future_box!(bitbucket::get_manifest_uri(&repo_path, &path)), RepoSite::Bitbucket => bitbucket::get_manifest_uri(&repo_path, &path),
};
let uri = match uri {
Ok(uri) => uri,
Err(error) => return Box::pin(err(error)),
}; };
let request = Request::get(uri.clone()) let request = Request::get(uri.clone())
.header(USER_AGENT, "deps.rs") .header(USER_AGENT, "deps.rs")
.body(Body::empty()).unwrap(); .body(Body::empty())
.unwrap();
Box::new(self.0.call(request).from_err().and_then(move |response| { Box::pin(
let status = response.status(); self.0
if !status.is_success() { .call(request)
try_future!(Err(anyhow!("Status code {} for URI {}", status, uri))); .err_into()
} .and_then(move |response| {
let status = response.status();
let body_future = response.into_body().concat2().from_err(); if status.is_success() {
ok(response)
body_future } else {
.and_then(|body| String::from_utf8(body.to_vec()).map_err(|err| err.into())) err(anyhow!("Status code {} for URI {}", status, uri))
.into() }
})) })
.and_then(|response| body::to_bytes(response.into_body()).err_into())
.and_then(|bytes| ready(String::from_utf8(bytes.to_vec())).err_into()),
)
} }
} }

View file

@ -1,35 +1,36 @@
use std::str; use std::{pin::Pin, sync::Arc, task::Context, task::Poll};
use std::sync::Arc;
use anyhow::{anyhow, ensure, Error}; use anyhow::Error;
use futures::{future, future::done, Future, IntoFuture, Stream}; use futures::{future::ready, Future};
use hyper::{Body, Error as HyperError, Method, Request, Response}; use hyper::{service::Service, Body, Error as HyperError, Request, Response};
use rustsec::database::Database; use rustsec::database::Database;
use rustsec::repository::DEFAULT_URL;
use tokio_service::Service;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct FetchAdvisoryDatabase<S>(pub S); pub struct FetchAdvisoryDatabase<S>(pub S);
impl<S> Service for FetchAdvisoryDatabase<S> impl<S> Service<()> for FetchAdvisoryDatabase<S>
where where
S: Service<Request = Request<Body>, Response = Response<Body>, Error = HyperError> S: Service<Request<Body>, Response = Response<Body>, Error = HyperError> + Clone,
+ Clone
+ 'static,
S::Future: 'static, S::Future: 'static,
{ {
type Request = ();
type Response = Arc<Database>; type Response = Arc<Database>;
type Error = Error; type Error = Error;
type Future = Box<dyn Future<Item = Self::Response, Error = Self::Error> + Send>; type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>> + Send>>;
fn call(&self, _req: ()) -> Self::Future { fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
let service = self.0.clone(); // TODO: should be this when async client is used again
// self.0.poll_ready(cx).map_err(|err| err.into())
Poll::Ready(Ok(()))
}
Box::new(done( // TODO: make fetch async again
fn call(&mut self, _req: ()) -> Self::Future {
let _service = self.0.clone();
Box::pin(ready(
rustsec::Database::fetch() rustsec::Database::fetch()
.map(|db| Arc::new(db)) .map(|db| Arc::new(db))
.map_err(|err| anyhow!("err fetching rustsec DB")), .map_err(|err| err.into()),
)) ))
} }
} }
@ -39,7 +40,7 @@ where
// impl<S> Service for FetchAdvisoryDatabase<S> // impl<S> Service for FetchAdvisoryDatabase<S>
// where // where
// S: Service<Request = Request, Response = Response, Error = HyperError> + Clone + 'static, // S: Service<Request = Request, Response = Response, Error = HyperError> + Clone,
// S::Future: 'static, // S::Future: 'static,
// { // {
// type Request = (); // type Request = ();

View file

@ -1,22 +1,20 @@
#![deny(rust_2018_idioms)] #![deny(rust_2018_idioms)]
#![allow(unused)] #![warn(missing_debug_implementations)]
#[macro_use] use std::{
extern crate try_future; env,
net::{IpAddr, Ipv4Addr, SocketAddr, UdpSocket},
use std::env; sync::Mutex,
use std::net::{IpAddr, Ipv4Addr, SocketAddr, UdpSocket}; };
use std::sync::Mutex;
use cadence::{QueuingMetricSink, UdpMetricSink}; use cadence::{QueuingMetricSink, UdpMetricSink};
use futures::{Future, Stream}; use hyper::{
use hyper::server::conn::AddrStream; server::conn::AddrStream,
use hyper::service::{make_service_fn, service_fn_ok}; service::{make_service_fn, service_fn},
use hyper::Client; Client, Server,
};
use hyper_tls::HttpsConnector; use hyper_tls::HttpsConnector;
use slog::Drain; use slog::{o, Drain};
use slog::{info, o};
use tokio_core::reactor::Core;
mod engine; mod engine;
mod interactors; mod interactors;
@ -26,7 +24,7 @@ mod server;
mod utils; mod utils;
use self::engine::Engine; use self::engine::Engine;
use self::server::Server; use self::server::App;
fn init_metrics() -> QueuingMetricSink { fn init_metrics() -> QueuingMetricSink {
let socket = UdpSocket::bind("0.0.0.0:0").unwrap(); let socket = UdpSocket::bind("0.0.0.0:0").unwrap();
@ -36,21 +34,15 @@ fn init_metrics() -> QueuingMetricSink {
QueuingMetricSink::from(sink) QueuingMetricSink::from(sink)
} }
fn main() { #[tokio::main]
async fn main() {
let logger = slog::Logger::root( let logger = slog::Logger::root(
Mutex::new(slog_json::Json::default(std::io::stderr())).map(slog::Fuse), Mutex::new(slog_json::Json::default(std::io::stderr())).map(slog::Fuse),
o!("version" => env!("CARGO_PKG_VERSION")), o!("version" => env!("CARGO_PKG_VERSION")),
); );
let metrics = init_metrics(); let metrics = init_metrics();
let client = Client::builder().build(HttpsConnector::new());
let mut core = Core::new().expect("failed to create event loop");
let handle = core.handle();
let connector = HttpsConnector::new(4).expect("failed to create https connector");
let client = Client::builder().build(connector);
let port = env::var("PORT") let port = env::var("PORT")
.unwrap_or_else(|_| "8080".to_string()) .unwrap_or_else(|_| "8080".to_string())
@ -62,15 +54,23 @@ fn main() {
let mut engine = Engine::new(client.clone(), logger.clone()); let mut engine = Engine::new(client.clone(), logger.clone());
engine.set_metrics(metrics); engine.set_metrics(metrics);
let server = Server::new(logger.clone(), engine); let make_svc = make_service_fn(move |_socket: &AddrStream| {
let make_svc = make_service_fn(move |socket: &AddrStream| { let logger = logger.clone();
let server = server.clone(); let engine = engine.clone();
futures::future::ok::<_, hyper::Error>(server)
async move {
let server = App::new(logger.clone(), engine.clone());
Ok::<_, hyper::Error>(service_fn(move |req| {
let server = server.clone();
async move { server.handle(req).await }
}))
}
}); });
let server = hyper::Server::bind(&addr).serve(make_svc); let server = Server::bind(&addr).serve(make_svc);
println!("Server running on port {}", port); println!("Server running on port {}", port);
hyper::rt::run(server.map_err(|e| {
if let Err(e) = server.await {
eprintln!("server error: {}", e); eprintln!("server error: {}", e);
})); }
} }

View file

@ -1,7 +1,6 @@
use std::borrow::Borrow; use std::{borrow::Borrow, str::FromStr};
use std::str::FromStr;
use anyhow::{anyhow, ensure, Error}; use anyhow::{anyhow, Error};
use indexmap::IndexMap; use indexmap::IndexMap;
use relative_path::RelativePathBuf; use relative_path::RelativePathBuf;
use semver::{Version, VersionReq}; use semver::{Version, VersionReq};

View file

@ -1,4 +1,4 @@
use anyhow::{anyhow, ensure, Error}; use anyhow::{anyhow, Error};
use indexmap::IndexMap; use indexmap::IndexMap;
use relative_path::RelativePathBuf; use relative_path::RelativePathBuf;
use semver::VersionReq; use semver::VersionReq;

View file

@ -1,15 +1,14 @@
use std::env; use std::{env, sync::Arc};
use std::sync::Arc;
use futures::{future, Future, IntoFuture}; use futures::future;
use hyper::header::{CONTENT_TYPE, LOCATION}; use hyper::{
use hyper::service::Service; header::{CONTENT_TYPE, LOCATION},
use hyper::{Body, Error as HyperError, Method, Request, Response, StatusCode}; Body, Error as HyperError, Method, Request, Response, StatusCode,
};
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
use route_recognizer::{Params, Router}; use route_recognizer::{Params, Router};
use semver::VersionReq; use semver::VersionReq;
use slog::Logger; use slog::{error, o, Logger};
use slog::{error, o};
mod assets; mod assets;
mod views; mod views;
@ -19,13 +18,13 @@ use crate::models::crates::{CrateName, CratePath};
use crate::models::repo::RepoPath; use crate::models::repo::RepoPath;
use crate::models::SubjectPath; use crate::models::SubjectPath;
#[derive(Clone, Copy, PartialEq)] #[derive(Debug, Clone, Copy, PartialEq)]
enum StatusFormat { enum StatusFormat {
Html, Html,
Svg, Svg,
} }
#[derive(Clone, Copy)] #[derive(Debug, Clone, Copy)]
enum StaticFile { enum StaticFile {
StyleCss, StyleCss,
FaviconPng, FaviconPng,
@ -40,14 +39,14 @@ enum Route {
} }
#[derive(Clone)] #[derive(Clone)]
pub struct Server { pub struct App {
logger: Logger, logger: Logger,
engine: Engine, engine: Engine,
router: Arc<Router<Route>>, router: Arc<Router<Route>>,
} }
impl Server { impl App {
pub fn new(logger: Logger, engine: Engine) -> Server { pub fn new(logger: Logger, engine: Engine) -> App {
let mut router = Router::new(); let mut router = Router::new();
router.add("/", Route::Index); router.add("/", Route::Index);
@ -74,21 +73,14 @@ impl Server {
Route::CrateStatus(StatusFormat::Svg), Route::CrateStatus(StatusFormat::Svg),
); );
Server { App {
logger, logger,
engine, engine,
router: Arc::new(router), router: Arc::new(router),
} }
} }
}
impl Service for Server { pub async fn handle(&self, req: Request<Body>) -> Result<Response<Body>, HyperError> {
type ReqBody = Body;
type ResBody = Body;
type Error = hyper::Error;
type Future = Box<dyn Future<Item = Response<Self::ResBody>, Error = Self::Error> + Send>;
fn call(&mut self, req: Request<Self::ReqBody>) -> Self::Future {
let logger = self let logger = self
.logger .logger
.new(o!("http_path" => req.uri().path().to_owned())); .new(o!("http_path" => req.uri().path().to_owned()));
@ -97,188 +89,194 @@ impl Service for Server {
match route_match.handler { match route_match.handler {
&Route::Index => { &Route::Index => {
if *req.method() == Method::GET { if *req.method() == Method::GET {
return Box::new(self.index(req, route_match.params, logger)); return self.index(req, route_match.params, logger).await;
} }
} }
&Route::RepoStatus(format) => { &Route::RepoStatus(format) => {
if *req.method() == Method::GET { if *req.method() == Method::GET {
return Box::new(self.repo_status(req, route_match.params, logger, format)); return self
.repo_status(req, route_match.params, logger, format)
.await;
} }
} }
&Route::CrateStatus(format) => { &Route::CrateStatus(format) => {
if *req.method() == Method::GET { if *req.method() == Method::GET {
return Box::new(self.crate_status( return self
req, .crate_status(req, route_match.params, logger, format)
route_match.params, .await;
logger,
format,
));
} }
} }
&Route::CrateRedirect => { &Route::CrateRedirect => {
if *req.method() == Method::GET { if *req.method() == Method::GET {
return Box::new(self.crate_redirect(req, route_match.params, logger)); return self.crate_redirect(req, route_match.params, logger).await;
} }
} }
&Route::Static(file) => { &Route::Static(file) => {
if *req.method() == Method::GET { if *req.method() == Method::GET {
return Box::new(future::ok(Server::static_file(file))); return Ok(App::static_file(file));
} }
} }
} }
} }
let mut response = Response::builder(); Ok(Response::builder()
response.status(StatusCode::NOT_FOUND); .status(StatusCode::NOT_FOUND)
Box::new(future::ok(response.body(Body::empty()).unwrap())) .body(Body::empty())
.unwrap())
} }
} }
impl Server { impl App {
fn index( async fn index(
&self, &self,
_req: Request<Body>, _req: Request<Body>,
_params: Params, _params: Params,
logger: Logger, logger: Logger,
) -> impl Future<Item = Response<Body>, Error = HyperError> + Send { ) -> Result<Response<Body>, HyperError> {
self.engine let engine = self.engine.clone();
.get_popular_repos()
.join(self.engine.get_popular_crates()) let popular =
.then(move |popular_result| match popular_result { future::try_join(engine.get_popular_repos(), engine.get_popular_crates()).await;
Err(err) => {
error!(logger, "error: {}", err); match popular {
let mut response = Err(err) => {
views::html::error::render("Could not retrieve popular items", ""); error!(logger, "error: {}", err);
*response.status_mut() = StatusCode::INTERNAL_SERVER_ERROR; let mut response =
future::ok(response) views::html::error::render("Could not retrieve popular items", "");
} *response.status_mut() = StatusCode::INTERNAL_SERVER_ERROR;
Ok((popular_repos, popular_crates)) => { Ok(response)
future::ok(views::html::index::render(popular_repos, popular_crates)) }
} Ok((popular_repos, popular_crates)) => {
}) Ok(views::html::index::render(popular_repos, popular_crates))
}
}
} }
fn repo_status( async fn repo_status(
&self, &self,
_req: Request<Body>, _req: Request<Body>,
params: Params, params: Params,
logger: Logger, logger: Logger,
format: StatusFormat, format: StatusFormat,
) -> impl Future<Item = Response<Body>, Error = HyperError> + Send { ) -> Result<Response<Body>, HyperError> {
let server = self.clone(); let server = self.clone();
let site = params.find("site").expect("route param 'site' not found"); let site = params.find("site").expect("route param 'site' not found");
let qual = params.find("qual").expect("route param 'qual' not found"); let qual = params.find("qual").expect("route param 'qual' not found");
let name = params.find("name").expect("route param 'name' not found"); let name = params.find("name").expect("route param 'name' not found");
RepoPath::from_parts(site, qual, name) let repo_path_result = RepoPath::from_parts(site, qual, name);
.into_future()
.then(move |repo_path_result| match repo_path_result { match repo_path_result {
Err(err) => { Err(err) => {
error!(logger, "error: {}", err); error!(logger, "error: {}", err);
let mut response = views::html::error::render( let mut response = views::html::error::render(
"Could not parse repository path", "Could not parse repository path",
"Please make sure to provide a valid repository path.", "Please make sure to provide a valid repository path.",
); );
*response.status_mut() = StatusCode::BAD_REQUEST; *response.status_mut() = StatusCode::BAD_REQUEST;
future::Either::A(future::ok(response)) Ok(response)
}
Ok(repo_path) => {
let analyze_result = server
.engine
.analyze_repo_dependencies(repo_path.clone())
.await;
match analyze_result {
Err(err) => {
error!(logger, "error: {}", err);
let response =
App::status_format_analysis(None, format, SubjectPath::Repo(repo_path));
Ok(response)
}
Ok(analysis_outcome) => {
let response = App::status_format_analysis(
Some(analysis_outcome),
format,
SubjectPath::Repo(repo_path),
);
Ok(response)
}
} }
Ok(repo_path) => future::Either::B( }
server }
.engine
.analyze_repo_dependencies(repo_path.clone())
.then(move |analyze_result| match analyze_result {
Err(err) => {
error!(logger, "error: {}", err);
let response = Server::status_format_analysis(
None,
format,
SubjectPath::Repo(repo_path),
);
future::ok(response)
}
Ok(analysis_outcome) => {
let response = Server::status_format_analysis(
Some(analysis_outcome),
format,
SubjectPath::Repo(repo_path),
);
future::ok(response)
}
}),
),
})
} }
fn crate_redirect( async fn crate_redirect(
&self, &self,
_req: Request<Body>, _req: Request<Body>,
params: Params, params: Params,
logger: Logger, logger: Logger,
) -> impl Future<Item = Response<Body>, Error = HyperError> + Send { ) -> Result<Response<Body>, HyperError> {
let engine = self.engine.clone(); let engine = self.engine.clone();
let name = params.find("name").expect("route param 'name' not found"); let name = params.find("name").expect("route param 'name' not found");
let crate_name_result = name.parse::<CrateName>();
name.parse::<CrateName>() match crate_name_result {
.into_future() Err(err) => {
.then(move |crate_name_result| match crate_name_result { error!(logger, "error: {}", err);
Err(err) => { let mut response = views::html::error::render(
error!(logger, "error: {}", err); "Could not parse crate name",
let mut response = views::html::error::render( "Please make sure to provide a valid crate name.",
"Could not parse crate name", );
"Please make sure to provide a valid crate name.", *response.status_mut() = StatusCode::BAD_REQUEST;
); Ok(response)
*response.status_mut() = StatusCode::BAD_REQUEST; }
future::Either::A(future::ok(response))
Ok(crate_name) => {
let release_result = engine
.find_latest_crate_release(crate_name, VersionReq::any())
.await;
match release_result {
Err(err) => {
error!(logger, "error: {}", err);
let mut response = views::html::error::render(
"Could not fetch crate information",
"Please make sure to provide a valid crate name.",
);
*response.status_mut() = StatusCode::NOT_FOUND;
Ok(response)
}
Ok(None) => {
let mut response = views::html::error::render(
"Could not fetch crate information",
"Please make sure to provide a valid crate name.",
);
*response.status_mut() = StatusCode::NOT_FOUND;
Ok(response)
}
Ok(Some(release)) => {
let redirect_url = format!(
"{}/crate/{}/{}",
&SELF_BASE_URL as &str,
release.name.as_ref(),
release.version
);
let res = Response::builder()
.status(StatusCode::TEMPORARY_REDIRECT)
.header(LOCATION, redirect_url)
.body(Body::empty())
.unwrap();
Ok(res)
}
} }
Ok(crate_name) => future::Either::B( }
engine }
.find_latest_crate_release(crate_name, VersionReq::any())
.then(move |release_result| match release_result {
Err(err) => {
error!(logger, "error: {}", err);
let mut response = views::html::error::render(
"Could not fetch crate information",
"Please make sure to provide a valid crate name.",
);
*response.status_mut() = StatusCode::NOT_FOUND;
future::ok(response)
}
Ok(None) => {
let mut response = views::html::error::render(
"Could not fetch crate information",
"Please make sure to provide a valid crate name.",
);
*response.status_mut() = StatusCode::NOT_FOUND;
future::ok(response)
}
Ok(Some(release)) => {
let mut response = Response::builder();
response.status(StatusCode::TEMPORARY_REDIRECT);
let url = format!(
"{}/crate/{}/{}",
&SELF_BASE_URL as &str,
release.name.as_ref(),
release.version
);
response.header(LOCATION, url);
let response = response.body(Body::empty()).unwrap();
future::ok(response)
}
}),
),
})
} }
fn crate_status( async fn crate_status(
&self, &self,
_req: Request<Body>, _req: Request<Body>,
params: Params, params: Params,
logger: Logger, logger: Logger,
format: StatusFormat, format: StatusFormat,
) -> impl Future<Item = Response<Body>, Error = HyperError> + Send { ) -> Result<Response<Body>, HyperError> {
let server = self.clone(); let server = self.clone();
let name = params.find("name").expect("route param 'name' not found"); let name = params.find("name").expect("route param 'name' not found");
@ -286,43 +284,46 @@ impl Server {
.find("version") .find("version")
.expect("route param 'version' not found"); .expect("route param 'version' not found");
CratePath::from_parts(name, version) let crate_path_result = CratePath::from_parts(name, version);
.into_future()
.then(move |crate_path_result| match crate_path_result { match crate_path_result {
Err(err) => { Err(err) => {
error!(logger, "error: {}", err); error!(logger, "error: {}", err);
let mut response = views::html::error::render( let mut response = views::html::error::render(
"Could not parse crate path", "Could not parse crate path",
"Please make sure to provide a valid crate name and version.", "Please make sure to provide a valid crate name and version.",
); );
*response.status_mut() = StatusCode::BAD_REQUEST; *response.status_mut() = StatusCode::BAD_REQUEST;
future::Either::A(future::ok(response)) Ok(response)
}
Ok(crate_path) => {
let analyze_result = server
.engine
.analyze_crate_dependencies(crate_path.clone())
.await;
match analyze_result {
Err(err) => {
error!(logger, "error: {}", err);
let response = App::status_format_analysis(
None,
format,
SubjectPath::Crate(crate_path),
);
Ok(response)
}
Ok(analysis_outcome) => {
let response = App::status_format_analysis(
Some(analysis_outcome),
format,
SubjectPath::Crate(crate_path),
);
Ok(response)
}
} }
Ok(crate_path) => future::Either::B( }
server }
.engine
.analyze_crate_dependencies(crate_path.clone())
.then(move |analyze_result| match analyze_result {
Err(err) => {
error!(logger, "error: {}", err);
let response = Server::status_format_analysis(
None,
format,
SubjectPath::Crate(crate_path),
);
future::ok(response)
}
Ok(analysis_outcome) => {
let response = Server::status_format_analysis(
Some(analysis_outcome),
format,
SubjectPath::Crate(crate_path),
);
future::ok(response)
}
}),
),
})
} }
fn status_format_analysis( fn status_format_analysis(

View file

@ -8,7 +8,7 @@ pub mod error;
pub mod index; pub mod index;
pub mod status; pub mod status;
use super::super::SELF_BASE_URL; use crate::server::SELF_BASE_URL;
fn render_html<B: Render>(title: &str, body: B) -> Response<Body> { fn render_html<B: Render>(title: &str, body: B) -> Response<Body> {
let rendered = html! { let rendered = html! {

View file

@ -6,8 +6,7 @@ use crate::engine::AnalyzeDependenciesOutcome;
use crate::models::crates::{AnalyzedDependencies, AnalyzedDependency, CrateName}; use crate::models::crates::{AnalyzedDependencies, AnalyzedDependency, CrateName};
use crate::models::repo::RepoSite; use crate::models::repo::RepoSite;
use crate::models::SubjectPath; use crate::models::SubjectPath;
use crate::server::views::badge;
use super::super::badge;
fn dependency_tables(crate_name: CrateName, deps: AnalyzedDependencies) -> Markup { fn dependency_tables(crate_name: CrateName, deps: AnalyzedDependencies) -> Markup {
html! { html! {

View file

@ -1,29 +1,31 @@
use std::fmt::{Debug, Formatter, Result as FmtResult}; use std::{
use std::hash::Hash; fmt::{Debug, Formatter, Result as FmtResult},
use std::ops::Deref; hash::Hash,
use std::sync::Mutex; sync::Mutex,
use std::time::{Duration, Instant}; task::Context,
task::Poll,
time::{Duration, Instant},
};
use anyhow::{anyhow, ensure, Error}; use anyhow::Error;
use futures::future::{FromErr, Shared, SharedItem}; use hyper::service::Service;
use futures::{Future, Poll};
use lru_cache::LruCache; use lru_cache::LruCache;
use tokio_service::Service;
pub struct Cache<S> pub struct Cache<S, Req>
where where
S: Service<Error = Error>, S: Service<Req>,
S::Request: Hash + Eq, Req: Hash + Eq,
{ {
inner: S, inner: S,
duration: Duration, duration: Duration,
cache: Mutex<LruCache<S::Request, (Instant, Shared<FromErr<S::Future, Error>>)>>, #[allow(unused)]
cache: Mutex<LruCache<Req, (Instant, S::Response)>>,
} }
impl<S> Debug for Cache<S> impl<S, Req> Debug for Cache<S, Req>
where where
S: Service<Error = Error> + Debug, S: Service<Req> + Debug,
S::Request: Hash + Eq, Req: Hash + Eq,
{ {
fn fmt(&self, fmt: &mut Formatter<'_>) -> FmtResult { fn fmt(&self, fmt: &mut Formatter<'_>) -> FmtResult {
fmt.debug_struct("Cache") fmt.debug_struct("Cache")
@ -33,12 +35,12 @@ where
} }
} }
impl<S> Cache<S> impl<S, Req> Cache<S, Req>
where where
S: Service<Error = Error>, S: Service<Req>,
S::Request: Hash + Eq, Req: Hash + Eq,
{ {
pub fn new(service: S, duration: Duration, capacity: usize) -> Cache<S> { pub fn new(service: S, duration: Duration, capacity: usize) -> Cache<S, Req> {
Cache { Cache {
inner: service, inner: service,
duration: duration, duration: duration,
@ -47,63 +49,62 @@ where
} }
} }
impl<S> Service for Cache<S> impl<S, Req> Service<Req> for Cache<S, Req>
where where
S: Service<Error = Error>, S: Service<Req, Error = Error>,
S::Request: Clone + Hash + Eq, S::Response: Clone,
Req: Clone + Hash + Eq,
{ {
type Request = S::Request; type Response = S::Response;
type Response = CachedItem<S::Response>;
type Error = Error; type Error = Error;
type Future = Cached<S::Future>; // WAS: type Future = Cached<S::Future>;
// type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>>>>;
type Future = S::Future;
fn call(&self, req: Self::Request) -> Self::Future { fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
let now = Instant::now(); Poll::Ready(Ok(()))
let mut cache = self.cache.lock().expect("lock poisoned"); }
if let Some(&mut (valid_until, ref shared_future)) = cache.get_mut(&req) {
if valid_until > now { fn call(&mut self, req: Req) -> Self::Future {
if let Some(Ok(_)) = shared_future.peek() { // TODO: re-add caching
return Cached(shared_future.clone()); // Box::pin({
} // let now = Instant::now();
} // let mut cache = self.cache.lock().expect("lock poisoned");
}
let shared_future = self.inner.call(req.clone()).from_err().shared(); // if let Some(&mut (valid_until, ref cached_response)) = cache.get_mut(&req) {
cache.insert(req, (now + self.duration, shared_future.clone())); // if valid_until > now {
Cached(shared_future) // return Box::pin(ok(cached_response.clone()));
// }
// }
self.inner.call(req.clone())
// .and_then(|response| {
// // cache.insert(req, (now + self.duration, response.clone()));
// ok(response)
// })
// })
} }
} }
pub struct Cached<F: Future<Error = Error>>(Shared<FromErr<F, Error>>); // pub struct Cached<F: Future>(Shared<F>);
impl<F> Debug for Cached<F> // impl<F> Debug for Cached<F>
where // where
F: Future<Error = Error> + Debug, // F: Future + Debug,
F::Item: Debug, // F::Output: Debug,
{ // {
fn fmt(&self, fmt: &mut Formatter<'_>) -> FmtResult { // fn fmt(&self, fmt: &mut Formatter<'_>) -> FmtResult {
self.0.fmt(fmt) // self.0.fmt(fmt)
} // }
} // }
impl<F: Future<Error = Error>> Future for Cached<F> { // // WAS: impl<F: Future<Error = Error>> Future for Cached<F> {
type Item = CachedItem<F::Item>; // impl<F: Future> Future for Cached<F> {
type Error = Error; // type Output = Result<F::Output, Error>;
fn poll(&mut self) -> Poll<Self::Item, Self::Error> { // fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
self.0 // self.0
.poll() // .poll()
.map_err(|_err| anyhow!("TODO: shared error not clone-able")) // .map_err(|_err| anyhow!("TODO: shared error not clone-able"))
.map(|item| item.map(CachedItem)) // }
} // }
}
#[derive(Debug)]
pub struct CachedItem<T>(SharedItem<T>);
impl<T> Deref for CachedItem<T> {
type Target = T;
fn deref(&self) -> &T {
&self.0.deref()
}
}