omserv 0.0.0.dev471__tar.gz → 0.0.0.dev486__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.

Potentially problematic release.


This version of omserv might be problematic. Click here for more details.

Files changed (60) hide show
  1. {omserv-0.0.0.dev471/omserv.egg-info → omserv-0.0.0.dev486}/PKG-INFO +4 -4
  2. {omserv-0.0.0.dev471 → omserv-0.0.0.dev486}/omserv/__about__.py +1 -1
  3. {omserv-0.0.0.dev471 → omserv-0.0.0.dev486}/omserv/server/events.py +5 -0
  4. {omserv-0.0.0.dev471 → omserv-0.0.0.dev486}/omserv/server/listener.py +1 -1
  5. {omserv-0.0.0.dev471 → omserv-0.0.0.dev486}/omserv/server/protocols/h2.py +12 -3
  6. {omserv-0.0.0.dev471 → omserv-0.0.0.dev486}/omserv/server/streams/httpstream.py +36 -2
  7. {omserv-0.0.0.dev471 → omserv-0.0.0.dev486}/omserv/server/streams/wsstream.py +7 -1
  8. {omserv-0.0.0.dev471 → omserv-0.0.0.dev486/omserv.egg-info}/PKG-INFO +4 -4
  9. {omserv-0.0.0.dev471 → omserv-0.0.0.dev486}/omserv.egg-info/requires.txt +3 -3
  10. {omserv-0.0.0.dev471 → omserv-0.0.0.dev486}/pyproject.toml +4 -4
  11. {omserv-0.0.0.dev471 → omserv-0.0.0.dev486}/LICENSE +0 -0
  12. {omserv-0.0.0.dev471 → omserv-0.0.0.dev486}/MANIFEST.in +0 -0
  13. {omserv-0.0.0.dev471 → omserv-0.0.0.dev486}/README.md +0 -0
  14. {omserv-0.0.0.dev471 → omserv-0.0.0.dev486}/omserv/.omlish-manifests.json +0 -0
  15. {omserv-0.0.0.dev471 → omserv-0.0.0.dev486}/omserv/__init__.py +0 -0
  16. {omserv-0.0.0.dev471 → omserv-0.0.0.dev486}/omserv/apps/__init__.py +0 -0
  17. {omserv-0.0.0.dev471 → omserv-0.0.0.dev486}/omserv/apps/base.py +0 -0
  18. {omserv-0.0.0.dev471 → omserv-0.0.0.dev486}/omserv/apps/inject.py +0 -0
  19. {omserv-0.0.0.dev471 → omserv-0.0.0.dev486}/omserv/apps/markers.py +0 -0
  20. {omserv-0.0.0.dev471 → omserv-0.0.0.dev486}/omserv/apps/routes.py +0 -0
  21. {omserv-0.0.0.dev471 → omserv-0.0.0.dev486}/omserv/apps/sessions.py +0 -0
  22. {omserv-0.0.0.dev471 → omserv-0.0.0.dev486}/omserv/apps/templates.py +0 -0
  23. {omserv-0.0.0.dev471 → omserv-0.0.0.dev486}/omserv/nginx/__init__.py +0 -0
  24. {omserv-0.0.0.dev471 → omserv-0.0.0.dev486}/omserv/nginx/build.py +0 -0
  25. {omserv-0.0.0.dev471 → omserv-0.0.0.dev486}/omserv/nginx/logs.py +0 -0
  26. {omserv-0.0.0.dev471 → omserv-0.0.0.dev486}/omserv/nginx/patches/__init__.py +0 -0
  27. {omserv-0.0.0.dev471 → omserv-0.0.0.dev486}/omserv/nginx/patches/nginx-1.28.0_http_status.patch +0 -0
  28. {omserv-0.0.0.dev471 → omserv-0.0.0.dev486}/omserv/nginx/stubstatus.py +0 -0
  29. {omserv-0.0.0.dev471 → omserv-0.0.0.dev486}/omserv/nodes/__init__.py +0 -0
  30. {omserv-0.0.0.dev471 → omserv-0.0.0.dev486}/omserv/nodes/models.py +0 -0
  31. {omserv-0.0.0.dev471 → omserv-0.0.0.dev486}/omserv/nodes/registry.py +0 -0
  32. {omserv-0.0.0.dev471 → omserv-0.0.0.dev486}/omserv/nodes/sql.py +0 -0
  33. {omserv-0.0.0.dev471 → omserv-0.0.0.dev486}/omserv/server/LICENSE +0 -0
  34. {omserv-0.0.0.dev471 → omserv-0.0.0.dev486}/omserv/server/__init__.py +0 -0
  35. {omserv-0.0.0.dev471 → omserv-0.0.0.dev486}/omserv/server/config.py +0 -0
  36. {omserv-0.0.0.dev471 → omserv-0.0.0.dev486}/omserv/server/debug.py +0 -0
  37. {omserv-0.0.0.dev471 → omserv-0.0.0.dev486}/omserv/server/default.py +0 -0
  38. {omserv-0.0.0.dev471 → omserv-0.0.0.dev486}/omserv/server/headers.py +0 -0
  39. {omserv-0.0.0.dev471 → omserv-0.0.0.dev486}/omserv/server/inject.py +0 -0
  40. {omserv-0.0.0.dev471 → omserv-0.0.0.dev486}/omserv/server/lifespans.py +0 -0
  41. {omserv-0.0.0.dev471 → omserv-0.0.0.dev486}/omserv/server/multiprocess.py +0 -0
  42. {omserv-0.0.0.dev471 → omserv-0.0.0.dev486}/omserv/server/protocols/__init__.py +0 -0
  43. {omserv-0.0.0.dev471 → omserv-0.0.0.dev486}/omserv/server/protocols/h11.py +0 -0
  44. {omserv-0.0.0.dev471 → omserv-0.0.0.dev486}/omserv/server/protocols/protocols.py +0 -0
  45. {omserv-0.0.0.dev471 → omserv-0.0.0.dev486}/omserv/server/protocols/types.py +0 -0
  46. {omserv-0.0.0.dev471 → omserv-0.0.0.dev486}/omserv/server/resources/__init__.py +0 -0
  47. {omserv-0.0.0.dev471 → omserv-0.0.0.dev486}/omserv/server/resources/favicon.ico +0 -0
  48. {omserv-0.0.0.dev471 → omserv-0.0.0.dev486}/omserv/server/server.py +0 -0
  49. {omserv-0.0.0.dev471 → omserv-0.0.0.dev486}/omserv/server/sockets.py +0 -0
  50. {omserv-0.0.0.dev471 → omserv-0.0.0.dev486}/omserv/server/ssl.py +0 -0
  51. {omserv-0.0.0.dev471 → omserv-0.0.0.dev486}/omserv/server/streams/__init__.py +0 -0
  52. {omserv-0.0.0.dev471 → omserv-0.0.0.dev486}/omserv/server/streams/utils.py +0 -0
  53. {omserv-0.0.0.dev471 → omserv-0.0.0.dev486}/omserv/server/taskspawner.py +0 -0
  54. {omserv-0.0.0.dev471 → omserv-0.0.0.dev486}/omserv/server/types.py +0 -0
  55. {omserv-0.0.0.dev471 → omserv-0.0.0.dev486}/omserv/server/workercontext.py +0 -0
  56. {omserv-0.0.0.dev471 → omserv-0.0.0.dev486}/omserv.egg-info/SOURCES.txt +0 -0
  57. {omserv-0.0.0.dev471 → omserv-0.0.0.dev486}/omserv.egg-info/dependency_links.txt +0 -0
  58. {omserv-0.0.0.dev471 → omserv-0.0.0.dev486}/omserv.egg-info/entry_points.txt +0 -0
  59. {omserv-0.0.0.dev471 → omserv-0.0.0.dev486}/omserv.egg-info/top_level.txt +0 -0
  60. {omserv-0.0.0.dev471 → omserv-0.0.0.dev486}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: omserv
