Compare commits

..

21 Commits
0.1.3 ... main

Author SHA1 Message Date
MeexReay
ed0b1dc22c ver 0.1.4 2025-04-23 01:47:11 +01:00
205e1ed373 try to fix read_until and writed more debug logs 2025-04-13 17:25:52 +03:00
d618f10156 open connection log 2025-04-13 12:49:16 +03:00
2756856286 close debug log 2025-04-13 12:00:24 +03:00
e39c5152fd Merge branch 'main' of https://git.meex.lol/MeexReay/flowgate 2025-04-13 11:50:04 +03:00
0f7b4e82ed aaaa 2025-04-13 11:46:00 +03:00
01dda00800 conf and readme update 2025-04-13 01:22:40 +03:00
e593decbed more logging and make threadpool config working 2025-04-12 23:27:18 +03:00
377090869f more config and some fixes 2025-04-12 23:09:01 +03:00
143c701620 threadpools 2025-04-12 13:51:06 +03:00
396d8918bd fix keep alive tried to read closed connection 2025-04-10 22:16:19 +03:00
fe41dc8339 refactor lib 2025-04-10 21:47:11 +03:00
5538820e03 rewrite in sync back 2025-04-10 21:42:16 +03:00
7f2e84256a run and start methods 2025-04-10 21:02:47 +03:00
f65a496d51 fix 100% cpu 2025-04-10 15:27:02 +03:00
614e1900b7 fix request 2025-04-10 02:40:56 +03:00
e135292817 fix read by content-length 2025-04-10 01:38:28 +03:00
da3e5aade0 bufreader + stream combine 2025-04-09 22:01:01 +03:00
2f7d558e02 fix bufreader and chunked transfer 2025-04-07 18:57:06 +03:00
ce0a1443dc bufreader 2025-04-07 16:47:32 +03:00
cda627cfb5 remove threadpool in code 2025-04-07 02:08:36 +03:00
13 changed files with 739 additions and 880 deletions

418
Cargo.lock generated
View File

