ominfra 0.0.0.dev149__py3-none-any.whl → 0.0.0.dev150__py3-none-any.whl
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.
- ominfra/clouds/aws/auth.py +7 -9
- ominfra/clouds/aws/journald2aws/driver.py +4 -4
- ominfra/clouds/aws/logs.py +4 -5
- ominfra/clouds/gcp/auth.py +1 -1
- ominfra/configs.py +3 -4
- ominfra/journald/messages.py +2 -2
- ominfra/journald/tailer.py +2 -2
- ominfra/manage/commands/base.py +2 -2
- ominfra/manage/commands/interp.py +3 -3
- ominfra/manage/commands/subprocess.py +3 -4
- ominfra/manage/deploy/paths.py +12 -15
- ominfra/manage/main.py +72 -75
- ominfra/manage/remote/_main.py +6 -7
- ominfra/manage/remote/execution.py +7 -10
- ominfra/manage/remote/spawning.py +3 -3
- ominfra/scripts/journald2aws.py +449 -93
- ominfra/scripts/manage.py +770 -186
- ominfra/scripts/supervisor.py +1137 -781
- ominfra/supervisor/groupsimpl.py +2 -2
- ominfra/supervisor/http.py +5 -5
- ominfra/supervisor/main.py +1 -1
- ominfra/supervisor/processimpl.py +2 -2
- ominfra/supervisor/spawningimpl.py +8 -9
- ominfra/supervisor/supervisor.py +2 -2
- {ominfra-0.0.0.dev149.dist-info → ominfra-0.0.0.dev150.dist-info}/METADATA +3 -3
- {ominfra-0.0.0.dev149.dist-info → ominfra-0.0.0.dev150.dist-info}/RECORD +30 -30
- {ominfra-0.0.0.dev149.dist-info → ominfra-0.0.0.dev150.dist-info}/LICENSE +0 -0
- {ominfra-0.0.0.dev149.dist-info → ominfra-0.0.0.dev150.dist-info}/WHEEL +0 -0
- {ominfra-0.0.0.dev149.dist-info → ominfra-0.0.0.dev150.dist-info}/entry_points.txt +0 -0
- {ominfra-0.0.0.dev149.dist-info → ominfra-0.0.0.dev150.dist-info}/top_level.txt +0 -0
ominfra/scripts/supervisor.py
CHANGED
@@ -34,6 +34,7 @@
|
|
34
34
|
# EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
35
35
|
import abc
|
36
36
|
import base64
|
37
|
+
import collections
|
37
38
|
import collections.abc
|
38
39
|
import contextlib
|
39
40
|
import ctypes as ct
|
@@ -105,6 +106,11 @@ CallableT = ta.TypeVar('CallableT', bound=ta.Callable)
|
|
105
106
|
|
106
107
|
# ../../omlish/lite/check.py
|
107
108
|
SizedT = ta.TypeVar('SizedT', bound=ta.Sized)
|
109
|
+
CheckMessage = ta.Union[str, ta.Callable[..., ta.Optional[str]], None] # ta.TypeAlias
|
110
|
+
CheckLateConfigureFn = ta.Callable[['Checks'], None]
|
111
|
+
CheckOnRaiseFn = ta.Callable[[Exception], None] # ta.TypeAlias
|
112
|
+
CheckExceptionFactory = ta.Callable[..., Exception] # ta.TypeAlias
|
113
|
+
CheckArgsRenderer = ta.Callable[..., ta.Optional[str]] # ta.TypeAlias
|
108
114
|
|
109
115
|
# ../../omlish/lite/socket.py
|
110
116
|
SocketAddress = ta.Any
|
@@ -119,12 +125,12 @@ A2 = ta.TypeVar('A2')
|
|
119
125
|
EventCallback = ta.Callable[['Event'], None]
|
120
126
|
ProcessOutputChannel = ta.Literal['stdout', 'stderr'] # ta.TypeAlias
|
121
127
|
|
128
|
+
# ../../omlish/http/parsing.py
|
129
|
+
HttpHeaders = http.client.HTTPMessage # ta.TypeAlias
|
130
|
+
|
122
131
|
# ../../omlish/lite/contextmanagers.py
|
123
132
|
ExitStackedT = ta.TypeVar('ExitStackedT', bound='ExitStacked')
|
124
133
|
|
125
|
-
# ../../omlish/lite/http/parsing.py
|
126
|
-
HttpHeaders = http.client.HTTPMessage # ta.TypeAlias
|
127
|
-
|
128
134
|
# ../../omlish/lite/inject.py
|
129
135
|
U = ta.TypeVar('U')
|
130
136
|
InjectorKeyCls = ta.Union[type, ta.NewType]
|
@@ -135,10 +141,10 @@ InjectorBindingOrBindings = ta.Union['InjectorBinding', 'InjectorBindings']
|
|
135
141
|
# ../configs.py
|
136
142
|
ConfigMapping = ta.Mapping[str, ta.Any]
|
137
143
|
|
138
|
-
# ../../omlish/
|
144
|
+
# ../../omlish/http/handlers.py
|
139
145
|
HttpHandler = ta.Callable[['HttpHandlerRequest'], 'HttpHandlerResponse']
|
140
146
|
|
141
|
-
# ../../omlish/
|
147
|
+
# ../../omlish/http/coroserver.py
|
142
148
|
CoroHttpServerFactory = ta.Callable[[SocketAddress], 'CoroHttpServer']
|
143
149
|
|
144
150
|
|
@@ -1430,6 +1436,25 @@ def parse_octal(arg: ta.Union[str, int]) -> int:
|
|
1430
1436
|
raise ValueError(f'{arg} can not be converted to an octal type') # noqa
|
1431
1437
|
|
1432
1438
|
|
1439
|
+
########################################
|
1440
|
+
# ../../../omlish/http/versions.py
|
1441
|
+
|
1442
|
+
|
1443
|
+
class HttpProtocolVersion(ta.NamedTuple):
|
1444
|
+
major: int
|
1445
|
+
minor: int
|
1446
|
+
|
1447
|
+
def __str__(self) -> str:
|
1448
|
+
return f'HTTP/{self.major}.{self.minor}'
|
1449
|
+
|
1450
|
+
|
1451
|
+
class HttpProtocolVersions:
|
1452
|
+
HTTP_0_9 = HttpProtocolVersion(0, 9)
|
1453
|
+
HTTP_1_0 = HttpProtocolVersion(1, 0)
|
1454
|
+
HTTP_1_1 = HttpProtocolVersion(1, 1)
|
1455
|
+
HTTP_2_0 = HttpProtocolVersion(2, 0)
|
1456
|
+
|
1457
|
+
|
1433
1458
|
########################################
|
1434
1459
|
# ../../../omlish/io/fdio/pollers.py
|
1435
1460
|
|
@@ -1698,123 +1723,454 @@ def async_cached_nullary(fn): # ta.Callable[..., T]) -> ta.Callable[..., T]:
|
|
1698
1723
|
|
1699
1724
|
########################################
|
1700
1725
|
# ../../../omlish/lite/check.py
|
1726
|
+
"""
|
1727
|
+
TODO:
|
1728
|
+
- def maybe(v: lang.Maybe[T])
|
1729
|
+
- patch / override lite.check ?
|
1730
|
+
- checker interface?
|
1731
|
+
"""
|
1732
|
+
|
1733
|
+
|
1734
|
+
##
|
1701
1735
|
|
1702
1736
|
|
1703
|
-
|
1704
|
-
|
1705
|
-
|
1706
|
-
return v
|
1737
|
+
class Checks:
|
1738
|
+
def __init__(self) -> None:
|
1739
|
+
super().__init__()
|
1707
1740
|
|
1741
|
+
self._config_lock = threading.RLock()
|
1742
|
+
self._on_raise_fns: ta.Sequence[CheckOnRaiseFn] = []
|
1743
|
+
self._exception_factory: CheckExceptionFactory = Checks.default_exception_factory
|
1744
|
+
self._args_renderer: ta.Optional[CheckArgsRenderer] = None
|
1745
|
+
self._late_configure_fns: ta.Sequence[CheckLateConfigureFn] = []
|
1746
|
+
|
1747
|
+
@staticmethod
|
1748
|
+
def default_exception_factory(exc_cls: ta.Type[Exception], *args, **kwargs) -> Exception:
|
1749
|
+
return exc_cls(*args, **kwargs) # noqa
|
1708
1750
|
|
1709
|
-
|
1710
|
-
if isinstance(v, spec):
|
1711
|
-
raise TypeError(v)
|
1712
|
-
return v
|
1751
|
+
#
|
1713
1752
|
|
1753
|
+
def register_on_raise(self, fn: CheckOnRaiseFn) -> None:
|
1754
|
+
with self._config_lock:
|
1755
|
+
self._on_raise_fns = [*self._on_raise_fns, fn]
|
1714
1756
|
|
1715
|
-
def
|
1716
|
-
|
1717
|
-
|
1757
|
+
def unregister_on_raise(self, fn: CheckOnRaiseFn) -> None:
|
1758
|
+
with self._config_lock:
|
1759
|
+
self._on_raise_fns = [e for e in self._on_raise_fns if e != fn]
|
1718
1760
|
|
1761
|
+
#
|
1719
1762
|
|
1720
|
-
def
|
1721
|
-
|
1722
|
-
raise ValueError
|
1723
|
-
return v
|
1763
|
+
def set_exception_factory(self, factory: CheckExceptionFactory) -> None:
|
1764
|
+
self._exception_factory = factory
|
1724
1765
|
|
1766
|
+
def set_args_renderer(self, renderer: ta.Optional[CheckArgsRenderer]) -> None:
|
1767
|
+
self._args_renderer = renderer
|
1725
1768
|
|
1726
|
-
|
1727
|
-
if v:
|
1728
|
-
raise ValueError(v)
|
1729
|
-
return v
|
1769
|
+
#
|
1730
1770
|
|
1771
|
+
def register_late_configure(self, fn: CheckLateConfigureFn) -> None:
|
1772
|
+
with self._config_lock:
|
1773
|
+
self._late_configure_fns = [*self._late_configure_fns, fn]
|
1731
1774
|
|
1732
|
-
def
|
1733
|
-
|
1734
|
-
|
1735
|
-
return v
|
1775
|
+
def _late_configure(self) -> None:
|
1776
|
+
if not self._late_configure_fns:
|
1777
|
+
return
|
1736
1778
|
|
1779
|
+
with self._config_lock:
|
1780
|
+
if not (lc := self._late_configure_fns):
|
1781
|
+
return
|
1737
1782
|
|
1738
|
-
|
1739
|
-
|
1740
|
-
raise ValueError(msg)
|
1783
|
+
for fn in lc:
|
1784
|
+
fn(self)
|
1741
1785
|
|
1786
|
+
self._late_configure_fns = []
|
1742
1787
|
|
1743
|
-
|
1744
|
-
if not v:
|
1745
|
-
raise ValueError(msg)
|
1788
|
+
#
|
1746
1789
|
|
1790
|
+
class _ArgsKwargs:
|
1791
|
+
def __init__(self, *args, **kwargs):
|
1792
|
+
self.args = args
|
1793
|
+
self.kwargs = kwargs
|
1747
1794
|
|
1748
|
-
def
|
1749
|
-
|
1750
|
-
|
1751
|
-
|
1795
|
+
def _raise(
|
1796
|
+
self,
|
1797
|
+
exception_type: ta.Type[Exception],
|
1798
|
+
default_message: str,
|
1799
|
+
message: CheckMessage,
|
1800
|
+
ak: _ArgsKwargs = _ArgsKwargs(),
|
1801
|
+
*,
|
1802
|
+
render_fmt: ta.Optional[str] = None,
|
1803
|
+
) -> ta.NoReturn:
|
1804
|
+
exc_args = ()
|
1805
|
+
if callable(message):
|
1806
|
+
message = ta.cast(ta.Callable, message)(*ak.args, **ak.kwargs)
|
1807
|
+
if isinstance(message, tuple):
|
1808
|
+
message, *exc_args = message # type: ignore
|
1752
1809
|
|
1810
|
+
if message is None:
|
1811
|
+
message = default_message
|
1753
1812
|
|
1754
|
-
|
1755
|
-
if l == r:
|
1756
|
-
raise ValueError(l, r)
|
1757
|
-
return l
|
1813
|
+
self._late_configure()
|
1758
1814
|
|
1815
|
+
if render_fmt is not None and (af := self._args_renderer) is not None:
|
1816
|
+
rendered_args = af(render_fmt, *ak.args)
|
1817
|
+
if rendered_args is not None:
|
1818
|
+
message = f'{message} : {rendered_args}'
|
1759
1819
|
|
1760
|
-
|
1761
|
-
|
1762
|
-
|
1763
|
-
|
1820
|
+
exc = self._exception_factory(
|
1821
|
+
exception_type,
|
1822
|
+
message,
|
1823
|
+
*exc_args,
|
1824
|
+
*ak.args,
|
1825
|
+
**ak.kwargs,
|
1826
|
+
)
|
1764
1827
|
|
1828
|
+
for fn in self._on_raise_fns:
|
1829
|
+
fn(exc)
|
1765
1830
|
|
1766
|
-
|
1767
|
-
if l is r:
|
1768
|
-
raise ValueError(l, r)
|
1769
|
-
return l
|
1831
|
+
raise exc
|
1770
1832
|
|
1833
|
+
#
|
1771
1834
|
|
1772
|
-
def
|
1773
|
-
|
1774
|
-
|
1775
|
-
|
1835
|
+
def _unpack_isinstance_spec(self, spec: ta.Any) -> tuple:
|
1836
|
+
if isinstance(spec, type):
|
1837
|
+
return (spec,)
|
1838
|
+
if not isinstance(spec, tuple):
|
1839
|
+
spec = (spec,)
|
1840
|
+
if None in spec:
|
1841
|
+
spec = tuple(filter(None, spec)) + (None.__class__,) # noqa
|
1842
|
+
if ta.Any in spec:
|
1843
|
+
spec = (object,)
|
1844
|
+
return spec
|
1845
|
+
|
1846
|
+
def isinstance(self, v: ta.Any, spec: ta.Union[ta.Type[T], tuple], msg: CheckMessage = None) -> T: # noqa
|
1847
|
+
if not isinstance(v, self._unpack_isinstance_spec(spec)):
|
1848
|
+
self._raise(
|
1849
|
+
TypeError,
|
1850
|
+
'Must be instance',
|
1851
|
+
msg,
|
1852
|
+
Checks._ArgsKwargs(v, spec),
|
1853
|
+
render_fmt='not isinstance(%s, %s)',
|
1854
|
+
)
|
1776
1855
|
|
1856
|
+
return v
|
1777
1857
|
|
1778
|
-
def
|
1779
|
-
|
1780
|
-
|
1781
|
-
return v
|
1858
|
+
def of_isinstance(self, spec: ta.Union[ta.Type[T], tuple], msg: CheckMessage = None) -> ta.Callable[[ta.Any], T]:
|
1859
|
+
def inner(v):
|
1860
|
+
return self.isinstance(v, self._unpack_isinstance_spec(spec), msg)
|
1782
1861
|
|
1862
|
+
return inner
|
1783
1863
|
|
1784
|
-
def
|
1785
|
-
|
1786
|
-
|
1864
|
+
def cast(self, v: ta.Any, cls: ta.Type[T], msg: CheckMessage = None) -> T: # noqa
|
1865
|
+
if not isinstance(v, cls):
|
1866
|
+
self._raise(
|
1867
|
+
TypeError,
|
1868
|
+
'Must be instance',
|
1869
|
+
msg,
|
1870
|
+
Checks._ArgsKwargs(v, cls),
|
1871
|
+
)
|
1787
1872
|
|
1873
|
+
return v
|
1788
1874
|
|
1789
|
-
def
|
1790
|
-
|
1791
|
-
|
1792
|
-
return v
|
1875
|
+
def of_cast(self, cls: ta.Type[T], msg: CheckMessage = None) -> ta.Callable[[T], T]:
|
1876
|
+
def inner(v):
|
1877
|
+
return self.cast(v, cls, msg)
|
1793
1878
|
|
1879
|
+
return inner
|
1794
1880
|
|
1795
|
-
def
|
1796
|
-
|
1797
|
-
|
1798
|
-
|
1881
|
+
def not_isinstance(self, v: T, spec: ta.Any, msg: CheckMessage = None) -> T: # noqa
|
1882
|
+
if isinstance(v, self._unpack_isinstance_spec(spec)):
|
1883
|
+
self._raise(
|
1884
|
+
TypeError,
|
1885
|
+
'Must not be instance',
|
1886
|
+
msg,
|
1887
|
+
Checks._ArgsKwargs(v, spec),
|
1888
|
+
render_fmt='isinstance(%s, %s)',
|
1889
|
+
)
|
1799
1890
|
|
1891
|
+
return v
|
1800
1892
|
|
1801
|
-
|
1802
|
-
|
1893
|
+
def of_not_isinstance(self, spec: ta.Any, msg: CheckMessage = None) -> ta.Callable[[T], T]:
|
1894
|
+
def inner(v):
|
1895
|
+
return self.not_isinstance(v, self._unpack_isinstance_spec(spec), msg)
|
1803
1896
|
|
1897
|
+
return inner
|
1804
1898
|
|
1805
|
-
|
1806
|
-
major: int
|
1807
|
-
minor: int
|
1899
|
+
##
|
1808
1900
|
|
1809
|
-
def
|
1810
|
-
|
1901
|
+
def issubclass(self, v: ta.Type[T], spec: ta.Any, msg: CheckMessage = None) -> ta.Type[T]: # noqa
|
1902
|
+
if not issubclass(v, spec):
|
1903
|
+
self._raise(
|
1904
|
+
TypeError,
|
1905
|
+
'Must be subclass',
|
1906
|
+
msg,
|
1907
|
+
Checks._ArgsKwargs(v, spec),
|
1908
|
+
render_fmt='not issubclass(%s, %s)',
|
1909
|
+
)
|
1811
1910
|
|
1911
|
+
return v
|
1812
1912
|
|
1813
|
-
|
1814
|
-
|
1815
|
-
|
1816
|
-
|
1817
|
-
|
1913
|
+
def not_issubclass(self, v: ta.Type[T], spec: ta.Any, msg: CheckMessage = None) -> ta.Type[T]: # noqa
|
1914
|
+
if issubclass(v, spec):
|
1915
|
+
self._raise(
|
1916
|
+
TypeError,
|
1917
|
+
'Must not be subclass',
|
1918
|
+
msg,
|
1919
|
+
Checks._ArgsKwargs(v, spec),
|
1920
|
+
render_fmt='issubclass(%s, %s)',
|
1921
|
+
)
|
1922
|
+
|
1923
|
+
return v
|
1924
|
+
|
1925
|
+
#
|
1926
|
+
|
1927
|
+
def in_(self, v: T, c: ta.Container[T], msg: CheckMessage = None) -> T:
|
1928
|
+
if v not in c:
|
1929
|
+
self._raise(
|
1930
|
+
ValueError,
|
1931
|
+
'Must be in',
|
1932
|
+
msg,
|
1933
|
+
Checks._ArgsKwargs(v, c),
|
1934
|
+
render_fmt='%s not in %s',
|
1935
|
+
)
|
1936
|
+
|
1937
|
+
return v
|
1938
|
+
|
1939
|
+
def not_in(self, v: T, c: ta.Container[T], msg: CheckMessage = None) -> T:
|
1940
|
+
if v in c:
|
1941
|
+
self._raise(
|
1942
|
+
ValueError,
|
1943
|
+
'Must not be in',
|
1944
|
+
msg,
|
1945
|
+
Checks._ArgsKwargs(v, c),
|
1946
|
+
render_fmt='%s in %s',
|
1947
|
+
)
|
1948
|
+
|
1949
|
+
return v
|
1950
|
+
|
1951
|
+
def empty(self, v: SizedT, msg: CheckMessage = None) -> SizedT:
|
1952
|
+
if len(v) != 0:
|
1953
|
+
self._raise(
|
1954
|
+
ValueError,
|
1955
|
+
'Must be empty',
|
1956
|
+
msg,
|
1957
|
+
Checks._ArgsKwargs(v),
|
1958
|
+
render_fmt='%s',
|
1959
|
+
)
|
1960
|
+
|
1961
|
+
return v
|
1962
|
+
|
1963
|
+
def iterempty(self, v: ta.Iterable[T], msg: CheckMessage = None) -> ta.Iterable[T]:
|
1964
|
+
it = iter(v)
|
1965
|
+
try:
|
1966
|
+
next(it)
|
1967
|
+
except StopIteration:
|
1968
|
+
pass
|
1969
|
+
else:
|
1970
|
+
self._raise(
|
1971
|
+
ValueError,
|
1972
|
+
'Must be empty',
|
1973
|
+
msg,
|
1974
|
+
Checks._ArgsKwargs(v),
|
1975
|
+
render_fmt='%s',
|
1976
|
+
)
|
1977
|
+
|
1978
|
+
return v
|
1979
|
+
|
1980
|
+
def not_empty(self, v: SizedT, msg: CheckMessage = None) -> SizedT:
|
1981
|
+
if len(v) == 0:
|
1982
|
+
self._raise(
|
1983
|
+
ValueError,
|
1984
|
+
'Must not be empty',
|
1985
|
+
msg,
|
1986
|
+
Checks._ArgsKwargs(v),
|
1987
|
+
render_fmt='%s',
|
1988
|
+
)
|
1989
|
+
|
1990
|
+
return v
|
1991
|
+
|
1992
|
+
def unique(self, it: ta.Iterable[T], msg: CheckMessage = None) -> ta.Iterable[T]:
|
1993
|
+
dupes = [e for e, c in collections.Counter(it).items() if c > 1]
|
1994
|
+
if dupes:
|
1995
|
+
self._raise(
|
1996
|
+
ValueError,
|
1997
|
+
'Must be unique',
|
1998
|
+
msg,
|
1999
|
+
Checks._ArgsKwargs(it, dupes),
|
2000
|
+
)
|
2001
|
+
|
2002
|
+
return it
|
2003
|
+
|
2004
|
+
def single(self, obj: ta.Iterable[T], message: CheckMessage = None) -> T:
|
2005
|
+
try:
|
2006
|
+
[value] = obj
|
2007
|
+
except ValueError:
|
2008
|
+
self._raise(
|
2009
|
+
ValueError,
|
2010
|
+
'Must be single',
|
2011
|
+
message,
|
2012
|
+
Checks._ArgsKwargs(obj),
|
2013
|
+
render_fmt='%s',
|
2014
|
+
)
|
2015
|
+
|
2016
|
+
return value
|
2017
|
+
|
2018
|
+
def opt_single(self, obj: ta.Iterable[T], message: CheckMessage = None) -> ta.Optional[T]:
|
2019
|
+
it = iter(obj)
|
2020
|
+
try:
|
2021
|
+
value = next(it)
|
2022
|
+
except StopIteration:
|
2023
|
+
return None
|
2024
|
+
|
2025
|
+
try:
|
2026
|
+
next(it)
|
2027
|
+
except StopIteration:
|
2028
|
+
return value # noqa
|
2029
|
+
|
2030
|
+
self._raise(
|
2031
|
+
ValueError,
|
2032
|
+
'Must be empty or single',
|
2033
|
+
message,
|
2034
|
+
Checks._ArgsKwargs(obj),
|
2035
|
+
render_fmt='%s',
|
2036
|
+
)
|
2037
|
+
|
2038
|
+
raise RuntimeError # noqa
|
2039
|
+
|
2040
|
+
#
|
2041
|
+
|
2042
|
+
def none(self, v: ta.Any, msg: CheckMessage = None) -> None:
|
2043
|
+
if v is not None:
|
2044
|
+
self._raise(
|
2045
|
+
ValueError,
|
2046
|
+
'Must be None',
|
2047
|
+
msg,
|
2048
|
+
Checks._ArgsKwargs(v),
|
2049
|
+
render_fmt='%s',
|
2050
|
+
)
|
2051
|
+
|
2052
|
+
def not_none(self, v: ta.Optional[T], msg: CheckMessage = None) -> T:
|
2053
|
+
if v is None:
|
2054
|
+
self._raise(
|
2055
|
+
ValueError,
|
2056
|
+
'Must not be None',
|
2057
|
+
msg,
|
2058
|
+
Checks._ArgsKwargs(v),
|
2059
|
+
render_fmt='%s',
|
2060
|
+
)
|
2061
|
+
|
2062
|
+
return v
|
2063
|
+
|
2064
|
+
#
|
2065
|
+
|
2066
|
+
def equal(self, v: T, o: ta.Any, msg: CheckMessage = None) -> T:
|
2067
|
+
if o != v:
|
2068
|
+
self._raise(
|
2069
|
+
ValueError,
|
2070
|
+
'Must be equal',
|
2071
|
+
msg,
|
2072
|
+
Checks._ArgsKwargs(v, o),
|
2073
|
+
render_fmt='%s != %s',
|
2074
|
+
)
|
2075
|
+
|
2076
|
+
return v
|
2077
|
+
|
2078
|
+
def is_(self, v: T, o: ta.Any, msg: CheckMessage = None) -> T:
|
2079
|
+
if o is not v:
|
2080
|
+
self._raise(
|
2081
|
+
ValueError,
|
2082
|
+
'Must be the same',
|
2083
|
+
msg,
|
2084
|
+
Checks._ArgsKwargs(v, o),
|
2085
|
+
render_fmt='%s is not %s',
|
2086
|
+
)
|
2087
|
+
|
2088
|
+
return v
|
2089
|
+
|
2090
|
+
def is_not(self, v: T, o: ta.Any, msg: CheckMessage = None) -> T:
|
2091
|
+
if o is v:
|
2092
|
+
self._raise(
|
2093
|
+
ValueError,
|
2094
|
+
'Must not be the same',
|
2095
|
+
msg,
|
2096
|
+
Checks._ArgsKwargs(v, o),
|
2097
|
+
render_fmt='%s is %s',
|
2098
|
+
)
|
2099
|
+
|
2100
|
+
return v
|
2101
|
+
|
2102
|
+
def callable(self, v: T, msg: CheckMessage = None) -> T: # noqa
|
2103
|
+
if not callable(v):
|
2104
|
+
self._raise(
|
2105
|
+
TypeError,
|
2106
|
+
'Must be callable',
|
2107
|
+
msg,
|
2108
|
+
Checks._ArgsKwargs(v),
|
2109
|
+
render_fmt='%s',
|
2110
|
+
)
|
2111
|
+
|
2112
|
+
return v # type: ignore
|
2113
|
+
|
2114
|
+
def non_empty_str(self, v: ta.Optional[str], msg: CheckMessage = None) -> str:
|
2115
|
+
if not isinstance(v, str) or not v:
|
2116
|
+
self._raise(
|
2117
|
+
ValueError,
|
2118
|
+
'Must be non-empty str',
|
2119
|
+
msg,
|
2120
|
+
Checks._ArgsKwargs(v),
|
2121
|
+
render_fmt='%s',
|
2122
|
+
)
|
2123
|
+
|
2124
|
+
return v
|
2125
|
+
|
2126
|
+
def replacing(self, expected: ta.Any, old: ta.Any, new: T, msg: CheckMessage = None) -> T:
|
2127
|
+
if old != expected:
|
2128
|
+
self._raise(
|
2129
|
+
ValueError,
|
2130
|
+
'Must be replacing',
|
2131
|
+
msg,
|
2132
|
+
Checks._ArgsKwargs(expected, old, new),
|
2133
|
+
render_fmt='%s -> %s -> %s',
|
2134
|
+
)
|
2135
|
+
|
2136
|
+
return new
|
2137
|
+
|
2138
|
+
def replacing_none(self, old: ta.Any, new: T, msg: CheckMessage = None) -> T:
|
2139
|
+
if old is not None:
|
2140
|
+
self._raise(
|
2141
|
+
ValueError,
|
2142
|
+
'Must be replacing None',
|
2143
|
+
msg,
|
2144
|
+
Checks._ArgsKwargs(old, new),
|
2145
|
+
render_fmt='%s -> %s',
|
2146
|
+
)
|
2147
|
+
|
2148
|
+
return new
|
2149
|
+
|
2150
|
+
#
|
2151
|
+
|
2152
|
+
def arg(self, v: bool, msg: CheckMessage = None) -> None:
|
2153
|
+
if not v:
|
2154
|
+
self._raise(
|
2155
|
+
RuntimeError,
|
2156
|
+
'Argument condition not met',
|
2157
|
+
msg,
|
2158
|
+
Checks._ArgsKwargs(v),
|
2159
|
+
render_fmt='%s',
|
2160
|
+
)
|
2161
|
+
|
2162
|
+
def state(self, v: bool, msg: CheckMessage = None) -> None:
|
2163
|
+
if not v:
|
2164
|
+
self._raise(
|
2165
|
+
RuntimeError,
|
2166
|
+
'State condition not met',
|
2167
|
+
msg,
|
2168
|
+
Checks._ArgsKwargs(v),
|
2169
|
+
render_fmt='%s',
|
2170
|
+
)
|
2171
|
+
|
2172
|
+
|
2173
|
+
check = Checks()
|
1818
2174
|
|
1819
2175
|
|
1820
2176
|
########################################
|
@@ -2569,868 +2925,868 @@ def get_user(name: str) -> User:
|
|
2569
2925
|
|
2570
2926
|
|
2571
2927
|
########################################
|
2572
|
-
# ../../../omlish/
|
2573
|
-
|
2574
|
-
|
2575
|
-
|
2576
|
-
|
2577
|
-
|
2578
|
-
|
2579
|
-
|
2580
|
-
|
2581
|
-
|
2582
|
-
|
2583
|
-
|
2584
|
-
|
2585
|
-
|
2928
|
+
# ../../../omlish/http/parsing.py
|
2929
|
+
# PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2
|
2930
|
+
# --------------------------------------------
|
2931
|
+
#
|
2932
|
+
# 1. This LICENSE AGREEMENT is between the Python Software Foundation ("PSF"), and the Individual or Organization
|
2933
|
+
# ("Licensee") accessing and otherwise using this software ("Python") in source or binary form and its associated
|
2934
|
+
# documentation.
|
2935
|
+
#
|
2936
|
+
# 2. Subject to the terms and conditions of this License Agreement, PSF hereby grants Licensee a nonexclusive,
|
2937
|
+
# royalty-free, world-wide license to reproduce, analyze, test, perform and/or display publicly, prepare derivative
|
2938
|
+
# works, distribute, and otherwise use Python alone or in any derivative version, provided, however, that PSF's License
|
2939
|
+
# Agreement and PSF's notice of copyright, i.e., "Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009,
|
2940
|
+
# 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017 Python Software Foundation; All Rights Reserved" are retained in Python
|
2941
|
+
# alone or in any derivative version prepared by Licensee.
|
2942
|
+
#
|
2943
|
+
# 3. In the event Licensee prepares a derivative work that is based on or incorporates Python or any part thereof, and
|
2944
|
+
# wants to make the derivative work available to others as provided herein, then Licensee hereby agrees to include in
|
2945
|
+
# any such work a brief summary of the changes made to Python.
|
2946
|
+
#
|
2947
|
+
# 4. PSF is making Python available to Licensee on an "AS IS" basis. PSF MAKES NO REPRESENTATIONS OR WARRANTIES,
|
2948
|
+
# EXPRESS OR IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND DISCLAIMS ANY REPRESENTATION OR WARRANTY
|
2949
|
+
# OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON WILL NOT INFRINGE ANY THIRD PARTY
|
2950
|
+
# RIGHTS.
|
2951
|
+
#
|
2952
|
+
# 5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL
|
2953
|
+
# DAMAGES OR LOSS AS A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON, OR ANY DERIVATIVE THEREOF, EVEN IF
|
2954
|
+
# ADVISED OF THE POSSIBILITY THEREOF.
|
2955
|
+
#
|
2956
|
+
# 6. This License Agreement will automatically terminate upon a material breach of its terms and conditions.
|
2957
|
+
#
|
2958
|
+
# 7. Nothing in this License Agreement shall be deemed to create any relationship of agency, partnership, or joint
|
2959
|
+
# venture between PSF and Licensee. This License Agreement does not grant permission to use PSF trademarks or trade
|
2960
|
+
# name in a trademark sense to endorse or promote products or services of Licensee, or any third party.
|
2961
|
+
#
|
2962
|
+
# 8. By copying, installing or otherwise using Python, Licensee agrees to be bound by the terms and conditions of this
|
2963
|
+
# License Agreement.
|
2586
2964
|
|
2587
|
-
def __repr__(self) -> str:
|
2588
|
-
return attr_repr(self, 'buffer')
|
2589
2965
|
|
2590
|
-
|
2591
|
-
pass
|
2966
|
+
##
|
2592
2967
|
|
2593
|
-
#
|
2594
2968
|
|
2595
|
-
|
2969
|
+
class ParseHttpRequestResult(abc.ABC): # noqa
|
2970
|
+
__slots__ = (
|
2971
|
+
'server_version',
|
2972
|
+
'request_line',
|
2973
|
+
'request_version',
|
2974
|
+
'version',
|
2975
|
+
'headers',
|
2976
|
+
'close_connection',
|
2977
|
+
)
|
2596
2978
|
|
2597
2979
|
def __init__(
|
2598
2980
|
self,
|
2599
|
-
delimiters: ta.Iterable[int] = DEFAULT_DELIMITERS,
|
2600
2981
|
*,
|
2601
|
-
|
2602
|
-
|
2982
|
+
server_version: HttpProtocolVersion,
|
2983
|
+
request_line: str,
|
2984
|
+
request_version: HttpProtocolVersion,
|
2985
|
+
version: HttpProtocolVersion,
|
2986
|
+
headers: ta.Optional[HttpHeaders],
|
2987
|
+
close_connection: bool,
|
2603
2988
|
) -> None:
|
2604
2989
|
super().__init__()
|
2605
2990
|
|
2606
|
-
self.
|
2607
|
-
self.
|
2608
|
-
self.
|
2991
|
+
self.server_version = server_version
|
2992
|
+
self.request_line = request_line
|
2993
|
+
self.request_version = request_version
|
2994
|
+
self.version = version
|
2995
|
+
self.headers = headers
|
2996
|
+
self.close_connection = close_connection
|
2609
2997
|
|
2610
|
-
|
2998
|
+
def __repr__(self) -> str:
|
2999
|
+
return f'{self.__class__.__name__}({", ".join(f"{a}={getattr(self, a)!r}" for a in self.__slots__)})'
|
2611
3000
|
|
2612
|
-
#
|
2613
3001
|
|
2614
|
-
|
2615
|
-
|
2616
|
-
return self._buf is None
|
3002
|
+
class EmptyParsedHttpResult(ParseHttpRequestResult):
|
3003
|
+
pass
|
2617
3004
|
|
2618
|
-
def tell(self) -> int:
|
2619
|
-
if (buf := self._buf) is None:
|
2620
|
-
raise self.ClosedError(self)
|
2621
|
-
return buf.tell()
|
2622
3005
|
|
2623
|
-
|
2624
|
-
|
2625
|
-
|
2626
|
-
|
3006
|
+
class ParseHttpRequestError(ParseHttpRequestResult):
|
3007
|
+
__slots__ = (
|
3008
|
+
'code',
|
3009
|
+
'message',
|
3010
|
+
*ParseHttpRequestResult.__slots__,
|
3011
|
+
)
|
2627
3012
|
|
2628
|
-
def
|
2629
|
-
|
2630
|
-
|
2631
|
-
|
2632
|
-
|
2633
|
-
r = p
|
2634
|
-
return r
|
3013
|
+
def __init__(
|
3014
|
+
self,
|
3015
|
+
*,
|
3016
|
+
code: http.HTTPStatus,
|
3017
|
+
message: ta.Union[str, ta.Tuple[str, str]],
|
2635
3018
|
|
2636
|
-
|
2637
|
-
|
2638
|
-
|
2639
|
-
return chunk
|
3019
|
+
**kwargs: ta.Any,
|
3020
|
+
) -> None:
|
3021
|
+
super().__init__(**kwargs)
|
2640
3022
|
|
2641
|
-
|
2642
|
-
|
2643
|
-
buf.seek(0)
|
2644
|
-
buf.truncate()
|
2645
|
-
return ret
|
3023
|
+
self.code = code
|
3024
|
+
self.message = message
|
2646
3025
|
|
2647
|
-
class Incomplete(ta.NamedTuple):
|
2648
|
-
b: bytes
|
2649
3026
|
|
2650
|
-
|
2651
|
-
|
2652
|
-
|
3027
|
+
class ParsedHttpRequest(ParseHttpRequestResult):
|
3028
|
+
__slots__ = (
|
3029
|
+
'method',
|
3030
|
+
'path',
|
3031
|
+
'headers',
|
3032
|
+
'expects_continue',
|
3033
|
+
*[a for a in ParseHttpRequestResult.__slots__ if a != 'headers'],
|
3034
|
+
)
|
2653
3035
|
|
2654
|
-
|
2655
|
-
self
|
3036
|
+
def __init__(
|
3037
|
+
self,
|
3038
|
+
*,
|
3039
|
+
method: str,
|
3040
|
+
path: str,
|
3041
|
+
headers: HttpHeaders,
|
3042
|
+
expects_continue: bool,
|
2656
3043
|
|
2657
|
-
|
2658
|
-
|
3044
|
+
**kwargs: ta.Any,
|
3045
|
+
) -> None:
|
3046
|
+
super().__init__(
|
3047
|
+
headers=headers,
|
3048
|
+
**kwargs,
|
3049
|
+
)
|
2659
3050
|
|
2660
|
-
|
3051
|
+
self.method = method
|
3052
|
+
self.path = path
|
3053
|
+
self.expects_continue = expects_continue
|
2661
3054
|
|
2662
|
-
|
2663
|
-
i = 0
|
2664
|
-
while i < l:
|
2665
|
-
if (p := self._find_delim(data, i)) is None:
|
2666
|
-
break
|
3055
|
+
headers: HttpHeaders
|
2667
3056
|
|
2668
|
-
n = p + 1
|
2669
|
-
if self._keep_ends:
|
2670
|
-
p = n
|
2671
3057
|
|
2672
|
-
|
3058
|
+
#
|
2673
3059
|
|
2674
|
-
i = n
|
2675
3060
|
|
2676
|
-
|
2677
|
-
|
3061
|
+
class HttpRequestParser:
|
3062
|
+
DEFAULT_SERVER_VERSION = HttpProtocolVersions.HTTP_1_0
|
2678
3063
|
|
2679
|
-
|
2680
|
-
|
2681
|
-
|
3064
|
+
# The default request version. This only affects responses up until the point where the request line is parsed, so
|
3065
|
+
# it mainly decides what the client gets back when sending a malformed request line.
|
3066
|
+
# Most web servers default to HTTP 0.9, i.e. don't send a status line.
|
3067
|
+
DEFAULT_REQUEST_VERSION = HttpProtocolVersions.HTTP_0_9
|
2682
3068
|
|
2683
|
-
|
2684
|
-
remaining_data_len = l - i
|
2685
|
-
remaining_buf_capacity = self._max_size - buf.tell()
|
3069
|
+
#
|
2686
3070
|
|
2687
|
-
|
2688
|
-
|
2689
|
-
return
|
3071
|
+
DEFAULT_MAX_LINE: int = 0x10000
|
3072
|
+
DEFAULT_MAX_HEADERS: int = 100
|
2690
3073
|
|
2691
|
-
|
2692
|
-
yield self.Incomplete(self._append_and_reset(data[i:p]))
|
2693
|
-
i = p
|
3074
|
+
#
|
2694
3075
|
|
3076
|
+
def __init__(
|
3077
|
+
self,
|
3078
|
+
*,
|
3079
|
+
server_version: HttpProtocolVersion = DEFAULT_SERVER_VERSION,
|
2695
3080
|
|
2696
|
-
|
2697
|
-
|
3081
|
+
max_line: int = DEFAULT_MAX_LINE,
|
3082
|
+
max_headers: int = DEFAULT_MAX_HEADERS,
|
3083
|
+
) -> None:
|
2698
3084
|
super().__init__()
|
2699
|
-
self._lst: list[bytes] = []
|
2700
3085
|
|
2701
|
-
|
2702
|
-
|
2703
|
-
|
3086
|
+
if server_version >= HttpProtocolVersions.HTTP_2_0:
|
3087
|
+
raise ValueError(f'Unsupported protocol version: {server_version}')
|
3088
|
+
self._server_version = server_version
|
2704
3089
|
|
2705
|
-
|
2706
|
-
|
2707
|
-
d = lst[i]
|
3090
|
+
self._max_line = max_line
|
3091
|
+
self._max_headers = max_headers
|
2708
3092
|
|
2709
|
-
|
2710
|
-
*lst[:i],
|
2711
|
-
d[:e],
|
2712
|
-
])
|
3093
|
+
#
|
2713
3094
|
|
2714
|
-
|
2715
|
-
|
2716
|
-
|
2717
|
-
]
|
3095
|
+
@property
|
3096
|
+
def server_version(self) -> HttpProtocolVersion:
|
3097
|
+
return self._server_version
|
2718
3098
|
|
2719
|
-
|
3099
|
+
#
|
2720
3100
|
|
2721
|
-
def
|
2722
|
-
|
2723
|
-
|
2724
|
-
|
2725
|
-
|
3101
|
+
def _run_read_line_coro(
|
3102
|
+
self,
|
3103
|
+
gen: ta.Generator[int, bytes, T],
|
3104
|
+
read_line: ta.Callable[[int], bytes],
|
3105
|
+
) -> T:
|
3106
|
+
sz = next(gen)
|
3107
|
+
while True:
|
3108
|
+
try:
|
3109
|
+
sz = gen.send(read_line(sz))
|
3110
|
+
except StopIteration as e:
|
3111
|
+
return e.value
|
2726
3112
|
|
2727
|
-
|
2728
|
-
return None
|
3113
|
+
#
|
2729
3114
|
|
2730
|
-
|
2731
|
-
|
2732
|
-
|
2733
|
-
if (l := len(d)) >= r:
|
2734
|
-
return self._chop(i, r)
|
2735
|
-
c += l
|
3115
|
+
def parse_request_version(self, version_str: str) -> HttpProtocolVersion:
|
3116
|
+
if not version_str.startswith('HTTP/'):
|
3117
|
+
raise ValueError(version_str) # noqa
|
2736
3118
|
|
2737
|
-
|
3119
|
+
base_version_number = version_str.split('/', 1)[1]
|
3120
|
+
version_number_parts = base_version_number.split('.')
|
2738
3121
|
|
2739
|
-
|
2740
|
-
|
2741
|
-
|
3122
|
+
# RFC 2145 section 3.1 says there can be only one "." and
|
3123
|
+
# - major and minor numbers MUST be treated as separate integers;
|
3124
|
+
# - HTTP/2.4 is a lower version than HTTP/2.13, which in turn is lower than HTTP/12.3;
|
3125
|
+
# - Leading zeros MUST be ignored by recipients.
|
3126
|
+
if len(version_number_parts) != 2:
|
3127
|
+
raise ValueError(version_number_parts) # noqa
|
3128
|
+
if any(not component.isdigit() for component in version_number_parts):
|
3129
|
+
raise ValueError('non digit in http version') # noqa
|
3130
|
+
if any(len(component) > 10 for component in version_number_parts):
|
3131
|
+
raise ValueError('unreasonable length http version') # noqa
|
2742
3132
|
|
2743
|
-
|
2744
|
-
|
2745
|
-
|
3133
|
+
return HttpProtocolVersion(
|
3134
|
+
int(version_number_parts[0]),
|
3135
|
+
int(version_number_parts[1]),
|
3136
|
+
)
|
2746
3137
|
|
2747
|
-
|
3138
|
+
#
|
2748
3139
|
|
3140
|
+
def coro_read_raw_headers(self) -> ta.Generator[int, bytes, ta.List[bytes]]:
|
3141
|
+
raw_headers: ta.List[bytes] = []
|
3142
|
+
while True:
|
3143
|
+
line = yield self._max_line + 1
|
3144
|
+
if len(line) > self._max_line:
|
3145
|
+
raise http.client.LineTooLong('header line')
|
3146
|
+
raw_headers.append(line)
|
3147
|
+
if len(raw_headers) > self._max_headers:
|
3148
|
+
raise http.client.HTTPException(f'got more than {self._max_headers} headers')
|
3149
|
+
if line in (b'\r\n', b'\n', b''):
|
3150
|
+
break
|
3151
|
+
return raw_headers
|
2749
3152
|
|
2750
|
-
|
2751
|
-
|
2752
|
-
self,
|
2753
|
-
data: bytes,
|
2754
|
-
*,
|
2755
|
-
write_size: int = 0x10000,
|
2756
|
-
) -> None:
|
2757
|
-
super().__init__()
|
3153
|
+
def read_raw_headers(self, read_line: ta.Callable[[int], bytes]) -> ta.List[bytes]:
|
3154
|
+
return self._run_read_line_coro(self.coro_read_raw_headers(), read_line)
|
2758
3155
|
|
2759
|
-
|
2760
|
-
|
2761
|
-
self._write_size = write_size
|
3156
|
+
def parse_raw_headers(self, raw_headers: ta.Sequence[bytes]) -> HttpHeaders:
|
3157
|
+
return http.client.parse_headers(io.BytesIO(b''.join(raw_headers)))
|
2762
3158
|
|
2763
|
-
|
2764
|
-
data[i:i + write_size]
|
2765
|
-
for i in range(0, len(data), write_size)
|
2766
|
-
]
|
2767
|
-
self._pos = 0
|
3159
|
+
#
|
2768
3160
|
|
2769
|
-
|
2770
|
-
|
2771
|
-
return self._len - self._pos
|
3161
|
+
def coro_parse(self) -> ta.Generator[int, bytes, ParseHttpRequestResult]:
|
3162
|
+
raw_request_line = yield self._max_line + 1
|
2772
3163
|
|
2773
|
-
|
2774
|
-
lst = check_not_empty(self._lst)
|
3164
|
+
# Common result kwargs
|
2775
3165
|
|
2776
|
-
|
2777
|
-
|
2778
|
-
n = fn(check_not_empty(d))
|
2779
|
-
if not n:
|
2780
|
-
break
|
2781
|
-
t += n
|
3166
|
+
request_line = '-'
|
3167
|
+
request_version = self.DEFAULT_REQUEST_VERSION
|
2782
3168
|
|
2783
|
-
if
|
2784
|
-
|
2785
|
-
|
2786
|
-
*lst[i + 1:],
|
2787
|
-
]
|
2788
|
-
self._pos += t
|
3169
|
+
# Set to min(server, request) when it gets that far, but if it fails before that the server authoritatively
|
3170
|
+
# responds with its own version.
|
3171
|
+
version = self._server_version
|
2789
3172
|
|
2790
|
-
|
3173
|
+
headers: HttpHeaders | None = None
|
2791
3174
|
|
3175
|
+
close_connection = True
|
2792
3176
|
|
2793
|
-
|
2794
|
-
|
3177
|
+
def result_kwargs():
|
3178
|
+
return dict(
|
3179
|
+
server_version=self._server_version,
|
3180
|
+
request_line=request_line,
|
3181
|
+
request_version=request_version,
|
3182
|
+
version=version,
|
3183
|
+
headers=headers,
|
3184
|
+
close_connection=close_connection,
|
3185
|
+
)
|
2795
3186
|
|
3187
|
+
# Decode line
|
2796
3188
|
|
2797
|
-
|
2798
|
-
|
2799
|
-
|
2800
|
-
|
3189
|
+
if len(raw_request_line) > self._max_line:
|
3190
|
+
return ParseHttpRequestError(
|
3191
|
+
code=http.HTTPStatus.REQUEST_URI_TOO_LONG,
|
3192
|
+
message='Request line too long',
|
3193
|
+
**result_kwargs(),
|
3194
|
+
)
|
2801
3195
|
|
2802
|
-
|
3196
|
+
if not raw_request_line:
|
3197
|
+
return EmptyParsedHttpResult(**result_kwargs())
|
2803
3198
|
|
2804
|
-
|
2805
|
-
@abc.abstractmethod
|
2806
|
-
def closed(self) -> bool:
|
2807
|
-
raise NotImplementedError
|
3199
|
+
request_line = raw_request_line.decode('iso-8859-1').rstrip('\r\n')
|
2808
3200
|
|
2809
|
-
|
2810
|
-
def close(self) -> None:
|
2811
|
-
raise NotImplementedError
|
3201
|
+
# Split words
|
2812
3202
|
|
2813
|
-
|
3203
|
+
words = request_line.split()
|
3204
|
+
if len(words) == 0:
|
3205
|
+
return EmptyParsedHttpResult(**result_kwargs())
|
2814
3206
|
|
2815
|
-
|
2816
|
-
return False
|
3207
|
+
# Parse and set version
|
2817
3208
|
|
2818
|
-
|
2819
|
-
|
3209
|
+
if len(words) >= 3: # Enough to determine protocol version
|
3210
|
+
version_str = words[-1]
|
3211
|
+
try:
|
3212
|
+
request_version = self.parse_request_version(version_str)
|
2820
3213
|
|
2821
|
-
|
3214
|
+
except (ValueError, IndexError):
|
3215
|
+
return ParseHttpRequestError(
|
3216
|
+
code=http.HTTPStatus.BAD_REQUEST,
|
3217
|
+
message=f'Bad request version ({version_str!r})',
|
3218
|
+
**result_kwargs(),
|
3219
|
+
)
|
2822
3220
|
|
2823
|
-
|
2824
|
-
|
3221
|
+
if (
|
3222
|
+
request_version < HttpProtocolVersions.HTTP_0_9 or
|
3223
|
+
request_version >= HttpProtocolVersions.HTTP_2_0
|
3224
|
+
):
|
3225
|
+
return ParseHttpRequestError(
|
3226
|
+
code=http.HTTPStatus.HTTP_VERSION_NOT_SUPPORTED,
|
3227
|
+
message=f'Invalid HTTP version ({version_str})',
|
3228
|
+
**result_kwargs(),
|
3229
|
+
)
|
2825
3230
|
|
2826
|
-
|
2827
|
-
raise TypeError
|
3231
|
+
version = min([self._server_version, request_version])
|
2828
3232
|
|
2829
|
-
|
2830
|
-
|
3233
|
+
if version >= HttpProtocolVersions.HTTP_1_1:
|
3234
|
+
close_connection = False
|
2831
3235
|
|
3236
|
+
# Verify word count
|
2832
3237
|
|
2833
|
-
|
2834
|
-
|
2835
|
-
|
2836
|
-
|
2837
|
-
|
2838
|
-
|
2839
|
-
super().__init__()
|
3238
|
+
if not 2 <= len(words) <= 3:
|
3239
|
+
return ParseHttpRequestError(
|
3240
|
+
code=http.HTTPStatus.BAD_REQUEST,
|
3241
|
+
message=f'Bad request syntax ({request_line!r})',
|
3242
|
+
**result_kwargs(),
|
3243
|
+
)
|
2840
3244
|
|
2841
|
-
|
2842
|
-
self._sock: ta.Optional[socket.socket] = sock
|
3245
|
+
# Parse method and path
|
2843
3246
|
|
2844
|
-
|
2845
|
-
|
3247
|
+
method, path = words[:2]
|
3248
|
+
if len(words) == 2:
|
3249
|
+
close_connection = True
|
3250
|
+
if method != 'GET':
|
3251
|
+
return ParseHttpRequestError(
|
3252
|
+
code=http.HTTPStatus.BAD_REQUEST,
|
3253
|
+
message=f'Bad HTTP/0.9 request type ({method!r})',
|
3254
|
+
**result_kwargs(),
|
3255
|
+
)
|
2846
3256
|
|
2847
|
-
|
2848
|
-
|
2849
|
-
|
3257
|
+
# gh-87389: The purpose of replacing '//' with '/' is to protect against open redirect attacks possibly
|
3258
|
+
# triggered if the path starts with '//' because http clients treat //path as an absolute URI without scheme
|
3259
|
+
# (similar to http://path) rather than a path.
|
3260
|
+
if path.startswith('//'):
|
3261
|
+
path = '/' + path.lstrip('/') # Reduce to a single /
|
2850
3262
|
|
2851
|
-
|
2852
|
-
if self._sock is not None:
|
2853
|
-
self._sock.close()
|
2854
|
-
self._sock = None
|
3263
|
+
# Parse headers
|
2855
3264
|
|
3265
|
+
try:
|
3266
|
+
raw_gen = self.coro_read_raw_headers()
|
3267
|
+
raw_sz = next(raw_gen)
|
3268
|
+
while True:
|
3269
|
+
buf = yield raw_sz
|
3270
|
+
try:
|
3271
|
+
raw_sz = raw_gen.send(buf)
|
3272
|
+
except StopIteration as e:
|
3273
|
+
raw_headers = e.value
|
3274
|
+
break
|
2856
3275
|
|
2857
|
-
|
2858
|
-
# ../../../omlish/io/fdio/kqueue.py
|
3276
|
+
headers = self.parse_raw_headers(raw_headers)
|
2859
3277
|
|
3278
|
+
except http.client.LineTooLong as err:
|
3279
|
+
return ParseHttpRequestError(
|
3280
|
+
code=http.HTTPStatus.REQUEST_HEADER_FIELDS_TOO_LARGE,
|
3281
|
+
message=('Line too long', str(err)),
|
3282
|
+
**result_kwargs(),
|
3283
|
+
)
|
2860
3284
|
|
2861
|
-
|
2862
|
-
|
3285
|
+
except http.client.HTTPException as err:
|
3286
|
+
return ParseHttpRequestError(
|
3287
|
+
code=http.HTTPStatus.REQUEST_HEADER_FIELDS_TOO_LARGE,
|
3288
|
+
message=('Too many headers', str(err)),
|
3289
|
+
**result_kwargs(),
|
3290
|
+
)
|
2863
3291
|
|
2864
|
-
|
2865
|
-
DEFAULT_MAX_EVENTS = 1000
|
3292
|
+
# Check for connection directive
|
2866
3293
|
|
2867
|
-
|
2868
|
-
|
2869
|
-
|
2870
|
-
|
2871
|
-
|
2872
|
-
|
3294
|
+
conn_type = headers.get('Connection', '')
|
3295
|
+
if conn_type.lower() == 'close':
|
3296
|
+
close_connection = True
|
3297
|
+
elif (
|
3298
|
+
conn_type.lower() == 'keep-alive' and
|
3299
|
+
version >= HttpProtocolVersions.HTTP_1_1
|
3300
|
+
):
|
3301
|
+
close_connection = False
|
2873
3302
|
|
2874
|
-
|
3303
|
+
# Check for expect directive
|
2875
3304
|
|
2876
|
-
|
3305
|
+
expect = headers.get('Expect', '')
|
3306
|
+
if (
|
3307
|
+
expect.lower() == '100-continue' and
|
3308
|
+
version >= HttpProtocolVersions.HTTP_1_1
|
3309
|
+
):
|
3310
|
+
expects_continue = True
|
3311
|
+
else:
|
3312
|
+
expects_continue = False
|
2877
3313
|
|
2878
|
-
#
|
3314
|
+
# Return
|
2879
3315
|
|
2880
|
-
|
2881
|
-
|
2882
|
-
|
2883
|
-
|
2884
|
-
|
2885
|
-
|
3316
|
+
return ParsedHttpRequest(
|
3317
|
+
method=method,
|
3318
|
+
path=path,
|
3319
|
+
expects_continue=expects_continue,
|
3320
|
+
**result_kwargs(),
|
3321
|
+
)
|
2886
3322
|
|
2887
|
-
|
2888
|
-
|
2889
|
-
self._kqueue.close()
|
2890
|
-
self._kqueue = None
|
3323
|
+
def parse(self, read_line: ta.Callable[[int], bytes]) -> ParseHttpRequestResult:
|
3324
|
+
return self._run_read_line_coro(self.coro_parse(), read_line)
|
2891
3325
|
|
2892
|
-
def reopen(self) -> None:
|
2893
|
-
for fd in self._readable:
|
2894
|
-
self._register_readable(fd)
|
2895
|
-
for fd in self._writable:
|
2896
|
-
self._register_writable(fd)
|
2897
3326
|
|
2898
|
-
|
3327
|
+
########################################
|
3328
|
+
# ../../../omlish/io/buffers.py
|
2899
3329
|
|
2900
|
-
def _register_readable(self, fd: int) -> None:
|
2901
|
-
self._update_registration(fd, 'read', 'add')
|
2902
3330
|
|
2903
|
-
|
2904
|
-
|
3331
|
+
class DelimitingBuffer:
|
3332
|
+
"""
|
3333
|
+
https://github.com/python-trio/trio/issues/796 :|
|
3334
|
+
"""
|
2905
3335
|
|
2906
|
-
|
2907
|
-
self._update_registration(fd, 'read', 'del')
|
3336
|
+
#
|
2908
3337
|
|
2909
|
-
|
2910
|
-
|
3338
|
+
class Error(Exception):
|
3339
|
+
def __init__(self, buffer: 'DelimitingBuffer') -> None:
|
3340
|
+
super().__init__(buffer)
|
3341
|
+
self.buffer = buffer
|
2911
3342
|
|
2912
|
-
|
3343
|
+
def __repr__(self) -> str:
|
3344
|
+
return attr_repr(self, 'buffer')
|
2913
3345
|
|
2914
|
-
|
2915
|
-
|
2916
|
-
'write': select.KQ_FILTER_WRITE,
|
2917
|
-
}
|
3346
|
+
class ClosedError(Error):
|
3347
|
+
pass
|
2918
3348
|
|
2919
|
-
|
2920
|
-
'add': select.KQ_EV_ADD,
|
2921
|
-
'del': select.KQ_EV_DELETE,
|
2922
|
-
}
|
3349
|
+
#
|
2923
3350
|
|
2924
|
-
|
2925
|
-
self,
|
2926
|
-
fd: int,
|
2927
|
-
read_or_write: ta.Literal['read', 'write'],
|
2928
|
-
add_or_del: ta.Literal['add', 'del'],
|
2929
|
-
) -> None: # noqa
|
2930
|
-
ke = select.kevent(
|
2931
|
-
fd,
|
2932
|
-
filter=self._CONTROL_FILTER_BY_READ_OR_WRITE[read_or_write],
|
2933
|
-
flags=self._CONTROL_FLAGS_BY_ADD_OR_DEL[add_or_del],
|
2934
|
-
)
|
2935
|
-
kq = self._get_kqueue()
|
2936
|
-
try:
|
2937
|
-
kq.control([ke], 0)
|
3351
|
+
DEFAULT_DELIMITERS: bytes = b'\n'
|
2938
3352
|
|
2939
|
-
|
2940
|
-
|
2941
|
-
|
2942
|
-
|
2943
|
-
|
2944
|
-
|
2945
|
-
|
2946
|
-
|
2947
|
-
else:
|
2948
|
-
raise
|
3353
|
+
def __init__(
|
3354
|
+
self,
|
3355
|
+
delimiters: ta.Iterable[int] = DEFAULT_DELIMITERS,
|
3356
|
+
*,
|
3357
|
+
keep_ends: bool = False,
|
3358
|
+
max_size: ta.Optional[int] = None,
|
3359
|
+
) -> None:
|
3360
|
+
super().__init__()
|
2949
3361
|
|
2950
|
-
|
3362
|
+
self._delimiters = frozenset(check.isinstance(d, int) for d in delimiters)
|
3363
|
+
self._keep_ends = keep_ends
|
3364
|
+
self._max_size = max_size
|
2951
3365
|
|
2952
|
-
|
2953
|
-
kq = self._get_kqueue()
|
2954
|
-
try:
|
2955
|
-
kes = kq.control(None, self._max_events, timeout)
|
3366
|
+
self._buf: ta.Optional[io.BytesIO] = io.BytesIO()
|
2956
3367
|
|
2957
|
-
|
2958
|
-
if exc.errno == errno.EINTR:
|
2959
|
-
return FdioPoller.PollResult(msg='EINTR encountered in poll', exc=exc)
|
2960
|
-
else:
|
2961
|
-
raise
|
3368
|
+
#
|
2962
3369
|
|
2963
|
-
|
2964
|
-
|
2965
|
-
|
2966
|
-
if ke.filter == select.KQ_FILTER_READ:
|
2967
|
-
r.append(ke.ident)
|
2968
|
-
if ke.filter == select.KQ_FILTER_WRITE:
|
2969
|
-
w.append(ke.ident)
|
2970
|
-
|
2971
|
-
return FdioPoller.PollResult(r, w)
|
2972
|
-
|
2973
|
-
KqueueFdioPoller = _KqueueFdioPoller
|
2974
|
-
else:
|
2975
|
-
KqueueFdioPoller = None
|
3370
|
+
@property
|
3371
|
+
def is_closed(self) -> bool:
|
3372
|
+
return self._buf is None
|
2976
3373
|
|
3374
|
+
def tell(self) -> int:
|
3375
|
+
if (buf := self._buf) is None:
|
3376
|
+
raise self.ClosedError(self)
|
3377
|
+
return buf.tell()
|
2977
3378
|
|
2978
|
-
|
2979
|
-
|
3379
|
+
def peek(self) -> bytes:
|
3380
|
+
if (buf := self._buf) is None:
|
3381
|
+
raise self.ClosedError(self)
|
3382
|
+
return buf.getvalue()
|
2980
3383
|
|
3384
|
+
def _find_delim(self, data: ta.Union[bytes, bytearray], i: int) -> ta.Optional[int]:
|
3385
|
+
r = None # type: int | None
|
3386
|
+
for d in self._delimiters:
|
3387
|
+
if (p := data.find(d, i)) >= 0:
|
3388
|
+
if r is None or p < r:
|
3389
|
+
r = p
|
3390
|
+
return r
|
2981
3391
|
|
2982
|
-
|
3392
|
+
def _append_and_reset(self, chunk: bytes) -> bytes:
|
3393
|
+
buf = check.not_none(self._buf)
|
3394
|
+
if not buf.tell():
|
3395
|
+
return chunk
|
2983
3396
|
|
3397
|
+
buf.write(chunk)
|
3398
|
+
ret = buf.getvalue()
|
3399
|
+
buf.seek(0)
|
3400
|
+
buf.truncate()
|
3401
|
+
return ret
|
2984
3402
|
|
2985
|
-
class
|
2986
|
-
|
3403
|
+
class Incomplete(ta.NamedTuple):
|
3404
|
+
b: bytes
|
2987
3405
|
|
2988
|
-
def
|
2989
|
-
|
2990
|
-
|
2991
|
-
es.__enter__()
|
2992
|
-
return self
|
3406
|
+
def feed(self, data: ta.Union[bytes, bytearray]) -> ta.Generator[ta.Union[bytes, Incomplete], None, None]:
|
3407
|
+
if (buf := self._buf) is None:
|
3408
|
+
raise self.ClosedError(self)
|
2993
3409
|
|
2994
|
-
|
2995
|
-
|
2996
|
-
return None
|
2997
|
-
self._exit_contexts()
|
2998
|
-
return es.__exit__(exc_type, exc_val, exc_tb)
|
3410
|
+
if not data:
|
3411
|
+
self._buf = None
|
2999
3412
|
|
3000
|
-
|
3001
|
-
|
3413
|
+
if buf.tell():
|
3414
|
+
yield self.Incomplete(buf.getvalue())
|
3002
3415
|
|
3003
|
-
|
3004
|
-
es = check_not_none(self._exit_stack)
|
3005
|
-
return es.enter_context(cm)
|
3416
|
+
return
|
3006
3417
|
|
3418
|
+
l = len(data)
|
3419
|
+
i = 0
|
3420
|
+
while i < l:
|
3421
|
+
if (p := self._find_delim(data, i)) is None:
|
3422
|
+
break
|
3007
3423
|
|
3008
|
-
|
3424
|
+
n = p + 1
|
3425
|
+
if self._keep_ends:
|
3426
|
+
p = n
|
3009
3427
|
|
3428
|
+
yield self._append_and_reset(data[i:p])
|
3010
3429
|
|
3011
|
-
|
3012
|
-
def defer(fn: ta.Callable) -> ta.Generator[ta.Callable, None, None]:
|
3013
|
-
try:
|
3014
|
-
yield fn
|
3015
|
-
finally:
|
3016
|
-
fn()
|
3430
|
+
i = n
|
3017
3431
|
|
3432
|
+
if i >= l:
|
3433
|
+
return
|
3018
3434
|
|
3019
|
-
|
3020
|
-
|
3021
|
-
|
3022
|
-
orig = getattr(obj, attr, not_set)
|
3023
|
-
try:
|
3024
|
-
setattr(obj, attr, val)
|
3025
|
-
if orig is not not_set:
|
3026
|
-
yield orig
|
3027
|
-
else:
|
3028
|
-
yield default
|
3029
|
-
finally:
|
3030
|
-
if orig is not_set:
|
3031
|
-
delattr(obj, attr)
|
3032
|
-
else:
|
3033
|
-
setattr(obj, attr, orig)
|
3435
|
+
if self._max_size is None:
|
3436
|
+
buf.write(data[i:])
|
3437
|
+
return
|
3034
3438
|
|
3439
|
+
while i < l:
|
3440
|
+
remaining_data_len = l - i
|
3441
|
+
remaining_buf_capacity = self._max_size - buf.tell()
|
3035
3442
|
|
3036
|
-
|
3037
|
-
|
3038
|
-
|
3039
|
-
# --------------------------------------------
|
3040
|
-
#
|
3041
|
-
# 1. This LICENSE AGREEMENT is between the Python Software Foundation ("PSF"), and the Individual or Organization
|
3042
|
-
# ("Licensee") accessing and otherwise using this software ("Python") in source or binary form and its associated
|
3043
|
-
# documentation.
|
3044
|
-
#
|
3045
|
-
# 2. Subject to the terms and conditions of this License Agreement, PSF hereby grants Licensee a nonexclusive,
|
3046
|
-
# royalty-free, world-wide license to reproduce, analyze, test, perform and/or display publicly, prepare derivative
|
3047
|
-
# works, distribute, and otherwise use Python alone or in any derivative version, provided, however, that PSF's License
|
3048
|
-
# Agreement and PSF's notice of copyright, i.e., "Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009,
|
3049
|
-
# 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017 Python Software Foundation; All Rights Reserved" are retained in Python
|
3050
|
-
# alone or in any derivative version prepared by Licensee.
|
3051
|
-
#
|
3052
|
-
# 3. In the event Licensee prepares a derivative work that is based on or incorporates Python or any part thereof, and
|
3053
|
-
# wants to make the derivative work available to others as provided herein, then Licensee hereby agrees to include in
|
3054
|
-
# any such work a brief summary of the changes made to Python.
|
3055
|
-
#
|
3056
|
-
# 4. PSF is making Python available to Licensee on an "AS IS" basis. PSF MAKES NO REPRESENTATIONS OR WARRANTIES,
|
3057
|
-
# EXPRESS OR IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND DISCLAIMS ANY REPRESENTATION OR WARRANTY
|
3058
|
-
# OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON WILL NOT INFRINGE ANY THIRD PARTY
|
3059
|
-
# RIGHTS.
|
3060
|
-
#
|
3061
|
-
# 5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL
|
3062
|
-
# DAMAGES OR LOSS AS A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON, OR ANY DERIVATIVE THEREOF, EVEN IF
|
3063
|
-
# ADVISED OF THE POSSIBILITY THEREOF.
|
3064
|
-
#
|
3065
|
-
# 6. This License Agreement will automatically terminate upon a material breach of its terms and conditions.
|
3066
|
-
#
|
3067
|
-
# 7. Nothing in this License Agreement shall be deemed to create any relationship of agency, partnership, or joint
|
3068
|
-
# venture between PSF and Licensee. This License Agreement does not grant permission to use PSF trademarks or trade
|
3069
|
-
# name in a trademark sense to endorse or promote products or services of Licensee, or any third party.
|
3070
|
-
#
|
3071
|
-
# 8. By copying, installing or otherwise using Python, Licensee agrees to be bound by the terms and conditions of this
|
3072
|
-
# License Agreement.
|
3443
|
+
if remaining_data_len < remaining_buf_capacity:
|
3444
|
+
buf.write(data[i:])
|
3445
|
+
return
|
3073
3446
|
|
3447
|
+
p = i + remaining_buf_capacity
|
3448
|
+
yield self.Incomplete(self._append_and_reset(data[i:p]))
|
3449
|
+
i = p
|
3074
3450
|
|
3075
|
-
##
|
3076
3451
|
|
3452
|
+
class ReadableListBuffer:
|
3453
|
+
def __init__(self) -> None:
|
3454
|
+
super().__init__()
|
3455
|
+
self._lst: list[bytes] = []
|
3077
3456
|
|
3078
|
-
|
3079
|
-
|
3080
|
-
|
3081
|
-
'request_line',
|
3082
|
-
'request_version',
|
3083
|
-
'version',
|
3084
|
-
'headers',
|
3085
|
-
'close_connection',
|
3086
|
-
)
|
3457
|
+
def feed(self, d: bytes) -> None:
|
3458
|
+
if d:
|
3459
|
+
self._lst.append(d)
|
3087
3460
|
|
3088
|
-
def
|
3089
|
-
|
3090
|
-
|
3091
|
-
server_version: HttpProtocolVersion,
|
3092
|
-
request_line: str,
|
3093
|
-
request_version: HttpProtocolVersion,
|
3094
|
-
version: HttpProtocolVersion,
|
3095
|
-
headers: ta.Optional[HttpHeaders],
|
3096
|
-
close_connection: bool,
|
3097
|
-
) -> None:
|
3098
|
-
super().__init__()
|
3461
|
+
def _chop(self, i: int, e: int) -> bytes:
|
3462
|
+
lst = self._lst
|
3463
|
+
d = lst[i]
|
3099
3464
|
|
3100
|
-
|
3101
|
-
|
3102
|
-
|
3103
|
-
|
3104
|
-
self.headers = headers
|
3105
|
-
self.close_connection = close_connection
|
3465
|
+
o = b''.join([
|
3466
|
+
*lst[:i],
|
3467
|
+
d[:e],
|
3468
|
+
])
|
3106
3469
|
|
3107
|
-
|
3108
|
-
|
3470
|
+
self._lst = [
|
3471
|
+
*([d[e:]] if e < len(d) else []),
|
3472
|
+
*lst[i + 1:],
|
3473
|
+
]
|
3109
3474
|
|
3475
|
+
return o
|
3110
3476
|
|
3111
|
-
|
3112
|
-
|
3477
|
+
def read(self, n: ta.Optional[int] = None) -> ta.Optional[bytes]:
|
3478
|
+
if n is None:
|
3479
|
+
o = b''.join(self._lst)
|
3480
|
+
self._lst = []
|
3481
|
+
return o
|
3113
3482
|
|
3483
|
+
if not (lst := self._lst):
|
3484
|
+
return None
|
3114
3485
|
|
3115
|
-
|
3116
|
-
|
3117
|
-
|
3118
|
-
|
3119
|
-
|
3120
|
-
|
3486
|
+
c = 0
|
3487
|
+
for i, d in enumerate(lst):
|
3488
|
+
r = n - c
|
3489
|
+
if (l := len(d)) >= r:
|
3490
|
+
return self._chop(i, r)
|
3491
|
+
c += l
|
3121
3492
|
|
3122
|
-
|
3123
|
-
self,
|
3124
|
-
*,
|
3125
|
-
code: http.HTTPStatus,
|
3126
|
-
message: ta.Union[str, ta.Tuple[str, str]],
|
3493
|
+
return None
|
3127
3494
|
|
3128
|
-
|
3129
|
-
|
3130
|
-
|
3495
|
+
def read_until(self, delim: bytes = b'\n') -> ta.Optional[bytes]:
|
3496
|
+
if not (lst := self._lst):
|
3497
|
+
return None
|
3131
3498
|
|
3132
|
-
|
3133
|
-
|
3499
|
+
for i, d in enumerate(lst):
|
3500
|
+
if (p := d.find(delim)) >= 0:
|
3501
|
+
return self._chop(i, p + len(delim))
|
3134
3502
|
|
3503
|
+
return None
|
3135
3504
|
|
3136
|
-
class ParsedHttpRequest(ParseHttpRequestResult):
|
3137
|
-
__slots__ = (
|
3138
|
-
'method',
|
3139
|
-
'path',
|
3140
|
-
'headers',
|
3141
|
-
'expects_continue',
|
3142
|
-
*[a for a in ParseHttpRequestResult.__slots__ if a != 'headers'],
|
3143
|
-
)
|
3144
3505
|
|
3506
|
+
class IncrementalWriteBuffer:
|
3145
3507
|
def __init__(
|
3146
3508
|
self,
|
3509
|
+
data: bytes,
|
3147
3510
|
*,
|
3148
|
-
|
3149
|
-
path: str,
|
3150
|
-
headers: HttpHeaders,
|
3151
|
-
expects_continue: bool,
|
3152
|
-
|
3153
|
-
**kwargs: ta.Any,
|
3511
|
+
write_size: int = 0x10000,
|
3154
3512
|
) -> None:
|
3155
|
-
super().__init__(
|
3156
|
-
headers=headers,
|
3157
|
-
**kwargs,
|
3158
|
-
)
|
3159
|
-
|
3160
|
-
self.method = method
|
3161
|
-
self.path = path
|
3162
|
-
self.expects_continue = expects_continue
|
3163
|
-
|
3164
|
-
headers: HttpHeaders
|
3513
|
+
super().__init__()
|
3165
3514
|
|
3515
|
+
check.not_empty(data)
|
3516
|
+
self._len = len(data)
|
3517
|
+
self._write_size = write_size
|
3166
3518
|
|
3167
|
-
|
3168
|
-
|
3519
|
+
self._lst = [
|
3520
|
+
data[i:i + write_size]
|
3521
|
+
for i in range(0, len(data), write_size)
|
3522
|
+
]
|
3523
|
+
self._pos = 0
|
3169
3524
|
|
3170
|
-
|
3171
|
-
|
3525
|
+
@property
|
3526
|
+
def rem(self) -> int:
|
3527
|
+
return self._len - self._pos
|
3172
3528
|
|
3173
|
-
|
3174
|
-
|
3175
|
-
# Most web servers default to HTTP 0.9, i.e. don't send a status line.
|
3176
|
-
DEFAULT_REQUEST_VERSION = HttpProtocolVersions.HTTP_0_9
|
3529
|
+
def write(self, fn: ta.Callable[[bytes], int]) -> int:
|
3530
|
+
lst = check.not_empty(self._lst)
|
3177
3531
|
|
3178
|
-
|
3532
|
+
t = 0
|
3533
|
+
for i, d in enumerate(lst): # noqa
|
3534
|
+
n = fn(check.not_empty(d))
|
3535
|
+
if not n:
|
3536
|
+
break
|
3537
|
+
t += n
|
3179
3538
|
|
3180
|
-
|
3181
|
-
|
3539
|
+
if t:
|
3540
|
+
self._lst = [
|
3541
|
+
*([d[n:]] if n < len(d) else []),
|
3542
|
+
*lst[i + 1:],
|
3543
|
+
]
|
3544
|
+
self._pos += t
|
3182
3545
|
|
3183
|
-
|
3546
|
+
return t
|
3184
3547
|
|
3185
|
-
def __init__(
|
3186
|
-
self,
|
3187
|
-
*,
|
3188
|
-
server_version: HttpProtocolVersion = DEFAULT_SERVER_VERSION,
|
3189
3548
|
|
3190
|
-
|
3191
|
-
|
3192
|
-
) -> None:
|
3193
|
-
super().__init__()
|
3549
|
+
########################################
|
3550
|
+
# ../../../omlish/io/fdio/handlers.py
|
3194
3551
|
|
3195
|
-
if server_version >= HttpProtocolVersions.HTTP_2_0:
|
3196
|
-
raise ValueError(f'Unsupported protocol version: {server_version}')
|
3197
|
-
self._server_version = server_version
|
3198
3552
|
|
3199
|
-
|
3200
|
-
|
3553
|
+
class FdioHandler(abc.ABC):
|
3554
|
+
@abc.abstractmethod
|
3555
|
+
def fd(self) -> int:
|
3556
|
+
raise NotImplementedError
|
3201
3557
|
|
3202
3558
|
#
|
3203
3559
|
|
3204
3560
|
@property
|
3205
|
-
|
3206
|
-
|
3561
|
+
@abc.abstractmethod
|
3562
|
+
def closed(self) -> bool:
|
3563
|
+
raise NotImplementedError
|
3564
|
+
|
3565
|
+
@abc.abstractmethod
|
3566
|
+
def close(self) -> None:
|
3567
|
+
raise NotImplementedError
|
3207
3568
|
|
3208
3569
|
#
|
3209
3570
|
|
3210
|
-
def
|
3211
|
-
|
3212
|
-
|
3213
|
-
|
3214
|
-
|
3215
|
-
sz = next(gen)
|
3216
|
-
while True:
|
3217
|
-
try:
|
3218
|
-
sz = gen.send(read_line(sz))
|
3219
|
-
except StopIteration as e:
|
3220
|
-
return e.value
|
3571
|
+
def readable(self) -> bool:
|
3572
|
+
return False
|
3573
|
+
|
3574
|
+
def writable(self) -> bool:
|
3575
|
+
return False
|
3221
3576
|
|
3222
3577
|
#
|
3223
3578
|
|
3224
|
-
def
|
3225
|
-
|
3226
|
-
raise ValueError(version_str) # noqa
|
3579
|
+
def on_readable(self) -> None:
|
3580
|
+
raise TypeError
|
3227
3581
|
|
3228
|
-
|
3229
|
-
|
3582
|
+
def on_writable(self) -> None:
|
3583
|
+
raise TypeError
|
3230
3584
|
|
3231
|
-
|
3232
|
-
|
3233
|
-
# - HTTP/2.4 is a lower version than HTTP/2.13, which in turn is lower than HTTP/12.3;
|
3234
|
-
# - Leading zeros MUST be ignored by recipients.
|
3235
|
-
if len(version_number_parts) != 2:
|
3236
|
-
raise ValueError(version_number_parts) # noqa
|
3237
|
-
if any(not component.isdigit() for component in version_number_parts):
|
3238
|
-
raise ValueError('non digit in http version') # noqa
|
3239
|
-
if any(len(component) > 10 for component in version_number_parts):
|
3240
|
-
raise ValueError('unreasonable length http version') # noqa
|
3585
|
+
def on_error(self, exc: ta.Optional[BaseException] = None) -> None: # noqa
|
3586
|
+
pass
|
3241
3587
|
|
3242
|
-
return HttpProtocolVersion(
|
3243
|
-
int(version_number_parts[0]),
|
3244
|
-
int(version_number_parts[1]),
|
3245
|
-
)
|
3246
3588
|
|
3247
|
-
|
3589
|
+
class SocketFdioHandler(FdioHandler, abc.ABC):
|
3590
|
+
def __init__(
|
3591
|
+
self,
|
3592
|
+
addr: SocketAddress,
|
3593
|
+
sock: socket.socket,
|
3594
|
+
) -> None:
|
3595
|
+
super().__init__()
|
3248
3596
|
|
3249
|
-
|
3250
|
-
|
3251
|
-
while True:
|
3252
|
-
line = yield self._max_line + 1
|
3253
|
-
if len(line) > self._max_line:
|
3254
|
-
raise http.client.LineTooLong('header line')
|
3255
|
-
raw_headers.append(line)
|
3256
|
-
if len(raw_headers) > self._max_headers:
|
3257
|
-
raise http.client.HTTPException(f'got more than {self._max_headers} headers')
|
3258
|
-
if line in (b'\r\n', b'\n', b''):
|
3259
|
-
break
|
3260
|
-
return raw_headers
|
3597
|
+
self._addr = addr
|
3598
|
+
self._sock: ta.Optional[socket.socket] = sock
|
3261
3599
|
|
3262
|
-
def
|
3263
|
-
return
|
3600
|
+
def fd(self) -> int:
|
3601
|
+
return check.not_none(self._sock).fileno()
|
3264
3602
|
|
3265
|
-
|
3266
|
-
|
3603
|
+
@property
|
3604
|
+
def closed(self) -> bool:
|
3605
|
+
return self._sock is None
|
3267
3606
|
|
3268
|
-
|
3607
|
+
def close(self) -> None:
|
3608
|
+
if self._sock is not None:
|
3609
|
+
self._sock.close()
|
3610
|
+
self._sock = None
|
3269
3611
|
|
3270
|
-
def coro_parse(self) -> ta.Generator[int, bytes, ParseHttpRequestResult]:
|
3271
|
-
raw_request_line = yield self._max_line + 1
|
3272
3612
|
|
3273
|
-
|
3613
|
+
########################################
|
3614
|
+
# ../../../omlish/io/fdio/kqueue.py
|
3274
3615
|
|
3275
|
-
request_line = '-'
|
3276
|
-
request_version = self.DEFAULT_REQUEST_VERSION
|
3277
3616
|
|
3278
|
-
|
3279
|
-
|
3280
|
-
version = self._server_version
|
3617
|
+
KqueueFdioPoller: ta.Optional[ta.Type[FdioPoller]]
|
3618
|
+
if sys.platform == 'darwin' or sys.platform.startswith('freebsd'):
|
3281
3619
|
|
3282
|
-
|
3620
|
+
class _KqueueFdioPoller(FdioPoller):
|
3621
|
+
DEFAULT_MAX_EVENTS = 1000
|
3283
3622
|
|
3284
|
-
|
3623
|
+
def __init__(
|
3624
|
+
self,
|
3625
|
+
*,
|
3626
|
+
max_events: int = DEFAULT_MAX_EVENTS,
|
3627
|
+
) -> None:
|
3628
|
+
super().__init__()
|
3285
3629
|
|
3286
|
-
|
3287
|
-
return dict(
|
3288
|
-
server_version=self._server_version,
|
3289
|
-
request_line=request_line,
|
3290
|
-
request_version=request_version,
|
3291
|
-
version=version,
|
3292
|
-
headers=headers,
|
3293
|
-
close_connection=close_connection,
|
3294
|
-
)
|
3630
|
+
self._max_events = max_events
|
3295
3631
|
|
3296
|
-
|
3632
|
+
self._kqueue: ta.Optional[ta.Any] = None
|
3297
3633
|
|
3298
|
-
|
3299
|
-
return ParseHttpRequestError(
|
3300
|
-
code=http.HTTPStatus.REQUEST_URI_TOO_LONG,
|
3301
|
-
message='Request line too long',
|
3302
|
-
**result_kwargs(),
|
3303
|
-
)
|
3634
|
+
#
|
3304
3635
|
|
3305
|
-
|
3306
|
-
|
3636
|
+
def _get_kqueue(self) -> 'select.kqueue':
|
3637
|
+
if (kq := self._kqueue) is not None:
|
3638
|
+
return kq
|
3639
|
+
kq = select.kqueue()
|
3640
|
+
self._kqueue = kq
|
3641
|
+
return kq
|
3307
3642
|
|
3308
|
-
|
3643
|
+
def close(self) -> None:
|
3644
|
+
if self._kqueue is not None:
|
3645
|
+
self._kqueue.close()
|
3646
|
+
self._kqueue = None
|
3309
3647
|
|
3310
|
-
|
3648
|
+
def reopen(self) -> None:
|
3649
|
+
for fd in self._readable:
|
3650
|
+
self._register_readable(fd)
|
3651
|
+
for fd in self._writable:
|
3652
|
+
self._register_writable(fd)
|
3311
3653
|
|
3312
|
-
|
3313
|
-
if len(words) == 0:
|
3314
|
-
return EmptyParsedHttpResult(**result_kwargs())
|
3654
|
+
#
|
3315
3655
|
|
3316
|
-
|
3656
|
+
def _register_readable(self, fd: int) -> None:
|
3657
|
+
self._update_registration(fd, 'read', 'add')
|
3317
3658
|
|
3318
|
-
|
3319
|
-
|
3320
|
-
try:
|
3321
|
-
request_version = self.parse_request_version(version_str)
|
3659
|
+
def _register_writable(self, fd: int) -> None:
|
3660
|
+
self._update_registration(fd, 'write', 'add')
|
3322
3661
|
|
3323
|
-
|
3324
|
-
|
3325
|
-
code=http.HTTPStatus.BAD_REQUEST,
|
3326
|
-
message=f'Bad request version ({version_str!r})',
|
3327
|
-
**result_kwargs(),
|
3328
|
-
)
|
3662
|
+
def _unregister_readable(self, fd: int) -> None:
|
3663
|
+
self._update_registration(fd, 'read', 'del')
|
3329
3664
|
|
3330
|
-
|
3331
|
-
|
3332
|
-
request_version >= HttpProtocolVersions.HTTP_2_0
|
3333
|
-
):
|
3334
|
-
return ParseHttpRequestError(
|
3335
|
-
code=http.HTTPStatus.HTTP_VERSION_NOT_SUPPORTED,
|
3336
|
-
message=f'Invalid HTTP version ({version_str})',
|
3337
|
-
**result_kwargs(),
|
3338
|
-
)
|
3665
|
+
def _unregister_writable(self, fd: int) -> None:
|
3666
|
+
self._update_registration(fd, 'write', 'del')
|
3339
3667
|
|
3340
|
-
|
3668
|
+
#
|
3341
3669
|
|
3342
|
-
|
3343
|
-
|
3670
|
+
_CONTROL_FILTER_BY_READ_OR_WRITE: ta.ClassVar[ta.Mapping[ta.Literal['read', 'write'], int]] = {
|
3671
|
+
'read': select.KQ_FILTER_READ,
|
3672
|
+
'write': select.KQ_FILTER_WRITE,
|
3673
|
+
}
|
3344
3674
|
|
3345
|
-
|
3675
|
+
_CONTROL_FLAGS_BY_ADD_OR_DEL: ta.ClassVar[ta.Mapping[ta.Literal['add', 'del'], int]] = {
|
3676
|
+
'add': select.KQ_EV_ADD,
|
3677
|
+
'del': select.KQ_EV_DELETE,
|
3678
|
+
}
|
3346
3679
|
|
3347
|
-
|
3348
|
-
|
3349
|
-
|
3350
|
-
|
3351
|
-
|
3680
|
+
def _update_registration(
|
3681
|
+
self,
|
3682
|
+
fd: int,
|
3683
|
+
read_or_write: ta.Literal['read', 'write'],
|
3684
|
+
add_or_del: ta.Literal['add', 'del'],
|
3685
|
+
) -> None: # noqa
|
3686
|
+
ke = select.kevent(
|
3687
|
+
fd,
|
3688
|
+
filter=self._CONTROL_FILTER_BY_READ_OR_WRITE[read_or_write],
|
3689
|
+
flags=self._CONTROL_FLAGS_BY_ADD_OR_DEL[add_or_del],
|
3352
3690
|
)
|
3691
|
+
kq = self._get_kqueue()
|
3692
|
+
try:
|
3693
|
+
kq.control([ke], 0)
|
3353
3694
|
|
3354
|
-
|
3695
|
+
except OSError as exc:
|
3696
|
+
if exc.errno == errno.EBADF:
|
3697
|
+
# log.debug('EBADF encountered in kqueue. Invalid file descriptor %s', ke.ident)
|
3698
|
+
pass
|
3699
|
+
elif exc.errno == errno.ENOENT:
|
3700
|
+
# Can happen when trying to remove an already closed socket
|
3701
|
+
if add_or_del == 'add':
|
3702
|
+
raise
|
3703
|
+
else:
|
3704
|
+
raise
|
3355
3705
|
|
3356
|
-
|
3357
|
-
if len(words) == 2:
|
3358
|
-
close_connection = True
|
3359
|
-
if method != 'GET':
|
3360
|
-
return ParseHttpRequestError(
|
3361
|
-
code=http.HTTPStatus.BAD_REQUEST,
|
3362
|
-
message=f'Bad HTTP/0.9 request type ({method!r})',
|
3363
|
-
**result_kwargs(),
|
3364
|
-
)
|
3706
|
+
#
|
3365
3707
|
|
3366
|
-
|
3367
|
-
|
3368
|
-
|
3369
|
-
|
3370
|
-
path = '/' + path.lstrip('/') # Reduce to a single /
|
3708
|
+
def poll(self, timeout: ta.Optional[float]) -> FdioPoller.PollResult:
|
3709
|
+
kq = self._get_kqueue()
|
3710
|
+
try:
|
3711
|
+
kes = kq.control(None, self._max_events, timeout)
|
3371
3712
|
|
3372
|
-
|
3713
|
+
except OSError as exc:
|
3714
|
+
if exc.errno == errno.EINTR:
|
3715
|
+
return FdioPoller.PollResult(msg='EINTR encountered in poll', exc=exc)
|
3716
|
+
else:
|
3717
|
+
raise
|
3373
3718
|
|
3374
|
-
|
3375
|
-
|
3376
|
-
|
3377
|
-
|
3378
|
-
|
3379
|
-
|
3380
|
-
|
3381
|
-
except StopIteration as e:
|
3382
|
-
raw_headers = e.value
|
3383
|
-
break
|
3719
|
+
r: ta.List[int] = []
|
3720
|
+
w: ta.List[int] = []
|
3721
|
+
for ke in kes:
|
3722
|
+
if ke.filter == select.KQ_FILTER_READ:
|
3723
|
+
r.append(ke.ident)
|
3724
|
+
if ke.filter == select.KQ_FILTER_WRITE:
|
3725
|
+
w.append(ke.ident)
|
3384
3726
|
|
3385
|
-
|
3727
|
+
return FdioPoller.PollResult(r, w)
|
3386
3728
|
|
3387
|
-
|
3388
|
-
|
3389
|
-
|
3390
|
-
message=('Line too long', str(err)),
|
3391
|
-
**result_kwargs(),
|
3392
|
-
)
|
3729
|
+
KqueueFdioPoller = _KqueueFdioPoller
|
3730
|
+
else:
|
3731
|
+
KqueueFdioPoller = None
|
3393
3732
|
|
3394
|
-
except http.client.HTTPException as err:
|
3395
|
-
return ParseHttpRequestError(
|
3396
|
-
code=http.HTTPStatus.REQUEST_HEADER_FIELDS_TOO_LARGE,
|
3397
|
-
message=('Too many headers', str(err)),
|
3398
|
-
**result_kwargs(),
|
3399
|
-
)
|
3400
3733
|
|
3401
|
-
|
3734
|
+
########################################
|
3735
|
+
# ../../../omlish/lite/contextmanagers.py
|
3402
3736
|
|
3403
|
-
conn_type = headers.get('Connection', '')
|
3404
|
-
if conn_type.lower() == 'close':
|
3405
|
-
close_connection = True
|
3406
|
-
elif (
|
3407
|
-
conn_type.lower() == 'keep-alive' and
|
3408
|
-
version >= HttpProtocolVersions.HTTP_1_1
|
3409
|
-
):
|
3410
|
-
close_connection = False
|
3411
3737
|
|
3412
|
-
|
3738
|
+
##
|
3413
3739
|
|
3414
|
-
expect = headers.get('Expect', '')
|
3415
|
-
if (
|
3416
|
-
expect.lower() == '100-continue' and
|
3417
|
-
version >= HttpProtocolVersions.HTTP_1_1
|
3418
|
-
):
|
3419
|
-
expects_continue = True
|
3420
|
-
else:
|
3421
|
-
expects_continue = False
|
3422
3740
|
|
3423
|
-
|
3741
|
+
class ExitStacked:
|
3742
|
+
_exit_stack: ta.Optional[contextlib.ExitStack] = None
|
3424
3743
|
|
3425
|
-
|
3426
|
-
|
3427
|
-
|
3428
|
-
|
3429
|
-
|
3430
|
-
)
|
3744
|
+
def __enter__(self: ExitStackedT) -> ExitStackedT:
|
3745
|
+
check.state(self._exit_stack is None)
|
3746
|
+
es = self._exit_stack = contextlib.ExitStack()
|
3747
|
+
es.__enter__()
|
3748
|
+
return self
|
3431
3749
|
|
3432
|
-
def
|
3433
|
-
|
3750
|
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
3751
|
+
if (es := self._exit_stack) is None:
|
3752
|
+
return None
|
3753
|
+
self._exit_contexts()
|
3754
|
+
return es.__exit__(exc_type, exc_val, exc_tb)
|
3755
|
+
|
3756
|
+
def _exit_contexts(self) -> None:
|
3757
|
+
pass
|
3758
|
+
|
3759
|
+
def _enter_context(self, cm: ta.ContextManager[T]) -> T:
|
3760
|
+
es = check.not_none(self._exit_stack)
|
3761
|
+
return es.enter_context(cm)
|
3762
|
+
|
3763
|
+
|
3764
|
+
##
|
3765
|
+
|
3766
|
+
|
3767
|
+
@contextlib.contextmanager
|
3768
|
+
def defer(fn: ta.Callable) -> ta.Generator[ta.Callable, None, None]:
|
3769
|
+
try:
|
3770
|
+
yield fn
|
3771
|
+
finally:
|
3772
|
+
fn()
|
3773
|
+
|
3774
|
+
|
3775
|
+
@contextlib.contextmanager
|
3776
|
+
def attr_setting(obj, attr, val, *, default=None): # noqa
|
3777
|
+
not_set = object()
|
3778
|
+
orig = getattr(obj, attr, not_set)
|
3779
|
+
try:
|
3780
|
+
setattr(obj, attr, val)
|
3781
|
+
if orig is not not_set:
|
3782
|
+
yield orig
|
3783
|
+
else:
|
3784
|
+
yield default
|
3785
|
+
finally:
|
3786
|
+
if orig is not_set:
|
3787
|
+
delattr(obj, attr)
|
3788
|
+
else:
|
3789
|
+
setattr(obj, attr, orig)
|
3434
3790
|
|
3435
3791
|
|
3436
3792
|
########################################
|
@@ -3577,7 +3933,7 @@ class FnInjectorProvider(InjectorProvider):
|
|
3577
3933
|
fn: ta.Any
|
3578
3934
|
|
3579
3935
|
def __post_init__(self) -> None:
|
3580
|
-
|
3936
|
+
check.not_isinstance(self.fn, type)
|
3581
3937
|
|
3582
3938
|
def provider_fn(self) -> InjectorProviderFn:
|
3583
3939
|
def pfn(i: Injector) -> ta.Any:
|
@@ -3591,7 +3947,7 @@ class CtorInjectorProvider(InjectorProvider):
|
|
3591
3947
|
cls_: type
|
3592
3948
|
|
3593
3949
|
def __post_init__(self) -> None:
|
3594
|
-
|
3950
|
+
check.isinstance(self.cls_, type)
|
3595
3951
|
|
3596
3952
|
def provider_fn(self) -> InjectorProviderFn:
|
3597
3953
|
def pfn(i: Injector) -> ta.Any:
|
@@ -3613,7 +3969,7 @@ class SingletonInjectorProvider(InjectorProvider):
|
|
3613
3969
|
p: InjectorProvider
|
3614
3970
|
|
3615
3971
|
def __post_init__(self) -> None:
|
3616
|
-
|
3972
|
+
check.isinstance(self.p, InjectorProvider)
|
3617
3973
|
|
3618
3974
|
def provider_fn(self) -> InjectorProviderFn:
|
3619
3975
|
v = not_set = object()
|
@@ -3633,7 +3989,7 @@ class LinkInjectorProvider(InjectorProvider):
|
|
3633
3989
|
k: InjectorKey
|
3634
3990
|
|
3635
3991
|
def __post_init__(self) -> None:
|
3636
|
-
|
3992
|
+
check.isinstance(self.k, InjectorKey)
|
3637
3993
|
|
3638
3994
|
def provider_fn(self) -> InjectorProviderFn:
|
3639
3995
|
def pfn(i: Injector) -> ta.Any:
|
@@ -3830,7 +4186,7 @@ def build_injection_kwargs_target(
|
|
3830
4186
|
|
3831
4187
|
skip_names: ta.Set[str] = set()
|
3832
4188
|
if skip_kwargs is not None:
|
3833
|
-
skip_names.update(
|
4189
|
+
skip_names.update(check.not_isinstance(skip_kwargs, str))
|
3834
4190
|
|
3835
4191
|
seen: ta.Set[InjectorKey] = set()
|
3836
4192
|
kws: ta.List[InjectionKwarg] = []
|
@@ -3891,8 +4247,8 @@ class _Injector(Injector):
|
|
3891
4247
|
def __init__(self, bs: InjectorBindings, p: ta.Optional[Injector] = None) -> None:
|
3892
4248
|
super().__init__()
|
3893
4249
|
|
3894
|
-
self._bs =
|
3895
|
-
self._p: ta.Optional[Injector] =
|
4250
|
+
self._bs = check.isinstance(bs, InjectorBindings)
|
4251
|
+
self._p: ta.Optional[Injector] = check.isinstance(p, (Injector, type(None)))
|
3896
4252
|
|
3897
4253
|
self._pfm = {k: v.provider_fn() for k, v in build_injector_provider_map(bs).items()}
|
3898
4254
|
|
@@ -3923,8 +4279,8 @@ class _Injector(Injector):
|
|
3923
4279
|
return Maybe.empty()
|
3924
4280
|
|
3925
4281
|
def handle_provision(self, key: InjectorKey, mv: Maybe) -> Maybe:
|
3926
|
-
|
3927
|
-
|
4282
|
+
check.in_(key, self._seen_keys)
|
4283
|
+
check.not_in(key, self._provisions)
|
3928
4284
|
self._provisions[key] = mv
|
3929
4285
|
return mv
|
3930
4286
|
|
@@ -4038,7 +4394,7 @@ class InjectorBinder:
|
|
4038
4394
|
|
4039
4395
|
@classmethod
|
4040
4396
|
def bind_as_fn(cls, icls: ta.Type[T]) -> ta.Type[T]:
|
4041
|
-
|
4397
|
+
check.isinstance(icls, type)
|
4042
4398
|
if icls not in cls._FN_TYPES:
|
4043
4399
|
cls._FN_TYPES = (*cls._FN_TYPES, icls)
|
4044
4400
|
return icls
|
@@ -4095,7 +4451,7 @@ class InjectorBinder:
|
|
4095
4451
|
to_fn = obj
|
4096
4452
|
if key is None:
|
4097
4453
|
insp = _injection_inspect(obj)
|
4098
|
-
key_cls: ta.Any = check_valid_injector_key_cls(
|
4454
|
+
key_cls: ta.Any = check_valid_injector_key_cls(check.not_none(insp.type_hints.get('return')))
|
4099
4455
|
key = InjectorKey(key_cls)
|
4100
4456
|
else:
|
4101
4457
|
if to_const is not None:
|
@@ -4794,10 +5150,10 @@ class ProxyObjMarshaler(ObjMarshaler):
|
|
4794
5150
|
m: ta.Optional[ObjMarshaler] = None
|
4795
5151
|
|
4796
5152
|
def marshal(self, o: ta.Any, ctx: 'ObjMarshalContext') -> ta.Any:
|
4797
|
-
return
|
5153
|
+
return check.not_none(self.m).marshal(o, ctx)
|
4798
5154
|
|
4799
5155
|
def unmarshal(self, o: ta.Any, ctx: 'ObjMarshalContext') -> ta.Any:
|
4800
|
-
return
|
5156
|
+
return check.not_none(self.m).unmarshal(o, ctx)
|
4801
5157
|
|
4802
5158
|
|
4803
5159
|
@dc.dataclass(frozen=True)
|
@@ -4956,19 +5312,19 @@ class DatetimeObjMarshaler(ObjMarshaler):
|
|
4956
5312
|
|
4957
5313
|
class DecimalObjMarshaler(ObjMarshaler):
|
4958
5314
|
def marshal(self, o: ta.Any, ctx: 'ObjMarshalContext') -> ta.Any:
|
4959
|
-
return str(
|
5315
|
+
return str(check.isinstance(o, decimal.Decimal))
|
4960
5316
|
|
4961
5317
|
def unmarshal(self, v: ta.Any, ctx: 'ObjMarshalContext') -> ta.Any:
|
4962
|
-
return decimal.Decimal(
|
5318
|
+
return decimal.Decimal(check.isinstance(v, str))
|
4963
5319
|
|
4964
5320
|
|
4965
5321
|
class FractionObjMarshaler(ObjMarshaler):
|
4966
5322
|
def marshal(self, o: ta.Any, ctx: 'ObjMarshalContext') -> ta.Any:
|
4967
|
-
fr =
|
5323
|
+
fr = check.isinstance(o, fractions.Fraction)
|
4968
5324
|
return [fr.numerator, fr.denominator]
|
4969
5325
|
|
4970
5326
|
def unmarshal(self, v: ta.Any, ctx: 'ObjMarshalContext') -> ta.Any:
|
4971
|
-
num, denom =
|
5327
|
+
num, denom = check.isinstance(v, list)
|
4972
5328
|
return fractions.Fraction(num, denom)
|
4973
5329
|
|
4974
5330
|
|
@@ -5270,7 +5626,7 @@ def build_config_named_children(
|
|
5270
5626
|
lst: ta.List[ConfigMapping] = []
|
5271
5627
|
if isinstance(o, ta.Mapping):
|
5272
5628
|
for k, v in o.items():
|
5273
|
-
|
5629
|
+
check.isinstance(v, ta.Mapping)
|
5274
5630
|
if name_key in v:
|
5275
5631
|
n = v[name_key]
|
5276
5632
|
if k != n:
|
@@ -5280,7 +5636,7 @@ def build_config_named_children(
|
|
5280
5636
|
lst.append({name_key: k, **v})
|
5281
5637
|
|
5282
5638
|
else:
|
5283
|
-
|
5639
|
+
check.not_isinstance(o, str)
|
5284
5640
|
lst.extend(o)
|
5285
5641
|
|
5286
5642
|
seen = set()
|
@@ -5411,7 +5767,7 @@ class SupervisorSetup(abc.ABC):
|
|
5411
5767
|
|
5412
5768
|
|
5413
5769
|
########################################
|
5414
|
-
# ../../../omlish/
|
5770
|
+
# ../../../omlish/http/handlers.py
|
5415
5771
|
|
5416
5772
|
|
5417
5773
|
@dc.dataclass(frozen=True)
|
@@ -5586,7 +5942,7 @@ def parse_logging_level(value: ta.Union[str, int]) -> int:
|
|
5586
5942
|
|
5587
5943
|
|
5588
5944
|
########################################
|
5589
|
-
# ../../../omlish/
|
5945
|
+
# ../../../omlish/http/coroserver.py
|
5590
5946
|
# PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2
|
5591
5947
|
# --------------------------------------------
|
5592
5948
|
#
|
@@ -5966,13 +6322,13 @@ class CoroHttpServer:
|
|
5966
6322
|
yield o
|
5967
6323
|
|
5968
6324
|
elif isinstance(o, self.AnyReadIo):
|
5969
|
-
i =
|
6325
|
+
i = check.isinstance((yield o), bytes)
|
5970
6326
|
|
5971
6327
|
elif isinstance(o, self._Response):
|
5972
6328
|
i = None
|
5973
6329
|
r = self._preprocess_response(o)
|
5974
6330
|
b = self._build_response_bytes(r)
|
5975
|
-
|
6331
|
+
check.none((yield self.WriteIo(b)))
|
5976
6332
|
|
5977
6333
|
else:
|
5978
6334
|
raise TypeError(o)
|
@@ -5995,7 +6351,7 @@ class CoroHttpServer:
|
|
5995
6351
|
sz = next(gen)
|
5996
6352
|
while True:
|
5997
6353
|
try:
|
5998
|
-
line =
|
6354
|
+
line = check.isinstance((yield self.ReadLineIo(sz)), bytes)
|
5999
6355
|
sz = gen.send(line)
|
6000
6356
|
except StopIteration as e:
|
6001
6357
|
parsed = e.value
|
@@ -6014,11 +6370,11 @@ class CoroHttpServer:
|
|
6014
6370
|
yield self._build_error_response(err)
|
6015
6371
|
return
|
6016
6372
|
|
6017
|
-
parsed =
|
6373
|
+
parsed = check.isinstance(parsed, ParsedHttpRequest)
|
6018
6374
|
|
6019
6375
|
# Log
|
6020
6376
|
|
6021
|
-
|
6377
|
+
check.none((yield self.ParsedRequestLogIo(parsed)))
|
6022
6378
|
|
6023
6379
|
# Handle CONTINUE
|
6024
6380
|
|
@@ -6034,7 +6390,7 @@ class CoroHttpServer:
|
|
6034
6390
|
|
6035
6391
|
request_data: ta.Optional[bytes]
|
6036
6392
|
if (cl := parsed.headers.get('Content-Length')) is not None:
|
6037
|
-
request_data =
|
6393
|
+
request_data = check.isinstance((yield self.ReadIo(int(cl))), bytes)
|
6038
6394
|
else:
|
6039
6395
|
request_data = None
|
6040
6396
|
|
@@ -6042,7 +6398,7 @@ class CoroHttpServer:
|
|
6042
6398
|
|
6043
6399
|
handler_request = HttpHandlerRequest(
|
6044
6400
|
client_address=self._client_address,
|
6045
|
-
method=
|
6401
|
+
method=check.not_none(parsed.method),
|
6046
6402
|
path=parsed.path,
|
6047
6403
|
headers=parsed.headers,
|
6048
6404
|
data=request_data,
|
@@ -6340,7 +6696,7 @@ class CoroHttpServerConnectionFdioHandler(SocketFdioHandler):
|
|
6340
6696
|
write_size: int = 0x10000,
|
6341
6697
|
log_handler: ta.Optional[ta.Callable[[CoroHttpServer, CoroHttpServer.AnyLogIo], None]] = None,
|
6342
6698
|
) -> None:
|
6343
|
-
|
6699
|
+
check.state(not sock.getblocking())
|
6344
6700
|
|
6345
6701
|
super().__init__(addr, sock)
|
6346
6702
|
|
@@ -6364,7 +6720,7 @@ class CoroHttpServerConnectionFdioHandler(SocketFdioHandler):
|
|
6364
6720
|
#
|
6365
6721
|
|
6366
6722
|
def _next_io(self) -> None: # noqa
|
6367
|
-
coro =
|
6723
|
+
coro = check.not_none(self._srv_coro)
|
6368
6724
|
|
6369
6725
|
d: bytes | None = None
|
6370
6726
|
o = self._cur_io
|
@@ -6397,7 +6753,7 @@ class CoroHttpServerConnectionFdioHandler(SocketFdioHandler):
|
|
6397
6753
|
o = None
|
6398
6754
|
|
6399
6755
|
elif isinstance(o, CoroHttpServer.WriteIo):
|
6400
|
-
|
6756
|
+
check.none(self._write_buf)
|
6401
6757
|
self._write_buf = IncrementalWriteBuffer(o.data, write_size=self._write_size)
|
6402
6758
|
break
|
6403
6759
|
|
@@ -6418,7 +6774,7 @@ class CoroHttpServerConnectionFdioHandler(SocketFdioHandler):
|
|
6418
6774
|
|
6419
6775
|
def on_readable(self) -> None:
|
6420
6776
|
try:
|
6421
|
-
buf =
|
6777
|
+
buf = check.not_none(self._sock).recv(self._read_size)
|
6422
6778
|
except BlockingIOError:
|
6423
6779
|
return
|
6424
6780
|
except ConnectionResetError:
|
@@ -6434,12 +6790,12 @@ class CoroHttpServerConnectionFdioHandler(SocketFdioHandler):
|
|
6434
6790
|
self._next_io()
|
6435
6791
|
|
6436
6792
|
def on_writable(self) -> None:
|
6437
|
-
|
6438
|
-
wb =
|
6793
|
+
check.isinstance(self._cur_io, CoroHttpServer.WriteIo)
|
6794
|
+
wb = check.not_none(self._write_buf)
|
6439
6795
|
while wb.rem > 0:
|
6440
6796
|
def send(d: bytes) -> int:
|
6441
6797
|
try:
|
6442
|
-
return
|
6798
|
+
return check.not_none(self._sock).send(d)
|
6443
6799
|
except ConnectionResetError:
|
6444
6800
|
self.close()
|
6445
6801
|
return 0
|
@@ -6832,7 +7188,7 @@ class ProcessGroupImpl(ProcessGroup):
|
|
6832
7188
|
|
6833
7189
|
by_name: ta.Dict[str, Process] = {}
|
6834
7190
|
for pconfig in self._config.processes or []:
|
6835
|
-
p =
|
7191
|
+
p = check.isinstance(self._process_factory(pconfig, self), Process)
|
6836
7192
|
if p.name in by_name:
|
6837
7193
|
raise KeyError(f'name {p.name} of process {p} already registered by {by_name[p.name]}')
|
6838
7194
|
by_name[pconfig.name] = p
|
@@ -7385,7 +7741,7 @@ class SocketServerFdioHandler(SocketFdioHandler):
|
|
7385
7741
|
return True
|
7386
7742
|
|
7387
7743
|
def on_readable(self) -> None:
|
7388
|
-
cli_sock, cli_addr =
|
7744
|
+
cli_sock, cli_addr = check.not_none(self._sock).accept()
|
7389
7745
|
cli_sock.setblocking(False)
|
7390
7746
|
|
7391
7747
|
self._on_connect(cli_sock, cli_addr)
|
@@ -7624,7 +7980,7 @@ class ProcessImpl(Process):
|
|
7624
7980
|
if stdin_fd is None:
|
7625
7981
|
raise OSError(errno.EPIPE, 'Process has no stdin channel')
|
7626
7982
|
|
7627
|
-
dispatcher =
|
7983
|
+
dispatcher = check.isinstance(self._dispatchers[stdin_fd], ProcessInputDispatcher)
|
7628
7984
|
if dispatcher.closed:
|
7629
7985
|
raise OSError(errno.EPIPE, "Process' stdin channel is closed")
|
7630
7986
|
|
@@ -8183,21 +8539,21 @@ class ProcessSpawningImpl(ProcessSpawning):
|
|
8183
8539
|
dispatchers: ta.List[FdioHandler] = []
|
8184
8540
|
|
8185
8541
|
if pipes.stdout is not None:
|
8186
|
-
dispatchers.append(
|
8542
|
+
dispatchers.append(check.isinstance(self._output_dispatcher_factory(
|
8187
8543
|
self.process,
|
8188
8544
|
ProcessCommunicationStdoutEvent,
|
8189
8545
|
pipes.stdout,
|
8190
8546
|
), ProcessOutputDispatcher))
|
8191
8547
|
|
8192
8548
|
if pipes.stderr is not None:
|
8193
|
-
dispatchers.append(
|
8549
|
+
dispatchers.append(check.isinstance(self._output_dispatcher_factory(
|
8194
8550
|
self.process,
|
8195
8551
|
ProcessCommunicationStderrEvent,
|
8196
8552
|
pipes.stderr,
|
8197
8553
|
), ProcessOutputDispatcher))
|
8198
8554
|
|
8199
8555
|
if pipes.stdin is not None:
|
8200
|
-
dispatchers.append(
|
8556
|
+
dispatchers.append(check.isinstance(self._input_dispatcher_factory(
|
8201
8557
|
self.process,
|
8202
8558
|
'stdin',
|
8203
8559
|
pipes.stdin,
|
@@ -8287,14 +8643,14 @@ class ProcessSpawningImpl(ProcessSpawning):
|
|
8287
8643
|
raise RuntimeError('Unreachable')
|
8288
8644
|
|
8289
8645
|
def _prepare_child_fds(self, pipes: ProcessPipes) -> None:
|
8290
|
-
os.dup2(
|
8646
|
+
os.dup2(check.not_none(pipes.child_stdin), 0)
|
8291
8647
|
|
8292
|
-
os.dup2(
|
8648
|
+
os.dup2(check.not_none(pipes.child_stdout), 1)
|
8293
8649
|
|
8294
8650
|
if self.config.redirect_stderr:
|
8295
|
-
os.dup2(
|
8651
|
+
os.dup2(check.not_none(pipes.child_stdout), 2)
|
8296
8652
|
else:
|
8297
|
-
os.dup2(
|
8653
|
+
os.dup2(check.not_none(pipes.child_stderr), 2)
|
8298
8654
|
|
8299
8655
|
for i in range(3, self._server_config.min_fds):
|
8300
8656
|
if i in self._inherited_fds:
|
@@ -8410,7 +8766,7 @@ class Supervisor:
|
|
8410
8766
|
if self._process_groups.get(config.name) is not None:
|
8411
8767
|
return False
|
8412
8768
|
|
8413
|
-
group =
|
8769
|
+
group = check.isinstance(self._process_group_factory(config), ProcessGroup)
|
8414
8770
|
for process in group:
|
8415
8771
|
process.after_setuid()
|
8416
8772
|
|