3
- Version: 0.0.0.dev471
3
+ Version: 0.0.0.dev486
4
4
  Summary: omserv
5
5
  Author: wrmsr
6
6
  License-Expression: BSD-3-Clause
@@ -14,18 +14,18 @@ Classifier: Programming Language :: Python :: 3.13
14
14
  Requires-Python: >=3.13
15
15
  Description-Content-Type: text/markdown
16
16
  License-File: LICENSE
17
- Requires-Dist: omlish==0.0.0.dev471
17
+ Requires-Dist: omlish==0.0.0.dev486
18
18
  Provides-Extra: all
19
19
  Requires-Dist: h11~=0.16; extra == "all"
20
20
  Requires-Dist: h2~=4.3; extra == "all"
21
21
  Requires-Dist: priority~=2.0; extra == "all"
22
- Requires-Dist: wsproto~=1.2; extra == "all"
22
+ Requires-Dist: wsproto~=1.3; extra == "all"
23
23
  Requires-Dist: jinja2~=3.1; extra == "all"
24
24
  Provides-Extra: server
25
25
  Requires-Dist: h11~=0.16; extra == "server"
26
26
  Requires-Dist: h2~=4.3; extra == "server"
27
27
  Requires-Dist: priority~=2.0; extra == "server"
28
- Requires-Dist: wsproto~=1.2; extra == "server"
28
+ Requires-Dist: wsproto~=1.3; extra == "server"
29
29
  Provides-Extra: templates
