Netizen
Netizen is a minimalist HTTP client with a symmetrical interface between async and sync modes. It doesn't aim to be feature-complete like requests or httpx.
Netizen is just enough for poking API endpoints and performing basic HTTP operations or testing. It suits me, as I prefer working closely with sockets and don't need high-level abstraction.
Features
- Symmetrical interface, e.g.
client.send()vsawait client.send() - The
retriesparameter makes it resilient and prevents flaky tests - ~500 lines of code
- No dependencies other than the Python Standard Library
Installation
pip install git+https://github.com/nggit/netizen.git
Handling a JSON response body
import asyncio
from netizen import HTTPClient
# sync
with HTTPClient('ip-api.com', 80) as client:
response = client.send(b'GET /json HTTP/1.1')
print(response.json())
# async
async def main():
async with HTTPClient('ip-api.com', 80) as client:
response = await client.send(b'GET /json HTTP/1.1')
print(await response.json())
asyncio.run(main())
Handling a raw response body with streaming
import asyncio
from netizen import HTTPClient
client = HTTPClient('example.com', 80)
# sync
with client:
response = client.send(b'GET / HTTP/1.1')
for data in response:
print('Received:', len(data), 'Bytes')
# async
async def main():
async with client:
response = await client.send(b'GET / HTTP/1.1')
async for data in response:
print('Received:', len(data), 'Bytes')
asyncio.run(main())
Append request headers via *args also body parameter
with HTTPClient('example.com', 80) as client:
response = client.send(
b'POST / HTTP/1.1',
b'Content-Type: application/json',
b'Content-Length: 14',
body=b'{"foo": "bar"}'
)
print('Status code:', response.status) # 403
print('Reason phrase:', response.message) # b'Forbidden'
# out of context, close the connection without reading the entire response body
If you don't specify any headers, then Content-Length will be automatically
inserted along with Content-Type: application/x-www-form-urlencoded.
with HTTPClient('example.com', 80) as client:
response = client.send(b'POST / HTTP/1.1', body=b'foo=bar')
Send multiple requests within the same context/connection
with HTTPClient('ip-api.com', 80) as client:
# first request
response = client.send(b'GET /json HTTP/1.1')
# the first response body must be consumed before sending another one
print(response.json())
# second request
response = client.send(b'GET /json HTTP/1.1')
print(response.body())
Handling URL redirects
from urllib.parse import urlparse
with HTTPClient('google.com', 443, ssl=True) as client:
response = client.send(b'GET / HTTP/1.1')
print('1. Status code:', response.status) # 301
print('1. Reason phrase:', response.message) # b'Moved Permanently'
print('1. Location:', response.url) # b'http://www.google.com/'
for data in response:
pass
if response.url:
url = urlparse(response.url)
if url.netloc: # b'www.google.com' (different host)
with HTTPClient(url.netloc.decode(), 443, ssl=True) as client:
response = client.send(b'GET %s HTTP/1.1' % url.path)
print('2. Status code:', response.status) # 200
print('2. Reason phrase:', response.message) # b'OK'
for data in response:
pass
else:
pass
Working directly with socket using client.sendall() and client.recv()
with HTTPClient('localhost', 8000, timeout=10, retries=10) as client:
response = client.send(
b'GET /chat HTTP/1.1',
b'Upgrade: WebSocket',
b'Connection: Upgrade',
b'Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==',
b'Sec-WebSocket-Version: 13'
)
if response.status == 101:
client.sendall(b'\x81\x0dHello, World!\x88\x02\x03\xe8')
print('Received:', client.recv(4096))
Manually craft a bad request with client.sendall() and client.end()
with HTTPClient('localhost', 8000) as client:
client.sendall(
b'POST /upload HTTP/1.1\r\n'
b'Host: localhost\r\n'
b'Content-Length: 5\r\n'
b'Transfer-Encoding: chunked\r\n\r\n'
)
client.sendall(b'0\r\n\r\n')
# we are not using the `client.send()`, but we need the response object?
response = client.end()
print(response.body())
License
MIT License
Release files for netizen 0.0.2
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| netizen-0.0.2.tar.gz | 9.2 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| netizen-0.0.2-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 17.6 kB
Release files / netizen-0.0.2.tar.gz
| Download URL | netizen-0.0.2.tar.gz |
|---|---|
| Size | 9.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
01c99426d58fa7319cbbd6f222caf72978ac18e9d977e5d0a860b7c5c94a1f8f
|
|
BLAKE2b-256 checksum How to use checksums |
dd2ec7a4ea11b704d5cdbdf271b04aaa2f8e95d206fb812552633bdbc2b73f18
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.0.1 CPython/3.12.11
|
Release files / netizen-0.0.2-py3-none-any.whl
| Download URL | netizen-0.0.2-py3-none-any.whl |
|---|---|
| Size | 8.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
c6846dd227dea3613fff0b276046d5aee8f89ea4d4cbc739c06938d8e67b7883
|
|
BLAKE2b-256 checksum How to use checksums |
ad5f25f52d7ab9e627707cbf94e6bb455c1614c97350b04baae9db49e05abe44
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.0.1 CPython/3.12.11
|