You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Default mode is async. Use with external select loop:
146
+
Everything is non-blocking by default — TCP connect, SSL handshake, and HTTP I/O all happen through `select()`. This is critical for embedded devices on slow networks (4G modems, ESP32 PPP) where each phase can take seconds.
# Manual select loop - handles connect, send, and receive
158
158
whileTrue:
159
159
r, w, _ = select.select(
160
160
client.read_sockets,
@@ -170,8 +170,25 @@ while True:
170
170
client.close()
171
171
```
172
172
173
+
### State machine
174
+
175
+
After `client.get('/path')`, the client progresses through states automatically via `process_events()`:
176
+
177
+
| State | Description | select watches |
178
+
|---|---|---|
179
+
|`STATE_CONNECTING`| TCP connect in progress | write |
180
+
|`STATE_SSL_HANDSHAKE`| SSL handshake in progress | read or write |
181
+
|`STATE_SENDING`| Sending request data | write |
182
+
|`STATE_RECEIVING_HEADERS`| Waiting for response headers | read |
183
+
|`STATE_RECEIVING_BODY`| Receiving response body | read |
184
+
|`STATE_COMPLETE`| Response ready | — |
185
+
186
+
The `state` property exposes the current state. The `is_connected` property returns `True` only after connect and handshake are complete.
187
+
173
188
### Parallel requests
174
189
190
+
All clients share one select loop. Connect, handshake, and data transfer happen concurrently:
191
+
175
192
```python
176
193
import select
177
194
import uhttp.client
@@ -182,11 +199,11 @@ clients = [
182
199
uhttp.client.HttpClient('http://httpbin.org'),
183
200
]
184
201
185
-
# Start all requests
202
+
# Start all requests (non-blocking connects begin immediately)
186
203
for i, client inenumerate(clients):
187
204
client.get('/delay/1', query={'n': i})
188
205
189
-
#Wait for all
206
+
#Single select loop handles all clients
190
207
results = {}
191
208
whilelen(results) <len(clients):
192
209
read_socks = []
@@ -209,6 +226,8 @@ for client in clients:
209
226
210
227
### Combined with HttpServer
211
228
229
+
Server and client in the same select loop — true single-threaded concurrency:
230
+
212
231
```python
213
232
import select
214
233
import uhttp.server
@@ -235,6 +254,72 @@ while True:
235
254
incoming.respond(data=response.data)
236
255
```
237
256
257
+
### HTTPS with non-blocking handshake
258
+
259
+
SSL handshake is also non-blocking. The client tracks whether `do_handshake()` needs to read or write, and exposes the socket only in the correct direction to prevent `select()` from spinning:
260
+
261
+
```python
262
+
import select
263
+
import ssl
264
+
import uhttp.client
265
+
266
+
ctx = ssl.create_default_context()
267
+
client = uhttp.client.HttpClient(
268
+
'api.example.com', port=443, ssl_context=ctx)
269
+
270
+
# Connect + SSL handshake + request all happen via select
271
+
client.get('/data')
272
+
273
+
whileTrue:
274
+
r, w, _ = select.select(
275
+
client.read_sockets,
276
+
client.write_sockets,
277
+
[], 10.0
278
+
)
279
+
response = client.process_events(r, w)
280
+
if response:
281
+
print(response.json())
282
+
break
283
+
284
+
client.close()
285
+
```
286
+
287
+
### Multiple HTTPS clients in parallel
288
+
289
+
```python
290
+
import select
291
+
import uhttp.client
292
+
293
+
urls = [
294
+
'https://api1.example.com/data',
295
+
'https://api2.example.com/data',
296
+
'https://api3.example.com/data',
297
+
]
298
+
299
+
clients = [uhttp.client.HttpClient(url) for url in urls]
300
+
for c in clients:
301
+
c.get('/') # All start non-blocking connects + SSL handshakes
0 commit comments