30
30
  Requires-Dist: jinja2~=3.1; extra == "templates"
31
31
  Dynamic: license-file
@@ -16,7 +16,7 @@ class Project(ProjectBase):
16
16
  'h11 ~= 0.16',
17
17
  'h2 ~= 4.3',
18
18
  'priority ~= 2.0',
19
- 'wsproto ~= 1.2',
19
+ 'wsproto ~= 1.3',
20
20
  ],
21
21
 
22
22
  'templates': [
@@ -57,6 +57,11 @@ class Data(ProtocolEvent):
57
57
  data: bytes
58
58
 
59
59
 
60
+ @dc.dataclass(frozen=True)
61
+ class Trailers(ProtocolEvent):
62
+ headers: list[tuple[bytes, bytes]]
63
+
64
+
60
65
  @dc.dataclass(frozen=True)
61
66
  class EndData(ProtocolEvent):
62
67
  pass
@@ -174,7 +174,7 @@ class Listener:
174
174
  binds = []
175
175
 
176
176
  for sock in sockets.insecure_sockets:
177
- listeners.append(anyio._core._eventloop.get_async_backend().create_tcp_listener(sock)) # noqa
177
+ listeners.append(anyio._core._eventloop.get_async_backend().create_tcp_listener(sock)) # type: ignore[attr-defined] # noqa
178
178
  bind = repr_socket_addr(sock.family, sock.getsockname())
179
179
  binds.append(f'http://{bind}')
180
180
  log.info('Running on http://%s (CTRL + C to quit)', bind)
@@ -24,6 +24,7 @@ from ..events import Request
24
24
  from ..events import Response
25
25
  from ..events import ServerEvent
26
26
  from ..events import StreamClosed
27
+ from ..events import Trailers
27
28
  from ..events import Updated
28
29
  from ..headers import filter_pseudo_headers
29
30
  from ..headers import response_headers
@@ -239,6 +240,10 @@ class H2Protocol(Protocol):
239
240
  await self.has_data.set()
240
241
  await self.stream_buffers[event.stream_id].drain()
241
242
 
243
+ elif isinstance(event, Trailers):
244
+ self.connection.send_headers(event.stream_id, event.headers)
245
+ await self._flush()
246
+
242
247
  elif isinstance(event, StreamClosed):
243
248
  await self._close_stream(event.stream_id)
244
249
  idle = len(self.streams) == 0 or all(
@@ -288,9 +293,13 @@ class H2Protocol(Protocol):
288
293
  )
289
294
 
290
295
  elif isinstance(event, h2.events.StreamEnded):
291
- await self.streams[check.not_none(event.stream_id)].handle(
292
- EndBody(stream_id=check.not_none(event.stream_id)),
293
- )
296
+ try:
297
+ stream = self.streams[check.not_none(event.stream_id)]
298
+ except KeyError:
299
+ # Response sent before full request received, nothing to do already closed.
300
+ pass
301
+ else:
302
+ await stream.handle(EndBody(stream_id=check.not_none(event.stream_id)))
294
303
 
295
304
  elif isinstance(event, h2.events.StreamReset):
296
305
  await self._close_stream(check.not_none(event.stream_id))
@@ -13,6 +13,7 @@ from ..events import ProtocolEvent
13
13
  from ..events import Request
14
14
  from ..events import Response
15
15
  from ..events import StreamClosed
16
+ from ..events import Trailers
16
17
  from ..taskspawner import TaskSpawner
17
18
  from ..types import AppWrapper
18
19
  from ..types import AsgiSendEvent
@@ -32,6 +33,7 @@ log = logs.get_module_logger(globals())
32
33
  ##
33
34
 
34
35
 
36
+ TRAILERS_VERSIONS = {'2', '3'}
35
37
  PUSH_VERSIONS = {'2', '3'}
36
38
  EARLY_HINTS_VERSIONS = {'2', '3'}
37
39
 
@@ -41,6 +43,7 @@ class AsgiHttpState(enum.Enum):
41
43
  # hence why this state tracking is required.
42
44
  REQUEST = enum.auto()
43
45
  RESPONSE = enum.auto()
46
+ TRAILERS = enum.auto()
44
47
  CLOSED = enum.auto()
45
48
 
46
49
 
@@ -101,6 +104,9 @@ class HttpStream:
101
104
  'extensions': {},
102
105
  }
