ominfra 0.0.0.dev149__py3-none-any.whl → 0.0.0.dev151__py3-none-any.whl
Sign up to get free protection for your applications and to get access to all the features.
- 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.dev151.dist-info}/METADATA +3 -3
- {ominfra-0.0.0.dev149.dist-info → ominfra-0.0.0.dev151.dist-info}/RECORD +30 -30
- {ominfra-0.0.0.dev149.dist-info → ominfra-0.0.0.dev151.dist-info}/LICENSE +0 -0
- {ominfra-0.0.0.dev149.dist-info → ominfra-0.0.0.dev151.dist-info}/WHEEL +0 -0
- {ominfra-0.0.0.dev149.dist-info → ominfra-0.0.0.dev151.dist-info}/entry_points.txt +0 -0
- {ominfra-0.0.0.dev149.dist-info → ominfra-0.0.0.dev151.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] # ta.TypeAlias
|
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
|
|