pyproxytools 0.4.1__tar.gz → 0.5.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {pyproxytools-0.4.1 → pyproxytools-0.5.1}/LICENSE +1 -1
- pyproxytools-0.5.1/MANIFEST.in +2 -0
- {pyproxytools-0.4.1/pyproxytools.egg-info → pyproxytools-0.5.1}/PKG-INFO +36 -23
- {pyproxytools-0.4.1 → pyproxytools-0.5.1}/README.md +25 -14
- {pyproxytools-0.4.1 → pyproxytools-0.5.1}/pyproject.toml +25 -3
- {pyproxytools-0.4.1 → pyproxytools-0.5.1}/pyproxy/__init__.py +1 -1
- pyproxytools-0.5.1/pyproxy/handlers/client.py +114 -0
- pyproxytools-0.5.1/pyproxy/handlers/http.py +257 -0
- pyproxytools-0.5.1/pyproxy/handlers/https.py +408 -0
- {pyproxytools-0.4.1 → pyproxytools-0.5.1}/pyproxy/modules/filter.py +6 -17
- pyproxytools-0.5.1/pyproxy/monitoring/__init__.py +57 -0
- pyproxytools-0.5.1/pyproxy/monitoring/auth.py +31 -0
- pyproxytools-0.5.1/pyproxy/monitoring/messages.pot +235 -0
- pyproxytools-0.5.1/pyproxy/monitoring/monitor.py +196 -0
- pyproxytools-0.5.1/pyproxy/monitoring/routes.py +253 -0
- pyproxytools-0.5.1/pyproxy/monitoring/translations/en/LC_MESSAGES/messages.mo +0 -0
- pyproxytools-0.5.1/pyproxy/monitoring/translations/en/LC_MESSAGES/messages.po +248 -0
- pyproxytools-0.5.1/pyproxy/monitoring/translations/fr/LC_MESSAGES/messages.mo +0 -0
- pyproxytools-0.5.1/pyproxy/monitoring/translations/fr/LC_MESSAGES/messages.po +248 -0
- pyproxytools-0.5.1/pyproxy/pyproxy.py +145 -0
- {pyproxytools-0.4.1 → pyproxytools-0.5.1}/pyproxy/server.py +84 -70
- {pyproxytools-0.4.1 → pyproxytools-0.5.1}/pyproxy/utils/args.py +10 -30
- pyproxytools-0.5.1/pyproxy/utils/config.py +102 -0
- {pyproxytools-0.4.1 → pyproxytools-0.5.1}/pyproxy/utils/crypto.py +1 -3
- pyproxytools-0.5.1/pyproxy/utils/http_req.py +24 -0
- {pyproxytools-0.4.1 → pyproxytools-0.5.1}/pyproxy/utils/logger.py +22 -3
- {pyproxytools-0.4.1 → pyproxytools-0.5.1/pyproxytools.egg-info}/PKG-INFO +36 -23
- {pyproxytools-0.4.1 → pyproxytools-0.5.1}/pyproxytools.egg-info/SOURCES.txt +9 -6
- pyproxytools-0.5.1/pyproxytools.egg-info/requires.txt +8 -0
- {pyproxytools-0.4.1 → pyproxytools-0.5.1}/pyproxytools.egg-info/top_level.txt +0 -1
- pyproxytools-0.5.1/requirements.txt +8 -0
- {pyproxytools-0.4.1 → pyproxytools-0.5.1}/setup.py +3 -1
- {pyproxytools-0.4.1 → pyproxytools-0.5.1}/tests/modules/test_filter.py +2 -6
- pyproxytools-0.5.1/tests/utils/test_http_req.py +39 -0
- {pyproxytools-0.4.1 → pyproxytools-0.5.1}/tests/utils/test_logger.py +15 -6
- pyproxytools-0.4.1/benchmark/benchmark.py +0 -165
- pyproxytools-0.4.1/benchmark/utils/html.py +0 -179
- pyproxytools-0.4.1/benchmark/utils/req.py +0 -43
- pyproxytools-0.4.1/pyproxy/handlers/client.py +0 -126
- pyproxytools-0.4.1/pyproxy/handlers/http.py +0 -197
- pyproxytools-0.4.1/pyproxy/handlers/https.py +0 -308
- pyproxytools-0.4.1/pyproxy/monitoring/web.py +0 -279
- pyproxytools-0.4.1/pyproxy/pyproxy.py +0 -120
- pyproxytools-0.4.1/pyproxy/utils/config.py +0 -124
- pyproxytools-0.4.1/pyproxy/utils/http_req.py +0 -53
- pyproxytools-0.4.1/pyproxy/utils/version.py +0 -0
- pyproxytools-0.4.1/pyproxytools.egg-info/requires.txt +0 -7
- pyproxytools-0.4.1/requirements.txt +0 -7
- pyproxytools-0.4.1/tests/modules/__init__.py +0 -0
- pyproxytools-0.4.1/tests/utils/__init__.py +0 -0
- pyproxytools-0.4.1/tests/utils/test_http_req.py +0 -69
- {pyproxytools-0.4.1 → pyproxytools-0.5.1}/pyproxy/__main__.py +0 -0
- {pyproxytools-0.4.1/benchmark/utils → pyproxytools-0.5.1/pyproxy/handlers}/__init__.py +0 -0
- {pyproxytools-0.4.1/pyproxy/handlers → pyproxytools-0.5.1/pyproxy/modules}/__init__.py +0 -0
- {pyproxytools-0.4.1 → pyproxytools-0.5.1}/pyproxy/modules/cancel_inspect.py +0 -0
- {pyproxytools-0.4.1 → pyproxytools-0.5.1}/pyproxy/modules/custom_header.py +0 -0
- {pyproxytools-0.4.1 → pyproxytools-0.5.1}/pyproxy/modules/shortcuts.py +0 -0
- {pyproxytools-0.4.1/pyproxy/modules → pyproxytools-0.5.1/pyproxy/utils}/__init__.py +0 -0
- {pyproxytools-0.4.1 → pyproxytools-0.5.1}/pyproxytools.egg-info/dependency_links.txt +0 -0
- {pyproxytools-0.4.1 → pyproxytools-0.5.1}/pyproxytools.egg-info/entry_points.txt +0 -0
- {pyproxytools-0.4.1 → pyproxytools-0.5.1}/setup.cfg +0 -0
- {pyproxytools-0.4.1/pyproxy/monitoring → pyproxytools-0.5.1/tests/modules}/__init__.py +0 -0
- {pyproxytools-0.4.1 → pyproxytools-0.5.1}/tests/modules/test_cancel_inspect.py +0 -0
- {pyproxytools-0.4.1 → pyproxytools-0.5.1}/tests/modules/test_custom_header.py +0 -0
- {pyproxytools-0.4.1 → pyproxytools-0.5.1}/tests/modules/test_shortcuts.py +0 -0
- {pyproxytools-0.4.1/pyproxy → pyproxytools-0.5.1/tests}/utils/__init__.py +0 -0
- {pyproxytools-0.4.1 → pyproxytools-0.5.1}/tests/utils/test_crypto.py +0 -0
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pyproxytools
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.5.1
|
|
4
4
|
Summary: Lightweight and fast python web proxy
|
|
5
|
-
Author:
|
|
5
|
+
Author: pyproxytools
|
|
6
6
|
License-Expression: MIT
|
|
7
|
-
Project-URL: Documentation, https://github.
|
|
8
|
-
Project-URL: Issue tracker, https://github.com/
|
|
7
|
+
Project-URL: Documentation, https://pyproxytools.github.io/pyproxy-docs/
|
|
8
|
+
Project-URL: Issue tracker, https://github.com/pyproxytools/pyproxy/issues
|
|
9
9
|
Classifier: Development Status :: 5 - Production/Stable
|
|
10
10
|
Classifier: Intended Audience :: Developers
|
|
11
11
|
Classifier: Natural Language :: English
|
|
@@ -15,6 +15,7 @@ Classifier: Programming Language :: Python :: 3.9
|
|
|
15
15
|
Classifier: Programming Language :: Python :: 3.10
|
|
16
16
|
Classifier: Programming Language :: Python :: 3.11
|
|
17
17
|
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
18
19
|
Classifier: Topic :: Internet
|
|
19
20
|
Classifier: Topic :: Software Development :: Libraries
|
|
20
21
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
@@ -22,12 +23,13 @@ Classifier: Topic :: Utilities
|
|
|
22
23
|
Classifier: Typing :: Typed
|
|
23
24
|
Description-Content-Type: text/markdown
|
|
24
25
|
License-File: LICENSE
|
|
25
|
-
Requires-Dist: rich-argparse>=1.7.
|
|
26
|
-
Requires-Dist: pyOpenSSL>=25.
|
|
27
|
-
Requires-Dist: requests>=2.
|
|
28
|
-
Requires-Dist: Flask>=3.1.
|
|
26
|
+
Requires-Dist: rich-argparse>=1.7.1
|
|
27
|
+
Requires-Dist: pyOpenSSL>=25.3.0
|
|
28
|
+
Requires-Dist: requests>=2.32.5
|
|
29
|
+
Requires-Dist: Flask>=3.1.2
|
|
29
30
|
Requires-Dist: Flask-HTTPAuth>=4.8.0
|
|
30
|
-
Requires-Dist:
|
|
31
|
+
Requires-Dist: Flask-Babel>=4.0.0
|
|
32
|
+
Requires-Dist: psutil>=7.1.0
|
|
31
33
|
Requires-Dist: colorlog>=6.9.0
|
|
32
34
|
Dynamic: license-file
|
|
33
35
|
|
|
@@ -39,14 +41,14 @@ Dynamic: license-file
|
|
|
39
41
|
**pyproxy** is a lightweight, fast, and customizable Python-based web proxy server designed to handle both HTTP and HTTPS traffic efficiently. It can be used for various purposes, including web scraping, traffic monitoring, and content filtering.
|
|
40
42
|
|
|
41
43
|
<p align="center">
|
|
42
|
-
<img src="https://img.shields.io/github/license/
|
|
43
|
-
<img src="https://img.shields.io/github/issues/
|
|
44
|
-
<img src="https://img.shields.io/github/issues-closed/
|
|
44
|
+
<img src="https://img.shields.io/github/license/pyproxytools/pyproxy?style=for-the-badge">
|
|
45
|
+
<img src="https://img.shields.io/github/issues/pyproxytools/pyproxy?style=for-the-badge">
|
|
46
|
+
<img src="https://img.shields.io/github/issues-closed/pyproxytools/pyproxy?style=for-the-badge">
|
|
45
47
|
<br>
|
|
46
|
-
<img src="https://img.shields.io/github/forks/
|
|
47
|
-
<img src="https://img.shields.io/github/stars/
|
|
48
|
-
<img src="https://img.shields.io/github/commit-activity/w/
|
|
49
|
-
<img src="https://img.shields.io/github/contributors/
|
|
48
|
+
<img src="https://img.shields.io/github/forks/pyproxytools/pyproxy?style=for-the-badge">
|
|
49
|
+
<img src="https://img.shields.io/github/stars/pyproxytools/pyproxy?style=for-the-badge">
|
|
50
|
+
<img src="https://img.shields.io/github/commit-activity/w/pyproxytools/pyproxy?style=for-the-badge">
|
|
51
|
+
<img src="https://img.shields.io/github/contributors/pyproxytools/pyproxy?style=for-the-badge">
|
|
50
52
|
<br>
|
|
51
53
|
<img src="https://img.shields.io/pypi/v/pyproxytools?style=for-the-badge">
|
|
52
54
|
<img src="https://img.shields.io/pypi/pyversions/pyproxytools?style=for-the-badge">
|
|
@@ -81,18 +83,24 @@ pip install pyproxytools
|
|
|
81
83
|
|
|
82
84
|
### Install from source
|
|
83
85
|
```bash
|
|
84
|
-
git clone https://github.com/
|
|
86
|
+
git clone https://github.com/pyproxytools/pyproxy.git
|
|
85
87
|
cd pyproxy
|
|
86
88
|
pip install -r requirements.txt
|
|
87
89
|
```
|
|
88
90
|
|
|
89
91
|
### Install with Docker
|
|
90
92
|
```bash
|
|
91
|
-
docker pull ghcr.io/
|
|
92
|
-
docker run -d ghcr.io/
|
|
93
|
+
docker pull ghcr.io/pyproxytools/pyproxy:latest
|
|
94
|
+
docker run -d ghcr.io/pyproxytools/pyproxy:latest
|
|
93
95
|
```
|
|
94
96
|
You can use slim images by adding `-slim` to the end of the tags
|
|
95
97
|
|
|
98
|
+
### Install with Compose
|
|
99
|
+
```bash
|
|
100
|
+
wget https://raw.githubusercontent.com/pyproxytools/pyproxy/main/docker-compose.yml
|
|
101
|
+
docker-compose up -d
|
|
102
|
+
```
|
|
103
|
+
|
|
96
104
|
## 🚀 **Usage**
|
|
97
105
|
|
|
98
106
|
### Start the proxy
|
|
@@ -103,7 +111,7 @@ The proxy will be available at: `0.0.0.0:8080`.
|
|
|
103
111
|
The access log will be available at `./logs/access.log`.
|
|
104
112
|
|
|
105
113
|
## 📚 **Documentation**
|
|
106
|
-
If you encounter any problems, or if you want to use the program in a particular way, I advise you to read the [documentation](https://github.
|
|
114
|
+
If you encounter any problems, or if you want to use the program in a particular way, I advise you to read the [documentation](https://pyproxytools.github.io/pyproxy-docs/).
|
|
107
115
|
|
|
108
116
|
## 🔧 **To do**
|
|
109
117
|
|
|
@@ -112,7 +120,7 @@ If you encounter any problems, or if you want to use the program in a particular
|
|
|
112
120
|
|
|
113
121
|
## 🏎️ **Benchmark**
|
|
114
122
|
|
|
115
|
-
If you're interested in benchmarking the performance of the proxy or comparing request times with and without a proxy, please refer to the [Benchmark
|
|
123
|
+
If you're interested in benchmarking the performance of the proxy or comparing request times with and without a proxy, please refer to the [Benchmark repository](https://github.com/pyproxytools/pyproxy-benchmark) for detailed instructions on how to run the benchmarking tests and generate reports.
|
|
116
124
|
|
|
117
125
|
## 📄 **License**
|
|
118
126
|
|
|
@@ -124,12 +132,17 @@ Contributions are welcome and appreciated! If you'd like to improve this project
|
|
|
124
132
|
|
|
125
133
|
## 📦 Deployment with Ansible
|
|
126
134
|
|
|
127
|
-
If you want to deploy **pyproxy** automatically to remote servers (via source or Docker), an official [Ansible role](https://github.com/
|
|
135
|
+
If you want to deploy **pyproxy** automatically to remote servers (via source or Docker), an official [Ansible role](https://github.com/pyproxytools/pyproxy-ansible) is available:
|
|
128
136
|
|
|
129
137
|
* 🔧 Install from source or run as a Docker container
|
|
130
138
|
* 📁 Supports customization of ports, versions, and paths
|
|
131
139
|
* 🚀 Easily integrable into your infrastructure or CI/CD pipelines
|
|
132
140
|
|
|
133
|
-
👉 Check out the [ansible
|
|
141
|
+
👉 Check out the [pyproxy-ansible](https://github.com/pyproxytools/pyproxy-ansible) repository for more details and usage instructions.
|
|
142
|
+
|
|
143
|
+
## 🧩 **Python SDK for Administration and Monitoring**
|
|
144
|
+
|
|
145
|
+
A dedicated Python SDK is available to interact programmatically with the **pyproxy** administration and monitoring interface: [**pyproxy-sdk-py**](https://github.com/pyproxytools/pyproxy-sdk-py).
|
|
146
|
+
This SDK provides a simple and consistent API to manage your proxy configuration remotely — allowing you to add or delete domains, manage URLs, create and remove clients, and perform other administrative tasks. It is designed to integrate easily into automation scripts, dashboards, or backend systems that need to control and monitor pyproxy instances.
|
|
134
147
|
|
|
135
148
|
---
|
|
@@ -6,14 +6,14 @@
|
|
|
6
6
|
**pyproxy** is a lightweight, fast, and customizable Python-based web proxy server designed to handle both HTTP and HTTPS traffic efficiently. It can be used for various purposes, including web scraping, traffic monitoring, and content filtering.
|
|
7
7
|
|
|
8
8
|
<p align="center">
|
|
9
|
-
<img src="https://img.shields.io/github/license/
|
|
10
|
-
<img src="https://img.shields.io/github/issues/
|
|
11
|
-
<img src="https://img.shields.io/github/issues-closed/
|
|
9
|
+
<img src="https://img.shields.io/github/license/pyproxytools/pyproxy?style=for-the-badge">
|
|
10
|
+
<img src="https://img.shields.io/github/issues/pyproxytools/pyproxy?style=for-the-badge">
|
|
11
|
+
<img src="https://img.shields.io/github/issues-closed/pyproxytools/pyproxy?style=for-the-badge">
|
|
12
12
|
<br>
|
|
13
|
-
<img src="https://img.shields.io/github/forks/
|
|
14
|
-
<img src="https://img.shields.io/github/stars/
|
|
15
|
-
<img src="https://img.shields.io/github/commit-activity/w/
|
|
16
|
-
<img src="https://img.shields.io/github/contributors/
|
|
13
|
+
<img src="https://img.shields.io/github/forks/pyproxytools/pyproxy?style=for-the-badge">
|
|
14
|
+
<img src="https://img.shields.io/github/stars/pyproxytools/pyproxy?style=for-the-badge">
|
|
15
|
+
<img src="https://img.shields.io/github/commit-activity/w/pyproxytools/pyproxy?style=for-the-badge">
|
|
16
|
+
<img src="https://img.shields.io/github/contributors/pyproxytools/pyproxy?style=for-the-badge">
|
|
17
17
|
<br>
|
|
18
18
|
<img src="https://img.shields.io/pypi/v/pyproxytools?style=for-the-badge">
|
|
19
19
|
<img src="https://img.shields.io/pypi/pyversions/pyproxytools?style=for-the-badge">
|
|
@@ -48,18 +48,24 @@ pip install pyproxytools
|
|
|
48
48
|
|
|
49
49
|
### Install from source
|
|
50
50
|
```bash
|
|
51
|
-
git clone https://github.com/
|
|
51
|
+
git clone https://github.com/pyproxytools/pyproxy.git
|
|
52
52
|
cd pyproxy
|
|
53
53
|
pip install -r requirements.txt
|
|
54
54
|
```
|
|
55
55
|
|
|
56
56
|
### Install with Docker
|
|
57
57
|
```bash
|
|
58
|
-
docker pull ghcr.io/
|
|
59
|
-
docker run -d ghcr.io/
|
|
58
|
+
docker pull ghcr.io/pyproxytools/pyproxy:latest
|
|
59
|
+
docker run -d ghcr.io/pyproxytools/pyproxy:latest
|
|
60
60
|
```
|
|
61
61
|
You can use slim images by adding `-slim` to the end of the tags
|
|
62
62
|
|
|
63
|
+
### Install with Compose
|
|
64
|
+
```bash
|
|
65
|
+
wget https://raw.githubusercontent.com/pyproxytools/pyproxy/main/docker-compose.yml
|
|
66
|
+
docker-compose up -d
|
|
67
|
+
```
|
|
68
|
+
|
|
63
69
|
## 🚀 **Usage**
|
|
64
70
|
|
|
65
71
|
### Start the proxy
|
|
@@ -70,7 +76,7 @@ The proxy will be available at: `0.0.0.0:8080`.
|
|
|
70
76
|
The access log will be available at `./logs/access.log`.
|
|
71
77
|
|
|
72
78
|
## 📚 **Documentation**
|
|
73
|
-
If you encounter any problems, or if you want to use the program in a particular way, I advise you to read the [documentation](https://github.
|
|
79
|
+
If you encounter any problems, or if you want to use the program in a particular way, I advise you to read the [documentation](https://pyproxytools.github.io/pyproxy-docs/).
|
|
74
80
|
|
|
75
81
|
## 🔧 **To do**
|
|
76
82
|
|
|
@@ -79,7 +85,7 @@ If you encounter any problems, or if you want to use the program in a particular
|
|
|
79
85
|
|
|
80
86
|
## 🏎️ **Benchmark**
|
|
81
87
|
|
|
82
|
-
If you're interested in benchmarking the performance of the proxy or comparing request times with and without a proxy, please refer to the [Benchmark
|
|
88
|
+
If you're interested in benchmarking the performance of the proxy or comparing request times with and without a proxy, please refer to the [Benchmark repository](https://github.com/pyproxytools/pyproxy-benchmark) for detailed instructions on how to run the benchmarking tests and generate reports.
|
|
83
89
|
|
|
84
90
|
## 📄 **License**
|
|
85
91
|
|
|
@@ -91,12 +97,17 @@ Contributions are welcome and appreciated! If you'd like to improve this project
|
|
|
91
97
|
|
|
92
98
|
## 📦 Deployment with Ansible
|
|
93
99
|
|
|
94
|
-
If you want to deploy **pyproxy** automatically to remote servers (via source or Docker), an official [Ansible role](https://github.com/
|
|
100
|
+
If you want to deploy **pyproxy** automatically to remote servers (via source or Docker), an official [Ansible role](https://github.com/pyproxytools/pyproxy-ansible) is available:
|
|
95
101
|
|
|
96
102
|
* 🔧 Install from source or run as a Docker container
|
|
97
103
|
* 📁 Supports customization of ports, versions, and paths
|
|
98
104
|
* 🚀 Easily integrable into your infrastructure or CI/CD pipelines
|
|
99
105
|
|
|
100
|
-
👉 Check out the [ansible
|
|
106
|
+
👉 Check out the [pyproxy-ansible](https://github.com/pyproxytools/pyproxy-ansible) repository for more details and usage instructions.
|
|
107
|
+
|
|
108
|
+
## 🧩 **Python SDK for Administration and Monitoring**
|
|
109
|
+
|
|
110
|
+
A dedicated Python SDK is available to interact programmatically with the **pyproxy** administration and monitoring interface: [**pyproxy-sdk-py**](https://github.com/pyproxytools/pyproxy-sdk-py).
|
|
111
|
+
This SDK provides a simple and consistent API to manage your proxy configuration remotely — allowing you to add or delete domains, manage URLs, create and remove clients, and perform other administrative tasks. It is designed to integrate easily into automation scripts, dashboards, or backend systems that need to control and monitor pyproxy instances.
|
|
101
112
|
|
|
102
113
|
---
|
|
@@ -6,7 +6,7 @@ license = "MIT"
|
|
|
6
6
|
license-files = [
|
|
7
7
|
"LICENSE",
|
|
8
8
|
]
|
|
9
|
-
authors = [{name = "
|
|
9
|
+
authors = [{name = "pyproxytools"}]
|
|
10
10
|
classifiers = [
|
|
11
11
|
"Development Status :: 5 - Production/Stable",
|
|
12
12
|
"Intended Audience :: Developers",
|
|
@@ -17,6 +17,7 @@ classifiers = [
|
|
|
17
17
|
"Programming Language :: Python :: 3.10",
|
|
18
18
|
"Programming Language :: Python :: 3.11",
|
|
19
19
|
"Programming Language :: Python :: 3.12",
|
|
20
|
+
"Programming Language :: Python :: 3.13",
|
|
20
21
|
"Topic :: Internet",
|
|
21
22
|
"Topic :: Software Development :: Libraries",
|
|
22
23
|
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
@@ -26,8 +27,8 @@ classifiers = [
|
|
|
26
27
|
dynamic = ["version", "dependencies"]
|
|
27
28
|
|
|
28
29
|
[project.urls]
|
|
29
|
-
Documentation = "https://github.
|
|
30
|
-
"Issue tracker" = "https://github.com/
|
|
30
|
+
Documentation = "https://pyproxytools.github.io/pyproxy-docs/"
|
|
31
|
+
"Issue tracker" = "https://github.com/pyproxytools/pyproxy/issues"
|
|
31
32
|
|
|
32
33
|
[tool.setuptools.packages]
|
|
33
34
|
find = {}
|
|
@@ -35,6 +36,27 @@ find = {}
|
|
|
35
36
|
[tool.setuptools.dynamic]
|
|
36
37
|
dependencies = { file = "requirements.txt" }
|
|
37
38
|
|
|
39
|
+
[tool.ruff]
|
|
40
|
+
line-length = 100
|
|
41
|
+
src = ["pyproxy", "tests"]
|
|
42
|
+
exclude = [
|
|
43
|
+
".git",
|
|
44
|
+
"__pycache__",
|
|
45
|
+
"build",
|
|
46
|
+
"dist",
|
|
47
|
+
"venv",
|
|
48
|
+
"setup.py",
|
|
49
|
+
]
|
|
50
|
+
|
|
51
|
+
[tool.ruff.lint]
|
|
52
|
+
select = ["E", "F", "W", "S"]
|
|
53
|
+
|
|
54
|
+
[tool.ruff.format]
|
|
55
|
+
quote-style = "double"
|
|
56
|
+
indent-style = "space"
|
|
57
|
+
line-ending = "auto"
|
|
58
|
+
skip-magic-trailing-comma = false
|
|
59
|
+
|
|
38
60
|
[project.scripts]
|
|
39
61
|
pyproxy = "pyproxy.pyproxy:main"
|
|
40
62
|
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
"""
|
|
2
|
+
pyproxy.handlers.client.py
|
|
3
|
+
|
|
4
|
+
This module defines the ProxyHandlers class used by the proxy server to process
|
|
5
|
+
HTTP and HTTPS client connections. It handles request forwarding, blocking, shortcut
|
|
6
|
+
redirection, custom headers, and optional SSL inspection.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import threading
|
|
10
|
+
|
|
11
|
+
from pyproxy.handlers.http import HttpHandler
|
|
12
|
+
from pyproxy.handlers.https import HttpsHandler
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class ProxyHandlers:
|
|
16
|
+
"""
|
|
17
|
+
ProxyHandlers manages client connections for a proxy server, handling both HTTP
|
|
18
|
+
and HTTPS requests. It processes request forwarding, blocking, SSL inspection,
|
|
19
|
+
and custom headers based on configuration settings. This class is responsible
|
|
20
|
+
for dispatching the correct handler for HTTP or HTTPS requests and managing
|
|
21
|
+
connection-related operations.
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
def __init__(
|
|
25
|
+
self,
|
|
26
|
+
html_403,
|
|
27
|
+
logger_config,
|
|
28
|
+
filter_config,
|
|
29
|
+
ssl_config,
|
|
30
|
+
filter_queue,
|
|
31
|
+
filter_result_queue,
|
|
32
|
+
shortcuts_queue,
|
|
33
|
+
shortcuts_result_queue,
|
|
34
|
+
cancel_inspect_queue,
|
|
35
|
+
cancel_inspect_result_queue,
|
|
36
|
+
custom_header_queue,
|
|
37
|
+
custom_header_result_queue,
|
|
38
|
+
console_logger,
|
|
39
|
+
shortcuts,
|
|
40
|
+
custom_header,
|
|
41
|
+
active_connections,
|
|
42
|
+
proxy_config,
|
|
43
|
+
):
|
|
44
|
+
self.html_403 = html_403
|
|
45
|
+
self.logger_config = logger_config
|
|
46
|
+
self.filter_config = filter_config
|
|
47
|
+
self.ssl_config = ssl_config
|
|
48
|
+
self.filter_queue = filter_queue
|
|
49
|
+
self.filter_result_queue = filter_result_queue
|
|
50
|
+
self.shortcuts_queue = shortcuts_queue
|
|
51
|
+
self.shortcuts_result_queue = shortcuts_result_queue
|
|
52
|
+
self.cancel_inspect_queue = cancel_inspect_queue
|
|
53
|
+
self.cancel_inspect_result_queue = cancel_inspect_result_queue
|
|
54
|
+
self.custom_header_queue = custom_header_queue
|
|
55
|
+
self.custom_header_result_queue = custom_header_result_queue
|
|
56
|
+
self.console_logger = console_logger
|
|
57
|
+
self.config_shortcuts = shortcuts
|
|
58
|
+
self.config_custom_header = custom_header
|
|
59
|
+
self.proxy_config = proxy_config
|
|
60
|
+
self.active_connections = active_connections
|
|
61
|
+
|
|
62
|
+
def _create_handler(self, handler_class, **extra_kwargs):
|
|
63
|
+
"""
|
|
64
|
+
Factory to create handler instance with shared common parameters.
|
|
65
|
+
"""
|
|
66
|
+
params = dict(
|
|
67
|
+
html_403=self.html_403,
|
|
68
|
+
logger_config=self.logger_config,
|
|
69
|
+
filter_config=self.filter_config,
|
|
70
|
+
filter_queue=self.filter_queue,
|
|
71
|
+
filter_result_queue=self.filter_result_queue,
|
|
72
|
+
shortcuts_queue=self.shortcuts_queue,
|
|
73
|
+
shortcuts_result_queue=self.shortcuts_result_queue,
|
|
74
|
+
custom_header_queue=self.custom_header_queue,
|
|
75
|
+
custom_header_result_queue=self.custom_header_result_queue,
|
|
76
|
+
console_logger=self.console_logger,
|
|
77
|
+
shortcuts=self.config_shortcuts,
|
|
78
|
+
custom_header=self.config_custom_header,
|
|
79
|
+
proxy_config=self.proxy_config,
|
|
80
|
+
active_connections=self.active_connections,
|
|
81
|
+
)
|
|
82
|
+
params.update(extra_kwargs)
|
|
83
|
+
return handler_class(**params)
|
|
84
|
+
|
|
85
|
+
def handle_client(self, client_socket):
|
|
86
|
+
"""
|
|
87
|
+
Handles an incoming client connection by processing the request and forwarding
|
|
88
|
+
it to the appropriate handler based on whether the request is HTTP or HTTPS.
|
|
89
|
+
|
|
90
|
+
Args:
|
|
91
|
+
client_socket (socket): The socket object for the client connection.
|
|
92
|
+
"""
|
|
93
|
+
try:
|
|
94
|
+
client_socket.settimeout(10)
|
|
95
|
+
request = client_socket.recv(4096)
|
|
96
|
+
|
|
97
|
+
if not request:
|
|
98
|
+
return
|
|
99
|
+
|
|
100
|
+
first_line = request.decode(errors="ignore").split("\n")[0]
|
|
101
|
+
if first_line.startswith("CONNECT"):
|
|
102
|
+
https_handler = self._create_handler(
|
|
103
|
+
HttpsHandler,
|
|
104
|
+
ssl_config=self.ssl_config,
|
|
105
|
+
cancel_inspect_queue=self.cancel_inspect_queue,
|
|
106
|
+
cancel_inspect_result_queue=self.cancel_inspect_result_queue,
|
|
107
|
+
)
|
|
108
|
+
https_handler.handle_https_connection(client_socket, first_line)
|
|
109
|
+
else:
|
|
110
|
+
http_handler = self._create_handler(HttpHandler)
|
|
111
|
+
http_handler.handle_http_request(client_socket, request)
|
|
112
|
+
finally:
|
|
113
|
+
client_socket.close()
|
|
114
|
+
self.active_connections.pop(threading.get_ident(), None)
|
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
"""
|
|
2
|
+
pyproxy.handlers.http.py
|
|
3
|
+
|
|
4
|
+
This module defines the HttpHandler class used by the proxy server to process
|
|
5
|
+
HTTP client connections. It handles request forwarding, blocking, and custom headers.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import socket
|
|
9
|
+
import os
|
|
10
|
+
import threading
|
|
11
|
+
from urllib.parse import urlparse
|
|
12
|
+
|
|
13
|
+
from pyproxy.utils.http_req import extract_headers
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class HttpHandler:
|
|
17
|
+
"""
|
|
18
|
+
HttpHandler manages client HTTP connections for a proxy server,
|
|
19
|
+
handling request forwarding, filtering, blocking, and custom header modification
|
|
20
|
+
based on configuration settings.
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
def __init__(
|
|
24
|
+
self,
|
|
25
|
+
html_403,
|
|
26
|
+
logger_config,
|
|
27
|
+
filter_config,
|
|
28
|
+
filter_queue,
|
|
29
|
+
filter_result_queue,
|
|
30
|
+
shortcuts_queue,
|
|
31
|
+
shortcuts_result_queue,
|
|
32
|
+
custom_header_queue,
|
|
33
|
+
custom_header_result_queue,
|
|
34
|
+
console_logger,
|
|
35
|
+
shortcuts,
|
|
36
|
+
custom_header,
|
|
37
|
+
active_connections,
|
|
38
|
+
proxy_config,
|
|
39
|
+
):
|
|
40
|
+
self.html_403 = html_403
|
|
41
|
+
self.logger_config = logger_config
|
|
42
|
+
self.filter_config = filter_config
|
|
43
|
+
self.filter_queue = filter_queue
|
|
44
|
+
self.filter_result_queue = filter_result_queue
|
|
45
|
+
self.shortcuts_queue = shortcuts_queue
|
|
46
|
+
self.shortcuts_result_queue = shortcuts_result_queue
|
|
47
|
+
self.custom_header_queue = custom_header_queue
|
|
48
|
+
self.custom_header_result_queue = custom_header_result_queue
|
|
49
|
+
self.console_logger = console_logger
|
|
50
|
+
self.config_shortcuts = shortcuts
|
|
51
|
+
self.config_custom_header = custom_header
|
|
52
|
+
self.proxy_config = proxy_config
|
|
53
|
+
self.active_connections = active_connections
|
|
54
|
+
|
|
55
|
+
def _get_modified_headers(self, url, request_text):
|
|
56
|
+
"""
|
|
57
|
+
Extract headers from a request
|
|
58
|
+
"""
|
|
59
|
+
headers = extract_headers(request_text)
|
|
60
|
+
self.custom_header_queue.put(url)
|
|
61
|
+
try:
|
|
62
|
+
new_headers = self.custom_header_result_queue.get(timeout=5)
|
|
63
|
+
headers.update(new_headers)
|
|
64
|
+
except Exception:
|
|
65
|
+
self.console_logger.warning("Timeout while getting custom headers for %s", url)
|
|
66
|
+
return headers
|
|
67
|
+
|
|
68
|
+
def _rebuild_http_request(self, request_line, headers, body=""):
|
|
69
|
+
"""
|
|
70
|
+
Reconstructs an HTTP request with the new headers.
|
|
71
|
+
"""
|
|
72
|
+
header_lines = [f"{key}: {value}" for key, value in headers.items()]
|
|
73
|
+
reconstructed_headers = "\r\n".join(header_lines)
|
|
74
|
+
return f"{request_line}\r\n{reconstructed_headers}\r\n\r\n{body}".encode()
|
|
75
|
+
|
|
76
|
+
def _apply_shortcut(self, url: str) -> str | None:
|
|
77
|
+
"""
|
|
78
|
+
Checks if a shortcut is defined for the given domain.
|
|
79
|
+
"""
|
|
80
|
+
if self.config_shortcuts and os.path.isfile(self.config_shortcuts):
|
|
81
|
+
parsed_url = urlparse(url)
|
|
82
|
+
domain = parsed_url.hostname
|
|
83
|
+
self.shortcuts_queue.put(domain)
|
|
84
|
+
try:
|
|
85
|
+
return self.shortcuts_result_queue.get(timeout=5)
|
|
86
|
+
except Exception:
|
|
87
|
+
self.console_logger.warning("Timeout while getting shortcut for %s", url)
|
|
88
|
+
return None
|
|
89
|
+
|
|
90
|
+
def _is_blocked(self, url: str) -> bool:
|
|
91
|
+
"""
|
|
92
|
+
Checks if a URL is blocked by the configuration filter.
|
|
93
|
+
"""
|
|
94
|
+
if not self.filter_config.no_filter:
|
|
95
|
+
self.filter_queue.put(url)
|
|
96
|
+
try:
|
|
97
|
+
result = self.filter_result_queue.get(timeout=5)
|
|
98
|
+
return result[1] == "Blocked"
|
|
99
|
+
except Exception:
|
|
100
|
+
self.console_logger.warning("Timeout while filtering %s", url)
|
|
101
|
+
return False
|
|
102
|
+
|
|
103
|
+
def _send_403(self, client_socket, url, first_line):
|
|
104
|
+
"""
|
|
105
|
+
Sends an HTTP 403 Forbidden response to the client.
|
|
106
|
+
"""
|
|
107
|
+
if not self.logger_config.no_logging_block:
|
|
108
|
+
method, domain_port, protocol = first_line.split(" ")
|
|
109
|
+
domain, port = domain_port.split(":")
|
|
110
|
+
self.logger_config.block_logger.info(
|
|
111
|
+
"",
|
|
112
|
+
extra={
|
|
113
|
+
"ip_src": client_socket.getpeername()[0],
|
|
114
|
+
"url": url,
|
|
115
|
+
"method": method,
|
|
116
|
+
"domain": domain,
|
|
117
|
+
"port": port,
|
|
118
|
+
"protocol": protocol,
|
|
119
|
+
},
|
|
120
|
+
)
|
|
121
|
+
with open(self.html_403, "r", encoding="utf-8") as f:
|
|
122
|
+
custom_403_page = f.read()
|
|
123
|
+
response = (
|
|
124
|
+
f"HTTP/1.1 403 Forbidden\r\n"
|
|
125
|
+
f"Content-Length: {len(custom_403_page)}\r\n"
|
|
126
|
+
f"\r\n"
|
|
127
|
+
f"{custom_403_page}"
|
|
128
|
+
)
|
|
129
|
+
client_socket.sendall(response.encode())
|
|
130
|
+
client_socket.close()
|
|
131
|
+
self.active_connections.pop(threading.get_ident(), None)
|
|
132
|
+
|
|
133
|
+
def handle_http_request(self, client_socket, request):
|
|
134
|
+
"""
|
|
135
|
+
Processes an HTTP request, checks for URL filtering, applies shortcuts,
|
|
136
|
+
and forwards the request to the target server if not blocked.
|
|
137
|
+
|
|
138
|
+
Args:
|
|
139
|
+
client_socket (socket): The socket object for the client connection.
|
|
140
|
+
request (bytes): The raw HTTP request sent by the client.
|
|
141
|
+
"""
|
|
142
|
+
first_line = request.decode(errors="ignore").split("\n")[0]
|
|
143
|
+
url = first_line.split(" ")[1]
|
|
144
|
+
|
|
145
|
+
if self.config_shortcuts and os.path.isfile(self.config_shortcuts):
|
|
146
|
+
shortcut_url = self._apply_shortcut(url)
|
|
147
|
+
if shortcut_url:
|
|
148
|
+
response = (
|
|
149
|
+
f"HTTP/1.1 302 Found\r\nLocation: {shortcut_url}\r\nContent-Length: 0\r\n\r\n"
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
client_socket.sendall(response.encode())
|
|
153
|
+
client_socket.close()
|
|
154
|
+
self.active_connections.pop(threading.get_ident(), None)
|
|
155
|
+
return
|
|
156
|
+
|
|
157
|
+
if self._is_blocked(url):
|
|
158
|
+
self._send_403(client_socket, url, first_line)
|
|
159
|
+
return
|
|
160
|
+
|
|
161
|
+
if self.config_custom_header and os.path.isfile(self.config_custom_header):
|
|
162
|
+
request_text = request.decode(errors="ignore")
|
|
163
|
+
request_lines = request_text.split("\r\n")
|
|
164
|
+
headers = self._get_modified_headers(url, request_text)
|
|
165
|
+
request_line = request_lines[0]
|
|
166
|
+
body = request_text.split("\r\n\r\n", 1)[1] if "\r\n\r\n" in request_text else ""
|
|
167
|
+
modified_request = self._rebuild_http_request(request_line, headers, body)
|
|
168
|
+
|
|
169
|
+
self.forward_request_to_server(client_socket, modified_request, url, first_line)
|
|
170
|
+
|
|
171
|
+
else:
|
|
172
|
+
self.forward_request_to_server(client_socket, request, url, first_line)
|
|
173
|
+
|
|
174
|
+
def forward_request_to_server(self, client_socket, request, url, first_line):
|
|
175
|
+
"""
|
|
176
|
+
Forwards the HTTP request to the target server and sends the response back to the client.
|
|
177
|
+
|
|
178
|
+
Args:
|
|
179
|
+
client_socket (socket): The socket object for the client connection.
|
|
180
|
+
request (bytes): The raw HTTP request sent by the client.
|
|
181
|
+
url (str): The target URL from the HTTP request.
|
|
182
|
+
first_line (str): The first line of the HTTP request (e.g., "GET / HTTP/1.1").
|
|
183
|
+
"""
|
|
184
|
+
if self.proxy_config.enable:
|
|
185
|
+
server_host, server_port = self.proxy_config.host, self.proxy_config.port
|
|
186
|
+
else:
|
|
187
|
+
parsed_url = urlparse(url)
|
|
188
|
+
server_host = parsed_url.hostname
|
|
189
|
+
server_port = parsed_url.port or (443 if parsed_url.scheme == "https" else 80)
|
|
190
|
+
|
|
191
|
+
try:
|
|
192
|
+
ip_address = socket.gethostbyname(server_host)
|
|
193
|
+
except socket.gaierror:
|
|
194
|
+
ip_address = server_host
|
|
195
|
+
|
|
196
|
+
thread_id = threading.get_ident()
|
|
197
|
+
|
|
198
|
+
if thread_id in self.active_connections:
|
|
199
|
+
self.active_connections[thread_id].update(
|
|
200
|
+
{
|
|
201
|
+
"target_ip": ip_address,
|
|
202
|
+
"target_domain": server_host,
|
|
203
|
+
"target_port": server_port,
|
|
204
|
+
"bytes_sent": 0,
|
|
205
|
+
"bytes_received": 0,
|
|
206
|
+
}
|
|
207
|
+
)
|
|
208
|
+
|
|
209
|
+
try:
|
|
210
|
+
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
211
|
+
server_socket.connect((server_host, server_port))
|
|
212
|
+
server_socket.sendall(request)
|
|
213
|
+
server_socket.settimeout(5)
|
|
214
|
+
self.active_connections[thread_id]["bytes_sent"] += len(request)
|
|
215
|
+
|
|
216
|
+
while True:
|
|
217
|
+
try:
|
|
218
|
+
response = server_socket.recv(4096)
|
|
219
|
+
if response:
|
|
220
|
+
client_socket.send(response)
|
|
221
|
+
self.active_connections[thread_id]["bytes_received"] += len(response)
|
|
222
|
+
else:
|
|
223
|
+
break
|
|
224
|
+
except socket.timeout:
|
|
225
|
+
break
|
|
226
|
+
except (socket.timeout, socket.gaierror, ConnectionRefusedError, OSError) as e:
|
|
227
|
+
self.console_logger.error("Error connecting to the server %s : %s", server_host, e)
|
|
228
|
+
response = (
|
|
229
|
+
f"HTTP/1.1 502 Bad Gateway\r\n"
|
|
230
|
+
f"Content-Length: {len('Bad Gateway')} \r\n"
|
|
231
|
+
"\r\n"
|
|
232
|
+
f"Bad Gateway"
|
|
233
|
+
)
|
|
234
|
+
client_socket.sendall(response.encode())
|
|
235
|
+
client_socket.close()
|
|
236
|
+
self.active_connections.pop(thread_id, None)
|
|
237
|
+
finally:
|
|
238
|
+
if not self.logger_config.no_logging_access:
|
|
239
|
+
method, url, protocol = first_line.split(" ")
|
|
240
|
+
|
|
241
|
+
conn_data = self.active_connections.get(thread_id, {})
|
|
242
|
+
self.logger_config.access_logger.info(
|
|
243
|
+
"",
|
|
244
|
+
extra={
|
|
245
|
+
"ip_src": client_socket.getpeername()[0],
|
|
246
|
+
"url": f"http://{server_host}",
|
|
247
|
+
"method": method,
|
|
248
|
+
"domain": parsed_url.hostname,
|
|
249
|
+
"port": parsed_url.port,
|
|
250
|
+
"protocol": protocol,
|
|
251
|
+
"bytes_sent": conn_data.get("bytes_sent", 0),
|
|
252
|
+
"bytes_received": conn_data.get("bytes_received", 0),
|
|
253
|
+
},
|
|
254
|
+
)
|
|
255
|
+
client_socket.close()
|
|
256
|
+
server_socket.close()
|
|
257
|
+
self.active_connections.pop(thread_id, None)
|