103
106
 
107
+ if event.http_version in TRAILERS_VERSIONS:
108
+ self.scope['extensions']['http.response.trailers'] = {}
109
+
104
110
  if event.http_version in PUSH_VERSIONS:
105
111
  self.scope['extensions']['http.response.push'] = {}
106
112
 
@@ -200,7 +206,12 @@ class HttpStream:
200
206
  await self.send(Body(stream_id=self.stream_id, data=bytes(message.get('body', b''))))
201
207
 
202
208
  if not message.get('more_body', False):
203
- if self.state != AsgiHttpState.CLOSED:
209
+ await self.send(EndBody(stream_id=self.stream_id))
210
+
211
+ if self.response.get('trailers', False):
212
+ self.state = AsgiHttpState.TRAILERS
213
+
214
+ else:
204
215
  self.state = AsgiHttpState.CLOSED
205
216
 
206
217
  log_access(
@@ -210,7 +221,30 @@ class HttpStream:
210
221
  time.time() - self.start_time,
211
222
  )
212
223
 
213
- await self.send(EndBody(stream_id=self.stream_id))
224
+ await self.send(StreamClosed(stream_id=self.stream_id))
225
+
226
+ elif (
227
+ message['type'] == 'http.response.trailers' and
228
+ self.scope['http_version'] in TRAILERS_VERSIONS and
229
+ self.state == AsgiHttpState.TRAILERS
230
+ ):
231
+ for name, value in self.scope['headers']:
232
+ if name == b'te' and value == b'trailers':
233
+ headers = build_and_validate_headers(message['headers'])
234
+
235
+ await self.send(Trailers(stream_id=self.stream_id, headers=headers))
236
+
237
+ break
238
+
239
+ if not message.get('more_trailers', False):
240
+ self.state = AsgiHttpState.CLOSED
241
+
242
+ log_access(
243
+ self.config,
244
+ self.scope,
245
+ self.response, # type: ignore # noqa
246
+ time.time() - self.start_time,
247
+ )
214
248
 
215
249
  await self.send(StreamClosed(stream_id=self.stream_id))
216
250
 
@@ -65,6 +65,7 @@ class Handshake:
65
65
  ) -> None:
66
66
  super().__init__()
67
67
 
68
+ self.accepted = False
68
69
  self.http_version = http_version
69
70
  self.connection_tokens: list[str] | None = None
70
71
  self.extensions: list[str] | None = None
@@ -149,6 +150,7 @@ class Handshake:
149
150
 
150
151
  headers.append((name, value))
151
152
 
153
+ self.accepted = True
152
154
  return status_code, headers, wsp.Connection(wsp.ConnectionType.SERVER, extensions)
153
155
 
154
156
 
@@ -261,6 +263,10 @@ class WsStream:
261
263
  self.connection.receive_data(event.data)
262
264
  await self._handle_events()
263
265
 
266
+ elif isinstance(event, (Body, Data)) and not self.handshake.accepted:
267
+ await self._send_error_response(400)
268
+ self.closed = True
269
+
264
270
  elif isinstance(event, StreamClosed):
265
271
  self.closed = True
266
272
 
