init
This commit is contained in:
commit
29b0ecfc17
479
Cargo.lock
generated
Normal file
479
Cargo.lock
generated
Normal file
@ -0,0 +1,479 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "addr2line"
|
||||
version = "0.22.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678"
|
||||
dependencies = [
|
||||
"gimli",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "adler"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0"
|
||||
|
||||
[[package]]
|
||||
name = "backtrace"
|
||||
version = "0.3.72"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "17c6a35df3749d2e8bb1b7b21a976d82b15548788d2735b9d82f329268f71a11"
|
||||
dependencies = [
|
||||
"addr2line",
|
||||
"cc",
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"miniz_oxide",
|
||||
"object",
|
||||
"rustc-demangle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "2.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1"
|
||||
|
||||
[[package]]
|
||||
name = "bytes"
|
||||
version = "1.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9"
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.98"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "41c270e7540d725e65ac7f1b212ac8ce349719624d7bcff99f8e2e488e8cf03f"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "ezhttp"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"serde_json",
|
||||
"tokio",
|
||||
"urlencoding",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gimli"
|
||||
version = "0.29.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd"
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024"
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.155"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c"
|
||||
|
||||
[[package]]
|
||||
name = "lock_api"
|
||||
version = "0.4.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"scopeguard",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.7.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d"
|
||||
|
||||
[[package]]
|
||||
name = "miniz_oxide"
|
||||
version = "0.7.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "87dfd01fe195c66b572b37921ad8803d010623c0aca821bea2302239d155cdae"
|
||||
dependencies = [
|
||||
"adler",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mio"
|
||||
version = "0.8.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"wasi",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num_cpus"
|
||||
version = "1.16.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43"
|
||||
dependencies = [
|
||||
"hermit-abi",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "object"
|
||||
version = "0.35.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b8ec7ab813848ba4522158d5517a6093db1ded27575b070f4177b8d12b41db5e"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot"
|
||||
version = "0.12.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27"
|
||||
dependencies = [
|
||||
"lock_api",
|
||||
"parking_lot_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot_core"
|
||||
version = "0.9.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"redox_syscall",
|
||||
"smallvec",
|
||||
"windows-targets 0.52.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pin-project-lite"
|
||||
version = "0.2.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.85"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "22244ce15aa966053a896d1accb3a6e68469b97c7f33f284b99f0d576879fc23"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.36"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "469052894dcb553421e483e4209ee581a45100d31b4018de03e5a7ad86374a7e"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc-demangle"
|
||||
version = "0.1.24"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f"
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
|
||||
|
||||
[[package]]
|
||||
name = "scopeguard"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.203"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7253ab4de971e72fb7be983802300c30b5a7f0c2e56fab8abfc6a214307c0094"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.203"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.117"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"ryu",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "signal-hook-registry"
|
||||
version = "1.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "1.13.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
|
||||
|
||||
[[package]]
|
||||
name = "socket2"
|
||||
version = "0.5.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.66"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c42f3f41a2de00b01c0aaad383c5a45241efc8b2d1eda5661812fda5f3cdcff5"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio"
|
||||
version = "1.38.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ba4f4a02a7a80d6f274636f0aa95c7e383b912d41fe721a31f29e29698585a4a"
|
||||
dependencies = [
|
||||
"backtrace",
|
||||
"bytes",
|
||||
"libc",
|
||||
"mio",
|
||||
"num_cpus",
|
||||
"parking_lot",
|
||||
"pin-project-lite",
|
||||
"signal-hook-registry",
|
||||
"socket2",
|
||||
"tokio-macros",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-macros"
|
||||
version = "2.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5f5ae998a069d4b5aba8ee9dad856af7d520c3699e6159b185c2acd48155d39a"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
|
||||
|
||||
[[package]]
|
||||
name = "urlencoding"
|
||||
version = "2.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da"
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.11.0+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.48.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
|
||||
dependencies = [
|
||||
"windows-targets 0.48.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
|
||||
dependencies = [
|
||||
"windows-targets 0.52.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm 0.48.5",
|
||||
"windows_aarch64_msvc 0.48.5",
|
||||
"windows_i686_gnu 0.48.5",
|
||||
"windows_i686_msvc 0.48.5",
|
||||
"windows_x86_64_gnu 0.48.5",
|
||||
"windows_x86_64_gnullvm 0.48.5",
|
||||
"windows_x86_64_msvc 0.48.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.52.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm 0.52.5",
|
||||
"windows_aarch64_msvc 0.52.5",
|
||||
"windows_i686_gnu 0.52.5",
|
||||
"windows_i686_gnullvm",
|
||||
"windows_i686_msvc 0.52.5",
|
||||
"windows_x86_64_gnu 0.52.5",
|
||||
"windows_x86_64_gnullvm 0.52.5",
|
||||
"windows_x86_64_msvc 0.52.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.52.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.52.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.52.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnullvm"
|
||||
version = "0.52.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.52.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.52.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.52.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.52.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0"
|
14
Cargo.toml
Normal file
14
Cargo.toml
Normal file
@ -0,0 +1,14 @@
|
||||
[package]
|
||||
name = "ezhttp"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
description = "lightweight http server"
|
||||
license-file = "LICENSE"
|
||||
readme = "README.md"
|
||||
keywords = ["http", "server", "site"]
|
||||
|
||||
[dependencies]
|
||||
urlencoding = "2.1.3"
|
||||
serde_json = "1.0"
|
||||
tokio = { version = "1", features = ["full"] }
|
13
LICENSE
Normal file
13
LICENSE
Normal file
@ -0,0 +1,13 @@
|
||||
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||
Version 2, December 2004
|
||||
|
||||
Copyright (C) 2004 Sam Hocevar <sam@hocevar.net>
|
||||
|
||||
Everyone is permitted to copy and distribute verbatim or modified
|
||||
copies of this license document, and changing it is allowed as long
|
||||
as the name is changed.
|
||||
|
||||
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. You just DO WHAT THE FUCK YOU WANT TO.
|
59
README.md
Normal file
59
README.md
Normal file
@ -0,0 +1,59 @@
|
||||
# EzHttp
|
||||
Easy http server on rust
|
||||
|
||||
Example:
|
||||
```rust
|
||||
|
||||
use ezhttp::{Headers, HttpResponse, HttpRequest, HttpServer};
|
||||
use tokio::{runtime::Runtime, net::TcpListener};
|
||||
|
||||
struct EzSite {
|
||||
index_page: String
|
||||
}
|
||||
|
||||
impl HttpServer for EzSite {
|
||||
async fn on_request(&mut self, req: &HttpRequest) -> Option<HttpResponse> {
|
||||
println!("{} > {} {}", req.addr, req.method, req.page);
|
||||
|
||||
if req.page == "/" {
|
||||
Some(
|
||||
HttpResponse::from_str(
|
||||
Headers::from(vec![
|
||||
("Content-Type", "text/html")
|
||||
]),
|
||||
"200 OK".to_string(),
|
||||
&self.index_page
|
||||
)
|
||||
)
|
||||
} else {
|
||||
None // just shutdown socket
|
||||
}
|
||||
}
|
||||
|
||||
async fn on_start(&mut self, host: &str, listener: &TcpListener) {
|
||||
println!("Http server started on {}", host);
|
||||
}
|
||||
|
||||
async fn on_close(&mut self) {
|
||||
println!("Http server closed");
|
||||
}
|
||||
}
|
||||
|
||||
impl EzSite {
|
||||
fn new(index_page: &str) -> Self {
|
||||
EzSite {
|
||||
index_page: index_page.to_string()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let site = EzSite::new("Hello World!");
|
||||
let host = "localhost:8080";
|
||||
|
||||
Runtime::new().unwrap().block_on(async move {
|
||||
ezhttp::start_server(site, host).await.unwrap();
|
||||
});
|
||||
}
|
||||
|
||||
```
|
697
src/lib.rs
Normal file
697
src/lib.rs
Normal file
@ -0,0 +1,697 @@
|
||||
use serde_json::Value;
|
||||
use std::{
|
||||
boxed::Box, error::Error, net::{IpAddr, SocketAddr, ToSocketAddrs}, ptr::read, sync::Arc
|
||||
};
|
||||
use tokio::io::{AsyncReadExt, AsyncWriteExt, BufReader};
|
||||
use tokio::{
|
||||
io::AsyncBufReadExt,
|
||||
net::{TcpListener, TcpStream},
|
||||
sync::Mutex,
|
||||
};
|
||||
use urlencoding::{decode, encode};
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Headers {
|
||||
entries: Vec<(String, String)>,
|
||||
}
|
||||
|
||||
impl Headers {
|
||||
pub fn from_entries(entries: Vec<(String, String)>) -> Self {
|
||||
Headers { entries: entries }
|
||||
}
|
||||
|
||||
pub fn from(entries: Vec<(&str, &str)>) -> Self {
|
||||
Headers {
|
||||
entries: entries
|
||||
.iter()
|
||||
.map(|v| (v.0.to_string(), v.1.to_string()))
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new() -> Self {
|
||||
Headers {
|
||||
entries: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn contains_value(self, value: String) -> bool {
|
||||
for (k, v) in self.entries {
|
||||
if v == value {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
pub fn contains_key(self, key: String) -> bool {
|
||||
for (k, v) in self.entries {
|
||||
if k == key.to_lowercase() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
pub fn get(self, key: String) -> Option<String> {
|
||||
for (k, v) in self.entries {
|
||||
if k == key.to_lowercase() {
|
||||
return Some(v);
|
||||
}
|
||||
}
|
||||
return None;
|
||||
}
|
||||
|
||||
pub fn put(&mut self, key: String, value: String) {
|
||||
for t in self.entries.iter_mut() {
|
||||
if t.0 == key.to_lowercase() {
|
||||
t.1 = value;
|
||||
return;
|
||||
}
|
||||
}
|
||||
self.entries.push((key.to_lowercase(), value));
|
||||
}
|
||||
|
||||
pub fn remove(&mut self, key: String) {
|
||||
for (i, t) in self.entries.iter_mut().enumerate() {
|
||||
if t.0 == key.to_lowercase() {
|
||||
self.entries.remove(i);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn keys(self) -> Vec<String> {
|
||||
let mut keys = Vec::new();
|
||||
for (k, v) in self.entries {
|
||||
keys.push(k.to_lowercase());
|
||||
}
|
||||
keys
|
||||
}
|
||||
|
||||
pub fn values(self) -> Vec<String> {
|
||||
let mut values = Vec::new();
|
||||
for (k, v) in self.entries {
|
||||
values.push(v);
|
||||
}
|
||||
values
|
||||
}
|
||||
|
||||
pub fn entries(self) -> Vec<(String, String)> {
|
||||
return self.entries;
|
||||
}
|
||||
|
||||
pub fn len(self) -> usize {
|
||||
return self.entries.len();
|
||||
}
|
||||
|
||||
pub fn clear(&mut self) {
|
||||
self.entries.clear();
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for Headers {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
std::fmt::Debug::fmt(self, f)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct HttpRequest {
|
||||
pub page: String,
|
||||
pub method: String,
|
||||
pub addr: String,
|
||||
pub headers: Headers,
|
||||
pub params: Value,
|
||||
pub data: Vec<u8>,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for HttpRequest {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
std::fmt::Debug::fmt(self, f)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct HttpResponse {
|
||||
pub headers: Headers,
|
||||
pub status_code: String,
|
||||
pub data: Vec<u8>,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for HttpResponse {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
std::fmt::Debug::fmt(self, f)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum HttpError {
|
||||
ReadLineEof,
|
||||
ReadLineUnknown,
|
||||
InvalidHeaders,
|
||||
InvalidQuery,
|
||||
InvalidContentSize,
|
||||
InvalidContent,
|
||||
JsonParseError,
|
||||
WriteHeadError,
|
||||
WriteBodyError,
|
||||
InvalidStatus,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for HttpError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
std::fmt::Debug::fmt(self, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl Error for HttpError {}
|
||||
|
||||
async fn read_line(data: &mut BufReader<&mut TcpStream>) -> Result<String, HttpError> {
|
||||
let mut buf = String::new();
|
||||
let mut buf = match data.read_line(&mut buf).await {
|
||||
Ok(i) => {
|
||||
if i == 0 {
|
||||
return Err(HttpError::ReadLineEof);
|
||||
}
|
||||
buf
|
||||
}
|
||||
Err(_) => return Err(HttpError::ReadLineUnknown),
|
||||
};
|
||||
Ok(buf.to_string())
|
||||
}
|
||||
|
||||
async fn read_line_crlf(data: &mut BufReader<&mut TcpStream>) -> Result<String, HttpError> {
|
||||
match read_line(data).await {
|
||||
Ok(i) => Ok(i[..i.len() - 2].to_string()),
|
||||
Err(e) => Err(e)
|
||||
}
|
||||
}
|
||||
|
||||
async fn read_line_lf(data: &mut BufReader<&mut TcpStream>) -> Result<String, HttpError> {
|
||||
match read_line(data).await {
|
||||
Ok(i) => Ok(i[..i.len() - 1].to_string()),
|
||||
Err(e) => Err(e)
|
||||
}
|
||||
}
|
||||
|
||||
fn rem_first(value: &str) -> &str {
|
||||
let mut chars = value.chars();
|
||||
chars.next();
|
||||
chars.as_str()
|
||||
}
|
||||
|
||||
fn split(text: String, delimiter: &str, times: usize) -> Vec<String> {
|
||||
match times {
|
||||
0 => text.split(delimiter).map(|v| v.to_string()).collect(),
|
||||
1 => {
|
||||
let mut v: Vec<String> = Vec::new();
|
||||
match text.split_once(delimiter) {
|
||||
Some(i) => {
|
||||
v.push(i.0.to_string());
|
||||
v.push(i.1.to_string());
|
||||
}
|
||||
None => {
|
||||
v.push(text);
|
||||
}
|
||||
}
|
||||
v
|
||||
}
|
||||
_ => text
|
||||
.splitn(times, delimiter)
|
||||
.map(|v| v.to_string())
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
|
||||
impl HttpRequest {
|
||||
pub fn new(page: &str, method: &str, params: Value, headers: Headers, data: Vec<u8>) -> Self {
|
||||
HttpRequest {
|
||||
page: page.to_string(),
|
||||
method: method.to_string(),
|
||||
addr: String::new(),
|
||||
params: params,
|
||||
headers: headers,
|
||||
data: data,
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn read(mut data: BufReader<&mut TcpStream>, addr: &SocketAddr) -> Result<HttpRequest, HttpError> {
|
||||
let octets = match addr.ip() {
|
||||
IpAddr::V4(ip) => ip.octets(),
|
||||
_ => [127, 0, 0, 1],
|
||||
};
|
||||
|
||||
let ip_str = octets[0].to_string().as_str().to_owned()
|
||||
+ "."
|
||||
+ octets[1].to_string().as_str()
|
||||
+ "."
|
||||
+ octets[2].to_string().as_str()
|
||||
+ "."
|
||||
+ octets[3].to_string().as_str();
|
||||
|
||||
let mut status = split(
|
||||
match read_line_crlf(&mut data).await {
|
||||
Ok(i) => i,
|
||||
Err(e) => return Err(e),
|
||||
},
|
||||
" ",
|
||||
3,
|
||||
);
|
||||
|
||||
let method = status[0].clone();
|
||||
let (page, query) = match status[1].split_once("?") {
|
||||
Some(i) => (i.0.to_string(), Some(i.1)),
|
||||
None => (status[1].clone(), None),
|
||||
};
|
||||
|
||||
let mut headers = Headers::new();
|
||||
|
||||
loop {
|
||||
let text = match read_line_crlf(&mut data).await {
|
||||
Ok(i) => i,
|
||||
Err(_) => return Err(HttpError::InvalidHeaders),
|
||||
};
|
||||
|
||||
if text.len() == 0 {
|
||||
break;
|
||||
}
|
||||
|
||||
let (key, value) = match text.split_once(": ") {
|
||||
Some(i) => i,
|
||||
None => return Err(HttpError::InvalidHeaders),
|
||||
};
|
||||
|
||||
headers.put(key.to_lowercase(), value.to_string());
|
||||
}
|
||||
|
||||
let mut params = serde_json::Map::new();
|
||||
|
||||
if let Some(i) = query {
|
||||
for ele in i.split("&") {
|
||||
let (k, v) = match ele.split_once("=") {
|
||||
Some(i) => i,
|
||||
None => return Err(HttpError::InvalidQuery),
|
||||
};
|
||||
|
||||
params.insert(
|
||||
match decode(k) {
|
||||
Ok(i) => i.to_string(),
|
||||
Err(_) => return Err(HttpError::InvalidQuery),
|
||||
},
|
||||
match decode(v) {
|
||||
Ok(i) => Value::String(i.to_string()),
|
||||
Err(_) => return Err(HttpError::InvalidQuery),
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
let mut reqdata: Vec<u8> = Vec::new();
|
||||
|
||||
if let Some(content_size) = headers.clone().get("content-length".to_string()) {
|
||||
let content_size: usize = match content_size.parse() {
|
||||
Ok(i) => i,
|
||||
Err(_) => return Err(HttpError::InvalidContentSize),
|
||||
};
|
||||
|
||||
if content_size > reqdata.len() {
|
||||
let mut buf: Vec<u8> = Vec::new();
|
||||
buf.resize(content_size - reqdata.len(), 0);
|
||||
|
||||
match data.read_exact(&mut buf).await {
|
||||
Ok(i) => i,
|
||||
Err(_) => return Err(HttpError::InvalidContent),
|
||||
};
|
||||
|
||||
reqdata.append(&mut buf);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(content_type) = headers.clone().get("content-type".to_string()) {
|
||||
let mut body = match String::from_utf8(reqdata.clone()) {
|
||||
Ok(i) => i,
|
||||
Err(_) => return Err(HttpError::InvalidContent),
|
||||
};
|
||||
|
||||
match content_type.as_str() {
|
||||
"application/json" => {
|
||||
let val: Value = match serde_json::from_str(&body) {
|
||||
Ok(i) => i,
|
||||
Err(_) => return Err(HttpError::JsonParseError),
|
||||
};
|
||||
|
||||
if let Value::Object(mut dict) = val {
|
||||
params.append(&mut dict);
|
||||
}
|
||||
}
|
||||
"multipart/form-data" => {
|
||||
let boundary = "--".to_string()
|
||||
+ &content_type[(content_type.find("boundary=").unwrap() + 9)..]
|
||||
+ "\r\n";
|
||||
for part in body.split(boundary.as_str()) {
|
||||
let lines: Vec<&str> = part.split("\r\n").collect();
|
||||
if lines.len() >= 3 {
|
||||
if lines[0].starts_with("Content-Disposition: form-data; name=\"") {
|
||||
let name: &str =
|
||||
&lines[0]["Content-Disposition: form-data; name=\"".len()..];
|
||||
let name: &str = &name[..name.len() - 1];
|
||||
params
|
||||
.insert(name.to_string(), Value::String(lines[2].to_string()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
"application/x-www-form-urlencoded" => {
|
||||
if body.starts_with("?") {
|
||||
body = rem_first(body.as_str()).to_string()
|
||||
}
|
||||
|
||||
for ele in body.split("&") {
|
||||
let (k, v) = match ele.split_once("=") {
|
||||
Some(i) => i,
|
||||
None => return Err(HttpError::InvalidQuery),
|
||||
};
|
||||
|
||||
params.insert(
|
||||
match decode(k) {
|
||||
Ok(i) => i.to_string(),
|
||||
Err(_) => return Err(HttpError::InvalidQuery),
|
||||
},
|
||||
match decode(v) {
|
||||
Ok(i) => Value::String(i.to_string()),
|
||||
Err(_) => return Err(HttpError::InvalidQuery),
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(HttpRequest {
|
||||
page: page,
|
||||
method: method,
|
||||
addr: ip_str.to_string(),
|
||||
params: Value::Object(params),
|
||||
headers: headers,
|
||||
data: reqdata.clone(),
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn read_with_rrs(mut data: BufReader<&mut TcpStream>, addr: &SocketAddr) -> Result<HttpRequest, HttpError> {
|
||||
let addr = match read_line_lf(&mut data).await {
|
||||
Ok(i) => i,
|
||||
Err(e) => { return Err(e); }
|
||||
}.to_socket_addrs().unwrap().collect::<Vec<SocketAddr>>()[0];
|
||||
HttpRequest::read(data, &addr).await
|
||||
}
|
||||
|
||||
pub fn params_to_page(&mut self) {
|
||||
let mut query = String::new();
|
||||
|
||||
let mut i: bool = !self.page.contains("?");
|
||||
|
||||
if let Value::Object(obj) = self.params.clone() {
|
||||
for (k, v) in obj {
|
||||
query.push_str(if i { "?" } else { "&" });
|
||||
query.push_str(encode(k.as_str()).to_string().as_str());
|
||||
query.push_str("=");
|
||||
query.push_str(encode(v.as_str().unwrap()).to_string().as_str());
|
||||
i = false;
|
||||
}
|
||||
}
|
||||
|
||||
self.page += query.as_str();
|
||||
}
|
||||
|
||||
pub async fn write(self, data: &mut TcpStream) -> Result<(), HttpError> {
|
||||
let mut head: String = String::new();
|
||||
head.push_str(&self.method);
|
||||
head.push_str(" ");
|
||||
head.push_str(&self.page);
|
||||
head.push_str(" HTTP/1.1");
|
||||
head.push_str("\r\n");
|
||||
|
||||
for (k, v) in self.headers.entries {
|
||||
head.push_str(&k);
|
||||
head.push_str(": ");
|
||||
head.push_str(&v);
|
||||
head.push_str("\r\n");
|
||||
}
|
||||
|
||||
head.push_str("\r\n");
|
||||
|
||||
match data.write_all(head.as_bytes()).await {
|
||||
Ok(i) => i,
|
||||
Err(_) => return Err(HttpError::WriteHeadError),
|
||||
};
|
||||
|
||||
if !self.data.is_empty() {
|
||||
match data.write_all(&self.data).await {
|
||||
Ok(i) => i,
|
||||
Err(_) => return Err(HttpError::WriteBodyError),
|
||||
};
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl HttpResponse {
|
||||
pub fn new(headers: Headers, status_code: String, data: Vec<u8>) -> Self {
|
||||
HttpResponse {
|
||||
headers: headers,
|
||||
data: data,
|
||||
status_code: status_code,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_str(headers: Headers, status_code: String, data: &str) -> Self {
|
||||
HttpResponse {
|
||||
headers: headers,
|
||||
data: data.to_string().into_bytes(),
|
||||
status_code: status_code,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_text(self) -> String {
|
||||
match String::from_utf8(self.data) {
|
||||
Ok(i) => i,
|
||||
Err(_) => String::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_json(self) -> Value {
|
||||
match serde_json::from_str(self.get_text().as_str()) {
|
||||
Ok(i) => i,
|
||||
Err(_) => Value::Null,
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn read(dread: &mut TcpStream) -> Result<HttpResponse, HttpError> {
|
||||
let mut data = BufReader::new(dread);
|
||||
|
||||
let status = match read_line_crlf(&mut data).await {
|
||||
Ok(i) => i,
|
||||
Err(e) => {
|
||||
return Err(e);
|
||||
}
|
||||
};
|
||||
|
||||
let (_, status_code) = match status.split_once(" ") {
|
||||
Some(i) => i,
|
||||
None => return Err(HttpError::InvalidStatus),
|
||||
};
|
||||
|
||||
let mut headers = Headers::new();
|
||||
|
||||
loop {
|
||||
let text = match read_line_crlf(&mut data).await {
|
||||
Ok(i) => i,
|
||||
Err(_) => return Err(HttpError::InvalidHeaders),
|
||||
};
|
||||
|
||||
if text.len() == 0 {
|
||||
break;
|
||||
}
|
||||
|
||||
let (key, value) = match text.split_once(": ") {
|
||||
Some(i) => i,
|
||||
None => return Err(HttpError::InvalidHeaders),
|
||||
};
|
||||
|
||||
headers.put(key.to_lowercase(), value.to_string());
|
||||
}
|
||||
|
||||
let mut reqdata: Vec<u8> = Vec::new();
|
||||
|
||||
if let Some(content_size) = headers.clone().get("content-length".to_string()) {
|
||||
let content_size: usize = match content_size.parse() {
|
||||
Ok(i) => i,
|
||||
Err(_) => return Err(HttpError::InvalidContentSize),
|
||||
};
|
||||
|
||||
if content_size > reqdata.len() {
|
||||
let mut buf: Vec<u8> = Vec::new();
|
||||
buf.resize(content_size - reqdata.len(), 0);
|
||||
|
||||
match data.read_exact(&mut buf).await {
|
||||
Ok(i) => i,
|
||||
Err(_) => return Err(HttpError::InvalidContent),
|
||||
};
|
||||
|
||||
reqdata.append(&mut buf);
|
||||
}
|
||||
} else {
|
||||
loop {
|
||||
let mut buf: Vec<u8> = vec![0; 1024 * 32];
|
||||
|
||||
let mut buf_len: usize = match data.read(&mut buf).await {
|
||||
Ok(i) => i,
|
||||
Err(e) => {
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
if buf_len == 0 {
|
||||
break;
|
||||
}
|
||||
|
||||
buf.truncate(buf_len);
|
||||
|
||||
reqdata.append(&mut buf);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(HttpResponse {
|
||||
headers: headers,
|
||||
status_code: status_code.to_string(),
|
||||
data: reqdata,
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn write(self, data: &mut TcpStream) -> Result<(), &str> {
|
||||
let mut head: String = String::new();
|
||||
head.push_str("HTTP/1.1 ");
|
||||
head.push_str(&self.status_code);
|
||||
head.push_str("\r\n");
|
||||
|
||||
for (k, v) in self.headers.entries {
|
||||
head.push_str(&k);
|
||||
head.push_str(": ");
|
||||
head.push_str(&v);
|
||||
head.push_str("\r\n");
|
||||
}
|
||||
|
||||
head.push_str("\r\n");
|
||||
|
||||
match data.write_all(head.as_bytes()).await {
|
||||
Ok(i) => i,
|
||||
Err(_) => return Err("write head error"),
|
||||
};
|
||||
|
||||
match data.write_all(&self.data).await {
|
||||
Ok(i) => i,
|
||||
Err(_) => return Err("write body error"),
|
||||
};
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub trait HttpServer: Sync {
|
||||
async fn on_start(&mut self, host: &str, listener: &TcpListener);
|
||||
async fn on_close(&mut self);
|
||||
fn on_request(&mut self, req: &HttpRequest) -> impl std::future::Future<Output = Option<HttpResponse>> + std::marker::Send;
|
||||
}
|
||||
|
||||
pub async fn handle_connection<S: HttpServer + Send + 'static>(
|
||||
server: Arc<Mutex<S>>,
|
||||
mut sock: TcpStream,
|
||||
addr: std::net::SocketAddr,
|
||||
) where
|
||||
S: HttpServer,
|
||||
{
|
||||
let addr = sock.peer_addr().unwrap();
|
||||
|
||||
let req = match HttpRequest::read(BufReader::new(&mut sock), &addr).await {
|
||||
Ok(i) => i,
|
||||
Err(_) => {
|
||||
return;
|
||||
}
|
||||
};
|
||||
let resp = match server.lock().await.on_request(&req).await {
|
||||
Some(i) => i,
|
||||
None => {
|
||||
return;
|
||||
}
|
||||
};
|
||||
resp.write(&mut sock).await.unwrap();
|
||||
}
|
||||
|
||||
pub async fn start_server(mut server: impl HttpServer + Send + 'static, host: &str) -> Result<(), Box<dyn Error>> {
|
||||
let server = Arc::new(Mutex::new(server));
|
||||
let listener = TcpListener::bind(host).await?;
|
||||
|
||||
server.lock().await.on_start(host, &listener).await;
|
||||
|
||||
loop {
|
||||
let (sock, addr) = match listener.accept().await {
|
||||
Ok(i) => i,
|
||||
Err(_) => { break; }
|
||||
};
|
||||
|
||||
let now_server = Arc::clone(&server);
|
||||
tokio::spawn(handle_connection(now_server, sock, addr));
|
||||
}
|
||||
|
||||
server.lock().await.on_close().await;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
// http rrs
|
||||
pub async fn start_server_rrs(mut server: impl HttpServer + Send + 'static, host: &str) -> Result<(), Box<dyn Error>> {
|
||||
let server = Arc::new(Mutex::new(server));
|
||||
let listener = TcpListener::bind(host).await?;
|
||||
|
||||
server.lock().await.on_start(host, &listener).await;
|
||||
|
||||
loop {
|
||||
let (sock, addr) = match listener.accept().await {
|
||||
Ok(i) => i,
|
||||
Err(_) => { break; }
|
||||
};
|
||||
|
||||
let now_server = Arc::clone(&server);
|
||||
tokio::spawn(handle_connection_rrs(now_server, sock, addr));
|
||||
}
|
||||
|
||||
server.lock().await.on_close().await;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
// http rrs
|
||||
async fn handle_connection_rrs<S: HttpServer + Send + 'static>(
|
||||
server: Arc<Mutex<S>>,
|
||||
mut sock: TcpStream,
|
||||
addr: std::net::SocketAddr,
|
||||
) {
|
||||
let req = match HttpRequest::read_with_rrs(BufReader::new(&mut sock), &addr).await {
|
||||
Ok(i) => i,
|
||||
Err(_) => {
|
||||
return;
|
||||
}
|
||||
};
|
||||
let resp = match server.lock().await.on_request(&req).await {
|
||||
Some(i) => i,
|
||||
None => {
|
||||
return;
|
||||
}
|
||||
};
|
||||
resp.write(&mut sock).await.unwrap();
|
||||
}
|
51
src/main.rs
Normal file
51
src/main.rs
Normal file
@ -0,0 +1,51 @@
|
||||
use ezhttp::{Headers, HttpResponse, HttpRequest, HttpServer};
|
||||
use tokio::{runtime::Runtime, net::TcpListener};
|
||||
|
||||
struct EzSite {
|
||||
index_page: String
|
||||
}
|
||||
|
||||
impl HttpServer for EzSite {
|
||||
async fn on_request(&mut self, req: &HttpRequest) -> Option<HttpResponse> {
|
||||
println!("{} > {} {}", req.addr, req.method, req.page);
|
||||
|
||||
if req.page == "/" {
|
||||
Some(
|
||||
HttpResponse::from_str(
|
||||
Headers::from(vec![
|
||||
("Content-Type", "text/html")
|
||||
]),
|
||||
"200 OK".to_string(),
|
||||
&self.index_page
|
||||
)
|
||||
)
|
||||
} else {
|
||||
None // just shutdown socket
|
||||
}
|
||||
}
|
||||
|
||||
async fn on_start(&mut self, host: &str, listener: &TcpListener) {
|
||||
println!("Http server started on {}", host);
|
||||
}
|
||||
|
||||
async fn on_close(&mut self) {
|
||||
println!("Http server closed");
|
||||
}
|
||||
}
|
||||
|
||||
impl EzSite {
|
||||
fn new(index_page: &str) -> Self {
|
||||
EzSite {
|
||||
index_page: index_page.to_string()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let site = EzSite::new("Hello World!");
|
||||
let host = "localhost:8080";
|
||||
|
||||
Runtime::new().unwrap().block_on(async move {
|
||||
ezhttp::start_server(site, host).await.unwrap();
|
||||
});
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user