neptls 0.1.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.
- neptls-0.1.0/LICENSE +21 -0
- neptls-0.1.0/PKG-INFO +180 -0
- neptls-0.1.0/README.md +155 -0
- neptls-0.1.0/pyproject.toml +47 -0
- neptls-0.1.0/setup.cfg +4 -0
- neptls-0.1.0/src/neptls/__init__.py +52 -0
- neptls-0.1.0/src/neptls/__main__.py +3 -0
- neptls-0.1.0/src/neptls/async_client.py +52 -0
- neptls-0.1.0/src/neptls/cli.py +76 -0
- neptls-0.1.0/src/neptls/client.py +285 -0
- neptls-0.1.0/src/neptls/crypto.py +62 -0
- neptls-0.1.0/src/neptls/diagnostics.py +59 -0
- neptls-0.1.0/src/neptls/exceptions.py +29 -0
- neptls-0.1.0/src/neptls/fingerprints.py +76 -0
- neptls-0.1.0/src/neptls/models.py +120 -0
- neptls-0.1.0/src/neptls/pools.py +58 -0
- neptls-0.1.0/src/neptls/pow.py +54 -0
- neptls-0.1.0/src/neptls/profiles.py +81 -0
- neptls-0.1.0/src/neptls/proxy.py +35 -0
- neptls-0.1.0/src/neptls/py.typed +0 -0
- neptls-0.1.0/src/neptls/tls.py +118 -0
- neptls-0.1.0/src/neptls/user_agents.py +110 -0
- neptls-0.1.0/src/neptls.egg-info/PKG-INFO +180 -0
- neptls-0.1.0/src/neptls.egg-info/SOURCES.txt +27 -0
- neptls-0.1.0/src/neptls.egg-info/dependency_links.txt +1 -0
- neptls-0.1.0/src/neptls.egg-info/entry_points.txt +2 -0
- neptls-0.1.0/src/neptls.egg-info/top_level.txt +1 -0
- neptls-0.1.0/tests/test_core.py +68 -0
- neptls-0.1.0/tests/test_models.py +22 -0
neptls-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Diwas Khatri
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
neptls-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: neptls
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A dependency-light HTTP client and TLS research toolkit for authorized testing.
|
|
5
|
+
Author-email: Diwas Khatri <diwaskhatri07@users.noreply.github.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/diwaskhatri07/neptls
|
|
8
|
+
Project-URL: Repository, https://github.com/diwaskhatri07/neptls
|
|
9
|
+
Project-URL: Issues, https://github.com/diwaskhatri07/neptls/issues
|
|
10
|
+
Keywords: http,tls,networking,diagnostics,fingerprinting
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
19
|
+
Classifier: Topic :: Internet :: WWW/HTTP
|
|
20
|
+
Classifier: Topic :: Security
|
|
21
|
+
Requires-Python: >=3.10
|
|
22
|
+
Description-Content-Type: text/markdown
|
|
23
|
+
License-File: LICENSE
|
|
24
|
+
Dynamic: license-file
|
|
25
|
+
|
|
26
|
+
# NepTLS
|
|
27
|
+
|
|
28
|
+
NepTLS is a dependency-light Python HTTP client and TLS research toolkit. It
|
|
29
|
+
combines a requests-style API with structured TLS configuration, browser
|
|
30
|
+
network profiles, local fingerprint analysis, diagnostics, pools, and a
|
|
31
|
+
generic proof-of-work framework.
|
|
32
|
+
|
|
33
|
+
**Developed by Diwas Khatri (@diwaskhatri07).**
|
|
34
|
+
|
|
35
|
+
## Security boundary
|
|
36
|
+
|
|
37
|
+
NepTLS is for networking research, browser compatibility, API testing,
|
|
38
|
+
protocol interoperability, performance testing, debugging, authorized
|
|
39
|
+
automation, and defensive security research. It is not designed to bypass
|
|
40
|
+
CAPTCHAs, authentication, payment security, access controls, or anti-abuse
|
|
41
|
+
systems, and it does not include session theft or credential attack features.
|
|
42
|
+
Fingerprint objects describe and compare configurations; they do not spoof
|
|
43
|
+
browser security signals.
|
|
44
|
+
|
|
45
|
+
## Installation
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
python -m pip install neptls
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
NepTLS has no runtime dependencies and supports Python 3.10+.
|
|
52
|
+
|
|
53
|
+
## Quick start
|
|
54
|
+
|
|
55
|
+
```python
|
|
56
|
+
import neptls
|
|
57
|
+
|
|
58
|
+
response = neptls.get("https://example.com", timeout=10)
|
|
59
|
+
response.raise_for_status()
|
|
60
|
+
print(response.status_code)
|
|
61
|
+
print(response.text[:80])
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Reusable clients retain cookies and configuration:
|
|
65
|
+
|
|
66
|
+
```python
|
|
67
|
+
client = neptls.Client(
|
|
68
|
+
profile="chrome",
|
|
69
|
+
headers={"X-Research-Client": "neptls"},
|
|
70
|
+
retries=2,
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
response = client.post("https://httpbin.org/post", json={"hello": "world"})
|
|
74
|
+
print(response.json())
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Async calls use the same public API:
|
|
78
|
+
|
|
79
|
+
```python
|
|
80
|
+
import asyncio
|
|
81
|
+
from neptls import AsyncClient
|
|
82
|
+
|
|
83
|
+
async def main():
|
|
84
|
+
async with AsyncClient(timeout=10) as client:
|
|
85
|
+
response = await client.get("https://example.com")
|
|
86
|
+
print(response.status_code)
|
|
87
|
+
|
|
88
|
+
asyncio.run(main())
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## TLS and profiles
|
|
92
|
+
|
|
93
|
+
`TLSConfig` creates standard-library `ssl.SSLContext` objects and can be
|
|
94
|
+
serialized for experiments:
|
|
95
|
+
|
|
96
|
+
```python
|
|
97
|
+
from neptls import TLSConfig, TLSFingerprint
|
|
98
|
+
|
|
99
|
+
config = TLSConfig(minimum_version="TLSv1.2", alpn_protocols=("h2", "http/1.1"))
|
|
100
|
+
print(config.to_json())
|
|
101
|
+
print(TLSFingerprint.from_config(config).digest)
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Profiles are structured metadata, not browser impersonation:
|
|
105
|
+
|
|
106
|
+
```python
|
|
107
|
+
client = neptls.Client(profile="firefox")
|
|
108
|
+
print(client.fingerprint().to_dict())
|
|
109
|
+
print(neptls.profiles.get_profile("chrome").to_json())
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
## Fingerprints and user agents
|
|
113
|
+
|
|
114
|
+
```python
|
|
115
|
+
profile = neptls.fingerprint.generate(browser="chrome", platform="windows", seed=7)
|
|
116
|
+
print(profile.validate().to_json())
|
|
117
|
+
print(neptls.ua.chrome())
|
|
118
|
+
print(neptls.user_agents.parse(neptls.ua.random()))
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
The built-in user-agent catalog is intentionally curated. Load a properly
|
|
122
|
+
licensed dataset into `UserAgentDatabase` when your application needs a
|
|
123
|
+
larger pool; NepTLS does not bundle a copied 40,000-entry third-party list.
|
|
124
|
+
|
|
125
|
+
## Diagnostics
|
|
126
|
+
|
|
127
|
+
`inspect()` performs read-only DNS, TCP, TLS, and HTTP observations:
|
|
128
|
+
|
|
129
|
+
```python
|
|
130
|
+
report = neptls.inspect("https://example.com")
|
|
131
|
+
print(report["tls"]["version"])
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
## Pools, proxies, hashing, and generic PoW
|
|
135
|
+
|
|
136
|
+
```python
|
|
137
|
+
from neptls import Proxy
|
|
138
|
+
from neptls.crypto import sha256
|
|
139
|
+
from neptls.pools import Pool
|
|
140
|
+
from neptls.pow import Challenge, solve
|
|
141
|
+
|
|
142
|
+
pool = Pool(["profile-a", "profile-b"])
|
|
143
|
+
print(pool.next())
|
|
144
|
+
client = neptls.Client(proxy="http://127.0.0.1:8080")
|
|
145
|
+
print(sha256("protocol message"))
|
|
146
|
+
result = solve(Challenge("demo", difficulty=3))
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
The PoW implementation is generic and intentionally not tied to any
|
|
150
|
+
anti-abuse or security system.
|
|
151
|
+
|
|
152
|
+
## CLI
|
|
153
|
+
|
|
154
|
+
```bash
|
|
155
|
+
neptls version
|
|
156
|
+
neptls get https://example.com
|
|
157
|
+
neptls inspect https://example.com
|
|
158
|
+
neptls profile chrome
|
|
159
|
+
neptls ua mobile
|
|
160
|
+
neptls hash "protocol message" --algorithm sha256
|
|
161
|
+
neptls pow demo --difficulty 3
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
## Development
|
|
165
|
+
|
|
166
|
+
```bash
|
|
167
|
+
python -m unittest discover -s tests -v
|
|
168
|
+
python -m compileall -q src
|
|
169
|
+
python -m build
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
The project is organized into focused modules for HTTP transport, TLS models,
|
|
173
|
+
profiles, fingerprints, user agents, pools, proxy data, diagnostics, crypto,
|
|
174
|
+
PoW, and the CLI. Features that depend on native HTTP/2 or HTTP/3 stacks are
|
|
175
|
+
kept out of the initial dependency-free release until their transport and
|
|
176
|
+
platform behavior can be tested reliably.
|
|
177
|
+
|
|
178
|
+
## License and credits
|
|
179
|
+
|
|
180
|
+
NepTLS is released under the MIT License. See `LICENSE`.
|
neptls-0.1.0/README.md
ADDED
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
# NepTLS
|
|
2
|
+
|
|
3
|
+
NepTLS is a dependency-light Python HTTP client and TLS research toolkit. It
|
|
4
|
+
combines a requests-style API with structured TLS configuration, browser
|
|
5
|
+
network profiles, local fingerprint analysis, diagnostics, pools, and a
|
|
6
|
+
generic proof-of-work framework.
|
|
7
|
+
|
|
8
|
+
**Developed by Diwas Khatri (@diwaskhatri07).**
|
|
9
|
+
|
|
10
|
+
## Security boundary
|
|
11
|
+
|
|
12
|
+
NepTLS is for networking research, browser compatibility, API testing,
|
|
13
|
+
protocol interoperability, performance testing, debugging, authorized
|
|
14
|
+
automation, and defensive security research. It is not designed to bypass
|
|
15
|
+
CAPTCHAs, authentication, payment security, access controls, or anti-abuse
|
|
16
|
+
systems, and it does not include session theft or credential attack features.
|
|
17
|
+
Fingerprint objects describe and compare configurations; they do not spoof
|
|
18
|
+
browser security signals.
|
|
19
|
+
|
|
20
|
+
## Installation
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
python -m pip install neptls
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
NepTLS has no runtime dependencies and supports Python 3.10+.
|
|
27
|
+
|
|
28
|
+
## Quick start
|
|
29
|
+
|
|
30
|
+
```python
|
|
31
|
+
import neptls
|
|
32
|
+
|
|
33
|
+
response = neptls.get("https://example.com", timeout=10)
|
|
34
|
+
response.raise_for_status()
|
|
35
|
+
print(response.status_code)
|
|
36
|
+
print(response.text[:80])
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Reusable clients retain cookies and configuration:
|
|
40
|
+
|
|
41
|
+
```python
|
|
42
|
+
client = neptls.Client(
|
|
43
|
+
profile="chrome",
|
|
44
|
+
headers={"X-Research-Client": "neptls"},
|
|
45
|
+
retries=2,
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
response = client.post("https://httpbin.org/post", json={"hello": "world"})
|
|
49
|
+
print(response.json())
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Async calls use the same public API:
|
|
53
|
+
|
|
54
|
+
```python
|
|
55
|
+
import asyncio
|
|
56
|
+
from neptls import AsyncClient
|
|
57
|
+
|
|
58
|
+
async def main():
|
|
59
|
+
async with AsyncClient(timeout=10) as client:
|
|
60
|
+
response = await client.get("https://example.com")
|
|
61
|
+
print(response.status_code)
|
|
62
|
+
|
|
63
|
+
asyncio.run(main())
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## TLS and profiles
|
|
67
|
+
|
|
68
|
+
`TLSConfig` creates standard-library `ssl.SSLContext` objects and can be
|
|
69
|
+
serialized for experiments:
|
|
70
|
+
|
|
71
|
+
```python
|
|
72
|
+
from neptls import TLSConfig, TLSFingerprint
|
|
73
|
+
|
|
74
|
+
config = TLSConfig(minimum_version="TLSv1.2", alpn_protocols=("h2", "http/1.1"))
|
|
75
|
+
print(config.to_json())
|
|
76
|
+
print(TLSFingerprint.from_config(config).digest)
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
Profiles are structured metadata, not browser impersonation:
|
|
80
|
+
|
|
81
|
+
```python
|
|
82
|
+
client = neptls.Client(profile="firefox")
|
|
83
|
+
print(client.fingerprint().to_dict())
|
|
84
|
+
print(neptls.profiles.get_profile("chrome").to_json())
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## Fingerprints and user agents
|
|
88
|
+
|
|
89
|
+
```python
|
|
90
|
+
profile = neptls.fingerprint.generate(browser="chrome", platform="windows", seed=7)
|
|
91
|
+
print(profile.validate().to_json())
|
|
92
|
+
print(neptls.ua.chrome())
|
|
93
|
+
print(neptls.user_agents.parse(neptls.ua.random()))
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
The built-in user-agent catalog is intentionally curated. Load a properly
|
|
97
|
+
licensed dataset into `UserAgentDatabase` when your application needs a
|
|
98
|
+
larger pool; NepTLS does not bundle a copied 40,000-entry third-party list.
|
|
99
|
+
|
|
100
|
+
## Diagnostics
|
|
101
|
+
|
|
102
|
+
`inspect()` performs read-only DNS, TCP, TLS, and HTTP observations:
|
|
103
|
+
|
|
104
|
+
```python
|
|
105
|
+
report = neptls.inspect("https://example.com")
|
|
106
|
+
print(report["tls"]["version"])
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
## Pools, proxies, hashing, and generic PoW
|
|
110
|
+
|
|
111
|
+
```python
|
|
112
|
+
from neptls import Proxy
|
|
113
|
+
from neptls.crypto import sha256
|
|
114
|
+
from neptls.pools import Pool
|
|
115
|
+
from neptls.pow import Challenge, solve
|
|
116
|
+
|
|
117
|
+
pool = Pool(["profile-a", "profile-b"])
|
|
118
|
+
print(pool.next())
|
|
119
|
+
client = neptls.Client(proxy="http://127.0.0.1:8080")
|
|
120
|
+
print(sha256("protocol message"))
|
|
121
|
+
result = solve(Challenge("demo", difficulty=3))
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
The PoW implementation is generic and intentionally not tied to any
|
|
125
|
+
anti-abuse or security system.
|
|
126
|
+
|
|
127
|
+
## CLI
|
|
128
|
+
|
|
129
|
+
```bash
|
|
130
|
+
neptls version
|
|
131
|
+
neptls get https://example.com
|
|
132
|
+
neptls inspect https://example.com
|
|
133
|
+
neptls profile chrome
|
|
134
|
+
neptls ua mobile
|
|
135
|
+
neptls hash "protocol message" --algorithm sha256
|
|
136
|
+
neptls pow demo --difficulty 3
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
## Development
|
|
140
|
+
|
|
141
|
+
```bash
|
|
142
|
+
python -m unittest discover -s tests -v
|
|
143
|
+
python -m compileall -q src
|
|
144
|
+
python -m build
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
The project is organized into focused modules for HTTP transport, TLS models,
|
|
148
|
+
profiles, fingerprints, user agents, pools, proxy data, diagnostics, crypto,
|
|
149
|
+
PoW, and the CLI. Features that depend on native HTTP/2 or HTTP/3 stacks are
|
|
150
|
+
kept out of the initial dependency-free release until their transport and
|
|
151
|
+
platform behavior can be tested reliably.
|
|
152
|
+
|
|
153
|
+
## License and credits
|
|
154
|
+
|
|
155
|
+
NepTLS is released under the MIT License. See `LICENSE`.
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=68"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "neptls"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "A dependency-light HTTP client and TLS research toolkit for authorized testing."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.10"
|
|
11
|
+
license = "MIT"
|
|
12
|
+
authors = [{ name = "Diwas Khatri", email = "diwaskhatri07@users.noreply.github.com" }]
|
|
13
|
+
keywords = ["http", "tls", "networking", "diagnostics", "fingerprinting"]
|
|
14
|
+
classifiers = [
|
|
15
|
+
"Development Status :: 3 - Alpha",
|
|
16
|
+
"Intended Audience :: Developers",
|
|
17
|
+
"Programming Language :: Python :: 3",
|
|
18
|
+
"Programming Language :: Python :: 3 :: Only",
|
|
19
|
+
"Programming Language :: Python :: 3.10",
|
|
20
|
+
"Programming Language :: Python :: 3.11",
|
|
21
|
+
"Programming Language :: Python :: 3.12",
|
|
22
|
+
"Programming Language :: Python :: 3.13",
|
|
23
|
+
"Topic :: Internet :: WWW/HTTP",
|
|
24
|
+
"Topic :: Security",
|
|
25
|
+
]
|
|
26
|
+
dependencies = []
|
|
27
|
+
|
|
28
|
+
[project.urls]
|
|
29
|
+
Homepage = "https://github.com/diwaskhatri07/neptls"
|
|
30
|
+
Repository = "https://github.com/diwaskhatri07/neptls"
|
|
31
|
+
Issues = "https://github.com/diwaskhatri07/neptls/issues"
|
|
32
|
+
|
|
33
|
+
[project.scripts]
|
|
34
|
+
neptls = "neptls.cli:main"
|
|
35
|
+
|
|
36
|
+
[tool.setuptools]
|
|
37
|
+
package-dir = { "" = "src" }
|
|
38
|
+
|
|
39
|
+
[tool.setuptools.packages.find]
|
|
40
|
+
where = ["src"]
|
|
41
|
+
|
|
42
|
+
[tool.setuptools.package-data]
|
|
43
|
+
neptls = ["py.typed"]
|
|
44
|
+
|
|
45
|
+
[tool.ruff]
|
|
46
|
+
line-length = 100
|
|
47
|
+
target-version = "py310"
|
neptls-0.1.0/setup.cfg
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
"""NepTLS: an HTTP client and TLS research toolkit.
|
|
2
|
+
|
|
3
|
+
Developed by Diwas Khatri (@diwaskhatri07).
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from .async_client import AsyncClient
|
|
7
|
+
from .client import Client, delete, get, head, options, patch, post, put, request
|
|
8
|
+
from .diagnostics import diagnose, inspect
|
|
9
|
+
from .exceptions import HTTPStatusError, NepTLSError, RequestError, TimeoutError
|
|
10
|
+
from .fingerprints import ClientFingerprint
|
|
11
|
+
from .models import RequestTiming, Response
|
|
12
|
+
from .profiles import BrowserProfile, PROFILES, get_profile
|
|
13
|
+
from .proxy import Proxy
|
|
14
|
+
from .tls import TLSConfig, TLSFingerprint
|
|
15
|
+
from . import crypto, fingerprints as fingerprint, profiles, pow, user_agents
|
|
16
|
+
from .user_agents import ua
|
|
17
|
+
|
|
18
|
+
__version__ = "0.1.0"
|
|
19
|
+
|
|
20
|
+
__all__ = [
|
|
21
|
+
"AsyncClient",
|
|
22
|
+
"BrowserProfile",
|
|
23
|
+
"Client",
|
|
24
|
+
"ClientFingerprint",
|
|
25
|
+
"HTTPStatusError",
|
|
26
|
+
"NepTLSError",
|
|
27
|
+
"PROFILES",
|
|
28
|
+
"Proxy",
|
|
29
|
+
"RequestError",
|
|
30
|
+
"RequestTiming",
|
|
31
|
+
"Response",
|
|
32
|
+
"TLSConfig",
|
|
33
|
+
"TLSFingerprint",
|
|
34
|
+
"TimeoutError",
|
|
35
|
+
"crypto",
|
|
36
|
+
"delete",
|
|
37
|
+
"diagnose",
|
|
38
|
+
"fingerprint",
|
|
39
|
+
"get",
|
|
40
|
+
"get_profile",
|
|
41
|
+
"head",
|
|
42
|
+
"inspect",
|
|
43
|
+
"options",
|
|
44
|
+
"patch",
|
|
45
|
+
"post",
|
|
46
|
+
"pow",
|
|
47
|
+
"profiles",
|
|
48
|
+
"put",
|
|
49
|
+
"request",
|
|
50
|
+
"ua",
|
|
51
|
+
"user_agents",
|
|
52
|
+
]
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
"""Async API backed by the same tested synchronous transport."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import asyncio
|
|
6
|
+
from typing import Any
|
|
7
|
+
|
|
8
|
+
from .client import Client
|
|
9
|
+
from .models import Response
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class AsyncClient:
|
|
13
|
+
"""An asyncio-friendly client with a persistent cookie/profile session."""
|
|
14
|
+
|
|
15
|
+
def __init__(self, **kwargs: Any) -> None:
|
|
16
|
+
self._client = Client(**kwargs)
|
|
17
|
+
|
|
18
|
+
async def request(self, method: str, url: str, **kwargs: Any) -> Response:
|
|
19
|
+
return await asyncio.to_thread(self._client.request, method, url, **kwargs)
|
|
20
|
+
|
|
21
|
+
async def get(self, url: str, **kwargs: Any) -> Response:
|
|
22
|
+
return await self.request("GET", url, **kwargs)
|
|
23
|
+
|
|
24
|
+
async def post(self, url: str, **kwargs: Any) -> Response:
|
|
25
|
+
return await self.request("POST", url, **kwargs)
|
|
26
|
+
|
|
27
|
+
async def put(self, url: str, **kwargs: Any) -> Response:
|
|
28
|
+
return await self.request("PUT", url, **kwargs)
|
|
29
|
+
|
|
30
|
+
async def patch(self, url: str, **kwargs: Any) -> Response:
|
|
31
|
+
return await self.request("PATCH", url, **kwargs)
|
|
32
|
+
|
|
33
|
+
async def delete(self, url: str, **kwargs: Any) -> Response:
|
|
34
|
+
return await self.request("DELETE", url, **kwargs)
|
|
35
|
+
|
|
36
|
+
async def head(self, url: str, **kwargs: Any) -> Response:
|
|
37
|
+
return await self.request("HEAD", url, **kwargs)
|
|
38
|
+
|
|
39
|
+
async def options(self, url: str, **kwargs: Any) -> Response:
|
|
40
|
+
return await self.request("OPTIONS", url, **kwargs)
|
|
41
|
+
|
|
42
|
+
def fingerprint(self):
|
|
43
|
+
return self._client.fingerprint()
|
|
44
|
+
|
|
45
|
+
async def close(self) -> None:
|
|
46
|
+
await asyncio.to_thread(self._client.close)
|
|
47
|
+
|
|
48
|
+
async def __aenter__(self) -> "AsyncClient":
|
|
49
|
+
return self
|
|
50
|
+
|
|
51
|
+
async def __aexit__(self, *_: object) -> None:
|
|
52
|
+
await self.close()
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
"""Command-line interface for NepTLS."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import argparse
|
|
6
|
+
import json
|
|
7
|
+
import sys
|
|
8
|
+
from dataclasses import asdict
|
|
9
|
+
|
|
10
|
+
from . import __version__, get, profiles, ua
|
|
11
|
+
from .crypto import blake2, md5, sha256, sha512
|
|
12
|
+
from .diagnostics import inspect
|
|
13
|
+
from .pow import Challenge, solve
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def _parser() -> argparse.ArgumentParser:
|
|
17
|
+
parser = argparse.ArgumentParser(prog="neptls", description="NepTLS networking research toolkit")
|
|
18
|
+
sub = parser.add_subparsers(dest="command")
|
|
19
|
+
for method in ("get", "post", "put", "patch", "delete", "head", "options"):
|
|
20
|
+
command = sub.add_parser(method, help=f"perform an HTTP {method.upper()} request")
|
|
21
|
+
command.add_argument("url")
|
|
22
|
+
command.add_argument("--json", dest="json_body", help="JSON request body")
|
|
23
|
+
command.add_argument("--header", action="append", default=[], metavar="NAME:VALUE")
|
|
24
|
+
for name in ("inspect", "diagnose"):
|
|
25
|
+
command = sub.add_parser(name, help="inspect DNS, TCP, TLS, and HTTP")
|
|
26
|
+
command.add_argument("url")
|
|
27
|
+
command.add_argument("--json", action="store_true", dest="as_json")
|
|
28
|
+
sub.add_parser("version")
|
|
29
|
+
profile_command = sub.add_parser("profile", help="list browser profiles")
|
|
30
|
+
profile_command.add_argument("name", nargs="?")
|
|
31
|
+
ua_command = sub.add_parser("ua", help="query the user-agent catalog")
|
|
32
|
+
ua_command.add_argument("kind", nargs="?", default="random", choices=("random", "chrome", "firefox", "mobile"))
|
|
33
|
+
hash_command = sub.add_parser("hash", help="hash text")
|
|
34
|
+
hash_command.add_argument("value")
|
|
35
|
+
hash_command.add_argument("--algorithm", choices=("sha256", "sha512", "blake2", "md5"), default="sha256")
|
|
36
|
+
pow_command = sub.add_parser("pow", help="solve a generic proof-of-work challenge")
|
|
37
|
+
pow_command.add_argument("payload")
|
|
38
|
+
pow_command.add_argument("--difficulty", type=int, default=4)
|
|
39
|
+
return parser
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def main(argv: list[str] | None = None) -> int:
|
|
43
|
+
args = _parser().parse_args(argv)
|
|
44
|
+
if args.command == "version":
|
|
45
|
+
print(__version__)
|
|
46
|
+
return 0
|
|
47
|
+
if args.command in {"get", "post", "put", "patch", "delete", "head", "options"}:
|
|
48
|
+
headers = dict(item.split(":", 1) for item in args.header if ":" in item)
|
|
49
|
+
body = json.loads(args.json_body) if args.json_body else None
|
|
50
|
+
response = getattr(__import__("neptls"), args.command)(args.url, headers=headers, json=body)
|
|
51
|
+
print(response.text)
|
|
52
|
+
return 0 if response.ok else 1
|
|
53
|
+
if args.command in {"inspect", "diagnose"}:
|
|
54
|
+
print(json.dumps(inspect(args.url), indent=2, default=str))
|
|
55
|
+
return 0
|
|
56
|
+
if args.command == "profile":
|
|
57
|
+
value = profiles.PROFILES if not args.name else profiles.get_profile(args.name).to_dict()
|
|
58
|
+
print(json.dumps(value, indent=2, default=str))
|
|
59
|
+
return 0
|
|
60
|
+
if args.command == "ua":
|
|
61
|
+
print(getattr(ua, args.kind)())
|
|
62
|
+
return 0
|
|
63
|
+
if args.command == "hash":
|
|
64
|
+
function = {"sha256": sha256, "sha512": sha512, "blake2": blake2, "md5": md5}[args.algorithm]
|
|
65
|
+
print(function(args.value))
|
|
66
|
+
return 0
|
|
67
|
+
if args.command == "pow":
|
|
68
|
+
result = solve(Challenge(args.payload, difficulty=args.difficulty))
|
|
69
|
+
print(json.dumps(asdict(result), default=str))
|
|
70
|
+
return 0
|
|
71
|
+
_parser().print_help()
|
|
72
|
+
return 0
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
if __name__ == "__main__":
|
|
76
|
+
sys.exit(main())
|