omserv 0.0.0.dev395__tar.gz → 0.0.0.dev497__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.
Files changed (61) hide show
  1. {omserv-0.0.0.dev395/omserv.egg-info → omserv-0.0.0.dev497}/PKG-INFO +6 -6
  2. omserv-0.0.0.dev395/omserv/.manifests.json → omserv-0.0.0.dev497/omserv/.omlish-manifests.json +3 -3
  3. omserv-0.0.0.dev497/omserv/README.md +9 -0
  4. {omserv-0.0.0.dev395 → omserv-0.0.0.dev497}/omserv/__about__.py +2 -2
  5. {omserv-0.0.0.dev395 → omserv-0.0.0.dev497}/omserv/apps/markers.py +1 -1
  6. {omserv-0.0.0.dev395 → omserv-0.0.0.dev497}/omserv/apps/routes.py +3 -3
  7. {omserv-0.0.0.dev395 → omserv-0.0.0.dev497}/omserv/apps/sessions.py +2 -2
  8. {omserv-0.0.0.dev395 → omserv-0.0.0.dev497}/omserv/apps/templates.py +4 -2
  9. {omserv-0.0.0.dev395 → omserv-0.0.0.dev497}/omserv/nginx/build.py +1 -1
  10. {omserv-0.0.0.dev395 → omserv-0.0.0.dev497}/omserv/nginx/logs.py +3 -3
  11. {omserv-0.0.0.dev395 → omserv-0.0.0.dev497}/omserv/nodes/registry.py +2 -2
  12. {omserv-0.0.0.dev395 → omserv-0.0.0.dev497}/omserv/nodes/sql.py +2 -0
  13. {omserv-0.0.0.dev395 → omserv-0.0.0.dev497}/omserv/server/debug.py +2 -2
  14. {omserv-0.0.0.dev395 → omserv-0.0.0.dev497}/omserv/server/events.py +5 -0
  15. {omserv-0.0.0.dev395 → omserv-0.0.0.dev497}/omserv/server/lifespans.py +4 -2
  16. {omserv-0.0.0.dev395 → omserv-0.0.0.dev497}/omserv/server/listener.py +4 -3
  17. {omserv-0.0.0.dev395 → omserv-0.0.0.dev497}/omserv/server/protocols/h11.py +2 -0
  18. {omserv-0.0.0.dev395 → omserv-0.0.0.dev497}/omserv/server/protocols/h2.py +16 -8
  19. {omserv-0.0.0.dev395 → omserv-0.0.0.dev497}/omserv/server/protocols/protocols.py +1 -0
  20. {omserv-0.0.0.dev395 → omserv-0.0.0.dev497}/omserv/server/protocols/types.py +3 -1
  21. {omserv-0.0.0.dev395 → omserv-0.0.0.dev497}/omserv/server/server.py +2 -2
  22. {omserv-0.0.0.dev395 → omserv-0.0.0.dev497}/omserv/server/streams/httpstream.py +40 -4
  23. {omserv-0.0.0.dev395 → omserv-0.0.0.dev497}/omserv/server/streams/utils.py +3 -2
  24. {omserv-0.0.0.dev395 → omserv-0.0.0.dev497}/omserv/server/streams/wsstream.py +11 -4
  25. {omserv-0.0.0.dev395 → omserv-0.0.0.dev497}/omserv/server/taskspawner.py +3 -2
  26. {omserv-0.0.0.dev395 → omserv-0.0.0.dev497}/omserv/server/types.py +1 -0
  27. {omserv-0.0.0.dev395 → omserv-0.0.0.dev497}/omserv/server/workercontext.py +2 -0
  28. {omserv-0.0.0.dev395 → omserv-0.0.0.dev497/omserv.egg-info}/PKG-INFO +6 -6
  29. {omserv-0.0.0.dev395 → omserv-0.0.0.dev497}/omserv.egg-info/SOURCES.txt +2 -1
  30. {omserv-0.0.0.dev395 → omserv-0.0.0.dev497}/omserv.egg-info/requires.txt +5 -5
  31. {omserv-0.0.0.dev395 → omserv-0.0.0.dev497}/pyproject.toml +11 -7
  32. {omserv-0.0.0.dev395 → omserv-0.0.0.dev497}/LICENSE +0 -0
  33. {omserv-0.0.0.dev395 → omserv-0.0.0.dev497}/MANIFEST.in +0 -0
  34. {omserv-0.0.0.dev395 → omserv-0.0.0.dev497}/README.md +0 -0
  35. {omserv-0.0.0.dev395 → omserv-0.0.0.dev497}/omserv/__init__.py +0 -0
  36. {omserv-0.0.0.dev395 → omserv-0.0.0.dev497}/omserv/apps/__init__.py +0 -0
  37. {omserv-0.0.0.dev395 → omserv-0.0.0.dev497}/omserv/apps/base.py +0 -0
  38. {omserv-0.0.0.dev395 → omserv-0.0.0.dev497}/omserv/apps/inject.py +0 -0
  39. {omserv-0.0.0.dev395 → omserv-0.0.0.dev497}/omserv/nginx/__init__.py +0 -0
  40. {omserv-0.0.0.dev395 → omserv-0.0.0.dev497}/omserv/nginx/patches/__init__.py +0 -0
  41. {omserv-0.0.0.dev395 → omserv-0.0.0.dev497}/omserv/nginx/patches/nginx-1.28.0_http_status.patch +0 -0
  42. {omserv-0.0.0.dev395 → omserv-0.0.0.dev497}/omserv/nginx/stubstatus.py +0 -0
  43. {omserv-0.0.0.dev395 → omserv-0.0.0.dev497}/omserv/nodes/__init__.py +0 -0
  44. {omserv-0.0.0.dev395 → omserv-0.0.0.dev497}/omserv/nodes/models.py +0 -0
  45. {omserv-0.0.0.dev395 → omserv-0.0.0.dev497}/omserv/server/LICENSE +0 -0
  46. {omserv-0.0.0.dev395 → omserv-0.0.0.dev497}/omserv/server/__init__.py +0 -0
  47. {omserv-0.0.0.dev395 → omserv-0.0.0.dev497}/omserv/server/config.py +0 -0
  48. {omserv-0.0.0.dev395 → omserv-0.0.0.dev497}/omserv/server/default.py +0 -0
  49. {omserv-0.0.0.dev395 → omserv-0.0.0.dev497}/omserv/server/headers.py +0 -0
  50. {omserv-0.0.0.dev395 → omserv-0.0.0.dev497}/omserv/server/inject.py +0 -0
  51. {omserv-0.0.0.dev395 → omserv-0.0.0.dev497}/omserv/server/multiprocess.py +0 -0
  52. {omserv-0.0.0.dev395 → omserv-0.0.0.dev497}/omserv/server/protocols/__init__.py +0 -0
  53. {omserv-0.0.0.dev395 → omserv-0.0.0.dev497}/omserv/server/resources/__init__.py +0 -0
  54. {omserv-0.0.0.dev395 → omserv-0.0.0.dev497}/omserv/server/resources/favicon.ico +0 -0
  55. {omserv-0.0.0.dev395 → omserv-0.0.0.dev497}/omserv/server/sockets.py +0 -0
  56. {omserv-0.0.0.dev395 → omserv-0.0.0.dev497}/omserv/server/ssl.py +0 -0
  57. {omserv-0.0.0.dev395 → omserv-0.0.0.dev497}/omserv/server/streams/__init__.py +0 -0
  58. {omserv-0.0.0.dev395 → omserv-0.0.0.dev497}/omserv.egg-info/dependency_links.txt +0 -0
  59. {omserv-0.0.0.dev395 → omserv-0.0.0.dev497}/omserv.egg-info/entry_points.txt +0 -0
  60. {omserv-0.0.0.dev395 → omserv-0.0.0.dev497}/omserv.egg-info/top_level.txt +0 -0
  61. {omserv-0.0.0.dev395 → omserv-0.0.0.dev497}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: omserv