@@ -312,7 +318,7 @@ class WsStream:
312
318
  elif message['type'] == 'websocket.send' and self.state == AsgiWebsocketState.CONNECTED:
313
319
  event: wse.Event
314
320
  if message.get('bytes') is not None:
315
- event = wse.BytesMessage(data=bytes(message['bytes']))
321
+ event = wse.BytesMessage(data=bytearray(message['bytes']))
316
322
 
317
323
  elif not isinstance(message['text'], str):
318
324
  raise TypeError(f'{message["text"]} should be a str')
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: omserv
3
- Version: 0.0.0.dev471
3
+ Version: 0.0.0.dev486
4
4
  Summary: omserv
5
5
  Author: wrmsr
6
6
  License-Expression: BSD-3-Clause
@@ -14,18 +14,18 @@ Classifier: Programming Language :: Python :: 3.13
14
14
  Requires-Python: >=3.13
15
15
  Description-Content-Type: text/markdown
16
16
  License-File: LICENSE
17
- Requires-Dist: omlish==0.0.0.dev471
17
+ Requires-Dist: omlish==0.0.0.dev486
18
18
  Provides-Extra: all
19
19
  Requires-Dist: h11~=0.16; extra == "all"
20
20
  Requires-Dist: h2~=4.3; extra == "all"
21
21
  Requires-Dist: priority~=2.0; extra == "all"
22
- Requires-Dist: wsproto~=1.2; extra == "all"
22
+ Requires-Dist: wsproto~=1.3; extra == "all"
23
23
  Requires-Dist: jinja2~=3.1; extra == "all"
24
24
  Provides-Extra: server
25
25
  Requires-Dist: h11~=0.16; extra == "server"
26
26
  Requires-Dist: h2~=4.3; extra == "server"
27
27
  Requires-Dist: priority~=2.0; extra == "server"
28
- Requires-Dist: wsproto~=1.2; extra == "server"
28
+ Requires-Dist: wsproto~=1.3; extra == "server"
29
29
  Provides-Extra: templates
30
30
  Requires-Dist: jinja2~=3.1; extra == "templates"
31
31
  Dynamic: license-file
@@ -1,17 +1,17 @@
1
- omlish==0.0.0.dev471
1
+ omlish==0.0.0.dev486
2
2
 
3
3
  [all]
4
4
  h11~=0.16
5
5
  h2~=4.3
6
6
  priority~=2.0
7
- wsproto~=1.2
7
+ wsproto~=1.3
8
8
  jinja2~=3.1
9
9
 
10
10
  [server]
11
11
  h11~=0.16
12
12
  h2~=4.3
13
13
  priority~=2.0
14
- wsproto~=1.2
14
+ wsproto~=1.3
15
15
 
16
16
  [templates]
17
17
  jinja2~=3.1
@@ -13,7 +13,7 @@ urls = {source = 'https://github.com/wrmsr/omlish'}
13
13
  license = 'BSD-3-Clause'
14
14
  readme = 'README.md'
15
15
  requires-python = '>=3.13'
16
- version = '0.0.0.dev471'
16
+ version = '0.0.0.dev486'
17
17
  classifiers = [
18
18
  'Development Status :: 2 - Pre-Alpha',
19
19
  'Intended Audience :: Developers',
@@ -24,7 +24,7 @@ classifiers = [
24
24
  ]
25
25
  description = 'omserv'
26
26
  dependencies = [
27
- 'omlish == 0.0.0.dev471',
27
+ 'omlish == 0.0.0.dev486',
28
28
  ]
29
29
 
30
30
  [project.optional-dependencies]
@@ -32,14 +32,14 @@ all = [
32
32
  'h11 ~= 0.16',
33
33
  'h2 ~= 4.3',
34
34
  'priority ~= 2.0',
35
- 'wsproto ~= 1.2',
35
+ 'wsproto ~= 1.3',
36
36
  'jinja2 ~= 3.1',
37
37
  ]
38
38
  server = [
39
39
  'h11 ~= 0.16',
40
40
  'h2 ~= 4.3',
41
41
  'priority ~= 2.0',
42
- 'wsproto ~= 1.2',
42
+ 'wsproto ~= 1.3',
43
43
  ]
44
44
  templates = [
45
45
  'jinja2 ~= 3.1',
File without changes
File without changes
File without changes
File without changes