Repository: abhinavsingh/proxy.py
Pull request #827 (2021-11-29 13:15:44)

Add benchmarks comparison for `proxy.py`, `tornado`, `aiohttp`, `flask`
# Results

1 Million requests benchmark results @ 8000 QPS

| Server | Throughput (req/sec) |
| ------ | ------------ |
| proxy.py | 30351.6691 |
| aiohttp | 6615.5052 |
| tornado | 3301.1874 |
| Flask | 830.1539 |

PS: Only #proxypy is able to process all the 1M requests

# Single Core Comparison
- `proxy.py` uses all cores on the system to provide such throughput
- Use `--num-acceptors 1` flag to compare single core performance
- This yields `7829.2851 req/sec`, still `20%` faster :)

codecov[bot]: # [Codecov](https://codecov.io/gh/abhinavsingh/proxy.py/pull/827?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Abhinav+Singh) Report
> Merging [#827](https://codecov.io/gh/abhinavsingh/proxy.py/pull/827?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Abhinav+Singh) (25017de) into [develop](https://codecov.io/gh/abhinavsingh/proxy.py/commit/37c96c90e0fe13cac67cab1a9523ef7e90966ce2?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Abhinav+Singh) (37c96c9) will **decrease** coverage by `0.20%`.
> The diff coverage is `n/a`.

[![Impacted file tree graph](https://codecov.io/gh/abhinavsingh/proxy.py/pull/827/graphs/tree.svg?width=650&height=150&src=pr&token=Zh9J7b4la2&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Abhinav+Singh)](https://codecov.io/gh/abhinavsingh/proxy.py/pull/827?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Abhinav+Singh)

```diff
@@             Coverage Diff             @@
##           develop     #827      +/-   ##
===========================================
- Coverage    87.41%   87.21%   -0.21%     
===========================================
  Files          129      128       -1     
  Lines         5699     5692       -7     
  Branches       467      423      -44     
===========================================
- Hits          4982     4964      -18     
- Misses         614      619       +5     
- Partials       103      109       +6     
```

| Flag | Coverage Δ | |
|---|---|---|
| pytest | `87.21% <ø> (-0.09%)` | :arrow_down: |

Flags with carried forward coverage won't be shown. [Click here](https://docs.codecov.io/docs/carryforward-flags?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Abhinav+Singh#carryforward-flags-in-the-pull-request-comment) to find out more.

| [Impacted Files](https://codecov.io/gh/abhinavsingh/proxy.py/pull/827?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Abhinav+Singh) | Coverage Δ | |
|---|---|---|
| [proxy/common/types.py](https://codecov.io/gh/abhinavsingh/proxy.py/pull/827/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Abhinav+Singh#diff-cHJveHkvY29tbW9uL3R5cGVzLnB5) | `86.66% <0.00%> (-13.34%)` | :arrow_down: |
| [tests/test\_set\_open\_file\_limit.py](https://codecov.io/gh/abhinavsingh/proxy.py/pull/827/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Abhinav+Singh#diff-dGVzdHMvdGVzdF9zZXRfb3Blbl9maWxlX2xpbWl0LnB5) | `96.42% <0.00%> (-3.58%)` | :arrow_down: |
| [proxy/common/utils.py](https://codecov.io/gh/abhinavsingh/proxy.py/pull/827/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Abhinav+Singh#diff-cHJveHkvY29tbW9uL3V0aWxzLnB5) | `90.83% <0.00%> (-2.50%)` | :arrow_down: |
| [proxy/testing/test\_case.py](https://codecov.io/gh/abhinavsingh/proxy.py/pull/827/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Abhinav+Singh#diff-cHJveHkvdGVzdGluZy90ZXN0X2Nhc2UucHk=) | `97.95% <0.00%> (-2.05%)` | :arrow_down: |
| [proxy/common/flag.py](https://codecov.io/gh/abhinavsingh/proxy.py/pull/827/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Abhinav+Singh#diff-cHJveHkvY29tbW9uL2ZsYWcucHk=) | `91.04% <0.00%> (-1.50%)` | :arrow_down: |
| [proxy/http/handler.py](https://codecov.io/gh/abhinavsingh/proxy.py/pull/827/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Abhinav+Singh#diff-cHJveHkvaHR0cC9oYW5kbGVyLnB5) | `72.27% <0.00%> (-1.49%)` | :arrow_down: |
| [proxy/core/acceptor/threadless.py](https://codecov.io/gh/abhinavsingh/proxy.py/pull/827/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Abhinav+Singh#diff-cHJveHkvY29yZS9hY2NlcHRvci90aHJlYWRsZXNzLnB5) | `85.88% <0.00%> (-0.09%)` | :arrow_down: |
| [proxy/proxy.py](https://codecov.io/gh/abhinavsingh/proxy.py/pull/827/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Abhinav+Singh#diff-cHJveHkvcHJveHkucHk=) | `98.70% <0.00%> (-0.02%)` | :arrow_down: |
| [tests/http/test\_http\_parser.py](https://codecov.io/gh/abhinavsingh/proxy.py/pull/827/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Abhinav+Singh#diff-dGVzdHMvaHR0cC90ZXN0X2h0dHBfcGFyc2VyLnB5) | `100.00% <0.00%> (ø)` | |
| [benchmark/\_\_init\_\_.py](https://codecov.io/gh/abhinavsingh/proxy.py/pull/827/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Abhinav+Singh#diff-YmVuY2htYXJrL19faW5pdF9fLnB5) | | |
| ... and [1 more](https://codecov.io/gh/abhinavsingh/proxy.py/pull/827/diff?src=pr&el=tree-more&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Abhinav+Singh) | |

------

[Continue to review full report at Codecov](https://codecov.io/gh/abhinavsingh/proxy.py/pull/827?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Abhinav+Singh).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Abhinav+Singh)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/abhinavsingh/proxy.py/pull/827?src=pr&el=footer&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Abhinav+Singh). Last update [37c96c9...25017de](https://codecov.io/gh/abhinavsingh/proxy.py/pull/827?src=pr&el=lastupdated&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Abhinav+Singh). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Abhinav+Singh).


Patch:
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index db86982..44c5dc6 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -176,6 +176,7 @@ repos:
     - --show-error-context
     - --strict
     - --strict-optional
+    - benchmark/
     - examples/
     - proxy/
     - tests/
diff --git a/benchmark/README.md b/benchmark/README.md
new file mode 100644
index 0000000..4bd4f39
--- /dev/null
+++ b/benchmark/README.md
@@ -0,0 +1,254 @@
+# Benchmark
+
+# Table of Contents
+- [Usage](#usage)
+- [Results](#results)
+  - [Proxy.py](#proxypy)
+  - [Tornado](#tornado)
+  - [AioHttp](#aiohttp)
+  - [Flask](#flask)
+
+## Usage
+
+```console
+❯ git clone https://github.com/abhinavsingh/proxy.py.git
+❯ cd proxy.py
+❯ pip install -r benchmark/requirements.txt
+❯ ./benchmark/compare.sh > /tmp/compare.log 2>&1
+```
+
+## Results
+
+```console
+❯ cat /tmp/compare.log
+CONCURRENCY: 100 workers, QPS: 8000 req/sec, TOTAL DURATION: 1m, TIMEOUT: 1 sec
+```
+
+### Proxy.py
+
+```console
+=============================
+Benchmarking Proxy.Py
+Server (pid:32232) running
+
+
+Summary:
+  Total:	60.0028 secs
+  Slowest:	0.0932 secs
+  Fastest:	0.0010 secs
+  Average:	0.0060 secs
+  Requests/sec:	30351.6691
+
+  Total data:	34602515 bytes
+  Size/request:	34 bytes
+
+Response time histogram:
+  0.001 [1]	|
+  0.010 [994328]	|■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
+  0.019 [5110]	|
+  0.029 [426]	|
+  0.038 [105]	|
+  0.047 [15]	|
+  0.056 [9]	|
+  0.066 [0]	|
+  0.075 [0]	|
+  0.084 [0]	|
+  0.093 [6]	|
+
+
+Latency distribution:
+  10% in 0.0018 secs
+  25% in 0.0022 secs
+  50% in 0.0028 secs
+  75% in 0.0037 secs
+  90% in 0.0050 secs
+  95% in 0.0060 secs
+  99% in 0.0088 secs
+
+Details (average, fastest, slowest):
+  DNS+dialup:	0.0000 secs, 0.0010 secs, 0.0932 secs
+  DNS-lookup:	0.0000 secs, 0.0000 secs, 0.0000 secs
+  req write:	0.0000 secs, 0.0000 secs, 0.0071 secs
+  resp wait:	0.0059 secs, 0.0008 secs, 0.0932 secs
+  resp read:	0.0000 secs, 0.0000 secs, 0.0065 secs
+
+Status code distribution:
+  [200]	1000000 responses
+
+Server gracefully shutdown
+=============================
+```
+
+### Tornado
+
+```console
+=============================
+Benchmarking Tornado
+Server (pid:31161) running
+
+Summary:
+  Total:	60.0208 secs
+  Slowest:	0.1234 secs
+  Fastest:	0.0050 secs
+  Average:	0.0323 secs
+  Requests/sec:	3301.1874
+
+  Total data:	3515190 bytes
+  Size/request:	19 bytes
+
+Response time histogram:
+  0.005 [1]	|
+  0.017 [6]	|
+  0.029 [13657]	|■■■
+  0.040 [168114]	|■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
+  0.052 [2458]	|■
+  0.064 [411]	|
+  0.076 [194]	|
+  0.088 [72]	|
+  0.100 [6]	|
+  0.112 [34]	|
+  0.123 [57]	|
+
+
+Latency distribution:
+  10% in 0.0288 secs
+  25% in 0.0299 secs
+  50% in 0.0317 secs
+  75% in 0.0338 secs
+  90% in 0.0360 secs
+  95% in 0.0378 secs
+  99% in 0.0426 secs
+
+Details (average, fastest, slowest):
+  DNS+dialup:	0.0000 secs, 0.0050 secs, 0.1234 secs
+  DNS-lookup:	0.0000 secs, 0.0000 secs, 0.0000 secs
+  req write:	0.0000 secs, 0.0000 secs, 0.0005 secs
+  resp wait:	0.0322 secs, 0.0048 secs, 0.0960 secs
+  resp read:	0.0000 secs, 0.0000 secs, 0.0010 secs
+
+Status code distribution:
+  [200]	185010 responses
+
+Error distribution:
+  [13130]	Get "http://127.0.0.1:8888/http-route-example": dial tcp 127.0.0.1:8888: connect: connection refused
+
+Server gracefully shutdown
+=============================
+```
+
+### AioHttp
+
+```console
+=============================
+Benchmarking AIOHTTP
+Server (pid:31148) running
+
+Summary:
+  Total:	60.0098 secs
+  Slowest:	1.4160 secs
+  Fastest:	0.0026 secs
+  Average:	0.0153 secs
+  Requests/sec:	6615.5052
+
+  Total data:	7260812 bytes
+  Size/request:	19 bytes
+
+Response time histogram:
+  0.003 [1]	|
+  0.144 [381427]	|■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
+  0.285 [67]	|
+  0.427 [133]	|
+  0.568 [16]	|
+  0.709 [300]	|
+  0.851 [100]	|
+  0.992 [100]	|
+  1.133 [1]	|
+  1.275 [0]	|
+  1.416 [3]	|
+
+
+Latency distribution:
+  10% in 0.0129 secs
+  25% in 0.0133 secs
+  50% in 0.0138 secs
+  75% in 0.0146 secs
+  90% in 0.0155 secs
+  95% in 0.0164 secs
+  99% in 0.0203 secs
+
+Details (average, fastest, slowest):
+  DNS+dialup:	0.0000 secs, 0.0026 secs, 1.4160 secs
+  DNS-lookup:	0.0000 secs, 0.0000 secs, 0.0000 secs
+  req write:	0.0000 secs, 0.0000 secs, 0.0006 secs
+  resp wait:	0.0152 secs, 0.0025 secs, 1.4160 secs
+  resp read:	0.0000 secs, 0.0000 secs, 0.9854 secs
+
+Status code distribution:
+  [200]	382148 responses
+
+Error distribution:
+  [96]	Get "http://127.0.0.1:8080/http-route-example": context deadline exceeded (Client.Timeout exceeded while awaiting headers)
+  [14751]	Get "http://127.0.0.1:8080/http-route-example": dial tcp 127.0.0.1:8080: connect: connection refused
+
+Server gracefully shutdown
+=============================
+```
+
+### Flask
+
+```console
+=============================
+Benchmarking Flask
+Server (pid:31127) running
+
+Summary:
+  Total:	60.3213 secs
+  Slowest:	1.0974 secs
+  Fastest:	0.0007 secs
+  Average:	0.0634 secs
+  Requests/sec:	830.1539
+
+  Total data:	637602 bytes
+  Size/request:	19 bytes
+
+Response time histogram:
+  0.001 [1]	|
+  0.110 [32803]	|■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
+  0.220 [438]	|■
+  0.330 [198]	|
+  0.439 [0]	|
+  0.549 [1]	|
+  0.659 [1]	|
+  0.768 [2]	|
+  0.878 [1]	|
+  0.988 [94]	|
+  1.097 [19]	|
+
+
+Latency distribution:
+  10% in 0.0412 secs
+  25% in 0.0580 secs
+  50% in 0.0637 secs
+  75% in 0.0649 secs
+  90% in 0.0676 secs
+  95% in 0.0716 secs
+  99% in 0.1668 secs
+
+Details (average, fastest, slowest):
+  DNS+dialup:	0.0010 secs, 0.0007 secs, 1.0974 secs
+  DNS-lookup:	0.0000 secs, 0.0000 secs, 0.0000 secs
+  req write:	0.0000 secs, 0.0000 secs, 0.0007 secs
+  resp wait:	0.0620 secs, 0.0005 secs, 1.0967 secs
+  resp read:	0.0003 secs, 0.0000 secs, 0.9407 secs
+
+Status code distribution:
+  [200]	33558 responses
+
+Error distribution:
+  [3359]	Get "http://127.0.0.1:8000/http-route-example": context deadline exceeded (Client.Timeout exceeded while awaiting headers)
+  [12904]	Get "http://127.0.0.1:8000/http-route-example": dial tcp 127.0.0.1:8000: connect: connection refused
+  [255]	Get "http://127.0.0.1:8000/http-route-example": dial tcp 127.0.0.1:8000: i/o timeout (Client.Timeout exceeded while awaiting headers)
+
+Server gracefully shutdown
+=============================
+```
diff --git a/tests/benchmark/__init__.py b/benchmark/__init__.py
similarity index 100%
rename from tests/benchmark/__init__.py
rename to benchmark/__init__.py
diff --git a/benchmark/aiohttp/__init__.py b/benchmark/aiohttp/__init__.py
new file mode 100644
index 0000000..232621f
--- /dev/null
+++ b/benchmark/aiohttp/__init__.py
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 -*-
+"""
+    proxy.py
+    ~~~~~~~~
+    ⚡⚡⚡ Fast, Lightweight, Pluggable, TLS interception capable proxy server focused on
+    Network monitoring, controls & Application development, testing, debugging.
+
+    :copyright: (c) 2013-present by Abhinav Singh and contributors.
+    :license: BSD, see LICENSE for more details.
+"""
diff --git a/benchmark/aiohttp/server.py b/benchmark/aiohttp/server.py
new file mode 100644
index 0000000..9c9075b
--- /dev/null
+++ b/benchmark/aiohttp/server.py
@@ -0,0 +1,26 @@
+# -*- coding: utf-8 -*-
+"""
+    proxy.py
+    ~~~~~~~~
+    ⚡⚡⚡ Fast, Lightweight, Pluggable, TLS interception capable proxy server focused on
+    Network monitoring, controls & Application development, testing, debugging.
+
+    :copyright: (c) 2013-present by Abhinav Singh and contributors.
+    :license: BSD, see LICENSE for more details.
+"""
+from aiohttp import web
+
+
+async def handle(request: web.Request) -> web.StreamResponse:
+    return web.Response(body=b'HTTP route response')
+
+
+app = web.Application()
+
+app.add_routes(
+    [
+        web.get('/http-route-example', handle),
+    ],
+)
+
+web.run_app(app, host='127.0.0.1', port=8080)
diff --git a/benchmark/compare.sh b/benchmark/compare.sh
new file mode 100755
index 0000000..302712c
--- /dev/null
+++ b/benchmark/compare.sh
@@ -0,0 +1,119 @@
+#!/bin/bash
+#
+# proxy.py
+# ~~~~~~~~
+# ⚡⚡⚡ Fast, Lightweight, Programmable, TLS interception capable
+#     proxy server for Application debugging, testing and development.
+#
+# :copyright: (c) 2013-present by Abhinav Singh and contributors.
+# :license: BSD, see LICENSE for more details.
+#
+usage() {
+  echo "Usage: ./benchmark/compare.sh"
+  echo "You must run this script from proxy.py repo root."
+}
+
+DIRNAME=$(dirname "$0")
+if [ "$DIRNAME" != "./benchmark" ]; then
+  usage
+  exit 1
+fi
+
+BASENAME=$(basename "$0")
+if [ "$BASENAME" != "compare.sh" ]; then
+  usage
+  exit 1
+fi
+
+PWD=$(pwd)
+if [ $(basename $PWD) != "proxy.py" ]; then
+  usage
+  exit 1
+fi
+
+TIMEOUT=1
+QPS=8000
+CONCURRENCY=100
+DURATION=1m
+TOTAL_REQUESTS=100000
+OPEN_FILE_LIMIT=65536
+BACKLOG=OPEN_FILE_LIMIT
+
+SERVER_HOST=127.0.0.1
+
+AIOHTTP_PORT=8080
+FLASK_PORT=8000
+TORNADO_PORT=8888
+PROXYPY_PORT=8899
+
+ulimit -n $OPEN_FILE_LIMIT
+
+echo "CONCURRENCY: $CONCURRENCY workers, QPS: $QPS req/sec, TOTAL DURATION: $DURATION, TIMEOUT: $TIMEOUT sec"
+
+start_benchmark_stop() {
+  python ./benchmark/$1/server.py > /dev/null 2>&1 &
+  local SERVER_PID=$!
+  echo "Server (pid:$SERVER_PID) running"
+  sleep 1
+  hey \
+      -z $DURATION \
+      -c $CONCURRENCY \
+      -q $QPS \
+      -t $TIMEOUT \
+      http://127.0.0.1:$2/http-route-example
+  kill -15 $SERVER_PID
+  sleep 1
+  kill -0 $SERVER_PID > /dev/null 2>&1
+  local RUNNING=$?
+  if [ "$RUNNING" == "1" ]; then
+    echo "Server gracefully shutdown"
+  fi
+}
+
+benchmark_proxy_py() {
+  python -m proxy \
+    --hostname 127.0.0.1 \
+    --port $PROXYPY_PORT \
+    --backlog 65536 \
+    --open-file-limit 65536 \
+    --enable-web-server \
+    --plugin proxy.plugin.WebServerPlugin \
+    --disable-http-proxy \
+    --local-executor --log-file /dev/null > /dev/null 2>&1 &
+  local SERVER_PID=$!
+  echo "Server (pid:$SERVER_PID) running"
+  sleep 1
+  hey \
+      -z $DURATION \
+      -c $CONCURRENCY \
+      -q $QPS \
+      -t $TIMEOUT \
+      http://127.0.0.1:$PROXYPY_PORT/http-route-example
+  kill -15 $SERVER_PID
+  sleep 1
+  kill -0 $SERVER_PID > /dev/null 2>&1
+  local RUNNING=$?
+  if [ "$RUNNING" == "1" ]; then
+    echo "Server gracefully shutdown"
+  fi
+}
+
+echo "============================="
+echo "Benchmarking Proxy.Py"
+benchmark_proxy_py
+echo "============================="
+
+echo "============================="
+echo "Benchmarking AIOHTTP"
+start_benchmark_stop aiohttp $AIOHTTP_PORT
+echo "============================="
+
+echo "============================="
+echo "Benchmarking Tornado"
+start_benchmark_stop tornado $TORNADO_PORT
+echo "============================="
+
+echo "============================="
+echo "Benchmarking Flask"
+start_benchmark_stop flask $FLASK_PORT
+echo "============================="
diff --git a/benchmark/flask/__init__.py b/benchmark/flask/__init__.py
new file mode 100644
index 0000000..232621f
--- /dev/null
+++ b/benchmark/flask/__init__.py
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 -*-
+"""
+    proxy.py
+    ~~~~~~~~
+    ⚡⚡⚡ Fast, Lightweight, Pluggable, TLS interception capable proxy server focused on
+    Network monitoring, controls & Application development, testing, debugging.
+
+    :copyright: (c) 2013-present by Abhinav Singh and contributors.
+    :license: BSD, see LICENSE for more details.
+"""
diff --git a/benchmark/flask/server.py b/benchmark/flask/server.py
new file mode 100644
index 0000000..a18324c
--- /dev/null
+++ b/benchmark/flask/server.py
@@ -0,0 +1,22 @@
+# -*- coding: utf-8 -*-
+"""
+    proxy.py
+    ~~~~~~~~
+    ⚡⚡⚡ Fast, Lightweight, Pluggable, TLS interception capable proxy server focused on
+    Network monitoring, controls & Application development, testing, debugging.
+
+    :copyright: (c) 2013-present by Abhinav Singh and contributors.
+    :license: BSD, see LICENSE for more details.
+"""
+from flask import Flask
+
+app = Flask(__name__)
+
+
+@app.route('/http-route-example')   # type: ignore
+def hello_world() -> str:
+    return 'HTTP route response'
+
+
+if __name__ == '__main__':
+    app.run(host='127.0.0.1', port=8000)
diff --git a/benchmark/requirements.txt b/benchmark/requirements.txt
new file mode 100644
index 0000000..c67c342
--- /dev/null
+++ b/benchmark/requirements.txt
@@ -0,0 +1,3 @@
+aiohttp==3.8.1
+Flask==2.0.2
+tornado==6.1
diff --git a/benchmark/tornado/server.py b/benchmark/tornado/server.py
new file mode 100644
index 0000000..3af22ab
--- /dev/null
+++ b/benchmark/tornado/server.py
@@ -0,0 +1,30 @@
+# -*- coding: utf-8 -*-
+"""
+    proxy.py
+    ~~~~~~~~
+    ⚡⚡⚡ Fast, Lightweight, Pluggable, TLS interception capable proxy server focused on
+    Network monitoring, controls & Application development, testing, debugging.
+
+    :copyright: (c) 2013-present by Abhinav Singh and contributors.
+    :license: BSD, see LICENSE for more details.
+"""
+import tornado.ioloop
+import tornado.web
+
+
+# pylint: disable=W0223
+class MainHandler(tornado.web.RequestHandler):      # type: ignore[misc]
+    def get(self) -> None:
+        self.write('HTTP route response')
+
+
+def make_app() -> tornado.web.Application:
+    return tornado.web.Application([
+        (r'/http-route-example', MainHandler),
+    ])
+
+
+if __name__ == '__main__':
+    app = make_app()
+    app.listen(8888, address='127.0.0.1')
+    tornado.ioloop.IOLoop.current().start()
diff --git a/check.py b/check.py
index 038758a..af3b905 100644
--- a/check.py
+++ b/check.py
@@ -36,6 +36,7 @@ ALL_PY_FILES = (
     list(REPO_ROOT.glob('*.py')) +
     list((REPO_ROOT / 'proxy').rglob('*.py')) +
     list((REPO_ROOT / 'examples').rglob('*.py')) +
+    list((REPO_ROOT / 'benchmark').rglob('*.py')) +
     list((REPO_ROOT / 'tests').rglob('*.py'))
 )
 
diff --git a/tox.ini b/tox.ini
index daa25f9..e31f440 100644
--- a/tox.ini
+++ b/tox.ini
@@ -231,5 +231,6 @@ deps =
   pytest-mock >= 3.6.1
   -r docs/requirements.in
   -r requirements-tunnel.txt
+  -r benchmark/requirements.txt
 isolated_build = true
 skip_install = true


Test patch:
diff --git a/pytest.ini b/pytest.ini
index bc8ff5b..adf8bed 100644
--- a/pytest.ini
+++ b/pytest.ini
@@ -74,6 +74,7 @@ norecursedirs =
   build
   dist
   docs
+  benchmark
   examples
   proxy.egg-info
   .cache