From ca77851b494a6b2db3cfcbff93825a0060f3c570 Mon Sep 17 00:00:00 2001 From: Naim A <227396+naim94a@users.noreply.github.com> Date: Sun, 24 Jun 2018 13:20:18 +0300 Subject: [PATCH] WIP: major changes; * using RwLock instead of Mutex. * Added WebServer for API & http tracker using hyper. * added iterator trait for StackVec. * removed benchmarks since they require nightly rust. --- Cargo.lock | 514 +++++++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 3 + README.md | 56 +++--- src/main.rs | 13 +- src/server.rs | 122 ++++------- src/stackvec.rs | 53 ++--- src/tracker.rs | 224 +++++++++++++++++---- src/webserver.rs | 277 +++++++++++++++++++++++++ 8 files changed, 1076 insertions(+), 186 deletions(-) create mode 100644 src/webserver.rs diff --git a/Cargo.lock b/Cargo.lock index 381c65f..f461842 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,3 +1,11 @@ +[[package]] +name = "arrayvec" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "nodrop 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "bincode" version = "1.0.0" @@ -7,11 +15,251 @@ dependencies = [ "serde 1.0.66 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "bitflags" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "byteorder" version = "1.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "bytes" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "byteorder 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "cfg-if" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "crossbeam-deque" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "crossbeam-epoch 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "arrayvec 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "memoffset 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "crossbeam-utils" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "fnv" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "fuchsia-zircon" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bitflags 1.0.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)", +] + +[[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.21" +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.21 (registry+https://github.com/rust-lang/crates.io-index)", + "num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "h2" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "byteorder 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes 0.4.8 (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.21 (registry+https://github.com/rust-lang/crates.io-index)", + "http 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "indexmap 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "slab 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "string 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "http" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bytes 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "httparse" +version = "1.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "hyper" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bytes 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.21 (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.10 (registry+https://github.com/rust-lang/crates.io-index)", + "http 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "httparse 1.2.4 (registry+https://github.com/rust-lang/crates.io-index)", + "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "net2 0.2.32 (registry+https://github.com/rust-lang/crates.io-index)", + "time 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-executor 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-reactor 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-tcp 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-timer 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", + "want 0.0.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "indexmap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "iovec" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.42 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.2.8 (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.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "lazycell" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "libc" +version = "0.2.42" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "log" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "memoffset" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "mio" +version = "0.6.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "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.2 (registry+https://github.com/rust-lang/crates.io-index)", + "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "lazycell 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.42 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.2 (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.32 (registry+https://github.com/rust-lang/crates.io-index)", + "slab 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "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.32 (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 = "net2" +version = "0.2.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.42 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "nodrop" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "num_cpus" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.42 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "proc-macro2" version = "0.4.6" @@ -28,6 +276,26 @@ dependencies = [ "proc-macro2 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "rand" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.42 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "redox_syscall" +version = "0.1.40" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "scopeguard" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "serde" version = "1.0.66" @@ -43,6 +311,16 @@ dependencies = [ "syn 0.14.2 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "slab" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "string" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "syn" version = "0.14.2" @@ -53,13 +331,148 @@ dependencies = [ "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "time" +version = "0.1.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.42 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_syscall 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "mio 0.6.14 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-executor 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-fs 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-reactor 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-tcp 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-threadpool 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-timer 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-udp 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio-codec" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bytes 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio-executor" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio-fs" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-threadpool 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio-io" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bytes 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio-reactor" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "mio 0.6.14 (registry+https://github.com/rust-lang/crates.io-index)", + "slab 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-executor 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio-tcp" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bytes 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "mio 0.6.14 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-reactor 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio-threadpool" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "crossbeam-deque 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-executor 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio-timer" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-executor 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio-udp" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bytes 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "mio 0.6.14 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-codec 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-reactor 0.1.2 (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 = "udpt-rs" version = "0.1.0" dependencies = [ "bincode 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "hyper 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.66 (registry+https://github.com/rust-lang/crates.io-index)", "serde_derive 1.0.66 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -67,12 +480,113 @@ name = "unicode-xid" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "want" +version = "0.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "try-lock 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "winapi" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "winapi" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "winapi-build" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "ws2_32-sys" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + [metadata] +"checksum arrayvec 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)" = "a1e964f9e24d588183fcb43503abda40d288c8657dfc27311516ce2f05675aef" "checksum bincode 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bda13183df33055cbb84b847becce220d392df502ebe7a4a78d7021771ed94d0" +"checksum bitflags 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "d0c54bb8f454c567f21197eefcdbf5679d0bd99f2ddbe52e84c77061952e6789" "checksum byteorder 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "74c0b906e9446b0a2e4f760cdb3fa4b2c48cdc6db8766a845c54b6ff063fd2e9" +"checksum bytes 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "7dd32989a66957d3f0cba6588f15d4281a733f4e9ffc43fcd2385f57d3bf99ff" +"checksum cfg-if 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "405216fd8fe65f718daa7102ea808a946b6ce40c742998fbfd3463645552de18" +"checksum crossbeam-deque 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "fe8153ef04a7594ded05b427ffad46ddeaf22e63fd48d42b3e1e3bb4db07cae7" +"checksum crossbeam-epoch 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2af0e75710d6181e234c8ecc79f14a97907850a541b13b0be1dd10992f2e4620" +"checksum crossbeam-utils 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d636a8b3bcc1b409d7ffd3facef8f21dcb4009626adbd0c5e6c4305c07253c7b" +"checksum fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "2fad85553e09a6f881f739c29f0b00b0f01357c743266d478b68951ce23285f3" +"checksum fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" +"checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" +"checksum futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)" = "1a70b146671de62ec8c8ed572219ca5d594d9b06c0b364d5e67b722fc559b48c" +"checksum futures-cpupool 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "ab90cde24b3319636588d0c35fe03b1333857621051837ed769faefb4c2162e4" +"checksum h2 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "6229ac66d3392dd83288fe04defd4b353354b15bbe07820d53dda063a736afcc" +"checksum http 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "6861b042450b6333fa7212b6edffc2d6df22579042817d59d49f4f8afbaaaf74" +"checksum httparse 1.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "c2f407128745b78abc95c0ffbe4e5d37427fdc0d45470710cfef8c44522a2e37" +"checksum hyper 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)" = "ad39a4f15051ccd4ea6adf44df851e00fd9062c71734391d806246b94e69dc1f" +"checksum indexmap 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "08173ba1e906efb6538785a8844dd496f5d34f0a2d88038e95195172fc667220" +"checksum iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dbe6e417e7d0975db6512b90796e8ce223145ac4e33c377e4a42882a0e88bb08" +"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" +"checksum lazy_static 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e6412c5e2ad9584b0b8e979393122026cdd6d2a80b933f890dcd694ddbe73739" +"checksum lazycell 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a6f08839bc70ef4a3fe1d566d5350f519c5912ea86be0df1740a7d247c7fc0ef" +"checksum libc 0.2.42 (registry+https://github.com/rust-lang/crates.io-index)" = "b685088df2b950fccadf07a7187c8ef846a959c142338a48f9dc0b94517eb5f1" +"checksum log 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6fddaa003a65722a7fb9e26b0ce95921fe4ba590542ced664d8ce2fa26f9f3ac" +"checksum memoffset 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0f9dc261e2b62d7a622bf416ea3c5245cdd5d9a7fcc428c0d06804dfce1775b3" +"checksum mio 0.6.14 (registry+https://github.com/rust-lang/crates.io-index)" = "6d771e3ef92d58a8da8df7d6976bfca9371ed1de6619d9d5a5ce5b1f29b85bfe" +"checksum miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919" +"checksum net2 0.2.32 (registry+https://github.com/rust-lang/crates.io-index)" = "9044faf1413a1057267be51b5afba8eb1090bd2231c693664aa1db716fe1eae0" +"checksum nodrop 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "9a2228dca57108069a5262f2ed8bd2e82496d2e074a06d1ccc7ce1687b6ae0a2" +"checksum num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c51a3322e4bca9d212ad9a158a02abc6934d005490c054a2778df73a70aa0a30" "checksum proc-macro2 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "effdb53b25cdad54f8f48843d67398f7ef2e14f12c1b4cb4effc549a6462a4d6" "checksum quote 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "e44651a0dc4cdd99f71c83b561e221f714912d11af1a4dff0631f923d53af035" +"checksum rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "eba5f8cb59cc50ed56be8880a5c7b496bfd9bd26394e176bc67884094145c2c5" +"checksum redox_syscall 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)" = "c214e91d3ecf43e9a4e41e578973adeb14b474f2bee858742d127af75a0112b1" +"checksum scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "94258f53601af11e6a49f722422f6e3425c52b06245a5cf9bc09908b174f5e27" "checksum serde 1.0.66 (registry+https://github.com/rust-lang/crates.io-index)" = "e9a2d9a9ac5120e0f768801ca2b58ad6eec929dc9d1d616c162f208869c2ce95" "checksum serde_derive 1.0.66 (registry+https://github.com/rust-lang/crates.io-index)" = "0a90213fa7e0f5eac3f7afe2d5ff6b088af515052cc7303bd68c7e3b91a3fb79" +"checksum slab 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fdeff4cd9ecff59ec7e3744cbca73dfe5ac35c2aedb2cfba8a1c715a18912e9d" +"checksum string 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "31f98b200e7caca9efca50fc0aa69cd58a5ec81d5f6e75b2f3ecaad2e998972a" "checksum syn 0.14.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c67da57e61ebc7b7b6fff56bb34440ca3a83db037320b0507af4c10368deda7d" +"checksum time 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)" = "d825be0eb33fda1a7e68012d51e9c7f451dc1a69391e7fdc197060bb8c56667b" +"checksum tokio 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "8ee337e5f4e501fc32966fec6fe0ca0cc1c237b0b1b14a335f8bfe3c5f06e286" +"checksum tokio-codec 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "881e9645b81c2ce95fcb799ded2c29ffb9f25ef5bef909089a420e5961dd8ccb" +"checksum tokio-executor 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "8cac2a7883ff3567e9d66bb09100d09b33d90311feca0206c7ca034bc0c55113" +"checksum tokio-fs 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "fc42bae2f6e33865b99069d95bcddfc85c9f0849b4e7e7399eeee71956ef34d7" +"checksum tokio-io 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "a5c9635ee806f26d302b8baa1e145689a280d8f5aa8d0552e7344808da54cc21" +"checksum tokio-reactor 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e00ec63bbec2c97ce1178cb0587b2c438b2f6b09d3ee54a33c45a9cf0d530810" +"checksum tokio-tcp 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ec9b094851aadd2caf83ba3ad8e8c4ce65a42104f7b94d9e6550023f0407853f" +"checksum tokio-threadpool 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "b3c3873a6d8d0b636e024e77b9a82eaab6739578a06189ecd0e731c7308fbc5d" +"checksum tokio-timer 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "028b94314065b90f026a21826cffd62a4e40a92cda3e5c069cc7b02e5945f5e9" +"checksum tokio-udp 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "43eb534af6e8f37d43ab1b612660df14755c42bd003c5f8d2475ee78cc4600c0" +"checksum try-lock 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e604eb7b43c06650e854be16a2a03155743d3752dd1c943f6829e26b7a36e382" "checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" +"checksum want 0.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "2fffe09593e18ed34950d66dbf44c27deb2e03f3905c493f0641f9f99a3f2349" +"checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" +"checksum winapi 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "773ef9dcc5f24b7d850d0ff101e542ff24c3b090a9768e03ff889fdef41f00fd" +"checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" +"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" +"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +"checksum ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e" diff --git a/Cargo.toml b/Cargo.toml index 5da04ed..fb9e110 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,3 +11,6 @@ lto = true serde = "1.0.66" bincode = "1.0.0" serde_derive = "1.0.66" +hyper = "0.12" +futures = "0.1.21" +tokio = "0.1.7" \ No newline at end of file diff --git a/README.md b/README.md index 2464c7e..e4e1644 100644 --- a/README.md +++ b/README.md @@ -1,25 +1,31 @@ -# UDPT -_UDPT_ is a UDP based torrent tracker which fully implements [BEP-15](http://www.bittorrent.org/beps/bep_0015.html) & [BEP-41](http://www.bittorrent.org/beps/bep_0041.html). - -This project was written in Rust, it is a complete rewrite of a previous C/C++ UDPT project (which is still currently available in the `master` branch of the repository). - -## Features -* UDP torrent tracking server -* In memory database -* Choice of Dynamic/Static/Private tracker modes -* Ability to block a torrent from being tracked -* HTTP REST API for management -* Logging -* Windows Service or Linux/Unix daemon - -## Getting started -This rewrite is currently still under development and shouldn't be used at the moment. -If you'd like to contribute in making everything in the "Features" list come true, please feel free to submit a pull-request. - -Since we are using Rust, getting started is fairly easy: -```commandline -git clone https://github.com/naim94a/udpt.git -cd udpt -git checkout udpt-rs -cargo build -``` +# UDPT +_UDPT_ is a UDP based torrent tracker which fully implements [BEP-15](http://www.bittorrent.org/beps/bep_0015.html) & [BEP-41](http://www.bittorrent.org/beps/bep_0041.html). + +This project was written in Rust, it is a complete rewrite of a previous C/C++ UDPT project (which is still currently available in the `master` branch of the repository). + +## Features +* UDP torrent tracking server +* In memory database +* Choice of Dynamic/Static/Private tracker modes +* Ability to block a torrent from being tracked +* HTTP REST API for management +* Logging +* Windows Service or Linux/Unix daemon + +## Getting started +This rewrite is currently still under development and shouldn't be used at the moment. +If you'd like to contribute in making everything in the "Features" list come true, please feel free to submit a pull-request. + +Since we are using Rust, getting started is fairly easy: +```commandline +git clone https://github.com/naim94a/udpt.git +cd udpt +git checkout udpt-rs +cargo build +``` + +## Why was this project rewritten in rust? +For a few reasons, +1. Rust makes it harder to make mistakes than C/C++. +2. Rust allows easier cross-platform development with it's powerful standard library. +3. Integrated tests and benchmarks. diff --git a/src/main.rs b/src/main.rs index ef289a9..c9fdd38 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,15 +1,24 @@ extern crate bincode; extern crate serde; #[macro_use] extern crate serde_derive; +extern crate hyper; +extern crate futures; mod server; mod tracker; +mod stackvec; +mod webserver; fn main() { - let mut tracker = tracker::TorrentTracker::new(); + let tracker = std::sync::Arc::new(tracker::TorrentTracker::new()); let addr = "0.0.0.0:1212"; - let mut s = server::UDPTracker::new(addr, &mut tracker).unwrap(); + let s = std::sync::Arc::new(server::UDPTracker::new(addr, tracker.clone()).unwrap()); + + use std::str::FromStr; + let sa = std::net::SocketAddrV4::from_str("0.0.0.0:1213").unwrap(); + webserver::start_server(std::net::SocketAddr::from(sa), tracker.clone(), "myt0k3n"); + loop { match s.accept_packet() { Err(e) => { diff --git a/src/server.rs b/src/server.rs index 1afa8db..5357dd4 100644 --- a/src/server.rs +++ b/src/server.rs @@ -35,7 +35,7 @@ fn pack(data: &T) -> Option> { bo.big_endian(); match bo.serialize(data) { - Ok(bytes) => Some(bytes), + Ok(v) => Some(v), Err(_) => None, } } @@ -94,13 +94,13 @@ struct UDPAnnounceResponse { seeders: u32, } -pub struct UDPTracker<'a> { +pub struct UDPTracker { server: std::net::UdpSocket, - tracker: &'a mut tracker::TorrentTracker, + tracker: std::sync::Arc, } -impl<'a> UDPTracker<'a> { - pub fn new(bind_address: T, tracker: &mut tracker::TorrentTracker) -> Result { +impl UDPTracker { + pub fn new(bind_address: T, tracker: std::sync::Arc) -> Result { let server = match UdpSocket::bind(bind_address) { Ok(s) => s, Err(e) => { @@ -114,7 +114,7 @@ impl<'a> UDPTracker<'a> { }) } - fn handle_packet(&mut self, remote_address: &SocketAddr, payload: &[u8]) { + fn handle_packet(&self, remote_address: &SocketAddr, payload: &[u8]) { let header : UDPRequestHeader = match unpack(payload) { Some(val) => val, None => { @@ -154,7 +154,7 @@ impl<'a> UDPTracker<'a> { } } - fn handle_announce(&mut self, remote_addr: &SocketAddr, header: &UDPRequestHeader, payload: &[u8]) { + fn handle_announce(&self, remote_addr: &SocketAddr, header: &UDPRequestHeader, payload: &[u8]) { if header.connection_id != self.get_connection_id(remote_addr) { return; } @@ -182,23 +182,31 @@ impl<'a> UDPTracker<'a> { } let client_addr = SocketAddr::new(remote_addr.ip(), packet.port); - match self.tracker.get_torrent(&packet.info_hash, |torrent: &mut tracker::TorrentEntry | -> Result, &str> { - if torrent.is_flagged() { - return Err("Torrent has been flagged."); - } - torrent.update_peer(&packet.peer_id, &client_addr, packet.uploaded, packet.downloaded, packet.left, packet.event); - let stats = torrent.get_stats(); - let peers = torrent.get_peers(&client_addr); - if let Some(mut payload) = pack(&UDPAnnounceResponse { - header: UDPResponseHeader { - action: Actions::Announce, - transaction_id: packet.header.transaction_id, - }, - seeders: stats.0 as u32, - interval: 20, - leechers: stats.2 as u32, - }) { + match self.tracker.update_torrent_and_get_stats(&packet.info_hash, &packet.peer_id, &client_addr, packet.uploaded, packet.downloaded, packet.left, packet.event) { + tracker::TorrentStats::Stats {leechers, complete, seeders} => { + let peers = match self.tracker.get_torrent_peers(&packet.info_hash, &client_addr) { + Some(v) => v, + None => { + return; + } + }; + + let mut payload = match pack(&UDPAnnounceResponse { + header: UDPResponseHeader { + action: Actions::Announce, + transaction_id: packet.header.transaction_id, + }, + seeders, + interval: 20, + leechers, + }) { + Some(v) => v, + None => { + return; + } + }; + for peer in peers { match peer { SocketAddr::V4(ipv4) => { @@ -211,70 +219,22 @@ impl<'a> UDPTracker<'a> { let port_hton = client_addr.port().to_be(); payload.extend(&[(port_hton & 0xff) as u8, ((port_hton >> 8) & 0xff) as u8]); + } - return Ok(payload); - } - } - return Err(""); - }) { - Some(error_message) => { - match error_message { - Err(err) => { - if err.len() > 0 { - self.send_error(remote_addr, header, err); - } - }, - Ok(payload) => { - // send packet... - let _ = self.send_packet(remote_addr, payload.as_slice()); - } - } + let _ = self.send_packet(&client_addr, payload.as_slice()); }, - None => { - self.send_error(remote_addr, header, "Unregistered torrent"); + tracker::TorrentStats::TorrentFlagged => { + self.send_error(&client_addr, &packet.header, "torrent flagged."); + return; + }, + tracker::TorrentStats::TorrentNotRegistered => { + self.send_error(&client_addr, &packet.header, "torrent not registered."); return; } - }; - /* - if torrent.is_flagged() { - error = Some("Torrent was flagged"); - } else { - torrent.update_peer(&packet.peer_id, &client_addr, packet.uploaded, packet.downloaded, packet.left, packet.event); - - let stats = torrent.get_stats(); - let peers = torrent.get_peers(&client_addr); - - if let Some(mut payload) = pack(&UDPAnnounceResponse { - header: UDPResponseHeader { - action: Actions::Announce, - transaction_id: packet.header.transaction_id, - }, - seeders: stats.0 as u32, - interval: 20, - leechers: stats.2 as u32, - }) { - for peer in peers { - match peer { - SocketAddr::V4(ipv4) => { - payload.extend(&ipv4.ip().octets()); - }, - SocketAddr::V6(ipv6) => { - payload.extend(&ipv6.ip().octets()); - } - }; - - let port_hton = client_addr.port().to_be(); - payload.extend(&[(port_hton & 0xff) as u8, ((port_hton >> 8) & 0xff) as u8]); - } - } } - - if let Some(error_message) = error { - self.send_error(remote_addr, &packet.header, error_message); - }*/ } - fn handle_scrape(&self, remote_addr: &SocketAddr, header: &UDPRequestHeader, payload: &[u8]) { + fn handle_scrape(&self, remote_addr: &SocketAddr, header: &UDPRequestHeader, _payload: &[u8]) { if header.connection_id != self.get_connection_id(remote_addr) { return; } @@ -309,7 +269,7 @@ impl<'a> UDPTracker<'a> { } } - pub fn accept_packet(&mut self) -> Result<(), std::io::Error> { + pub fn accept_packet(&self) -> Result<(), std::io::Error> { let mut packet = [0u8; MAX_PACKET_SIZE]; match self.server.recv_from(&mut packet) { Ok((size, remote_address)) => { diff --git a/src/stackvec.rs b/src/stackvec.rs index 13b12ee..218df0f 100644 --- a/src/stackvec.rs +++ b/src/stackvec.rs @@ -12,12 +12,23 @@ impl<'a, T> StackVec<'a, T> { length: 0, } } -} -impl<'a, T> StackVec<'a, T> { pub fn len(&self) -> usize { self.length } + + pub fn as_slice(&self) -> &[T] { + &self.data[0..self.length] + } +} + +impl<'a, T> Extend for StackVec<'a, T> { + fn extend>(&mut self, iter: I) { + for item in iter { + self.data[self.length] = item; + self.length += 1; + } + } } impl<'a> io::Write for StackVec<'a, u8> { @@ -26,7 +37,7 @@ impl<'a> io::Write for StackVec<'a, u8> { // not enough space on buffer. return Err(io::Error::from(io::ErrorKind::WriteZero)); } - let mut writable = &mut self.data[self.length..][0..buf.len()]; + let writable = &mut self.data[self.length..][0..buf.len()]; writable.copy_from_slice(buf); self.length += buf.len(); Ok(buf.len()) @@ -52,40 +63,4 @@ mod tests { } assert_eq!(buf[1] as char, 'e'); } - - fn add_values(vec: &mut T) { - for i in 0..BUF_LEN { - assert!(vec.write(&[((i % 256) & 0xff) as u8]).is_ok()); - } - } - - use test::Bencher; - - const BUF_LEN : usize = 1024 * 1024 * 1; // 10MB - - #[bench] - fn vec_stack(bencher: &mut Bencher) { - bencher.iter(|| { - let mut buff = [0u8; BUF_LEN]; - let mut v = StackVec::from(&mut buff); - add_values(&mut v); - }); - } - - #[bench] - fn vec_heap(bencher: &mut Bencher) { - bencher.iter(|| { - let mut v = Vec::new(); - - add_values(&mut v); - }); - } - - #[bench] - fn vec_heap_cap(bencher: &mut Bencher) { - bencher.iter(|| { - let mut v = Vec::with_capacity(BUF_LEN); - add_values(&mut v); - }); - } } \ No newline at end of file diff --git a/src/tracker.rs b/src/tracker.rs index 84f7626..7e2f7c2 100644 --- a/src/tracker.rs +++ b/src/tracker.rs @@ -23,8 +23,66 @@ struct TorrentPeer { updated: std::time::SystemTime, } -type PeerId = [u8; 20]; -type InfoHash = [u8; 20]; +pub type PeerId = [u8; 20]; +pub type InfoHash = [u8; 20]; + +pub trait HexConv { + fn to_hex(&self) -> String; + fn from_hex(hex: &str) -> Option; +} + +impl HexConv for InfoHash { + fn to_hex(&self) -> String { + const HEX: &[char] = &['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f']; + let data = self; + let mut res = String::with_capacity(data.len() * 2); + for b in data { + res.push(HEX[((b >> 4) & 0x0fu8) as usize]); + res.push(HEX[(b & 0x0fu8) as usize]); + } + return res; + } + + fn from_hex(hex: &str) -> Option { + let mut res : InfoHash = [0u8; 20]; + if hex.len() != 2 * res.len() { + return None; + } + + let mut tmp = 0; + let mut idx = 0; + + for ch in hex.chars() { + if idx % 2 == 1 { + tmp <<= 4; + } + + let mut num = ch as u32; + if ch >= '0' && ch <= '9' { + num -= '0' as u32; + } else if ch >= 'a' && ch <= 'f' { + num -= 'a' as u32; + num += 10; + } else if ch >= 'A' && ch <= 'F' { + num -= 'A' as u32; + num += 10; + } else { + return None; + } + + tmp |= num & 0x0f; + + if idx % 2 == 1 { + res[(idx - 1) / 2] = tmp as u8; + tmp = 0; + } + + idx += 1; + } + + return Some(res); + } +} pub struct TorrentEntry { is_flagged: bool, @@ -35,6 +93,15 @@ pub struct TorrentEntry { } impl TorrentEntry { + pub fn new() -> TorrentEntry{ + TorrentEntry{ + is_flagged: false, + peers: std::collections::BTreeMap::new(), + completed: 0, + seeders: 0, + } + } + pub fn is_flagged(&self) -> bool { self.is_flagged } @@ -94,7 +161,7 @@ impl TorrentEntry { } struct TorrentDatabase { - torrent_peers: std::collections::BTreeMap, + torrent_peers: std::sync::RwLock>, } pub struct TorrentTracker { @@ -102,45 +169,63 @@ pub struct TorrentTracker { database: TorrentDatabase, } +pub enum TorrentStats { + TorrentFlagged, + TorrentNotRegistered, + Stats{ + seeders: u32, + leechers: u32, + complete: u32, + } +} + impl TorrentTracker { pub fn new() -> TorrentTracker { TorrentTracker{ mode: TrackerMode::DynamicMode, database: TorrentDatabase{ - torrent_peers: std::collections::BTreeMap::new(), + torrent_peers: std::sync::RwLock::new(std::collections::BTreeMap::new()), } } } /// Adding torrents is not relevant to dynamic trackers. - pub fn add_torrent(&mut self, info_hash: &InfoHash) { - self.database.torrent_peers.entry(*info_hash).or_insert(TorrentEntry{ - is_flagged: false, - peers: std::collections::BTreeMap::new(), - seeders: 0, - completed: 0, - }); + pub fn add_torrent(&self, info_hash: &InfoHash) -> Result<(), ()> { + let mut write_lock = self.database.torrent_peers.write().unwrap(); + match write_lock.entry(*info_hash) { + std::collections::btree_map::Entry::Vacant(ve) => { + ve.insert(TorrentEntry::new()); + return Ok(()); + }, + std::collections::btree_map::Entry::Occupied(entry) => { + return Err(()); + } + } } /// If the torrent is flagged, it will not be removed unless force is set to true. - pub fn remove_torrent(&mut self, info_hash: &InfoHash, force: bool) { - if !force { - if let Some(entry) = self.database.torrent_peers.get(info_hash) { - if entry.is_flagged { - // torrent is flagged, ignore request. - return; + pub fn remove_torrent(&self, info_hash: &InfoHash, force: bool) -> Result<(), ()> { + use std::collections::btree_map::Entry; + let mut entry_lock = self.database.torrent_peers.write().unwrap(); + let mut torrent_entry = entry_lock.entry(*info_hash); + match torrent_entry { + Entry::Vacant(_) => { + // no entry, nothing to do... + return Err(()); + }, + Entry::Occupied(entry) => { + if force || !entry.get().is_flagged() { + entry.remove(); + return Ok(()); } - } else { - // torrent not found, no point looking for it again... - return; - } + return Err(()); + }, } - self.database.torrent_peers.remove(info_hash); } /// flagged torrents will result in a tracking error. This is to allow enforcement against piracy. - pub fn set_torrent_flag(&mut self, info_hash: &InfoHash, is_flagged: bool) { - if let Some(mut entry) = self.database.torrent_peers.get_mut(info_hash) { + pub fn set_torrent_flag(&self, info_hash: &InfoHash, is_flagged: bool) { + if let Some(entry) = self.database.torrent_peers.write().unwrap().get_mut(info_hash) { if is_flagged && !entry.is_flagged { // empty peer list. entry.peers.clear(); @@ -149,23 +234,84 @@ impl TorrentTracker { } } - pub fn get_torrent(&mut self, info_hash: &InfoHash, action: F) -> Option - where F: Fn(&mut TorrentEntry) -> R - { - if let Some(torrent_entry) = self.database.torrent_peers.get_mut(info_hash) { - Some(action(torrent_entry)) - } else { - match self.mode { - TrackerMode::StaticMode => None, - TrackerMode::PrivateMode => None, - TrackerMode::DynamicMode => { - None - } + pub fn get_torrent_peers(&self, info_hash: &InfoHash, remote_addr: &std::net::SocketAddr) -> Option> { + let mut read_lock = self.database.torrent_peers.read().unwrap(); + match read_lock.get(info_hash) { + None => { + return None; } - } + Some(entry) => { + return Some(entry.get_peers(remote_addr)); + } + }; } - pub fn cleanup(&mut self) { - + pub fn update_torrent_and_get_stats(&self, info_hash: &InfoHash, peer_id: &PeerId, remote_address: &std::net::SocketAddr, uploaded: u64, downloaded: u64, left: u64, event: Events) -> TorrentStats { + use std::collections::btree_map::Entry; + let mut torrent_peers = self.database.torrent_peers.write().unwrap(); + let mut torrent_entry = match torrent_peers.entry(*info_hash) { + Entry::Vacant(vacant) => { + match self.mode { + TrackerMode::DynamicMode => { + vacant.insert(TorrentEntry::new()) + }, + _ => { + return TorrentStats::TorrentNotRegistered; + } + } + }, + Entry::Occupied(entry) => { + if entry.get().is_flagged() { + return TorrentStats::TorrentFlagged; + } + entry.into_mut() + }, + }; + + torrent_entry.update_peer(peer_id, remote_address, uploaded, downloaded, left, event); + + let (seeders, complete, leechers) = torrent_entry.get_stats(); + + return TorrentStats::Stats { + seeders, + leechers, + complete, + }; + } + + pub (crate) fn get_database(&self) -> std::sync::RwLockReadGuard>{ + self.database.torrent_peers.read().unwrap() } } + +#[cfg(test)] +mod tests { + use super::*; + + fn is_sync() {} + fn is_send() {} + + #[test] + fn tracker_send() { + is_send::(); + } + + #[test] + fn tracker_sync() { + is_sync::(); + } + + #[test] + fn test_ih2hex() { + let ih: InfoHash = [1u8, 2, 3, 4, 5, 6, 7, 8, 9, 0xa, + 0xb, 0xc, 0xd, 0xe, 0xf, 0x10, 0x11, 0x12, 0x13, 0xff]; + assert!(ih.to_hex() == "0102030405060708090a0b0c0d0e0f10111213ff"); + } + + #[test] + fn test_hex2ih() { + let ih = InfoHash::from_hex("0102030405060708090a0b0c0d0e0f10111213ff").unwrap(); + assert_eq!(ih, [1u8, 2, 3, 4, 5, 6, 7, 8, 9, 0xa, + 0xb, 0xc, 0xd, 0xe, 0xf, 0x10, 0x11, 0x12, 0x13, 0xff]); + } +} \ No newline at end of file diff --git a/src/webserver.rs b/src/webserver.rs new file mode 100644 index 0000000..ef6a20e --- /dev/null +++ b/src/webserver.rs @@ -0,0 +1,277 @@ +use std; +use hyper; +use futures::Future; +use futures::future::FutureResult; + +use std::net::SocketAddr; + +use tracker; +use tracker::TorrentTracker; + +enum APIError { + NoSuchMethod, + BadAPICall, + NotFound, + InvalidAccessToken, +} + +struct WebApplication { + tracker: std::sync::Arc, + token: std::sync::Arc, +} + +impl std::error::Error for APIError {} + +use std::fmt; +impl fmt::Debug for APIError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", "Hello world!") + } +} + +impl fmt::Display for APIError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", "Hello world!") + } +} + +impl WebApplication { + pub fn new(tracker: std::sync::Arc, token: String) -> WebApplication { + WebApplication{ + tracker, + token: std::sync::Arc::new(token), + } + } + + fn handle_root(&self, request: &hyper::Request) -> Result, APIError> { + Ok(hyper::Response::new(hyper::Body::from("https://naim94a.github.io/udpt"))) + } + + fn handle_announce(&self, request: &hyper::Request) -> Result, APIError> { + Err(APIError::NoSuchMethod) + } + + fn handle_scrape(&self, request: &hyper::Request) -> Result, APIError> { + Err(APIError::NoSuchMethod) + } + + fn handle_stats(&self, request: &hyper::Request) -> Result, APIError> { + Err(APIError::NoSuchMethod) + } + + fn parse_query(query: &str) -> std::collections::HashMap<&str, &str> { + let mut res = std::collections::HashMap::new(); + + let mut pair_start = 0; + + loop { + let remaining = &query[pair_start..]; + let pair_len = match remaining.find("&") { + Some(v) => v, + None => remaining.len(), + }; + + let pair_str = &remaining[..pair_len]; + + { + let key_end = match pair_str.find("=") { + Some(v) => v, + None => pair_str.len(), + }; + + let mut val_start = key_end + 1; + if val_start > pair_str.len() { + val_start = pair_str.len(); + } + + res.insert(&pair_str[..key_end], &pair_str[val_start..]); + } + + pair_start += pair_len + 1; + + if pair_start >= query.len() { + break; + } + } + + return res; + } + + fn handle_api(&self, request: &hyper::Request) -> Result, APIError> { + // before processing request, check client's access. + let partial = &request.uri().path()[4..]; // slice "/api" out... + + if partial == "/torrents" { + if let Some(q) = request.uri().query() { + let parsed_query = Self::parse_query(q); + match parsed_query.get("token") { + Some(&token) => { + if token != self.token.as_str() { + return Err(APIError::InvalidAccessToken); + } + }, + None => { + return Err(APIError::InvalidAccessToken); + } + } + + let action = match parsed_query.get("action") { + Some(&v) => v, + None => { + return Err(APIError::BadAPICall); + } + }; + if action == "list" { + let mut response = String::from("["); + let mut idx = 0; + + let db = self.tracker.get_database(); + for (info_hash, entry) in db.iter() { + use tracker::HexConv; + + if idx > 0 { + response += ", "; + } + response += "{\"info_hash\":\""; + response += info_hash.to_hex().as_str(); + response += "\"}"; + + idx += 1; + } + response += "]"; + + return Ok(hyper::Response::new(hyper::Body::from(response))); + } else if action == "add" { + use tracker::HexConv; + let info_hash: tracker::InfoHash = match parsed_query.get("info_hash") { + Some(&v) => { + match tracker::InfoHash::from_hex(v) { + Some(ih) => ih, + None => { + return Err(APIError::BadAPICall); + } + } + }, + None => { + return Err(APIError::BadAPICall); + } + }; + + match self.tracker.add_torrent(&info_hash) { + Ok(_) => { + return Ok(hyper::Response::new(hyper::Body::from("{\"ok\": 1}"))); + }, + Err(_) => { + let mut resp = hyper::Response::new(hyper::Body::from("{\"ok\": 0}")); + *resp.status_mut() = hyper::StatusCode::NOT_FOUND; + return Ok(resp); + } + } + } else if action == "remove" { + use tracker::HexConv; + let info_hash: tracker::InfoHash = match parsed_query.get("info_hash") { + Some(&v) => { + match tracker::InfoHash::from_hex(v) { + Some(ih) => ih, + None => { + return Err(APIError::BadAPICall); + } + } + }, + None => { + return Err(APIError::BadAPICall); + } + }; + + match self.tracker.remove_torrent(&info_hash, true) { + Ok(_) => { + return Ok(hyper::Response::new(hyper::Body::from("{\"ok\": 1}"))); + }, + Err(_) => { + let mut resp = hyper::Response::new(hyper::Body::from("{\"ok\": 0}")); + *resp.status_mut() = hyper::StatusCode::NOT_FOUND; + return Ok(resp); + } + }; + + } else if action == "info" { + + } else { + return Err(APIError::NoSuchMethod); + } + } else { + return Err(APIError::BadAPICall); + } + } + else { + return Err(APIError::NoSuchMethod); + } + + Ok(hyper::Response::new(hyper::Body::from("api"))) + } + + pub fn handle_request(&mut self, request: &hyper::Request) -> Result, APIError> { + if request.uri().path() == "/" { + return self.handle_root(request); + } else if request.uri().path() == "/announce" { + return self.handle_announce(request); + } else if request.uri().path() == "/scrape" { + return self.handle_scrape(request); + } else if request.uri().path() == "/stats" { + return self.handle_stats(request); + } else if request.uri().path().starts_with("/api") { + return self.handle_api(request); + } else { + Ok(hyper::Response::new(hyper::Body::from("Invalid url"))) + } + } + + fn handle_error(req: &hyper::Request, err: &APIError) -> hyper::Response { + hyper::Response::new(hyper::Body::from("Error report")) + } +} + +impl hyper::service::Service for WebApplication { + type ReqBody = hyper::Body; + type ResBody = hyper::Body; + type Error = APIError; + type Future = FutureResult, Self::Error>; + + fn call(&mut self, req: hyper::Request) -> Self::Future { + use futures; + + let mut res = match self.handle_request(&req) { + Ok(res) => res, + Err(err) => Self::handle_error(&req, &err), + }; + + futures::future::ok(res) + } +} + +impl hyper::service::NewService for WebApplication { + type ReqBody = hyper::Body; + type ResBody = hyper::Body; + type Error = APIError; + type Service = Self; + type Future = FutureResult; + type InitError = hyper::Error; + + fn new_service(&self) -> Self::Future { + use futures; + + futures::future::ok(WebApplication{ + tracker: self.tracker.clone(), + token: self.token.clone(), + }) + } +} + +pub fn start_server(addr: SocketAddr, tracker: std::sync::Arc, token: &str) { + let svc = WebApplication::new(tracker, String::from(token)); + let server = hyper::Server::bind(&addr).serve( svc); + + hyper::rt::run(server.map_err(|_e|{ + println!("error: {}", _e); + })); +}