tiny-proxy 0.2.0__tar.gz → 0.3.0__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.
- {tiny_proxy-0.2.0 → tiny_proxy-0.3.0}/MANIFEST.in +1 -0
- tiny_proxy-0.3.0/PKG-INFO +72 -0
- {tiny_proxy-0.2.0 → tiny_proxy-0.3.0}/README.md +4 -3
- tiny_proxy-0.3.0/pyproject.toml +59 -0
- tiny_proxy-0.3.0/tests/config.py +61 -0
- tiny_proxy-0.3.0/tests/conftest.py +165 -0
- tiny_proxy-0.3.0/tests/http_app.py +32 -0
- tiny_proxy-0.3.0/tests/http_server.py +37 -0
- tiny_proxy-0.3.0/tests/proxy_server.py +172 -0
- tiny_proxy-0.3.0/tests/utils.py +56 -0
- {tiny_proxy-0.2.0 → tiny_proxy-0.3.0}/tiny_proxy/__init__.py +1 -1
- tiny_proxy-0.3.0/tiny_proxy/_proxy/__init__.py +0 -0
- tiny_proxy-0.3.0/tiny_proxy.egg-info/PKG-INFO +72 -0
- {tiny_proxy-0.2.0 → tiny_proxy-0.3.0}/tiny_proxy.egg-info/SOURCES.txt +7 -1
- tiny_proxy-0.3.0/tiny_proxy.egg-info/requires.txt +1 -0
- tiny_proxy-0.2.0/PKG-INFO +0 -51
- tiny_proxy-0.2.0/pyproject.toml +0 -10
- tiny_proxy-0.2.0/setup.py +0 -45
- tiny_proxy-0.2.0/tiny_proxy.egg-info/PKG-INFO +0 -51
- tiny_proxy-0.2.0/tiny_proxy.egg-info/requires.txt +0 -1
- {tiny_proxy-0.2.0 → tiny_proxy-0.3.0}/LICENSE.txt +0 -0
- {tiny_proxy-0.2.0 → tiny_proxy-0.3.0}/setup.cfg +0 -0
- {tiny_proxy-0.2.0/tiny_proxy/_handlers → tiny_proxy-0.3.0/tests}/__init__.py +0 -0
- {tiny_proxy-0.2.0 → tiny_proxy-0.3.0}/tests/test_proxy.py +0 -0
- {tiny_proxy-0.2.0 → tiny_proxy-0.3.0}/tiny_proxy/_errors.py +0 -0
- {tiny_proxy-0.2.0/tiny_proxy/_proxy → tiny_proxy-0.3.0/tiny_proxy/_handlers}/__init__.py +0 -0
- {tiny_proxy-0.2.0 → tiny_proxy-0.3.0}/tiny_proxy/_handlers/base.py +0 -0
- {tiny_proxy-0.2.0 → tiny_proxy-0.3.0}/tiny_proxy/_handlers/http.py +0 -0
- {tiny_proxy-0.2.0 → tiny_proxy-0.3.0}/tiny_proxy/_handlers/socks4.py +0 -0
- {tiny_proxy-0.2.0 → tiny_proxy-0.3.0}/tiny_proxy/_handlers/socks5.py +0 -0
- {tiny_proxy-0.2.0 → tiny_proxy-0.3.0}/tiny_proxy/_proxy/abc.py +0 -0
- {tiny_proxy-0.2.0 → tiny_proxy-0.3.0}/tiny_proxy/_proxy/http.py +0 -0
- {tiny_proxy-0.2.0 → tiny_proxy-0.3.0}/tiny_proxy/_proxy/socks4.py +0 -0
- {tiny_proxy-0.2.0 → tiny_proxy-0.3.0}/tiny_proxy/_proxy/socks5.py +0 -0
- {tiny_proxy-0.2.0 → tiny_proxy-0.3.0}/tiny_proxy/_stream.py +0 -0
- {tiny_proxy-0.2.0 → tiny_proxy-0.3.0}/tiny_proxy/_tunnel.py +0 -0
- {tiny_proxy-0.2.0 → tiny_proxy-0.3.0}/tiny_proxy.egg-info/dependency_links.txt +0 -0
- {tiny_proxy-0.2.0 → tiny_proxy-0.3.0}/tiny_proxy.egg-info/top_level.txt +0 -0
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: tiny-proxy
|
|
3
|
+
Version: 0.3.0
|
|
4
|
+
Summary: Simple proxy server (SOCKS4(a), SOCKS5(h), HTTP CONNECT)
|
|
5
|
+
Author-email: Roman Snegirev <snegiryev@gmail.com>
|
|
6
|
+
License: Apache-2.0
|
|
7
|
+
Project-URL: homepage, https://github.com/romis2012/tiny-proxy
|
|
8
|
+
Project-URL: repository, https://github.com/romis2012/tiny-proxy
|
|
9
|
+
Keywords: socks,socks5,socks4,http,proxy,proxy server,asyncio,trio,anyio
|
|
10
|
+
Classifier: Development Status :: 4 - Beta
|
|
11
|
+
Classifier: Programming Language :: Python
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
21
|
+
Classifier: Operating System :: MacOS
|
|
22
|
+
Classifier: Operating System :: Microsoft
|
|
23
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
24
|
+
Classifier: Topic :: Internet :: WWW/HTTP
|
|
25
|
+
Classifier: Intended Audience :: Developers
|
|
26
|
+
Classifier: Framework :: AnyIO
|
|
27
|
+
Classifier: Framework :: AsyncIO
|
|
28
|
+
Classifier: Framework :: Trio
|
|
29
|
+
Classifier: License :: OSI Approved :: Apache Software License
|
|
30
|
+
Requires-Python: >=3.8.0
|
|
31
|
+
Description-Content-Type: text/markdown
|
|
32
|
+
License-File: LICENSE.txt
|
|
33
|
+
Requires-Dist: anyio<5.0.0,>=3.6.1
|
|
34
|
+
Dynamic: license-file
|
|
35
|
+
|
|
36
|
+
## tiny-proxy
|
|
37
|
+
|
|
38
|
+
[](https://github.com/romis2012/tiny-proxy/actions/workflows/ci.yml)
|
|
39
|
+
[](https://pypi.python.org/pypi/tiny-proxy)
|
|
40
|
+
[](https://github.com/romis2012/tiny-proxy)
|
|
41
|
+
<!-- [](https://codecov.io/gh/romis2012/tiny-proxy) -->
|
|
42
|
+
|
|
43
|
+
Simple proxy (SOCKS4(a), SOCKS5(h), HTTP CONNECT) server built with [anyio](https://github.com/agronholm/anyio).
|
|
44
|
+
It is used for testing [python-socks](https://github.com/romis2012/python-socks), [aiohttp-socks](https://github.com/romis2012/aiohttp-socks) and [httpx-socks](https://github.com/romis2012/httpx-socks) packages.
|
|
45
|
+
|
|
46
|
+
## Requirements
|
|
47
|
+
- Python >= 3.8
|
|
48
|
+
- anyio>=3.6.1
|
|
49
|
+
|
|
50
|
+
## Installation
|
|
51
|
+
```
|
|
52
|
+
pip install tiny-proxy
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Usage
|
|
56
|
+
|
|
57
|
+
```python
|
|
58
|
+
import anyio
|
|
59
|
+
|
|
60
|
+
from tiny_proxy import Socks5ProxyHandler
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
async def main():
|
|
64
|
+
handler = Socks5ProxyHandler(username='user', password='password')
|
|
65
|
+
listener = await anyio.create_tcp_listener(local_host='127.0.0.1', local_port=1080)
|
|
66
|
+
await listener.serve(handler.handle)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
if __name__ == '__main__':
|
|
70
|
+
anyio.run(main)
|
|
71
|
+
```
|
|
72
|
+
|
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
## tiny-proxy
|
|
2
2
|
|
|
3
3
|
[](https://github.com/romis2012/tiny-proxy/actions/workflows/ci.yml)
|
|
4
|
-
[](https://codecov.io/gh/romis2012/tiny-proxy)
|
|
5
4
|
[](https://pypi.python.org/pypi/tiny-proxy)
|
|
5
|
+
[](https://github.com/romis2012/tiny-proxy)
|
|
6
|
+
<!-- [](https://codecov.io/gh/romis2012/tiny-proxy) -->
|
|
6
7
|
|
|
7
|
-
Simple proxy (SOCKS4(a), SOCKS5(h), HTTP
|
|
8
|
+
Simple proxy (SOCKS4(a), SOCKS5(h), HTTP CONNECT) server built with [anyio](https://github.com/agronholm/anyio).
|
|
8
9
|
It is used for testing [python-socks](https://github.com/romis2012/python-socks), [aiohttp-socks](https://github.com/romis2012/aiohttp-socks) and [httpx-socks](https://github.com/romis2012/httpx-socks) packages.
|
|
9
10
|
|
|
10
11
|
## Requirements
|
|
11
|
-
- Python >= 3.
|
|
12
|
+
- Python >= 3.8
|
|
12
13
|
- anyio>=3.6.1
|
|
13
14
|
|
|
14
15
|
## Installation
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ['setuptools']
|
|
3
|
+
build-backend = 'setuptools.build_meta'
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = 'tiny-proxy'
|
|
7
|
+
license = { text = 'Apache-2.0' }
|
|
8
|
+
description = 'Simple proxy server (SOCKS4(a), SOCKS5(h), HTTP CONNECT)'
|
|
9
|
+
readme = 'README.md'
|
|
10
|
+
authors = [{ name = 'Roman Snegirev', email = 'snegiryev@gmail.com' }]
|
|
11
|
+
keywords = [
|
|
12
|
+
'socks',
|
|
13
|
+
'socks5',
|
|
14
|
+
'socks4',
|
|
15
|
+
'http',
|
|
16
|
+
'proxy',
|
|
17
|
+
'proxy server',
|
|
18
|
+
'asyncio',
|
|
19
|
+
'trio',
|
|
20
|
+
'anyio',
|
|
21
|
+
]
|
|
22
|
+
requires-python = ">=3.8.0"
|
|
23
|
+
dependencies = ['anyio>=3.6.1,<5.0.0']
|
|
24
|
+
dynamic = ['version']
|
|
25
|
+
classifiers = [
|
|
26
|
+
"Development Status :: 4 - Beta",
|
|
27
|
+
"Programming Language :: Python",
|
|
28
|
+
"Programming Language :: Python :: 3",
|
|
29
|
+
"Programming Language :: Python :: 3 :: Only",
|
|
30
|
+
"Programming Language :: Python :: 3.8",
|
|
31
|
+
"Programming Language :: Python :: 3.9",
|
|
32
|
+
"Programming Language :: Python :: 3.10",
|
|
33
|
+
"Programming Language :: Python :: 3.11",
|
|
34
|
+
"Programming Language :: Python :: 3.12",
|
|
35
|
+
"Programming Language :: Python :: 3.13",
|
|
36
|
+
"Programming Language :: Python :: 3.14",
|
|
37
|
+
"Operating System :: MacOS",
|
|
38
|
+
"Operating System :: Microsoft",
|
|
39
|
+
"Operating System :: POSIX :: Linux",
|
|
40
|
+
"Topic :: Internet :: WWW/HTTP",
|
|
41
|
+
"Intended Audience :: Developers",
|
|
42
|
+
"Framework :: AnyIO",
|
|
43
|
+
"Framework :: AsyncIO",
|
|
44
|
+
"Framework :: Trio",
|
|
45
|
+
"License :: OSI Approved :: Apache Software License",
|
|
46
|
+
]
|
|
47
|
+
|
|
48
|
+
[project.urls]
|
|
49
|
+
homepage = 'https://github.com/romis2012/tiny-proxy'
|
|
50
|
+
repository = 'https://github.com/romis2012/tiny-proxy'
|
|
51
|
+
|
|
52
|
+
[tool.setuptools.dynamic]
|
|
53
|
+
version = { attr = 'tiny_proxy.__version__' }
|
|
54
|
+
|
|
55
|
+
[tool.setuptools.packages.find]
|
|
56
|
+
include = ['tiny_proxy*']
|
|
57
|
+
|
|
58
|
+
[tool.pytest.ini_options]
|
|
59
|
+
asyncio_mode = 'strict'
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
TEST_HTTP_HOST_IPV4 = '127.0.0.1'
|
|
2
|
+
TEST_HTTP_PORT_IPV4 = 8881
|
|
3
|
+
TEST_HTTP_URL_IPV4 = f'http://{TEST_HTTP_HOST_IPV4}:{TEST_HTTP_PORT_IPV4}/'
|
|
4
|
+
|
|
5
|
+
TEST_HTTPS_HOST_IPV4 = '127.0.0.1'
|
|
6
|
+
TEST_HTTPS_PORT_IPV4 = 8882
|
|
7
|
+
TEST_HTTPS_URL_IPV4 = f'https://{TEST_HTTPS_HOST_IPV4}:{TEST_HTTPS_PORT_IPV4}/'
|
|
8
|
+
|
|
9
|
+
TEST_HTTPS_HOST_IPV6 = '::1'
|
|
10
|
+
TEST_HTTPS_PORT_IPV6 = 8883
|
|
11
|
+
TEST_HTTPS_URL_IPV6 = f'https://[{TEST_HTTPS_HOST_IPV6}]:{TEST_HTTPS_PORT_IPV6}/'
|
|
12
|
+
|
|
13
|
+
PROXY_USERNAME = 'username'
|
|
14
|
+
PROXY_PASSWORD = 'password'
|
|
15
|
+
|
|
16
|
+
PROXY_HOST = '127.0.0.1'
|
|
17
|
+
|
|
18
|
+
SOCKS5_PROXY_PORT = 7780
|
|
19
|
+
SOCKS5_PROXY_PORT_NO_AUTH = 7781
|
|
20
|
+
|
|
21
|
+
SOCKS4_PROXY_PORT = 7782
|
|
22
|
+
SOCKS4_PROXY_PORT_NO_AUTH = 7783
|
|
23
|
+
|
|
24
|
+
HTTP_PROXY_PORT = 7784
|
|
25
|
+
HTTP_PROXY_PORT_NO_AUTH = 7785
|
|
26
|
+
|
|
27
|
+
SOCKS5_PROXY_URL = 'socks5://{username}:{password}@{host}:{port}'.format(
|
|
28
|
+
host=PROXY_HOST,
|
|
29
|
+
port=SOCKS5_PROXY_PORT,
|
|
30
|
+
username=PROXY_USERNAME,
|
|
31
|
+
password=PROXY_PASSWORD,
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
SOCKS5_PROXY_URL_NO_AUTH = 'socks5://{host}:{port}'.format(
|
|
35
|
+
host=PROXY_HOST,
|
|
36
|
+
port=SOCKS5_PROXY_PORT_NO_AUTH,
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
SOCKS4_PROXY_URL = 'socks4://{username}:{password}@{host}:{port}'.format(
|
|
40
|
+
host=PROXY_HOST,
|
|
41
|
+
port=SOCKS4_PROXY_PORT,
|
|
42
|
+
username=PROXY_USERNAME,
|
|
43
|
+
password='',
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
SOCKS4_PROXY_URL_NO_AUTH = 'socks4://{host}:{port}'.format(
|
|
47
|
+
host=PROXY_HOST,
|
|
48
|
+
port=SOCKS4_PROXY_PORT_NO_AUTH,
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
HTTP_PROXY_URL = 'http://{username}:{password}@{host}:{port}'.format(
|
|
52
|
+
host=PROXY_HOST,
|
|
53
|
+
port=HTTP_PROXY_PORT,
|
|
54
|
+
username=PROXY_USERNAME,
|
|
55
|
+
password=PROXY_PASSWORD,
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
HTTP_PROXY_URL_NO_AUTH = 'http://{host}:{port}'.format(
|
|
59
|
+
host=PROXY_HOST,
|
|
60
|
+
port=HTTP_PROXY_PORT_NO_AUTH,
|
|
61
|
+
)
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import ssl
|
|
2
|
+
|
|
3
|
+
import pytest
|
|
4
|
+
import trustme
|
|
5
|
+
|
|
6
|
+
from tests.config import (
|
|
7
|
+
TEST_HTTP_HOST_IPV4,
|
|
8
|
+
TEST_HTTP_PORT_IPV4,
|
|
9
|
+
PROXY_HOST,
|
|
10
|
+
SOCKS5_PROXY_PORT,
|
|
11
|
+
PROXY_USERNAME,
|
|
12
|
+
PROXY_PASSWORD,
|
|
13
|
+
SOCKS5_PROXY_PORT_NO_AUTH,
|
|
14
|
+
SOCKS4_PROXY_PORT,
|
|
15
|
+
SOCKS4_PROXY_PORT_NO_AUTH,
|
|
16
|
+
HTTP_PROXY_PORT,
|
|
17
|
+
HTTP_PROXY_PORT_NO_AUTH,
|
|
18
|
+
TEST_HTTPS_HOST_IPV4,
|
|
19
|
+
TEST_HTTPS_PORT_IPV4,
|
|
20
|
+
TEST_HTTPS_HOST_IPV6,
|
|
21
|
+
TEST_HTTPS_PORT_IPV6,
|
|
22
|
+
)
|
|
23
|
+
from tests.http_server import HttpServerConfig, HttpServer
|
|
24
|
+
from tests.proxy_server import ProxyConfig, ProxyServerRunner
|
|
25
|
+
from tests.utils import wait_until_connectable
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@pytest.fixture(scope='session')
|
|
29
|
+
def ssl_ca() -> trustme.CA:
|
|
30
|
+
return trustme.CA()
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
@pytest.fixture(scope='session')
|
|
34
|
+
def ssl_cert(ssl_ca: trustme.CA) -> trustme.LeafCert:
|
|
35
|
+
return ssl_ca.issue_cert(
|
|
36
|
+
"localhost",
|
|
37
|
+
"127.0.0.1",
|
|
38
|
+
"::1",
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
@pytest.fixture(scope='session')
|
|
43
|
+
def ssl_certfile(ssl_cert: trustme.LeafCert):
|
|
44
|
+
with ssl_cert.cert_chain_pems[0].tempfile() as cert_path:
|
|
45
|
+
yield cert_path
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
@pytest.fixture(scope='session')
|
|
49
|
+
def ssl_keyfile(ssl_cert: trustme.LeafCert):
|
|
50
|
+
with ssl_cert.private_key_pem.tempfile() as private_key_path:
|
|
51
|
+
yield private_key_path
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
@pytest.fixture(scope='session')
|
|
55
|
+
def ssl_key_and_cert_chain_file(ssl_cert: trustme.LeafCert):
|
|
56
|
+
with ssl_cert.private_key_and_cert_chain_pem.tempfile() as path:
|
|
57
|
+
yield path
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
@pytest.fixture(scope='session')
|
|
61
|
+
def ssl_ca_cert_file(ssl_ca: trustme.CA):
|
|
62
|
+
with ssl_ca.cert_pem.tempfile() as ca_cert_pem:
|
|
63
|
+
yield ca_cert_pem
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
@pytest.fixture(scope='session')
|
|
67
|
+
def server_ssl_context(ssl_cert: trustme.LeafCert) -> ssl.SSLContext:
|
|
68
|
+
ssl_ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
|
|
69
|
+
ssl_cert.configure_cert(ssl_ctx)
|
|
70
|
+
return ssl_ctx
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
@pytest.fixture(scope='session')
|
|
74
|
+
def client_ssl_context(ssl_ca: trustme.CA, ssl_ca_cert_file) -> ssl.SSLContext:
|
|
75
|
+
ssl_ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
|
|
76
|
+
ssl_ctx.verify_mode = ssl.CERT_REQUIRED
|
|
77
|
+
ssl_ctx.check_hostname = True
|
|
78
|
+
|
|
79
|
+
# ssl_ctx.load_verify_locations(ssl_ca_cert_file)
|
|
80
|
+
ssl_ca.configure_trust(ssl_ctx)
|
|
81
|
+
|
|
82
|
+
return ssl_ctx
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
@pytest.fixture(scope='session', autouse=True)
|
|
86
|
+
def web_server(ssl_certfile, ssl_keyfile):
|
|
87
|
+
config = [
|
|
88
|
+
HttpServerConfig(
|
|
89
|
+
host=TEST_HTTP_HOST_IPV4,
|
|
90
|
+
port=TEST_HTTP_PORT_IPV4,
|
|
91
|
+
),
|
|
92
|
+
HttpServerConfig(
|
|
93
|
+
host=TEST_HTTPS_HOST_IPV4,
|
|
94
|
+
port=TEST_HTTPS_PORT_IPV4,
|
|
95
|
+
ssl_certfile=ssl_certfile,
|
|
96
|
+
ssl_keyfile=ssl_keyfile,
|
|
97
|
+
),
|
|
98
|
+
HttpServerConfig(
|
|
99
|
+
host=TEST_HTTPS_HOST_IPV6,
|
|
100
|
+
port=TEST_HTTPS_PORT_IPV6,
|
|
101
|
+
ssl_certfile=ssl_certfile,
|
|
102
|
+
ssl_keyfile=ssl_keyfile,
|
|
103
|
+
),
|
|
104
|
+
]
|
|
105
|
+
|
|
106
|
+
server = HttpServer(config=config)
|
|
107
|
+
server.run()
|
|
108
|
+
|
|
109
|
+
for cfg in config:
|
|
110
|
+
wait_until_connectable(host=cfg.host, port=cfg.port)
|
|
111
|
+
|
|
112
|
+
yield None
|
|
113
|
+
|
|
114
|
+
server.shutdown()
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
@pytest.fixture(scope='session', autouse=True)
|
|
118
|
+
def proxy_server():
|
|
119
|
+
config = [
|
|
120
|
+
ProxyConfig(
|
|
121
|
+
proxy_type='socks5',
|
|
122
|
+
host=PROXY_HOST,
|
|
123
|
+
port=SOCKS5_PROXY_PORT,
|
|
124
|
+
username=PROXY_USERNAME,
|
|
125
|
+
password=PROXY_PASSWORD,
|
|
126
|
+
),
|
|
127
|
+
ProxyConfig(
|
|
128
|
+
proxy_type='socks5',
|
|
129
|
+
host=PROXY_HOST,
|
|
130
|
+
port=SOCKS5_PROXY_PORT_NO_AUTH,
|
|
131
|
+
),
|
|
132
|
+
ProxyConfig(
|
|
133
|
+
proxy_type='socks4',
|
|
134
|
+
host=PROXY_HOST,
|
|
135
|
+
port=SOCKS4_PROXY_PORT,
|
|
136
|
+
username=PROXY_USERNAME,
|
|
137
|
+
password=None,
|
|
138
|
+
),
|
|
139
|
+
ProxyConfig(
|
|
140
|
+
proxy_type='socks4',
|
|
141
|
+
host=PROXY_HOST,
|
|
142
|
+
port=SOCKS4_PROXY_PORT_NO_AUTH,
|
|
143
|
+
),
|
|
144
|
+
ProxyConfig(
|
|
145
|
+
proxy_type='http',
|
|
146
|
+
host=PROXY_HOST,
|
|
147
|
+
port=HTTP_PROXY_PORT,
|
|
148
|
+
username=PROXY_USERNAME,
|
|
149
|
+
password=PROXY_PASSWORD,
|
|
150
|
+
),
|
|
151
|
+
ProxyConfig(
|
|
152
|
+
proxy_type='http',
|
|
153
|
+
host=PROXY_HOST,
|
|
154
|
+
port=HTTP_PROXY_PORT_NO_AUTH,
|
|
155
|
+
),
|
|
156
|
+
]
|
|
157
|
+
|
|
158
|
+
server = ProxyServerRunner(config=config)
|
|
159
|
+
server.run()
|
|
160
|
+
for cfg in config:
|
|
161
|
+
wait_until_connectable(host=cfg.host, port=cfg.port)
|
|
162
|
+
|
|
163
|
+
yield None
|
|
164
|
+
|
|
165
|
+
server.shutdown()
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import ssl
|
|
2
|
+
|
|
3
|
+
from aiohttp import web
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
async def index(_):
|
|
7
|
+
return web.Response(body='Index')
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
app = web.Application()
|
|
11
|
+
app.router.add_get('/', index)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def run_app(
|
|
15
|
+
host: str,
|
|
16
|
+
port: int,
|
|
17
|
+
ssl_certfile: str = None,
|
|
18
|
+
ssl_keyfile: str = None,
|
|
19
|
+
):
|
|
20
|
+
if ssl_certfile and ssl_keyfile:
|
|
21
|
+
ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
|
|
22
|
+
ssl_context.load_cert_chain(ssl_certfile, ssl_keyfile)
|
|
23
|
+
else:
|
|
24
|
+
ssl_context = None
|
|
25
|
+
|
|
26
|
+
web.run_app(
|
|
27
|
+
app,
|
|
28
|
+
host=host,
|
|
29
|
+
port=port,
|
|
30
|
+
ssl_context=ssl_context,
|
|
31
|
+
print=False, # type: ignore
|
|
32
|
+
)
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import typing
|
|
2
|
+
from multiprocessing import Process
|
|
3
|
+
|
|
4
|
+
from tests.http_app import run_app
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class HttpServerConfig(typing.NamedTuple):
|
|
8
|
+
host: str
|
|
9
|
+
port: int
|
|
10
|
+
ssl_certfile: str = None
|
|
11
|
+
ssl_keyfile: str = None
|
|
12
|
+
|
|
13
|
+
def to_dict(self):
|
|
14
|
+
d = {}
|
|
15
|
+
for key, val in self._asdict().items():
|
|
16
|
+
if val is not None:
|
|
17
|
+
d[key] = val
|
|
18
|
+
return d
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class HttpServer:
|
|
22
|
+
def __init__(self, config: typing.Iterable[HttpServerConfig]):
|
|
23
|
+
self.config = config
|
|
24
|
+
self.workers = []
|
|
25
|
+
|
|
26
|
+
def run(self):
|
|
27
|
+
for cfg in self.config:
|
|
28
|
+
print(f'Starting web server on {cfg.host}:{cfg.port}...')
|
|
29
|
+
p = Process(target=run_app, kwargs=cfg.to_dict())
|
|
30
|
+
self.workers.append(p)
|
|
31
|
+
|
|
32
|
+
for p in self.workers:
|
|
33
|
+
p.start()
|
|
34
|
+
|
|
35
|
+
def shutdown(self):
|
|
36
|
+
for p in self.workers:
|
|
37
|
+
p.terminate()
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import logging
|
|
3
|
+
import ssl
|
|
4
|
+
import typing
|
|
5
|
+
from contextlib import contextmanager
|
|
6
|
+
from multiprocessing import Process
|
|
7
|
+
|
|
8
|
+
from anyio import create_tcp_listener
|
|
9
|
+
from anyio.streams.tls import TLSListener
|
|
10
|
+
|
|
11
|
+
from tests.utils import cancel_all_tasks, cancel_tasks, wait_until_connectable
|
|
12
|
+
from tiny_proxy import HttpProxyHandler, Socks5ProxyHandler, Socks4ProxyHandler
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class ProxyConfig(typing.NamedTuple):
|
|
16
|
+
proxy_type: str
|
|
17
|
+
host: str
|
|
18
|
+
port: int
|
|
19
|
+
username: typing.Optional[str] = None
|
|
20
|
+
password: typing.Optional[str] = None
|
|
21
|
+
ssl_certfile: typing.Optional[str] = None
|
|
22
|
+
ssl_keyfile: typing.Optional[str] = None
|
|
23
|
+
|
|
24
|
+
def to_dict(self):
|
|
25
|
+
d = {}
|
|
26
|
+
for key, val in self._asdict().items():
|
|
27
|
+
if val is not None:
|
|
28
|
+
d[key] = val
|
|
29
|
+
return d
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class ProxyServer:
|
|
33
|
+
cls_map = {
|
|
34
|
+
'http': HttpProxyHandler,
|
|
35
|
+
'socks4': Socks4ProxyHandler,
|
|
36
|
+
'socks5': Socks5ProxyHandler,
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
def __init__(self, config: typing.Iterable[ProxyConfig], loop: asyncio.AbstractEventLoop):
|
|
40
|
+
self.loop = loop
|
|
41
|
+
self.config = config
|
|
42
|
+
self.logger = logging.getLogger(__name__)
|
|
43
|
+
self.server_tasks = []
|
|
44
|
+
|
|
45
|
+
def run(self):
|
|
46
|
+
proxies = self.config
|
|
47
|
+
for proxy in proxies:
|
|
48
|
+
server_task = self.loop.create_task(self._listen(**proxy.to_dict()))
|
|
49
|
+
self.server_tasks.append(server_task)
|
|
50
|
+
|
|
51
|
+
def run_forever(self):
|
|
52
|
+
self.run()
|
|
53
|
+
self.loop.run_forever()
|
|
54
|
+
|
|
55
|
+
def shutdown(self):
|
|
56
|
+
print('Shutting down...')
|
|
57
|
+
|
|
58
|
+
cancel_tasks(self.server_tasks, self.loop)
|
|
59
|
+
cancel_all_tasks(self.loop)
|
|
60
|
+
|
|
61
|
+
self.loop.run_until_complete(self.loop.shutdown_asyncgens())
|
|
62
|
+
try:
|
|
63
|
+
self.loop.run_until_complete(self.loop.shutdown_default_executor())
|
|
64
|
+
except AttributeError: # pragma: no cover
|
|
65
|
+
pass # shutdown_default_executor is new to Python 3.9
|
|
66
|
+
|
|
67
|
+
self.loop.close()
|
|
68
|
+
|
|
69
|
+
def __enter__(self):
|
|
70
|
+
return self
|
|
71
|
+
|
|
72
|
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
73
|
+
return self.shutdown()
|
|
74
|
+
|
|
75
|
+
async def _listen(
|
|
76
|
+
self,
|
|
77
|
+
proxy_type,
|
|
78
|
+
host,
|
|
79
|
+
port,
|
|
80
|
+
ssl_certfile=None,
|
|
81
|
+
ssl_keyfile=None,
|
|
82
|
+
**kwargs,
|
|
83
|
+
):
|
|
84
|
+
handler_cls = self.cls_map.get(proxy_type)
|
|
85
|
+
if not handler_cls:
|
|
86
|
+
raise RuntimeError(f'Unsupported type: {proxy_type}')
|
|
87
|
+
|
|
88
|
+
if ssl_certfile and ssl_keyfile:
|
|
89
|
+
ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
|
|
90
|
+
ssl_context.load_cert_chain(ssl_certfile, ssl_keyfile)
|
|
91
|
+
else:
|
|
92
|
+
ssl_context = None
|
|
93
|
+
|
|
94
|
+
print(f'Starting {proxy_type} proxy on {host}:{port}...')
|
|
95
|
+
|
|
96
|
+
handler = handler_cls(**kwargs)
|
|
97
|
+
|
|
98
|
+
listener = await create_tcp_listener(local_host=host, local_port=port)
|
|
99
|
+
if ssl_context is not None:
|
|
100
|
+
listener = TLSListener(listener=listener, ssl_context=ssl_context)
|
|
101
|
+
|
|
102
|
+
async with listener:
|
|
103
|
+
await listener.serve(handler.handle)
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def _start_proxy_server(config: typing.Iterable[ProxyConfig]):
|
|
107
|
+
import asyncio
|
|
108
|
+
|
|
109
|
+
loop = asyncio.new_event_loop()
|
|
110
|
+
asyncio.set_event_loop(loop)
|
|
111
|
+
|
|
112
|
+
server = ProxyServer(config=config, loop=loop)
|
|
113
|
+
|
|
114
|
+
try:
|
|
115
|
+
server.run_forever()
|
|
116
|
+
except (KeyboardInterrupt, SystemExit):
|
|
117
|
+
pass
|
|
118
|
+
finally:
|
|
119
|
+
server.shutdown()
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
class ProxyServerRunner:
|
|
123
|
+
def __init__(self, config: typing.Iterable[ProxyConfig]):
|
|
124
|
+
self.config = config
|
|
125
|
+
self.process = None
|
|
126
|
+
|
|
127
|
+
def run(self):
|
|
128
|
+
"""
|
|
129
|
+
https://pytest-cov.readthedocs.io/en/latest/subprocess-support.html#if-you-use-multiprocessing-process
|
|
130
|
+
or use Thread
|
|
131
|
+
"""
|
|
132
|
+
try:
|
|
133
|
+
from pytest_cov.embed import cleanup_on_sigterm # noqa
|
|
134
|
+
except ImportError:
|
|
135
|
+
pass
|
|
136
|
+
else:
|
|
137
|
+
cleanup_on_sigterm()
|
|
138
|
+
|
|
139
|
+
self.process = Process(target=_start_proxy_server, kwargs=dict(config=self.config))
|
|
140
|
+
self.process.daemon = True
|
|
141
|
+
self.process.start()
|
|
142
|
+
|
|
143
|
+
def shutdown(self):
|
|
144
|
+
self.process.terminate()
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
@contextmanager
|
|
148
|
+
def start_proxy_server(config: ProxyConfig):
|
|
149
|
+
"""
|
|
150
|
+
https://pytest-cov.readthedocs.io/en/latest/subprocess-support.html#if-you-use-multiprocessing-process
|
|
151
|
+
or use Thread
|
|
152
|
+
"""
|
|
153
|
+
try:
|
|
154
|
+
from pytest_cov.embed import cleanup_on_sigterm # noqa
|
|
155
|
+
except ImportError:
|
|
156
|
+
pass
|
|
157
|
+
else:
|
|
158
|
+
cleanup_on_sigterm()
|
|
159
|
+
|
|
160
|
+
process = Process(target=_start_proxy_server, kwargs=dict(config=[config]))
|
|
161
|
+
# process = Thread(target=_start_proxy_server, kwargs=dict(config=[config]))
|
|
162
|
+
process.daemon = True
|
|
163
|
+
process.start()
|
|
164
|
+
|
|
165
|
+
wait_until_connectable(host=config.host, port=config.port)
|
|
166
|
+
|
|
167
|
+
try:
|
|
168
|
+
yield None
|
|
169
|
+
finally:
|
|
170
|
+
process.terminate()
|
|
171
|
+
process.join()
|
|
172
|
+
pass
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import socket
|
|
3
|
+
import time
|
|
4
|
+
from typing import Iterable
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def is_connectable(host, port):
|
|
8
|
+
sock = None
|
|
9
|
+
try:
|
|
10
|
+
sock = socket.create_connection((host, port), timeout=1)
|
|
11
|
+
except socket.error:
|
|
12
|
+
return False
|
|
13
|
+
else:
|
|
14
|
+
return True
|
|
15
|
+
finally:
|
|
16
|
+
if sock is not None:
|
|
17
|
+
sock.close()
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def wait_until_connectable(host, port, timeout=10):
|
|
21
|
+
count = 0
|
|
22
|
+
while not is_connectable(host=host, port=port):
|
|
23
|
+
if count >= timeout:
|
|
24
|
+
raise Exception(
|
|
25
|
+
f'The proxy server has not available by ({host}, {port}) in {timeout:d} seconds'
|
|
26
|
+
)
|
|
27
|
+
count += 1
|
|
28
|
+
time.sleep(1)
|
|
29
|
+
return True
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def cancel_tasks(tasks: Iterable[asyncio.Task], loop: asyncio.AbstractEventLoop):
|
|
33
|
+
if not tasks:
|
|
34
|
+
return
|
|
35
|
+
|
|
36
|
+
for task in tasks:
|
|
37
|
+
task.cancel()
|
|
38
|
+
|
|
39
|
+
loop.run_until_complete(asyncio.gather(*tasks, return_exceptions=True))
|
|
40
|
+
|
|
41
|
+
for task in tasks:
|
|
42
|
+
if task.cancelled():
|
|
43
|
+
continue
|
|
44
|
+
if task.exception() is not None:
|
|
45
|
+
loop.call_exception_handler(
|
|
46
|
+
{
|
|
47
|
+
"message": "unhandled exception during asyncio.run() shutdown",
|
|
48
|
+
"exception": task.exception(),
|
|
49
|
+
"task": task,
|
|
50
|
+
}
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def cancel_all_tasks(loop: asyncio.AbstractEventLoop):
|
|
55
|
+
tasks = [task for task in asyncio.all_tasks(loop) if not task.done()]
|
|
56
|
+
cancel_tasks(tasks=tasks, loop=loop)
|
|
File without changes
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: tiny-proxy
|
|
3
|
+
Version: 0.3.0
|
|
4
|
+
Summary: Simple proxy server (SOCKS4(a), SOCKS5(h), HTTP CONNECT)
|
|
5
|
+
Author-email: Roman Snegirev <snegiryev@gmail.com>
|
|
6
|
+
License: Apache-2.0
|
|
7
|
+
Project-URL: homepage, https://github.com/romis2012/tiny-proxy
|
|
8
|
+
Project-URL: repository, https://github.com/romis2012/tiny-proxy
|
|
9
|
+
Keywords: socks,socks5,socks4,http,proxy,proxy server,asyncio,trio,anyio
|
|
10
|
+
Classifier: Development Status :: 4 - Beta
|
|
11
|
+
Classifier: Programming Language :: Python
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
21
|
+
Classifier: Operating System :: MacOS
|
|
22
|
+
Classifier: Operating System :: Microsoft
|
|
23
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
24
|
+
Classifier: Topic :: Internet :: WWW/HTTP
|
|
25
|
+
Classifier: Intended Audience :: Developers
|
|
26
|
+
Classifier: Framework :: AnyIO
|
|
27
|
+
Classifier: Framework :: AsyncIO
|
|
28
|
+
Classifier: Framework :: Trio
|
|
29
|
+
Classifier: License :: OSI Approved :: Apache Software License
|
|
30
|
+
Requires-Python: >=3.8.0
|
|
31
|
+
Description-Content-Type: text/markdown
|
|
32
|
+
License-File: LICENSE.txt
|
|
33
|
+
Requires-Dist: anyio<5.0.0,>=3.6.1
|
|
34
|
+
Dynamic: license-file
|
|
35
|
+
|
|
36
|
+
## tiny-proxy
|
|
37
|
+
|
|
38
|
+
[](https://github.com/romis2012/tiny-proxy/actions/workflows/ci.yml)
|
|
39
|
+
[](https://pypi.python.org/pypi/tiny-proxy)
|
|
40
|
+
[](https://github.com/romis2012/tiny-proxy)
|
|
41
|
+
<!-- [](https://codecov.io/gh/romis2012/tiny-proxy) -->
|
|
42
|
+
|
|
43
|
+
Simple proxy (SOCKS4(a), SOCKS5(h), HTTP CONNECT) server built with [anyio](https://github.com/agronholm/anyio).
|
|
44
|
+
It is used for testing [python-socks](https://github.com/romis2012/python-socks), [aiohttp-socks](https://github.com/romis2012/aiohttp-socks) and [httpx-socks](https://github.com/romis2012/httpx-socks) packages.
|
|
45
|
+
|
|
46
|
+
## Requirements
|
|
47
|
+
- Python >= 3.8
|
|
48
|
+
- anyio>=3.6.1
|
|
49
|
+
|
|
50
|
+
## Installation
|
|
51
|
+
```
|
|
52
|
+
pip install tiny-proxy
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Usage
|
|
56
|
+
|
|
57
|
+
```python
|
|
58
|
+
import anyio
|
|
59
|
+
|
|
60
|
+
from tiny_proxy import Socks5ProxyHandler
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
async def main():
|
|
64
|
+
handler = Socks5ProxyHandler(username='user', password='password')
|
|
65
|
+
listener = await anyio.create_tcp_listener(local_host='127.0.0.1', local_port=1080)
|
|
66
|
+
await listener.serve(handler.handle)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
if __name__ == '__main__':
|
|
70
|
+
anyio.run(main)
|
|
71
|
+
```
|
|
72
|
+
|
|
@@ -2,8 +2,14 @@ LICENSE.txt
|
|
|
2
2
|
MANIFEST.in
|
|
3
3
|
README.md
|
|
4
4
|
pyproject.toml
|
|
5
|
-
|
|
5
|
+
tests/__init__.py
|
|
6
|
+
tests/config.py
|
|
7
|
+
tests/conftest.py
|
|
8
|
+
tests/http_app.py
|
|
9
|
+
tests/http_server.py
|
|
10
|
+
tests/proxy_server.py
|
|
6
11
|
tests/test_proxy.py
|
|
12
|
+
tests/utils.py
|
|
7
13
|
tiny_proxy/__init__.py
|
|
8
14
|
tiny_proxy/_errors.py
|
|
9
15
|
tiny_proxy/_stream.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
anyio<5.0.0,>=3.6.1
|
tiny_proxy-0.2.0/PKG-INFO
DELETED
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.1
|
|
2
|
-
Name: tiny_proxy
|
|
3
|
-
Version: 0.2.0
|
|
4
|
-
Summary: Simple proxy server (SOCKS4(a), SOCKS5(h), HTTP tunnel)
|
|
5
|
-
Home-page: https://github.com/romis2012/tiny-proxy
|
|
6
|
-
Author: Roman Snegirev
|
|
7
|
-
Author-email: snegiryev@gmail.com
|
|
8
|
-
License: Apache 2
|
|
9
|
-
Keywords: socks socks5 socks4 http proxy server asyncio trio anyio
|
|
10
|
-
Platform: UNKNOWN
|
|
11
|
-
Description-Content-Type: text/markdown
|
|
12
|
-
License-File: LICENSE.txt
|
|
13
|
-
|
|
14
|
-
## tiny-proxy
|
|
15
|
-
|
|
16
|
-
[](https://github.com/romis2012/tiny-proxy/actions/workflows/ci.yml)
|
|
17
|
-
[](https://codecov.io/gh/romis2012/tiny-proxy)
|
|
18
|
-
[](https://pypi.python.org/pypi/tiny-proxy)
|
|
19
|
-
|
|
20
|
-
Simple proxy (SOCKS4(a), SOCKS5(h), HTTP tunnel) server built with [anyio](https://github.com/agronholm/anyio).
|
|
21
|
-
It is used for testing [python-socks](https://github.com/romis2012/python-socks), [aiohttp-socks](https://github.com/romis2012/aiohttp-socks) and [httpx-socks](https://github.com/romis2012/httpx-socks) packages.
|
|
22
|
-
|
|
23
|
-
## Requirements
|
|
24
|
-
- Python >= 3.7
|
|
25
|
-
- anyio>=3.6.1
|
|
26
|
-
|
|
27
|
-
## Installation
|
|
28
|
-
```
|
|
29
|
-
pip install tiny-proxy
|
|
30
|
-
```
|
|
31
|
-
|
|
32
|
-
## Usage
|
|
33
|
-
|
|
34
|
-
```python
|
|
35
|
-
import anyio
|
|
36
|
-
|
|
37
|
-
from tiny_proxy import Socks5ProxyHandler
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
async def main():
|
|
41
|
-
handler = Socks5ProxyHandler(username='user', password='password')
|
|
42
|
-
listener = await anyio.create_tcp_listener(local_host='127.0.0.1', local_port=1080)
|
|
43
|
-
await listener.serve(handler.handle)
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
if __name__ == '__main__':
|
|
47
|
-
anyio.run(main)
|
|
48
|
-
```
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
tiny_proxy-0.2.0/pyproject.toml
DELETED
tiny_proxy-0.2.0/setup.py
DELETED
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python
|
|
2
|
-
import os
|
|
3
|
-
import re
|
|
4
|
-
import sys
|
|
5
|
-
|
|
6
|
-
from setuptools import setup
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
def get_version():
|
|
10
|
-
here = os.path.dirname(os.path.abspath(__file__))
|
|
11
|
-
filename = os.path.join(here, 'tiny_proxy', '__init__.py')
|
|
12
|
-
contents = open(filename).read()
|
|
13
|
-
pattern = r"^__version__ = '(.*?)'$"
|
|
14
|
-
return re.search(pattern, contents, re.MULTILINE).group(1)
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
def get_long_description():
|
|
18
|
-
with open('README.md', mode='r', encoding='utf8') as f:
|
|
19
|
-
return f.read()
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
if sys.version_info < (3, 7):
|
|
23
|
-
raise RuntimeError('tiny-proxy requires Python 3.7+')
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
setup(
|
|
27
|
-
name='tiny_proxy',
|
|
28
|
-
author='Roman Snegirev',
|
|
29
|
-
author_email='snegiryev@gmail.com',
|
|
30
|
-
version=get_version(),
|
|
31
|
-
license='Apache 2',
|
|
32
|
-
url='https://github.com/romis2012/tiny-proxy',
|
|
33
|
-
description='Simple proxy server (SOCKS4(a), SOCKS5(h), HTTP tunnel)',
|
|
34
|
-
long_description=get_long_description(),
|
|
35
|
-
long_description_content_type='text/markdown',
|
|
36
|
-
packages=[
|
|
37
|
-
'tiny_proxy',
|
|
38
|
-
'tiny_proxy._proxy',
|
|
39
|
-
'tiny_proxy._handlers',
|
|
40
|
-
],
|
|
41
|
-
keywords='socks socks5 socks4 http proxy server asyncio trio anyio',
|
|
42
|
-
install_requires=[
|
|
43
|
-
'anyio>=3.6.1,<4.0.0',
|
|
44
|
-
],
|
|
45
|
-
)
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.1
|
|
2
|
-
Name: tiny-proxy
|
|
3
|
-
Version: 0.2.0
|
|
4
|
-
Summary: Simple proxy server (SOCKS4(a), SOCKS5(h), HTTP tunnel)
|
|
5
|
-
Home-page: https://github.com/romis2012/tiny-proxy
|
|
6
|
-
Author: Roman Snegirev
|
|
7
|
-
Author-email: snegiryev@gmail.com
|
|
8
|
-
License: Apache 2
|
|
9
|
-
Keywords: socks socks5 socks4 http proxy server asyncio trio anyio
|
|
10
|
-
Platform: UNKNOWN
|
|
11
|
-
Description-Content-Type: text/markdown
|
|
12
|
-
License-File: LICENSE.txt
|
|
13
|
-
|
|
14
|
-
## tiny-proxy
|
|
15
|
-
|
|
16
|
-
[](https://github.com/romis2012/tiny-proxy/actions/workflows/ci.yml)
|
|
17
|
-
[](https://codecov.io/gh/romis2012/tiny-proxy)
|
|
18
|
-
[](https://pypi.python.org/pypi/tiny-proxy)
|
|
19
|
-
|
|
20
|
-
Simple proxy (SOCKS4(a), SOCKS5(h), HTTP tunnel) server built with [anyio](https://github.com/agronholm/anyio).
|
|
21
|
-
It is used for testing [python-socks](https://github.com/romis2012/python-socks), [aiohttp-socks](https://github.com/romis2012/aiohttp-socks) and [httpx-socks](https://github.com/romis2012/httpx-socks) packages.
|
|
22
|
-
|
|
23
|
-
## Requirements
|
|
24
|
-
- Python >= 3.7
|
|
25
|
-
- anyio>=3.6.1
|
|
26
|
-
|
|
27
|
-
## Installation
|
|
28
|
-
```
|
|
29
|
-
pip install tiny-proxy
|
|
30
|
-
```
|
|
31
|
-
|
|
32
|
-
## Usage
|
|
33
|
-
|
|
34
|
-
```python
|
|
35
|
-
import anyio
|
|
36
|
-
|
|
37
|
-
from tiny_proxy import Socks5ProxyHandler
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
async def main():
|
|
41
|
-
handler = Socks5ProxyHandler(username='user', password='password')
|
|
42
|
-
listener = await anyio.create_tcp_listener(local_host='127.0.0.1', local_port=1080)
|
|
43
|
-
await listener.serve(handler.handle)
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
if __name__ == '__main__':
|
|
47
|
-
anyio.run(main)
|
|
48
|
-
```
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
anyio<4.0.0,>=3.6.1
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|