@ -2,21 +2,6 @@
# It is not intended for manual editing. # It is not intended for manual editing.
version = 4 version = 4
[[package]]
name = "addr2line"
version = "0.24.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1"
dependencies = [
"gimli",
]
[[package]]
name = "adler2"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627"
[[package]] [[package]]
name = "aho-corasick" name = "aho-corasick"
version = "1.1.3" version = "1.1.3"
@ -28,9 +13,9 @@ dependencies = [
[[package]] [[package]]
name = "anstream" name = "anstream"
version = "0.6.15" version = "0.6.18"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526" checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b"
dependencies = [ dependencies = [
"anstyle", "anstyle",
"anstyle-parse", "anstyle-parse",
@ -43,49 +28,44 @@ dependencies = [
[[package]] [[package]]
name = "anstyle" name = "anstyle"
version = "1.0.8" version = "1.0.10"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1" checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9"
[[package]] [[package]]
name = "anstyle-parse" name = "anstyle-parse"
version = "0.2.5" 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 = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb" checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9"
dependencies = [ dependencies = [
"utf8parse", "utf8parse",
] ]
[[package]] [[package]]
name = "anstyle-query" name = "anstyle-query"
version = "1.1.1" version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a" checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c"
dependencies = [ dependencies = [
"windows-sys 0.52.0", "windows-sys 0.59.0",
] ]
[[package]] [[package]]
name = "anstyle-wincon" name = "anstyle-wincon"
version = "3.0.4" version = "3.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8" checksum = "ca3534e77181a9cc07539ad51f2141fe32f6c3ffd4df76db8ad92346b003ae4e"
dependencies = [ dependencies = [
"anstyle", "anstyle",
"windows-sys 0.52.0", "once_cell",
"windows-sys 0.59.0",
] ]
[[package]] [[package]]
name = "anyhow" name = "anyhow"
version = "1.0.88" version = "1.0.97"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4e1496f8fb1fbf272686b8d37f523dab3e4a7443300055e74cdaa449f3114356" checksum = "dcfed56ad506cb2c684a14971b8861fdc3baaaae314b9e5f9bb532cbe3ba7a4f"
[[package]]
name = "autocfg"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
[[package]] [[package]]
name = "aws-lc-rs" name = "aws-lc-rs"
@ -110,21 +90,6 @@ dependencies = [
"fs_extra", "fs_extra",
] ]
[[package]]
name = "backtrace"
version = "0.3.74"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a"
dependencies = [
"addr2line",
"cfg-if",
"libc",
"miniz_oxide",
"object",
"rustc-demangle",
"windows-targets 0.52.6",
]
[[package]] [[package]]
name = "bindgen" name = "bindgen"
version = "0.69.5" version = "0.69.5"
@ -150,15 +115,9 @@ dependencies = [
[[package]] [[package]]
name = "bitflags" name = "bitflags"
version = "2.6.0" version = "2.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd"
[[package]]
name = "bytes"
version = "1.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a"
[[package]] [[package]]
name = "cc" name = "cc"
@ -219,18 +178,18 @@ dependencies = [
[[package]] [[package]]
name = "colorchoice" name = "colorchoice"
version = "1.0.2" version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0" checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990"
[[package]] [[package]]
name = "colored" name = "colored"
version = "2.1.0" version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cbf2150cce219b664a8a70df7a1f933836724b503f8a413af9365b4dcc4d90b8" checksum = "117725a109d387c937a1533ce01b450cbde6b88abceea8473c4d7a85853cda3c"
dependencies = [ dependencies = [
"lazy_static", "lazy_static",
"windows-sys 0.48.0", "windows-sys 0.59.0",
] ]
[[package]] [[package]]
@ -247,9 +206,9 @@ checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
[[package]] [[package]]
name = "env_filter" name = "env_filter"
version = "0.1.2" version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4f2c92ceda6ceec50f43169f9ee8424fe2db276791afde7b2cd8bc084cb376ab" checksum = "186e05a59d4c50738528153b83b0b0194d3a29507dfec16eccd4b342903397d0"
dependencies = [ dependencies = [
"log", "log",
"regex", "regex",
@ -270,9 +229,9 @@ dependencies = [
[[package]] [[package]]
name = "equivalent" name = "equivalent"
version = "1.0.1" version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
[[package]] [[package]]
name = "errno" name = "errno"
@ -292,11 +251,8 @@ dependencies = [
"ignore-result", "ignore-result",
"log", "log",
"rustls", "rustls",
"serde_json",
"serde_yml", "serde_yml",
"tokio", "threadpool",
"tokio-io-timeout",
"tokio-rustls",
"wildmatch", "wildmatch",
] ]
@ -329,12 +285,6 @@ dependencies = [
"wasi 0.14.2+wasi-0.2.4", "wasi 0.14.2+wasi-0.2.4",
] ]
[[package]]
name = "gimli"
version = "0.31.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f"
[[package]] [[package]]
name = "glob" name = "glob"
version = "0.3.2" version = "0.3.2"
@ -343,9 +293,15 @@ checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2"
[[package]] [[package]]
name = "hashbrown" name = "hashbrown"
version = "0.14.5" version = "0.15.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289"
[[package]]
name = "hermit-abi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024"
[[package]] [[package]]
name = "home" name = "home"
@ -364,9 +320,9 @@ checksum = "665ff4dce8edd10d490641ccb78949832f1ddbff02c584fb1f85ab888fe0e50c"
[[package]] [[package]]
name = "indexmap" name = "indexmap"
version = "2.5.0" version = "2.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68b900aa2f7301e21c36462b170ee99994de34dff39a4a6a528e80e7376d07e5" checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e"
dependencies = [ dependencies = [
"equivalent", "equivalent",
"hashbrown", "hashbrown",
@ -389,15 +345,15 @@ dependencies = [
[[package]] [[package]]
name = "itoa" name = "itoa"
version = "1.0.11" version = "1.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
[[package]] [[package]]
name = "jiff" name = "jiff"
version = "0.2.5" 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 = "c102670231191d07d37a35af3eb77f1f0dbf7a71be51a962dcd57ea607be7260" checksum = "1f33145a5cbea837164362c7bd596106eb7c5198f97d1ba6f6ebb3223952e488"
dependencies = [ dependencies = [
"jiff-static", "jiff-static",
"log", "log",
@ -408,9 +364,9 @@ dependencies = [
[[package]] [[package]]
name = "jiff-static" name = "jiff-static"
version = "0.2.5" 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 = "4cdde31a9d349f1b1f51a0b3714a5940ac022976f4b49485fc04be052b183b4c" checksum = "43ce13c40ec6956157a3635d97a1ee2df323b263f09ea14165131289cb0f5c19"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -452,7 +408,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34" checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"windows-targets 0.52.6", "windows-targets",
] ]
[[package]] [[package]]
@ -471,16 +427,6 @@ version = "0.4.15"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab"
[[package]]
name = "lock_api"
version = "0.4.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17"
dependencies = [
"autocfg",
"scopeguard",
]
[[package]] [[package]]
name = "log" name = "log"
version = "0.4.27" version = "0.4.27"
@ -499,26 +445,6 @@ 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 = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
[[package]]
name = "miniz_oxide"
version = "0.8.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ff70ce3e48ae43fa075863cef62e8b43b71a4f2382229920e0df362592919430"
dependencies = [
"adler2",
]
[[package]]
name = "mio"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd"
dependencies = [
"libc",
"wasi 0.11.0+wasi-snapshot-preview1",
"windows-sys 0.52.0",
]
[[package]] [[package]]
name = "nom" name = "nom"
version = "7.1.3" version = "7.1.3"
@ -530,12 +456,13 @@ dependencies = [
] ]
[[package]] [[package]]
name = "object" name = "num_cpus"
version = "0.36.7" version = "1.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43"
dependencies = [ dependencies = [
"memchr", "hermit-abi",
"libc",
] ]
[[package]] [[package]]
@ -544,35 +471,6 @@ version = "1.21.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
[[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.6",
]
[[package]]
name = "pin-project-lite"
version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b"
[[package]] [[package]]
name = "portable-atomic" name = "portable-atomic"
version = "1.11.0" version = "1.11.0"
@ -590,9 +488,9 @@ dependencies = [
[[package]] [[package]]
name = "prettyplease" name = "prettyplease"
version = "0.2.25" version = "0.2.32"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "64d1ec885c64d0457d564db4ec299b2dae3f9c02808b8ad9c3a089c591b18033" checksum = "664ec5419c51e34154eec046ebcba56312d5a2fc3b09a06da188e1ad21afadf6"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"syn", "syn",
@ -622,20 +520,11 @@ version = "5.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5" checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5"
[[package]]
name = "redox_syscall"
version = "0.5.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2f103c6d277498fbceb16e84d317e2a400f160f46904d5f5410848c829511a3"
dependencies = [
"bitflags",
]
[[package]] [[package]]
name = "regex" name = "regex"
version = "1.10.6" version = "1.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619" checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191"
dependencies = [ dependencies = [
"aho-corasick", "aho-corasick",
"memchr", "memchr",
@ -645,9 +534,9 @@ dependencies = [
[[package]] [[package]]
name = "regex-automata" name = "regex-automata"
version = "0.4.7" version = "0.4.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908"
dependencies = [ dependencies = [
"aho-corasick", "aho-corasick",
"memchr", "memchr",
@ -656,9 +545,9 @@ dependencies = [
[[package]] [[package]]
name = "regex-syntax" name = "regex-syntax"
version = "0.8.4" version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
[[package]] [[package]]
name = "ring" name = "ring"
@ -674,12 +563,6 @@ dependencies = [
"windows-sys 0.52.0", "windows-sys 0.52.0",
] ]
[[package]]
name = "rustc-demangle"
version = "0.1.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f"
[[package]] [[package]]
name = "rustc-hash" name = "rustc-hash"
version = "1.1.0" version = "1.1.0"
@ -701,9 +584,9 @@ dependencies = [
[[package]] [[package]]
name = "rustls" name = "rustls"
version = "0.23.25" version = "0.23.26"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "822ee9188ac4ec04a2f0531e55d035fb2de73f18b41a63c70c2712503b6fb13c" checksum = "df51b5869f3a441595eac5e8ff14d486ff285f7b8c0df8770e49c3b56351f0f0"
dependencies = [ dependencies = [
"aws-lc-rs", "aws-lc-rs",
"log", "log",
@ -734,15 +617,9 @@ dependencies = [
[[package]] [[package]]
name = "ryu" name = "ryu"
version = "1.0.18" version = "1.0.20"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f"
[[package]]
name = "scopeguard"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]] [[package]]
name = "serde" name = "serde"
@ -764,18 +641,6 @@ dependencies = [
"syn", "syn",
] ]
[[package]]
name = "serde_json"
version = "1.0.140"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373"
dependencies = [
"itoa",
"memchr",
"ryu",
"serde",
]
[[package]] [[package]]
name = "serde_yml" name = "serde_yml"
version = "0.0.12" version = "0.0.12"
@ -797,31 +662,6 @@ version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
[[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.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8917285742e9f3e1683f0a9c4e6b57960b7314d0b08d30d1ecd426713ee2eee9"
[[package]]
name = "socket2"
version = "0.5.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4f5fd57c80058a56cf5c777ab8a126398ece8e442983605d280a44ce79d0edef"
dependencies = [
"libc",
"windows-sys 0.52.0",
]
[[package]] [[package]]
name = "subtle" name = "subtle"
version = "2.6.1" version = "2.6.1"
@ -840,59 +680,19 @@ dependencies = [
] ]
[[package]] [[package]]
name = "tokio" name = "threadpool"
version = "1.44.2" version = "1.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6b88822cbe49de4185e3a4cbf8321dd487cf5fe0c5c65695fef6346371e9c48" checksum = "d050e60b33d41c19108b32cea32164033a9013fe3b46cbd4457559bfbf77afaa"
dependencies = [ dependencies = [
"backtrace", "num_cpus",
"bytes",
"libc",
"mio",
"parking_lot",
"pin-project-lite",
"signal-hook-registry",
"socket2",
"tokio-macros",
"windows-sys 0.52.0",
]
[[package]]
name = "tokio-io-timeout"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "30b74022ada614a1b4834de765f9bb43877f910cc8ce4be40e89042c9223a8bf"
dependencies = [
"pin-project-lite",
"tokio",
]
[[package]]
name = "tokio-macros"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "tokio-rustls"
version = "0.26.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e727b36a1a0e8b74c376ac2211e40c2c8af09fb4013c60d910495810f008e9b"
dependencies = [
"rustls",
"tokio",
] ]
[[package]] [[package]]
name = "unicode-ident" name = "unicode-ident"
version = "1.0.13" version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
[[package]] [[package]]
name = "untrusted" name = "untrusted"
@ -945,22 +745,13 @@ version = "2.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68ce1ab1f8c62655ebe1350f589c61e505cf94d385bc6a12899442d9081e71fd" checksum = "68ce1ab1f8c62655ebe1350f589c61e505cf94d385bc6a12899442d9081e71fd"
[[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]] [[package]]
name = "windows-sys" name = "windows-sys"
version = "0.52.0" version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
dependencies = [ dependencies = [
"windows-targets 0.52.6", "windows-targets",
] ]
[[package]] [[package]]
@ -969,22 +760,7 @@ version = "0.59.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
dependencies = [ dependencies = [
"windows-targets 0.52.6", "windows-targets",
]
[[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]] [[package]]
@ -993,46 +769,28 @@ version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
dependencies = [ dependencies = [
"windows_aarch64_gnullvm 0.52.6", "windows_aarch64_gnullvm",
"windows_aarch64_msvc 0.52.6", "windows_aarch64_msvc",
"windows_i686_gnu 0.52.6", "windows_i686_gnu",
"windows_i686_gnullvm", "windows_i686_gnullvm",
"windows_i686_msvc 0.52.6", "windows_i686_msvc",
"windows_x86_64_gnu 0.52.6", "windows_x86_64_gnu",
"windows_x86_64_gnullvm 0.52.6", "windows_x86_64_gnullvm",
"windows_x86_64_msvc 0.52.6", "windows_x86_64_msvc",
] ]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
[[package]] [[package]]
name = "windows_aarch64_gnullvm" name = "windows_aarch64_gnullvm"
version = "0.52.6" version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
[[package]]
name = "windows_aarch64_msvc"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
[[package]] [[package]]
name = "windows_aarch64_msvc" name = "windows_aarch64_msvc"
version = "0.52.6" version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
[[package]]
name = "windows_i686_gnu"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
[[package]] [[package]]
name = "windows_i686_gnu" name = "windows_i686_gnu"
version = "0.52.6" version = "0.52.6"
@ -1045,48 +803,24 @@ version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
[[package]]
name = "windows_i686_msvc"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
[[package]] [[package]]
name = "windows_i686_msvc" name = "windows_i686_msvc"
version = "0.52.6" version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
[[package]]
name = "windows_x86_64_gnu"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
[[package]] [[package]]
name = "windows_x86_64_gnu" name = "windows_x86_64_gnu"
version = "0.52.6" version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
[[package]] [[package]]
name = "windows_x86_64_gnullvm" name = "windows_x86_64_gnullvm"
version = "0.52.6" version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
[[package]]
name = "windows_x86_64_msvc"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
[[package]] [[package]]
name = "windows_x86_64_msvc" name = "windows_x86_64_msvc"
version = "0.52.6" version = "0.52.6"

View File

@ -1,16 +1,13 @@
[package] [package]
name = "flowgate" name = "flowgate"
version = "0.1.3" version = "0.1.4"
edition = "2021" edition = "2021"
[dependencies] [dependencies]
tokio = { version = "1.44.2", features = ["full"] }
tokio-io-timeout = "1.2.0"
tokio-rustls = "0.26.2"
rustls = "0.23.25" rustls = "0.23.25"
wildmatch = "2.4.0" wildmatch = "2.4.0"
serde_yml = "0.0.12" serde_yml = "0.0.12"
serde_json = "1.0.140"
log = "0.4.27" log = "0.4.27"
colog = "1.3.0" colog = "1.3.0"
ignore-result = "0.2.0" ignore-result = "0.2.0"
threadpool = "1.8.1"

View File

@ -3,22 +3,32 @@ HTTP requests redirection system
Features: Features:
- Request redirection - Request redirection
- TLS support - TLS support (via Rustls!)
- Keep-alive connections - Keep-alive connections
- Sending IP in header (X-Real-IP) - Sending IP in header (X-Real-IP)
- Multiple ip forwarding methods
- Accepts incoming ip forwarding
TODO: TODO:
- Remove panics
- Creating trees of flowgate
- Filter by headers - Filter by headers
- Modify response headers - Modify response headers
- HTTP/3 full support - HTTP/3 full support (quic/udp)
## How to use ## How to use
Firstly, download it from releases. or build from sources (read BUILD.md) \ Firstly, download it from releases. or build from sources (read BUILD.md) \
Just run it and configure in `conf.yml` file. Just run it and configure in `conf.yml` file.
### Logging
To get all logs (with debug ones), set this env var:
```
RUST_LOG=debug
```
Read more: [env_logger](https://docs.rs/env_logger/latest/env_logger/#enabling-logging)
## Configuration ## Configuration
### IP forwarding methods ### IP forwarding methods

View File

@ -1,8 +1,9 @@
http_host: localhost:80 # Http server host http_host: localhost:80 # Http server host (optional)
https_host: localhost:443 # Https server host https_host: localhost:443 # Https server host (optional)
connection_timeout: 10 # Read and write timeout of connections in seconds (optional, default - 10) connection_timeout: 10 # Read and write timeout of connections in seconds (optional, default - 10)
incoming_ip_forwarding: none # Read IP forwarding on incoming connections (optional, default - none) incoming_ip_forwarding: none # Read IP forwarding on incoming connections (optional, default - none)
threadpool_size: 10 # Size of the global threadpool (optional, default - 10)
sites: sites:
- domain: localhost # Site domain (use wildcard matching) - domain: localhost # Site domain (use wildcard matching)

View File

@ -1,6 +1,6 @@
use std::{fs, time::Duration}; use std::{fs, time::Duration};
use tokio::net::TcpStream; use std::net::TcpStream;
use serde_yml::{Number, Value}; use serde_yml::{Number, Value};
use wildmatch::WildMatch; use wildmatch::WildMatch;
@ -19,8 +19,8 @@ pub struct SiteConfig {
} }
impl SiteConfig { impl SiteConfig {
pub async fn connect(&self) -> Option<TcpStream> { pub fn connect(&self) -> Option<TcpStream> {
TcpStream::connect(self.host.clone()).await.ok() TcpStream::connect(self.host.clone()).ok()
} }
} }
@ -51,11 +51,11 @@ impl IpForwarding {
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct Config { pub struct Config {
pub sites: Vec<SiteConfig>, pub sites: Vec<SiteConfig>,
pub http_host: String, pub http_host: Option<String>,
pub https_host: String, pub https_host: Option<String>,
pub threadpool_size: usize,
pub connection_timeout: Duration, pub connection_timeout: Duration,
pub incoming_ip_forwarding: IpForwarding pub incoming_ip_forwarding: IpForwarding,
pub threadpool_size: usize
} }
impl Config { impl Config {
@ -63,11 +63,11 @@ impl Config {
let file_content = fs::read_to_string(filename).ok()?; let file_content = fs::read_to_string(filename).ok()?;
let doc = serde_yml::from_str::<Value>(file_content.as_str()).ok()?; let doc = serde_yml::from_str::<Value>(file_content.as_str()).ok()?;
let http_host = doc["http_host"].as_str()?.to_string(); let http_host = doc.get("http_host").and_then(|o| Some(o.as_str()?.to_string()));
let https_host = doc["https_host"].as_str()?.to_string(); let https_host = doc.get("https_host").and_then(|o| Some(o.as_str()?.to_string()));
let threadpool_size = doc.get("threadpool_size").and_then(|o| Some(o.as_u64()? as usize + 2)).unwrap_or(12);
let threadpool_size = doc.get("threadpool_size")
.unwrap_or(&Value::Number(Number::from(10))).as_u64()? as usize;
let connection_timeout = Duration::from_secs(doc.get("connection_timeout") let connection_timeout = Duration::from_secs(doc.get("connection_timeout")
.unwrap_or(&Value::Number(Number::from(10))).as_u64()?); .unwrap_or(&Value::Number(Number::from(10))).as_u64()?);
let incoming_ip_forwarding = doc.get("incoming_ip_forwarding") let incoming_ip_forwarding = doc.get("incoming_ip_forwarding")
@ -117,9 +117,9 @@ impl Config {
sites, sites,
http_host, http_host,
https_host, https_host,
threadpool_size,
connection_timeout, connection_timeout,
incoming_ip_forwarding incoming_ip_forwarding,
threadpool_size
}.clone()) }.clone())
} }

View File

@ -1,3 +0,0 @@
pub mod config;
pub mod server;
pub mod tls;

View File

@ -1,492 +0,0 @@
use std::{
error::Error,
net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6},
str::FromStr,
sync::Arc
};
use ignore_result::Ignore;
use tokio::{
io::{AsyncReadExt, AsyncWriteExt},
net::{TcpListener, TcpStream}
};
use log::info;
use tokio_io_timeout::TimeoutStream;
use tokio_rustls::TlsAcceptor;
use crate::tls::create_server_config;
use super::config::{
Config,
SiteConfig,
IpForwarding
};
pub struct FlowgateServer {
config: Arc<Config>,
}
struct Connection {
stream: TcpStream,
config: SiteConfig,
keep_alive: bool,
host: String,
}
impl FlowgateServer {
pub fn new(config: Arc<Config>) -> Self {
FlowgateServer { config }
}
pub async fn start(self) {
let local_self = Arc::new(self);
tokio::spawn({
let local_self = local_self.clone();
async move { local_self.run_http().await.ignore(); }
});
tokio::spawn({
let local_self = local_self.clone();
async move { local_self.run_https().await.ignore(); }
});
}
pub async fn run_http(self: Arc<Self>) -> Result<(), Box<dyn Error>> {
let listener = TcpListener::bind(&self.config.http_host).await?;
info!("HTTP server runned on {}", &self.config.http_host);
loop {
let Ok((stream, addr)) = listener.accept().await else { break };
let local_self = self.clone();
tokio::spawn(async move {
let mut stream = TimeoutStream::new(stream);
stream.set_write_timeout(Some(local_self.config.connection_timeout));
stream.set_read_timeout(Some(local_self.config.connection_timeout));
let mut stream = Box::pin(stream);
local_self.accept_stream(
&mut stream,
addr,
false
).await;
});
}
Ok(())
}
pub async fn run_https(self: Arc<Self>) -> Result<(), Box<dyn Error>> {
let listener = TcpListener::bind(&self.config.https_host).await?;
let acceptor = TlsAcceptor::from(Arc::new(create_server_config(self.config.clone()).await));
info!("HTTPS server runned on {}", &self.config.https_host);
loop {
let Ok((stream, addr)) = listener.accept().await else { break };
let local_self = self.clone();
let acceptor = acceptor.clone();
tokio::spawn(async move {
let mut stream = TimeoutStream::new(stream);
stream.set_write_timeout(Some(local_self.config.connection_timeout));
stream.set_read_timeout(Some(local_self.config.connection_timeout));
let Ok(mut stream) = acceptor.accept(Box::pin(stream)).await else { return };
local_self.accept_stream(
&mut stream,
addr,
true
).await;
});
}
Ok(())
}
async fn accept_stream(
self: Arc<Self>,
stream: &mut (impl AsyncReadExt + AsyncWriteExt + Unpin),
addr: SocketAddr,
https: bool
) -> Option<()> {
let mut conn = self.clone().read_request(stream, addr, https, None).await?;
if conn.keep_alive && conn.config.enable_keep_alive {
loop {
if !conn.config.support_keep_alive {
conn.stream.shutdown().await.ignore();
conn.stream = conn.config.connect().await?;
}
conn = self.clone().read_request(stream, addr, https, Some(conn)).await?;
}
}
conn.stream.shutdown().await.ignore();
stream.shutdown().await.ok()?;
Some(())
}
async fn read_request(
self: Arc<Self>,
stream: &mut (impl AsyncReadExt + AsyncWriteExt + Unpin),
addr: SocketAddr,
https: bool,
conn: Option<Connection>
) -> Option<Connection> {
let mut addr = addr;
match &self.config.incoming_ip_forwarding {
IpForwarding::Simple => {
let mut header = Vec::new();
{
let mut buf = [0; 1];
while let Ok(1) = stream.read(&mut buf).await {
let byte = buf[0];
if byte == b'\n' { break }
header.push(byte);
}
}
addr = SocketAddr::from_str(&String::from_utf8(header).ok()?).ok()?;
},
IpForwarding::Modern => {
let mut ipver = [0; 1];
stream.read(&mut ipver).await.ok()?;
addr = match ipver[0] {
0x01 => {
let mut octets = [0; 4];
stream.read(&mut octets).await.ok()?;
let mut port = [0; 2];
stream.read(&mut port).await.ok()?;
let port = u16::from_be_bytes(port);
SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::from(octets), port))
}, 0x02 => {
let mut octets = [0; 16];
stream.read(&mut octets).await.ok()?;
let mut port = [0; 2];
stream.read(&mut port).await.ok()?;
let port = u16::from_be_bytes(port);
SocketAddr::V6(SocketAddrV6::new(Ipv6Addr::from(octets), port, 0, 0))
}, _ => { return None },
};
},
_ => { }
}
let mut head = Vec::new();
{
let mut buf = [0; 1];
let mut counter = 0;
while let Ok(1) = stream.read(&mut buf).await {
let byte = buf[0];
head.push(byte);
counter = match (counter, byte) {
(0, b'\r') => 1,
(1, b'\n') => 2,
(2, b'\r') => 3,
(3, b'\n') => break,
_ => 0,
};
}
head.truncate(head.len() - 4);
}
if head.is_empty() { return None; }
let head_str = String::from_utf8(head.clone()).ok()?;
let head_str = head_str.trim_matches(char::from(0)).to_string();
let mut head_lines = head_str.split("\r\n");
let status = head_lines.next()?;
let status_seq: Vec<&str> = status.split(" ").collect();
let headers: Vec<(&str, &str)> = head_lines
.filter(|l| l.contains(": "))
.map(|l| l.split_once(": ").unwrap())
.collect();
let is_chunked = headers.iter()
.find(|o| o.0.to_lowercase() == "transfer-encoding")
.map(|o| o.1.split(",").map(|x| x.trim_matches(' ').to_string()).collect::<Vec<String>>())
.map(|o| o.contains(&"chunked".to_string()))
.unwrap_or(false);
if let IpForwarding::Header(header) = &self.config.incoming_ip_forwarding {
if let Some(ip) = headers.iter().find(|o| o.0 == header).map(|o| o.1) {
addr = SocketAddr::from_str(ip).ok()?;
}
}
let mut conn: Connection = if conn.is_none() {
let mut host = String::new();
let mut keep_alive = false;
for (key, value) in &headers {
match key.to_lowercase().as_str() {
"host" => host = value.to_string(),
"connection" => keep_alive = *value == "keep-alive",
_ => {}
}
}
let site = self.config.get_site(&host)?.clone();
Connection {
stream: site.connect().await?,
config: site,
keep_alive,
host
}
} else {
conn?
};
let content_length = headers
.iter()
.filter(|(k, _)| k.to_lowercase() == "content-length")
.next()
.map(|o| o.1.parse().ok())
.flatten()
.unwrap_or(0usize);
let mut reqbuf: Vec<u8> = Vec::new();
if let Some(replace_host) = conn.config.replace_host.clone() {
let mut new_head = Vec::new();
let mut is_status = true;
for line in head_str.split("\r\n") {
if is_status {
new_head.append(&mut line.as_bytes().to_vec());
is_status = false;
} else {
new_head.append(&mut b"\r\n".to_vec());
let (key, _) = line.split_once(": ")?;
if key.to_lowercase() == "host" {
new_head.append(&mut key.as_bytes().to_vec());
new_head.append(&mut b": ".to_vec());
new_head.append(&mut replace_host.as_bytes().to_vec());
} else {
new_head.append(&mut line.as_bytes().to_vec());
}
}
}
head = new_head;
}
match &conn.config.ip_forwarding {
IpForwarding::Header(header) => {
reqbuf.append(&mut status.to_string().as_bytes().to_vec());
reqbuf.append(&mut b"\r\n".to_vec());
for (key, value) in String::from_utf8(head.clone()).ok()?
.split("\r\n")
.skip(1)
.filter_map(|o| o.split_once(": ")) {
if *key.to_lowercase() == header.to_lowercase() { continue }
reqbuf.append(&mut key.to_string().as_bytes().to_vec());
reqbuf.append(&mut b": ".to_vec());
reqbuf.append(&mut value.to_string().as_bytes().to_vec());
reqbuf.append(&mut b"\r\n".to_vec());
}
reqbuf.append(&mut header.as_bytes().to_vec());
reqbuf.append(&mut b": ".to_vec());
reqbuf.append(&mut addr.to_string().as_bytes().to_vec());
reqbuf.append(&mut b"\r\n\r\n".to_vec());
},
IpForwarding::Simple => {
reqbuf.append(&mut addr.to_string().as_bytes().to_vec());
reqbuf.push(b'\n');
reqbuf.append(&mut head.clone());
reqbuf.append(&mut b"\r\n\r\n".to_vec());
},
IpForwarding::Modern => {
reqbuf.push(if addr.is_ipv4() { 0x01 } else { 0x02 });
match addr.ip() {
IpAddr::V4(ip) => {
reqbuf.append(&mut ip.octets().to_vec());
}, IpAddr::V6(ip) => {
reqbuf.append(&mut ip.octets().to_vec());
}
}
reqbuf.append(&mut addr.port().to_be_bytes().to_vec());
reqbuf.append(&mut head.clone());
reqbuf.append(&mut b"\r\n\r\n".to_vec());
},
IpForwarding::None => {
reqbuf.append(&mut head.clone());
reqbuf.append(&mut b"\r\n\r\n".to_vec());
}
}
conn.stream.write_all(&reqbuf).await.ok()?;
if content_length > 0 {
let mut read = 0usize;
let mut buf = vec![0; 4096];
while let Ok(size) = stream.read(&mut buf).await {
if size == 0 { break }
read += size;
buf.truncate(size);
conn.stream.write_all(&buf).await.ok()?;
buf = vec![0; 4096];
if read >= content_length { break }
}
} else if is_chunked {
loop {
let mut length = Vec::new();
{
let mut buf = [0; 1];
let mut counter = 0;
while let Ok(1) = stream.read(&mut buf).await {
let byte = buf[0];
length.push(byte);
counter = match (counter, byte) {
(0, b'\r') => 1,
(1, b'\n') => break,
_ => 0,
};
}
conn.stream.write_all(&length).await.ok()?;
length.truncate(length.len() - 2);
}
let length = String::from_utf8(length).ok()?;
let length = usize::from_str_radix(length.as_str(), 16).ok()?;
let mut data = vec![0u8; length+2];
stream.read_exact(&mut data).await.ok()?;
conn.stream.write_all(&data).await.ok()?;
if length == 0 {
break;
}
}
}
if conn.config.support_keep_alive {
let mut head = Vec::new();
{
let mut buf = [0; 1];
let mut counter = 0;
while let Ok(1) = conn.stream.read(&mut buf).await {
let byte = buf[0];
head.push(byte);
stream.write_all(&buf).await.ok()?;
counter = match (counter, byte) {
(0, b'\r') => 1,
(1, b'\n') => 2,
(2, b'\r') => 3,
(3, b'\n') => break,
_ => 0,
};
}
head.truncate(head.len() - 4);
}
if head.is_empty() { return None; }
let head_str = String::from_utf8(head.clone()).ok()?;
let head_str = head_str.trim_matches(char::from(0));
let headers = head_str.split("\r\n")
.skip(1)
.filter(|l| l.contains(": "))
.map(|l| l.split_once(": ").unwrap())
.map(|(k,v)| (k.to_lowercase(),v.to_string()))
.collect::<Vec<(String,String)>>();
let content_length = headers.iter()
.find(|(k, _)| k == "content-length")
.map(|o| o.1.parse().ok())
.flatten()
.unwrap_or(0usize);
let is_chunked = headers.iter()
.find(|o| o.0.to_lowercase() == "transfer-encoding")
.map(|o| o.1.split(",").map(|x| x.trim_matches(' ').to_string()).collect::<Vec<String>>())
.map(|o| o.contains(&"chunked".to_string()))
.unwrap_or(false);
if content_length > 0 {
let mut read = 0usize;
let mut buf = vec![0; 4096];
while let Ok(size) = conn.stream.read(&mut buf).await {
if size == 0 { break }
read += size;
buf.truncate(size);
stream.write_all(&buf).await.ok()?;
buf = vec![0; 4096];
if read == content_length { break }
}
} else if is_chunked {
loop {
let mut length = Vec::new();
{
let mut buf = [0; 1];
let mut counter = 0;
while let Ok(1) = conn.stream.read(&mut buf).await {
let byte = buf[0];
length.push(byte);
counter = match (counter, byte) {
(0, b'\r') => 1,
(1, b'\n') => break,
_ => 0,
};
}
stream.write_all(&length).await.ok()?;
length.truncate(length.len() - 2);
}
let length = String::from_utf8(length).ok()?;
let length = usize::from_str_radix(length.as_str(), 16).ok()?;
let mut data = vec![0u8; length+2];
conn.stream.read_exact(&mut data).await.ok()?;
stream.write_all(&data).await.ok()?;
if length == 0 {
break;
}
}
}
} else {
let mut buf = vec![0;1024];
while let Ok(n) = conn.stream.read(&mut buf).await {
if n == 0 { break }
buf.truncate(n);
stream.write_all(&buf).await.ok()?;
buf = vec![0;1024];
}
}
info!("{addr} > {} {}://{}{}", status_seq[0], if https { "https" } else { "http" }, conn.host, status_seq[1]);
Some(conn)
}
}

View File

@ -1,3 +1,7 @@
pub mod flowgate; pub mod config;
pub mod server;
pub mod tls;
pub use flowgate::*; pub use config::*;
pub use server::*;
pub use tls::*;

View File

@ -3,8 +3,7 @@ use std::{fs, path::Path, sync::Arc};
use flowgate::{config::Config, server::FlowgateServer}; use flowgate::{config::Config, server::FlowgateServer};
use ignore_result::Ignore; use ignore_result::Ignore;
#[tokio::main] fn main() {
async fn main() {
colog::init(); colog::init();
if !Path::new("conf.yml").exists() { if !Path::new("conf.yml").exists() {
@ -14,7 +13,5 @@ async fn main() {
let config = Arc::new(Config::parse("conf.yml").unwrap()); let config = Arc::new(Config::parse("conf.yml").unwrap());
let server = FlowgateServer::new(config.clone()); let server = FlowgateServer::new(config.clone());
server.start().await; server.run();
loop {}
} }

539
src/server.rs Executable file
View File

@ -0,0 +1,539 @@
use std::{
error::Error, io::{BufRead, BufReader, Read, Write},
net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6, TcpListener, TcpStream},
str::FromStr, sync::Arc
};
use ignore_result::Ignore;
use log::{debug, info};
use rustls::{ServerConnection, StreamOwned};
use threadpool::ThreadPool;
use super::{
tls::create_server_config,
config::{
Config,
SiteConfig,
IpForwarding
}
};
pub struct FlowgateServer {
config: Arc<Config>,
}
struct Connection {
stream: BufReader<TcpStream>,
config: SiteConfig,
keep_alive: bool,
host: String,
}
impl FlowgateServer {
pub fn new(config: Arc<Config>) -> Self {
FlowgateServer { config }
}
pub fn run(self) {
self.start().join();
}
pub fn start(self) -> ThreadPool {
let local_self = Arc::new(self);
let threadpool = ThreadPool::new(local_self.config.threadpool_size);
let mut handles = Vec::new();
handles.push(local_self.clone().start_http(&threadpool));
handles.push(local_self.clone().start_https(&threadpool));
threadpool
}
pub fn start_http(self: Arc<Self>, threadpool: &ThreadPool) {
threadpool.execute({
let local_self = self.clone();
let threadpool = threadpool.clone();
move || { local_self.run_http(&threadpool).ignore(); }
})
}
pub fn start_https(self: Arc<Self>, threadpool: &ThreadPool) {
threadpool.execute({
let local_self = self.clone();
let threadpool = threadpool.clone();
move || { local_self.run_https(&threadpool).ignore(); }
})
}
pub fn run_http(self: Arc<Self>, threadpool: &ThreadPool) -> Result<(), Box<dyn Error>> {
if let Some(host) = self.config.http_host.clone() {
let listener = TcpListener::bind(&host)?;
info!("HTTP server runned on {}", &host);
for stream in listener.incoming() {
if let Ok(mut stream) = stream {
let local_self = self.clone();
let Ok(addr) = stream.peer_addr() else { continue };
let Ok(_) = stream.set_write_timeout(Some(local_self.config.connection_timeout)) else { continue };
let Ok(_) = stream.set_read_timeout(Some(local_self.config.connection_timeout)) else { continue };
threadpool.execute(move || {
debug!("{} open connection", addr);
local_self.accept_stream(
&mut stream,
addr,
false
);
debug!("{} close connection", addr);
});
}
}
}
Ok(())
}
pub fn run_https(self: Arc<Self>, threadpool: &ThreadPool) -> Result<(), Box<dyn Error>> {
if let Some(host) = self.config.https_host.clone() {
let listener = TcpListener::bind(&host)?;
info!("HTTPS server runned on {}", &host);
let config = Arc::new(create_server_config(self.config.clone()));
for stream in listener.incoming() {
if let Ok(stream) = stream {
let local_self = self.clone();
let config = config.clone();
let Ok(addr) = stream.peer_addr() else { continue };
let Ok(_) = stream.set_write_timeout(Some(local_self.config.connection_timeout)) else { continue };
let Ok(_) = stream.set_read_timeout(Some(local_self.config.connection_timeout)) else { continue };
threadpool.execute(move || {
let Ok(connection) = ServerConnection::new(config) else { return };
debug!("{} open connection", addr);
let mut stream = StreamOwned::new(connection, stream);
while stream.conn.is_handshaking() {
let Ok(_) = stream.conn.complete_io(&mut stream.sock) else { debug!("{} close connection", addr);return };
}
local_self.accept_stream(
&mut stream,
addr,
true
);
debug!("{} close connection", addr);
});
}
}
}
Ok(())
}
fn accept_stream(
self: Arc<Self>,
stream: &mut (impl Read + Write + Shutdown),
addr: SocketAddr,
https: bool
) -> Option<()> {
let mut conn = self.clone().read_request(stream, addr, https, None)?;
if conn.keep_alive && conn.config.enable_keep_alive {
loop {
if !conn.config.support_keep_alive {
conn.stream.shutdown();
conn.stream = BufReader::new(conn.config.connect()?);
}
conn = self.clone().read_request(stream, addr, https, Some(conn))?;
}
}
conn.stream.shutdown();
stream.shutdown();
Some(())
}
fn read_request(
self: Arc<Self>,
stream: &mut (impl Read + Write + Shutdown),
addr: SocketAddr,
https: bool,
conn: Option<Connection>
) -> Option<Connection> {
let mut addr = addr;
let mut stream = BufReader::new(stream);
match &self.config.incoming_ip_forwarding {
IpForwarding::Simple => {
let mut header = Vec::new();
stream.read_until(b'\n', &mut header).ok()?;
header.truncate(header.len()-1);
addr = SocketAddr::from_str(&String::from_utf8(header).ok()?).ok()?;
},
IpForwarding::Modern => {
let mut header = [0];
stream.read(&mut header).ok()?;
addr = match header[0] {
0x01 => {
let mut octets = [0; 4];
stream.read(&mut octets).ok()?;
let mut port = [0; 2];
stream.read(&mut port).ok()?;
let port = u16::from_be_bytes(port);
SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::from(octets), port))
}, 0x02 => {
let mut octets = [0; 16];
stream.read(&mut octets).ok()?;
let mut port = [0; 2];
stream.read(&mut port).ok()?;
let port = u16::from_be_bytes(port);
SocketAddr::V6(SocketAddrV6::new(Ipv6Addr::from(octets), port, 0, 0))
}, _ => { return None },
};
},
_ => {}
}
let mut raw_status = read_until(&mut stream, b"\r\n")?;
let mut request = Vec::new();
request.append(&mut raw_status.clone());
raw_status.truncate(raw_status.len() - 2);
let status = String::from_utf8(raw_status.clone()).ok()?;
let status = status.split(" ").collect::<Vec<&str>>();
debug!("{} {} read status", addr, status[1]);
let mut content_length = 0;
let mut host = None;
let mut is_chunked = false;
let mut keep_alive = false;
let mut headers = Vec::new();
loop {
let mut header = read_until(&mut stream, b"\r\n")?;
header.truncate(header.len() - 2);
if header.is_empty() {
break;
}
let header = String::from_utf8(header).ok()?;
let (key, value) = header.split_once(": ")?;
headers.push((key.to_string(), value.to_string()));
match key.to_lowercase().as_str() {
"transfer-encoding" => {
if value.contains("chunked") {
is_chunked = true;
}
},
"host" => {
host = Some(value.to_string());
},
"connection" => {
keep_alive = value.to_lowercase().contains("keep-alive");
},
"content-length" => {
content_length = value.parse::<usize>().ok()?;
},
_ => {
if let IpForwarding::Header(header) = &self.config.incoming_ip_forwarding {
if key.to_lowercase() == header.to_lowercase() {
addr = SocketAddr::from_str(value).ok()?;
}
}
},
}
}
debug!("{} {} read headers", addr, status[1]);
let mut conn: Connection = if conn.is_none() {
let host = host?;
let site = self.config.get_site(&host)?.clone();
Connection {
stream: BufReader::new(site.connect()?),
config: site,
keep_alive,
host
}
} else {
conn?
};
debug!("{} {} got connection", addr, status[1]);
match &conn.config.ip_forwarding {
IpForwarding::Simple => {
request.append(&mut addr.to_string().as_bytes().to_vec());
request.push(b'\n');
},
IpForwarding::Modern => {
match addr.ip() {
IpAddr::V4(ip) => {
request.push(0x01);
request.append(&mut ip.octets().to_vec());
}, IpAddr::V6(ip) => {
request.push(0x02);
request.append(&mut ip.octets().to_vec());
}
}
request.append(&mut addr.port().to_be_bytes().to_vec());
},
_ => {}
}
for (key, value) in headers {
let mut value = value.to_string();
match key.to_lowercase().as_str() {
"host" => {
if let Some(replace_host) = conn.config.replace_host.clone() {
value = replace_host;
}
},
_ => {}
}
if let IpForwarding::Header(header) = &conn.config.ip_forwarding {
if key.to_lowercase() == header.to_lowercase() {
continue;
}
}
request.append(&mut key.as_bytes().to_vec());
request.append(&mut b": ".to_vec());
request.append(&mut value.as_bytes().to_vec());
request.append(&mut b"\r\n".to_vec());
}
if let IpForwarding::Header(header) = &conn.config.ip_forwarding {
request.append(&mut header.as_bytes().to_vec());
request.append(&mut b": ".to_vec());
request.append(&mut addr.to_string().as_bytes().to_vec());
request.append(&mut b"\r\n".to_vec());
}
request.append(&mut b"\r\n".to_vec());
debug!("{:?}", String::from_utf8_lossy(&request));
conn.stream.get_mut().write_all(&request).ok()?;
debug!("{} {} sent request to server", addr, status[1]);
if content_length > 0 {
let buffer = stream.buffer().to_vec();
conn.stream.get_mut().write_all(&buffer).ok()?;
stream.consume(buffer.len());
let mut read = buffer.len();
debug!("{} {} send part of body to server", addr, status[1]);
while read < content_length {
let mut buf = vec![0; 4096];
let size = conn.stream.get_mut().read(&mut buf).ok()?;
if size == 0 { break }
buf.truncate(size);
read += size;
debug!("{} {} send response body part {} to clientr", addr, status[1], size);
stream.get_mut().write_all(&buf).ok()?;
}
} else if is_chunked {
transfer_chunked(&mut stream, conn.stream.get_mut())?;
} else {
let buffer = stream.buffer().to_vec();
conn.stream.get_mut().write_all(&buffer).ok()?;
stream.consume(buffer.len());
}
debug!("{} {} send body to server", addr, status[1]);
if conn.config.support_keep_alive {
let mut response = Vec::new();
let raw_status = read_until(&mut conn.stream, b"\r\n")?;
response.append(&mut raw_status.clone());
let mut content_length = 0;
let mut is_chunked = false;
loop {
let mut header = read_until(&mut conn.stream, b"\r\n")?;
response.append(&mut header.clone());
if header.len() == 2 {
break;
}
header.truncate(header.len() - 2);
let header = String::from_utf8(header).ok()?;
let (key, value) = header.split_once(": ")?;
match key.to_lowercase().as_str() {
"transfer-encoding" => {
if value.contains("chunked") {
is_chunked = true;
}
},
"content-length" => {
content_length = value.parse::<usize>().ok()?;
},
_ => {}
}
}
stream.get_mut().write_all(&response).ok()?;
debug!("{} {} send response header to clientr", addr, status[1]);
if content_length > 0 {
let buffer = conn.stream.buffer().to_vec();
stream.get_mut().write_all(&buffer).ok()?;
conn.stream.consume(buffer.len());
debug!("{} {} send response body part {} to clientr", addr, status[1], buffer.len());
let mut read = buffer.len();
while read < content_length {
let mut buf = vec![0; 4096];
let size = conn.stream.get_mut().read(&mut buf).ok()?;
if size == 0 { break }
buf.truncate(size);
read += size;
debug!("{} {} send response body part {} to clientr", addr, status[1], size);
stream.get_mut().write_all(&buf).ok()?;
}
} else if is_chunked {
transfer_chunked(&mut conn.stream, stream.get_mut())?;
} else {
let buffer = conn.stream.buffer().to_vec();
stream.get_mut().write_all(&buffer).ok()?;
conn.stream.consume(buffer.len());
}
debug!("{} {} send response body to clientr", addr, status[1]);
} else {
let buffer = conn.stream.buffer();
stream.get_mut().write_all(buffer).ok()?;
conn.stream.consume(buffer.len());
let mut buf = vec![0;4096];
while let Ok(n) = conn.stream.get_mut().read(&mut buf) {
if n == 0 { break }
buf.truncate(n);
stream.get_mut().write_all(&buf).ok()?;
buf = vec![0;4096];
}
}
info!("{addr} > {} {}://{}{}", status[0], if https { "https" } else { "http" }, conn.host, status[1]);
Some(conn)
}
}
fn read_until(stream: &mut impl BufRead, delimiter: &[u8]) -> Option<Vec<u8>> {
let mut data = Vec::new();
let last_byte = *delimiter.last()?;
loop {
let mut buf = Vec::new();
let buf_len = stream.read_until(last_byte, &mut buf).ok()?;
debug!("read buf len until {} {:?}", buf_len, String::from_utf8_lossy(&buf));
if buf_len == 0 {
return None
}
data.append(&mut buf);
if data.ends_with(delimiter) {
break;
}
}
Some(data)
}
fn transfer_chunked(src: &mut impl BufRead, dest: &mut impl Write) -> Option<()> {
loop {
let mut length = read_until(src, b"\r\n")?;
dest.write_all(&length).ok()?;
length.truncate(length.len()-2);
let length = String::from_utf8(length).ok()?;
let length = usize::from_str_radix(length.as_str(), 16).ok()?;
let mut data = vec![0u8; length+2];
src.read_exact(&mut data).ok()?;
dest.write_all(&data).ok()?;
if length == 0 {
break;
}
}
Some(())
}
pub trait Shutdown {
fn shutdown(&self);
}
impl Shutdown for TcpStream {
fn shutdown(&self) {
TcpStream::shutdown(self, std::net::Shutdown::Both).ignore();
}
}
impl <T: Shutdown> Shutdown for BufReader<T> {
fn shutdown(&self) {
self.get_ref().shutdown();
}
}
impl <C, T: Read + Write + Shutdown> Shutdown for StreamOwned<C, T> {
fn shutdown(&self) {
self.sock.shutdown();
}
}

View File

@ -39,7 +39,7 @@ struct ResolvesServerCertWildcard {
} }
impl ResolvesServerCertWildcard { impl ResolvesServerCertWildcard {
pub async fn new(config: Arc<Config>) -> Self { pub fn new(config: Arc<Config>) -> Self {
Self { config } Self { config }
} }
} }
@ -56,8 +56,8 @@ impl ResolvesServerCert for ResolvesServerCertWildcard {
} }
} }
pub async fn create_server_config(config: Arc<Config>) -> ServerConfig { pub fn create_server_config(config: Arc<Config>) -> ServerConfig {
ServerConfig::builder() ServerConfig::builder()
.with_no_client_auth() .with_no_client_auth()
.with_cert_resolver(Arc::new(ResolvesServerCertWildcard::new(config).await)) .with_cert_resolver(Arc::new(ResolvesServerCertWildcard::new(config)))
} }

32
tests/half_request.py Executable file
View File

@ -0,0 +1,32 @@
import socket
HOST = '172.16.1.32'
PORT = 80
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.connect((HOST, PORT))
s.send(b"""GET / HTTP/1.1\r
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7\r
Accept-Encoding: gzip, deflate, br, zstd\r
Accept-Language: en-US,en;q=0.9\r
Cache-Control: no-cache\r
Connection: keep-alive\r
Host: git.meex.lol\r
Pragma: no-cache\r
Sec-Fetch-Dest: document\r
Sec-Fetch-Mode: navigate\r
Sec-Fetch-Site: none\r
Sec-Fetch-User: ?1\r
Upgrade-Insecure-Requests: 1\r
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/135.0.0.0 Safari/537.36\r
sec-ch-ua: \"Chromium\";v=\"135\", \"Not-A.Brand\";v=\"8\"\r
sec-ch-ua-mobile: ?0\r
sec-ch-ua-platform: \"Linux\"\r
\r
""")
content_length = 0
while True:
data = s.recv(1024)
print(data)

40
tests/http_client.py Executable file
View File

@ -0,0 +1,40 @@
import socket
HOST = '172.16.1.32'
PORT = 80
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.connect((HOST, PORT))
i = 0
while True:
s.send(b"""GET / HTTP/1.1\r
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7\r
Accept-Encoding: gzip, deflate, br, zstd\r
Accept-Language: en-US,en;q=0.9\r
Cache-Control: no-cache\r
Connection: keep-alive\r
Host: git.meex.lol\r
Pragma: no-cache\r
Sec-Fetch-Dest: document\r
Sec-Fetch-Mode: navigate\r
Sec-Fetch-Site: none\r
Sec-Fetch-User: ?1\r
Upgrade-Insecure-Requests: 1\r
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/135.0.0.0 Safari/537.36\r
sec-ch-ua: \"Chromium\";v=\"135\", \"Not-A.Brand\";v=\"8\"\r
sec-ch-ua-mobile: ?0\r
sec-ch-ua-platform: \"Linux\"\r
\r
""")
content_length = 0
while content_length < 9392:
data = s.recv(1024)
if data:
content_length += len(data)
# print(data.decode("utf8", errors="ignore"), end="")
else:
print("sdfsdf")
i += 1
print(i)