3
- Version: 0.0.0.dev395
3
+ Version: 0.0.0.dev497
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.dev395
17
+ Requires-Dist: omlish==0.0.0.dev497
18
18
  Provides-Extra: all
19
19
  Requires-Dist: h11~=0.16; extra == "all"
20
- Requires-Dist: h2~=4.2; extra == "all"
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
- Requires-Dist: h2~=4.2; extra == "server"
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
@@ -5,9 +5,9 @@
5
5
  "file": "omserv/nginx/logs.py",
6
6
  "line": 68,
7
7
  "value": {
8
- "$omdev.cli.types.CliModule": {
9
- "cmd_name": "nginxlogs",
10
- "mod_name": "omserv.nginx.logs"
8
+ "!omdev.cli.types.CliModule": {
9
+ "name": "nginxlogs",
10
+ "module": "omserv.nginx.logs"
11
11
  }
12
12
  }
13
13
  }
@@ -0,0 +1,9 @@
1
+ # Overview
2
+
3
+ Request serving code.
4
+
5
+ # Notable packages
6
+
7
+ - **[server](https://github.com/wrmsr/omlish/blob/master/omserv/server)** - Production web server based on
8
+ [hypercorn](https://github.com/pgjones/hypercorn). Converted to anyio, but still being refined and integrated with the
9
+ codebase.
@@ -14,9 +14,9 @@ class Project(ProjectBase):
14
14
  optional_dependencies = {
15
15
  'server': [
16
16
  'h11 ~= 0.16',
17
- 'h2 ~= 4.2',
17
+ 'h2 ~= 4.3',
18
18
  'priority ~= 2.0',
19
- 'wsproto ~= 1.2',
19
+ 'wsproto ~= 1.3',
20
20
  ],
21
21
 
22
22
  'templates': [
@@ -27,7 +27,7 @@ def append_app_marker(obj: T, *markers: AppMarker) -> T:
27
27
 
28
28
 
29
29
  def get_app_markers(obj: ta.Any) -> ta.Sequence[AppMarker]:
30
- return [md for md in get_object_metadata(obj) if isinstance(md, AppMarker)]
30
+ return get_object_metadata(obj, type=AppMarker)
31
31
 
32
32
 
33
33
  ##
@@ -5,13 +5,13 @@ TODO:
5
5
  """
6
6
  import contextlib
7
7
  import dataclasses as dc
8
- import logging
9
8
  import re
10
9
  import typing as ta
11
10
 
12
11
  from omlish import check
13
12
  from omlish import lang
14
13
  from omlish.http import asgi
14
+ from omlish.logs import all as logs
15
15
 
16
16
  from .base import BASE_SERVER_URL
17
17
  from .base import SCOPE
@@ -49,7 +49,7 @@ KNOWN_METHODS: tuple[str, ...] = (
49
49
  PatOrStr: ta.TypeAlias = re.Pattern | str
50
50
 
51
51
 
52
- log = logging.getLogger(__name__)
52
+ log = logs.get_module_logger(globals())
53
53
 
54
54
 
55
55
  ##
@@ -143,7 +143,7 @@ HANDLES_APP_MARKER_PROCESSORS: AppMarkerProcessorMap = {
143
143
  ##
144
144
 
145
145
 
146
- class RouteHandlerHolder(lang.Abstract): # noqa
146
+ class RouteHandlerHolder(lang.Abstract):
147
147
  def get_route_handlers(self) -> ta.Iterable[RouteHandler]:
148
148
  return get_marked_route_handlers(self)
149
149
 
@@ -1,17 +1,17 @@
1
1
  import contextvars
2
2
  import dataclasses as dc
3
- import logging
4
3
 
5
4
  from omlish import lang
6
5
  from omlish.http import asgi
7
6
  from omlish.http import sessions
7
+ from omlish.logs import all as logs
8
8
 
9
9
  from .markers import AppMarker
10
10
  from .markers import AppMarkerProcessor
11
11
  from .markers import append_app_marker
12
12
 
13
13
 
14
- log = logging.getLogger(__name__)
14
+ log = logs.get_module_logger(globals())
15
15
 
16
16
 
17
17
  ##
@@ -1,14 +1,15 @@
1
1
  import dataclasses as dc
2
2
  import importlib.resources
3
- import logging
4
3
  import typing as ta
5
4
 
6
5
  import jinja2
7
6
 
7
+ from omlish.logs import all as logs
8
+
8
9
  from .base import url_for
9
10
 
10
11
 
11
- log = logging.getLogger(__name__)
12
+ log = logs.get_module_logger(globals())
12
13
 
13
14
 
14
15
  ##
@@ -53,6 +54,7 @@ class JinjaTemplates:
53
54
  class _Loader(jinja2.BaseLoader):
54
55
  def __init__(self, owner: 'JinjaTemplates') -> None:
55
56
  super().__init__()
57
+
56
58
  self._owner = owner
57
59
 
58
60
  def get_source(self, environment, template):
@@ -20,7 +20,7 @@ from omlish import lang
20
20
  NGINX_VERSION = '1.28.0'
21
21
  NGINX_SRC_URL = f'https://nginx.org/download/nginx-{NGINX_VERSION}.tar.gz'
22
22
 
23
- NGINX_VTS_VERSION = '0.2.2'
23
+ NGINX_VTS_VERSION = '0.2.4'
24
24
  NGINX_VTS_SRC_URL = f'https://github.com/vozlt/nginx-module-vts/archive/refs/tags/v{NGINX_VTS_VERSION}.tar.gz'
25
25
 
26
26
 
@@ -66,9 +66,9 @@ def parse_nginx_log_line(
66
66
 
67
67
 
68
68
  # @omlish-manifest
69
- _CLI_MODULE = {'$omdev.cli.types.CliModule': {
70
- 'cmd_name': 'nginxlogs',
71
- 'mod_name': __name__,
69
+ _CLI_MODULE = {'!omdev.cli.types.CliModule': {
70
+ 'name': 'nginxlogs',
71
+ 'module': __name__,
72
72
  }}
73
73
 
74
74
 
@@ -1,6 +1,5 @@
1
1
  import contextlib
2
2
  import dataclasses as dc
3
- import logging
4
3
  import socket
5
4
  import typing as ta
6
5
  import uuid
@@ -12,6 +11,7 @@ from omlish import check
12
11
  from omlish import lang
13
12
  from omlish.asyncs import all as au
14
13
  from omlish.asyncs import anyio as anu
14
+ from omlish.logs import all as logs
15
15
  from omlish.sql import alchemy as sau
16
16
 
17
17
  from .models import Nodes
@@ -19,7 +19,7 @@ from .models import setup_db
19
19
  from .sql import utcnow
20
20
 
21
21
 
22
- log = logging.getLogger(__name__)
22
+ log = logs.get_module_logger(globals())
23
23
 
24
24
 
25
25
  ##
@@ -85,6 +85,7 @@ class CreateUpdateAtTrigger(sa.schema.DDLElement):
85
85
 
86
86
  def __init__(self, table_name: str) -> None:
87
87
  super().__init__()
88
+
88
89
  self.table_name = table_name
89
90
 
90
91
 
@@ -111,6 +112,7 @@ class DropUpdateAtTrigger(sa.schema.DDLElement):
111
112
 
112
113
  def __init__(self, table_name: str) -> None:
113
114
  super().__init__()
115
+
114
116
  self.table_name = table_name
115
117
 
116
118
 
@@ -1,10 +1,10 @@
1
- import logging
2
1
  import sys
3
2
 
4
3
  from omlish.diag import pydevd as pdu
4
+ from omlish.logs import all as logs
5
5
 
6
6
 
7
- log = logging.getLogger(__name__)
7
+ log = logs.get_module_logger(globals())
8
8
 
9
9
 
10
10
  ##
@@ -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
@@ -1,4 +1,3 @@
1
- import logging
2
1
  import typing as ta
3
2
 
4
3
  import anyio
@@ -6,6 +5,8 @@ import anyio.abc
6
5
  import anyio.from_thread
7
6
  import anyio.to_thread
8
7
 
8
+ from omlish.logs import all as logs
9
+
9
10
  from .config import Config
10
11
  from .debug import handle_error_debug
11
12
  from .types import AppWrapper
@@ -15,7 +16,7 @@ from .types import LifespanScope
15
16
  from .types import UnexpectedMessageError
16
17
 
17
18
 
18
- log = logging.getLogger(__name__)
19
+ log = logs.get_module_logger(globals())
19
20
 
20
21
 
21
22
  ##
@@ -37,6 +38,7 @@ class LifespanFailureError(Exception):
37
38
  class Lifespan:
38
39
  def __init__(self, app: AppWrapper, config: Config) -> None:
39
40
  super().__init__()
41
+
40
42
  self.app = app
41
43
  self.config = config
42
44
  self.startup = anyio.Event()
@@ -1,6 +1,5 @@
1
1
  import errno
2
2
  import functools
3
- import logging
4
3
  import os
5
4
  import random
6
5
  import signal # noqa
@@ -9,6 +8,8 @@ import typing as ta
9
8
  import anyio
10
9
  import anyio.abc
11
10
 
11
+ from omlish.logs import all as logs
12
+
12
13
  from .config import Config
13
14
  from .lifespans import Lifespan
14
15
  from .server import ServerFactory
@@ -22,7 +23,7 @@ from .workercontext import ShutdownError
22
23
  from .workercontext import WorkerContext
23
24
 
24
25
 
25
- log = logging.getLogger(__name__)
26
+ log = logs.get_module_logger(globals())
26
27
 
27
28
 
28
29
  ##
@@ -173,7 +174,7 @@ class Listener:
173
174
  binds = []
174
175
 
175
176
  for sock in sockets.insecure_sockets:
176
- 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
177
178
  bind = repr_socket_addr(sock.family, sock.getsockname())
178
179
  binds.append(f'http://{bind}')
179
180
  log.info('Running on http://%s (CTRL + C to quit)', bind)
@@ -43,6 +43,7 @@ STREAM_ID = 1
43
43
  class H2CProtocolRequiredError(Exception):
44
44
  def __init__(self, data: bytes, request: h11.Request) -> None:
45
45
  super().__init__()
46
+
46
47
  settings = ''
47
48
  headers = [(b':method', request.method), (b':path', request.target)]
48
49
  for name, value in request.headers:
@@ -60,6 +61,7 @@ class H2CProtocolRequiredError(Exception):
60
61
  class H2ProtocolAssumedError(Exception):
61
62
  def __init__(self, data: bytes) -> None:
62
63
  super().__init__()
64
+
63
65
  self.data = data
64
66
 
65
67
 
@@ -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
@@ -50,6 +51,7 @@ class BufferCompleteError(Exception):
50
51
  class StreamBuffer:
51
52
  def __init__(self, event_class: type[WaitableEvent]) -> None:
52
53
  super().__init__()
54
+
53
55
  self.buffer = bytearray()
54
56
  self._complete = False
55
57
  self._is_empty = event_class()
@@ -152,8 +154,7 @@ class H2Protocol(Protocol):
152
154
  await self._flush()
153
155
 
154
156
  if headers is not None:
155
- event = h2.events.RequestReceived()
156
- event.stream_id = 1
157
+ event = h2.events.RequestReceived(stream_id=1)
157
158
  event.headers = [hpack.HeaderTuple(*t) for t in headers]
158
159
  await self._create_stream(event)
159
160
  await self.streams[event.stream_id].handle(EndBody(stream_id=event.stream_id))
@@ -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))
@@ -395,7 +404,7 @@ class H2Protocol(Protocol):
395
404
 
396
405
  await self.streams[stream_id].handle(Request(
397
406
  stream_id=stream_id,
398
- headers=filter_pseudo_headers(check.not_none(request.headers)), # type: ignore[arg-type]
407
+ headers=filter_pseudo_headers(check.not_none(request.headers)),
399
408
  http_version='2',
400
409
  method=method,
401
410
  raw_path=raw_path,
@@ -428,8 +437,7 @@ class H2Protocol(Protocol):
428
437
  pass
429
438
 
430
439
  else:
431
- event = h2.events.RequestReceived()
432
- event.stream_id = push_stream_id
440
+ event = h2.events.RequestReceived(stream_id=push_stream_id)
433
441
  event.headers = [hpack.HeaderTuple(*t) for t in request_headers]
434
442
  await self._create_stream(event)
435
443
  await self.streams[event.stream_id].handle(EndBody(stream_id=event.stream_id))
@@ -29,6 +29,7 @@ class ProtocolWrapper:
29
29
  alpn_protocol: str | None = None,
30
30
  ) -> None:
31
31
  super().__init__()
32
+
32
33
  self.app = app
33
34
  self.config = config
34
35
  self.context = context
@@ -1,5 +1,7 @@
1
1
  import abc
2
2
 
3
+ from omlish import lang
4
+
3
5
  from ..events import ProtocolEvent
4
6
  from ..events import ServerEvent
5
7
 
@@ -7,7 +9,7 @@ from ..events import ServerEvent
7
9
  ##
8
10
 
9
11
 
10
- class Protocol(abc.ABC):
12
+ class Protocol(lang.Abstract):
11
13
  @abc.abstractmethod
12
14
  async def initiate(self) -> None:
13
15
  raise NotImplementedError
@@ -1,5 +1,4 @@
1
1
  import errno
2
- import logging
3
2
  import math
4
3
  import typing as ta
5
4
 
@@ -7,6 +6,7 @@ import anyio.abc
7
6
 
8
7
  from omlish import check
9
8
  from omlish import lang
9
+ from omlish.logs import all as logs
10
10
 
11
11
  from .config import Config
12
12
  from .events import Closed
@@ -20,7 +20,7 @@ from .types import AppWrapper
20
20
  from .workercontext import WorkerContext
21
21
 
22
22
 
23
- log = logging.getLogger(__name__)
23
+ log = logs.get_module_logger(globals())
24
24
 
25
25
 
26
26
  ##
@@ -1,9 +1,10 @@
1
1
  import enum
2
- import logging
3
2
  import time
4
3
  import typing as ta
5
4
  import urllib.parse
6
5
 
6
+ from omlish.logs import all as logs
7
+
7
8
  from ..config import Config
8
9
  from ..events import Body
9
10
  from ..events import EndBody
@@ -12,6 +13,7 @@ from ..events import ProtocolEvent
12
13
  from ..events import Request
13
14
  from ..events import Response
14
15
  from ..events import StreamClosed
16
+ from ..events import Trailers
15
17
  from ..taskspawner import TaskSpawner
16
18
  from ..types import AppWrapper
17
19
  from ..types import AsgiSendEvent
@@ -25,12 +27,13 @@ from .utils import suppress_body
25
27
  from .utils import valid_server_name
26
28
 
27
29
 
28
- log = logging.getLogger(__name__)
30
+ log = logs.get_module_logger(globals())
29
31
 
30
32
 
31
33
  ##
32
34
 
33
35
 
36
+ TRAILERS_VERSIONS = {'2', '3'}
34
37
  PUSH_VERSIONS = {'2', '3'}
35
38
  EARLY_HINTS_VERSIONS = {'2', '3'}
36
39
 
@@ -40,6 +43,7 @@ class AsgiHttpState(enum.Enum):
40
43
  # hence why this state tracking is required.
41
44
  REQUEST = enum.auto()
42
45
  RESPONSE = enum.auto()
46
+ TRAILERS = enum.auto()
43
47
  CLOSED = enum.auto()
44
48
 
45
49
 
@@ -56,6 +60,7 @@ class HttpStream:
56
60
  stream_id: int,
57
61
  ) -> None:
58
62
  super().__init__()
63
+
59
64
  self.app = app
60
65
  self.client = client
61
66
  self.closed = False
@@ -99,6 +104,9 @@ class HttpStream:
99
104
  'extensions': {},
100
105
  }
101
106
 
107
+ if event.http_version in TRAILERS_VERSIONS:
108
+ self.scope['extensions']['http.response.trailers'] = {}
109
+
102
110
  if event.http_version in PUSH_VERSIONS:
103
111
  self.scope['extensions']['http.response.push'] = {}
104
112
 
@@ -198,7 +206,12 @@ class HttpStream:
198
206
  await self.send(Body(stream_id=self.stream_id, data=bytes(message.get('body', b''))))
199
207
 
200
208
  if not message.get('more_body', False):
201
- 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:
202
215
  self.state = AsgiHttpState.CLOSED
203
216
 
204
217
  log_access(
@@ -208,7 +221,30 @@ class HttpStream:
208
221
  time.time() - self.start_time,
209
222
  )
210
223
 
211
- 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
+ )
212
248
 
213
249
  await self.send(StreamClosed(stream_id=self.stream_id))
214
250
 
@@ -1,12 +1,13 @@
1
- import logging
2
1
  import typing as ta
3
2
 
3
+ from omlish.logs import all as logs
4
+
4
5
  from ..config import Config
5
6
  from ..events import Request
6
7
  from ..types import Scope
7
8
 
8
9
 
9
- log = logging.getLogger(__name__)
10
+ log = logs.get_module_logger(globals())
10
11
 
11
12
 
12
13
  ##
@@ -1,18 +1,19 @@
1
1
  import encodings.idna # prevents `LookupError: unknown encoding: idna` # noqa
2
2
  import enum
3
3
  import io
4
- import logging
5
4
  import time
6
5
  import typing as ta
7
6
  import urllib.parse
8
7
 
9
- from omlish import check
10
8
  import wsproto as wsp
11
9
  import wsproto.events as wse
12
10
  import wsproto.extensions
13
11
  import wsproto.frame_protocol
14
12
  import wsproto.utilities
15
13
 
14
+ from omlish import check
15
+ from omlish.logs import all as logs
16
+
16
17
  from ..config import Config
17
18
  from ..events import Body
18
19
  from ..events import Data
@@ -37,7 +38,7 @@ from .utils import suppress_body
37
38
  from .utils import valid_server_name
38
39
 
39
40
 
40
- log = logging.getLogger(__name__)
41
+ log = logs.get_module_logger(globals())
41
42
 
42
43
 
43
44
  ##
@@ -64,6 +65,7 @@ class Handshake:
64
65
  ) -> None:
65
66
  super().__init__()
66
67
 
68
+ self.accepted = False
67
69
  self.http_version = http_version
68
70
  self.connection_tokens: list[str] | None = None
69
71
  self.extensions: list[str] | None = None
@@ -148,6 +150,7 @@ class Handshake:
148
150
 
149
151
  headers.append((name, value))
150
152
 
153
+ self.accepted = True
151
154
  return status_code, headers, wsp.Connection(wsp.ConnectionType.SERVER, extensions)
152
155
 
153
156
 
@@ -260,6 +263,10 @@ class WsStream:
260
263
  self.connection.receive_data(event.data)
261
264
  await self._handle_events()
262
265
 
266
+ elif isinstance(event, (Body, Data)) and not self.handshake.accepted:
267
+ await self._send_error_response(400)
268
+ self.closed = True
269
+
263
270
  elif isinstance(event, StreamClosed):
264
271
  self.closed = True
265
272
 
@@ -311,7 +318,7 @@ class WsStream:
311
318
  elif message['type'] == 'websocket.send' and self.state == AsgiWebsocketState.CONNECTED:
312
319
  event: wse.Event
313
320
  if message.get('bytes') is not None:
314
- event = wse.BytesMessage(data=bytes(message['bytes']))
321
+ event = wse.BytesMessage(data=bytearray(message['bytes']))
315
322
 
316
323
  elif not isinstance(message['text'], str):
317
324
  raise TypeError(f'{message["text"]} should be a str')
@@ -1,4 +1,3 @@
1
- import logging
2
1
  import types
3
2
  import typing as ta
4
3
 
@@ -8,6 +7,7 @@ import anyio.from_thread
8
7
  import anyio.to_thread
9
8
 
10
9
  from omlish import check
10
+ from omlish.logs import all as logs
11
11
 
12
12
  from .config import Config
13
13
  from .debug import handle_error_debug
@@ -18,7 +18,7 @@ from .types import AsgiSendEvent
18
18
  from .types import Scope
19
19
 
20
20
 
21
- log = logging.getLogger(__name__)
21
+ log = logs.get_module_logger(globals())
22
22
 
23
23
 
24
24
  ##
@@ -67,6 +67,7 @@ async def _handle(
67
67
  class TaskSpawner:
68
68
  def __init__(self) -> None:
69
69
  super().__init__()
70
+
70
71
  self._task_group: anyio.abc.TaskGroup | None = None
71
72
 
72
73
  async def start(
@@ -41,6 +41,7 @@ class UnexpectedMessageError(Exception):
41
41
  class AsgiWrapper:
42
42
  def __init__(self, app: AsgiFramework) -> None:
43
43
  super().__init__()
44
+
44
45
  self.app = app
45
46
 
46
47
  async def __call__(
@@ -9,6 +9,7 @@ from .types import WaitableEvent
9
9
  class WaitableEventWrapper:
10
10
  def __init__(self) -> None:
11
11
  super().__init__()
12
+
12
13
  self._event = anyio.Event()
13
14
 
14
15
  async def clear(self) -> None:
@@ -29,6 +30,7 @@ class WorkerContext:
29
30
 
30
31
  def __init__(self, max_requests: int | None) -> None:
31
32
  super().__init__()
33
+
32
34
  self.max_requests = max_requests
33
35
  self.requests = 0
34
36
  self.terminate = self.event_class()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: omserv
3
- Version: 0.0.0.dev395
3
+ Version: 0.0.0.dev497
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.dev395
17
+ Requires-Dist: omlish==0.0.0.dev497
18
18
  Provides-Extra: all
19
19
  Requires-Dist: h11~=0.16; extra == "all"
20
- Requires-Dist: h2~=4.2; extra == "all"
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
- Requires-Dist: h2~=4.2; extra == "server"
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
@@ -2,7 +2,8 @@ LICENSE
2
2
  MANIFEST.in
3
3
  README.md
4
4
  pyproject.toml
5
- omserv/.manifests.json
5
+ omserv/.omlish-manifests.json
6
+ omserv/README.md
6
7
  omserv/__about__.py
7
8
  omserv/__init__.py
8
9
  omserv.egg-info/PKG-INFO
@@ -1,17 +1,17 @@
1
- omlish==0.0.0.dev395
1
+ omlish==0.0.0.dev497
2
2
 
3
3
  [all]
4
4
  h11~=0.16
5
- h2~=4.2
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
- h2~=4.2
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.dev395'
16
+ version = '0.0.0.dev497'
17
17
  classifiers = [
18
18
  'Development Status :: 2 - Pre-Alpha',
19
19
  'Intended Audience :: Developers',
@@ -24,22 +24,22 @@ classifiers = [
24
24
  ]
25
25
  description = 'omserv'
26
26
  dependencies = [
27
- 'omlish == 0.0.0.dev395',
27
+ 'omlish == 0.0.0.dev497',
28
28
  ]
29
29
 
30
30
  [project.optional-dependencies]
31
31
  all = [
32
32
  'h11 ~= 0.16',
33
- 'h2 ~= 4.2',
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
- 'h2 ~= 4.2',
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',
@@ -67,9 +67,13 @@ exclude = [
67
67
  '*.cu',
68
68
  '*.g4',
69
69
  '*.h',
70
- '.manifests.json',
70
+ '*.hh',
71
+ '.omlish-manifests.json',
72
+ 'README',
73
+ 'README.md',
71
74
  'LICENSE',
72
75
  'LICENSE.txt',
76
+ 'AUTHORS',
73
77
  'nginx/patches/*.patch',
74
78
  'server/resources/*.ico',
75
79
  ]
File without changes
File without changes
File without changes
File without changes