Pyjoyment 0.1.0__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.
- Pyjo/App/ASGI.py +735 -0
- Pyjo/App/CGI.py +773 -0
- Pyjo/App/FastCGI.py +631 -0
- Pyjo/App/Lambda.py +987 -0
- Pyjo/App/WSGI.py +427 -0
- Pyjo/App/__init__.py +409 -0
- Pyjo/Asset/File.py +377 -0
- Pyjo/Asset/Memory.py +235 -0
- Pyjo/Asset/__init__.py +200 -0
- Pyjo/Base.py +215 -0
- Pyjo/Cache.py +119 -0
- Pyjo/Collection.py +570 -0
- Pyjo/Content/MultiPart.py +325 -0
- Pyjo/Content/Single.py +193 -0
- Pyjo/Content/__init__.py +806 -0
- Pyjo/Cookie/Request.py +90 -0
- Pyjo/Cookie/Response.py +294 -0
- Pyjo/Cookie/__init__.py +143 -0
- Pyjo/DOM/CSS.py +1019 -0
- Pyjo/DOM/HTML.py +550 -0
- Pyjo/DOM/__init__.py +1329 -0
- Pyjo/Date.py +294 -0
- Pyjo/EventEmitter.py +522 -0
- Pyjo/Exception.py +494 -0
- Pyjo/File.py +959 -0
- Pyjo/GetOpt.py +605 -0
- Pyjo/Headers.py +760 -0
- Pyjo/HelloWorld.py +77 -0
- Pyjo/Home.py +109 -0
- Pyjo/IOLoop/ChildProcess.py +591 -0
- Pyjo/IOLoop/Client.py +736 -0
- Pyjo/IOLoop/Datagram.py +982 -0
- Pyjo/IOLoop/Messages.py +664 -0
- Pyjo/IOLoop/Server.py +458 -0
- Pyjo/IOLoop/Stdio.py +446 -0
- Pyjo/IOLoop/Stream.py +644 -0
- Pyjo/IOLoop/Subprocess.py +419 -0
- Pyjo/IOLoop/TLS.py +360 -0
- Pyjo/IOLoop/__init__.py +1036 -0
- Pyjo/IOLoop/certs/README.md +41 -0
- Pyjo/IOLoop/certs/ca.crt +20 -0
- Pyjo/IOLoop/certs/server.crt +21 -0
- Pyjo/IOLoop/certs/server.key +28 -0
- Pyjo/JSON/Pointer.py +135 -0
- Pyjo/JSON/RPC.py +772 -0
- Pyjo/JSON/Schema.py +2506 -0
- Pyjo/JSON/__init__.py +573 -0
- Pyjo/Loader.py +342 -0
- Pyjo/Log.py +723 -0
- Pyjo/Message/Request.py +725 -0
- Pyjo/Message/Response.py +476 -0
- Pyjo/Message/__init__.py +830 -0
- Pyjo/Parameters.py +472 -0
- Pyjo/Path.py +419 -0
- Pyjo/Promise.py +894 -0
- Pyjo/Prove.py +1305 -0
- Pyjo/Reactor/Asyncio.py +854 -0
- Pyjo/Reactor/Poll.py +439 -0
- Pyjo/Reactor/__init__.py +525 -0
- Pyjo/SSE.py +119 -0
- Pyjo/Server/ASGI.py +486 -0
- Pyjo/Server/CGI.py +259 -0
- Pyjo/Server/Daemon.py +1007 -0
- Pyjo/Server/FastCGI.py +517 -0
- Pyjo/Server/Hot/__init__.py +687 -0
- Pyjo/Server/Hot/__main__.py +5 -0
- Pyjo/Server/Lambda.py +641 -0
- Pyjo/Server/Prefork.py +917 -0
- Pyjo/Server/Prethread.py +394 -0
- Pyjo/Server/Reload/Backend/Poll.py +114 -0
- Pyjo/Server/Reload/Backend/__init__.py +126 -0
- Pyjo/Server/Reload/__init__.py +482 -0
- Pyjo/Server/Reload/__main__.py +5 -0
- Pyjo/Server/Stdio.py +228 -0
- Pyjo/Server/WSGI.py +349 -0
- Pyjo/Server/__init__.py +396 -0
- Pyjo/String/Bytes.py +532 -0
- Pyjo/String/HTML.py +210 -0
- Pyjo/String/Mixin.py +333 -0
- Pyjo/String/Template.py +470 -0
- Pyjo/String/Unicode.py +553 -0
- Pyjo/String/__init__.py +0 -0
- Pyjo/TOML.py +232 -0
- Pyjo/Template.py +1290 -0
- Pyjo/Test/Pyjo.py +1454 -0
- Pyjo/Test/__init__.py +1233 -0
- Pyjo/Test/pytest_plugin.py +305 -0
- Pyjo/Transaction/HTTP.py +374 -0
- Pyjo/Transaction/WebSocket.py +709 -0
- Pyjo/Transaction/__init__.py +429 -0
- Pyjo/URL/Template.py +459 -0
- Pyjo/URL/__init__.py +651 -0
- Pyjo/Upload.py +139 -0
- Pyjo/UserAgent/CookieJar.py +391 -0
- Pyjo/UserAgent/Proxy.py +138 -0
- Pyjo/UserAgent/Server.py +239 -0
- Pyjo/UserAgent/Transactor.py +766 -0
- Pyjo/UserAgent/__init__.py +1243 -0
- Pyjo/Util.py +3567 -0
- Pyjo/WebSocket.py +332 -0
- Pyjo/YAML.py +1167 -0
- Pyjo/__init__.py +441 -0
- Pyjo/__main__.py +35 -0
- Pyjo/py.typed +0 -0
- Pyjoyment/Command/Author/__init__.py +20 -0
- Pyjoyment/Command/Author/generate/__init__.py +106 -0
- Pyjoyment/Command/Author/generate/app.py +299 -0
- Pyjoyment/Command/Author/generate/dockerfile.py +281 -0
- Pyjoyment/Command/Author/generate/kubernetes.py +514 -0
- Pyjoyment/Command/Author/generate/lambda_zip.py +372 -0
- Pyjoyment/Command/Author/generate/lite_app.py +113 -0
- Pyjoyment/Command/Author/generate/plugin.py +267 -0
- Pyjoyment/Command/Author/generate/pyproject.py +123 -0
- Pyjoyment/Command/Author/inflate.py +101 -0
- Pyjoyment/Command/__init__.py +332 -0
- Pyjoyment/Command/asgi.py +92 -0
- Pyjoyment/Command/cgi.py +90 -0
- Pyjoyment/Command/daemon.py +160 -0
- Pyjoyment/Command/eval.py +128 -0
- Pyjoyment/Command/fastcgi.py +99 -0
- Pyjoyment/Command/get.py +305 -0
- Pyjoyment/Command/lambda.py +117 -0
- Pyjoyment/Command/prefork.py +181 -0
- Pyjoyment/Command/prethread.py +126 -0
- Pyjoyment/Command/routes.py +133 -0
- Pyjoyment/Command/stdio.py +92 -0
- Pyjoyment/Command/version.py +141 -0
- Pyjoyment/Command/wsgi.py +93 -0
- Pyjoyment/Commands.py +546 -0
- Pyjoyment/Controller.py +1307 -0
- Pyjoyment/HelloWorld.py +56 -0
- Pyjoyment/Lite.py +494 -0
- Pyjoyment/Plugin/AllowedHosts.py +205 -0
- Pyjoyment/Plugin/Config.py +248 -0
- Pyjoyment/Plugin/DefaultHelpers.py +1202 -0
- Pyjoyment/Plugin/EPLRenderer.py +134 -0
- Pyjoyment/Plugin/EPRenderer.py +212 -0
- Pyjoyment/Plugin/HeaderCondition.py +98 -0
- Pyjoyment/Plugin/Health.py +325 -0
- Pyjoyment/Plugin/JSONConfig.py +172 -0
- Pyjoyment/Plugin/Mount.py +223 -0
- Pyjoyment/Plugin/PyRenderer.py +209 -0
- Pyjoyment/Plugin/TOMLConfig.py +141 -0
- Pyjoyment/Plugin/TagHelpers.py +959 -0
- Pyjoyment/Plugin/YAMLConfig.py +178 -0
- Pyjoyment/Plugin/__init__.py +80 -0
- Pyjoyment/Plugins.py +215 -0
- Pyjoyment/Renderer.py +673 -0
- Pyjoyment/Routes/Match.py +251 -0
- Pyjoyment/Routes/Pattern.py +443 -0
- Pyjoyment/Routes/Route.py +827 -0
- Pyjoyment/Routes/__init__.py +419 -0
- Pyjoyment/Sessions.py +255 -0
- Pyjoyment/Static.py +421 -0
- Pyjoyment/Types.py +238 -0
- Pyjoyment/Validator/Validation.py +395 -0
- Pyjoyment/Validator/__init__.py +304 -0
- Pyjoyment/__init__.py +1083 -0
- Pyjoyment/__main__.py +5 -0
- Pyjoyment/py.typed +0 -0
- Pyjoyment/resources/public/favicon.ico +0 -0
- Pyjoyment/resources/public/favicon.svg +345 -0
- Pyjoyment/resources/public/pyjo/bootstrap/LICENSE +21 -0
- Pyjoyment/resources/public/pyjo/bootstrap/bootstrap.bundle.min.js.map +1 -0
- Pyjoyment/resources/public/pyjo/bootstrap/bootstrap.css +6 -0
- Pyjoyment/resources/public/pyjo/bootstrap/bootstrap.js +7 -0
- Pyjoyment/resources/public/pyjo/bootstrap/bootstrap.min.css.map +1 -0
- Pyjoyment/resources/public/pyjo/failunicat.png +0 -0
- Pyjoyment/resources/public/pyjo/failunicat.svg +50 -0
- Pyjoyment/resources/public/pyjo/highlight.js/LICENSE +29 -0
- Pyjoyment/resources/public/pyjo/highlight.js/highlight-mojo-dark.css +56 -0
- Pyjoyment/resources/public/pyjo/highlight.js/highlight.min.js +1262 -0
- Pyjoyment/resources/public/pyjo/highlight.js/pyjoyment.min.js +1 -0
- Pyjoyment/resources/public/pyjo/logo-black-4x.png +0 -0
- Pyjoyment/resources/public/pyjo/logo-black-4x.svg +346 -0
- Pyjoyment/resources/public/pyjo/logo-white-2x.png +0 -0
- Pyjoyment/resources/public/pyjo/logo-white-2x.svg +346 -0
- Pyjoyment/resources/public/pyjo/logo-white-4x.png +0 -0
- Pyjoyment/resources/public/pyjo/logo-white-4x.svg +346 -0
- Pyjoyment/resources/public/pyjo/logo-white.png +0 -0
- Pyjoyment/resources/public/pyjo/logo-white.svg +346 -0
- Pyjoyment/resources/public/pyjo/logo.png +0 -0
- Pyjoyment/resources/public/pyjo/logo.svg +346 -0
- Pyjoyment/resources/public/pyjo/notfound.png +0 -0
- Pyjoyment/resources/public/pyjo/notfound.svg +3 -0
- Pyjoyment/resources/public/pyjo/nounicat.png +0 -0
- Pyjoyment/resources/public/pyjo/nounicat.svg +5 -0
- Pyjoyment/resources/public/pyjo/pinstripe-dark.png +0 -0
- Pyjoyment/resources/public/pyjo/pinstripe-dark.svg +4 -0
- Pyjoyment/resources/public/pyjo/pinstripe-light.png +0 -0
- Pyjoyment/resources/public/pyjo/pinstripe-light.svg +4 -0
- Pyjoyment/resources/public/pyjo/pyjo.css +204 -0
- Pyjoyment/resources/templates/pyjo/debug.html.ep +346 -0
- Pyjoyment/resources/templates/pyjo/exception.html.ep +32 -0
- Pyjoyment/resources/templates/pyjo/not_found.html.ep +45 -0
- pyjoyment-0.1.0.dist-info/METADATA +376 -0
- pyjoyment-0.1.0.dist-info/RECORD +200 -0
- pyjoyment-0.1.0.dist-info/WHEEL +4 -0
- pyjoyment-0.1.0.dist-info/entry_points.txt +8 -0
- pyjoyment-0.1.0.dist-info/licenses/LICENSE +201 -0
Pyjo/Headers.py
ADDED
|
@@ -0,0 +1,760 @@
|
|
|
1
|
+
r"""
|
|
2
|
+
Pyjo.Headers - HTTP headers
|
|
3
|
+
===========================
|
|
4
|
+
::
|
|
5
|
+
|
|
6
|
+
from Pyjo.Headers import Headers
|
|
7
|
+
|
|
8
|
+
# Parse
|
|
9
|
+
headers = Headers()
|
|
10
|
+
headers.parse('Content-Length: 42\x0d\x0a')
|
|
11
|
+
headers.parse('Content-Type: text/html\x0d\x0a\x0d\x0a')
|
|
12
|
+
print(headers.content_length)
|
|
13
|
+
print(headers.content_type)
|
|
14
|
+
|
|
15
|
+
# Or import the module, as in Google Python Style Guide
|
|
16
|
+
|
|
17
|
+
import Pyjo.Headers
|
|
18
|
+
|
|
19
|
+
# Parse
|
|
20
|
+
headers = Pyjo.Headers.new()
|
|
21
|
+
headers.parse('Content-Length: 42\x0d\x0a')
|
|
22
|
+
headers.parse('Content-Type: text/html\x0d\x0a\x0d\x0a')
|
|
23
|
+
print(headers.content_length)
|
|
24
|
+
print(headers.content_type)
|
|
25
|
+
|
|
26
|
+
# Build
|
|
27
|
+
headers = Pyjo.Headers.new()
|
|
28
|
+
headers.content_length = 42
|
|
29
|
+
headers.content_type = 'text/plain'
|
|
30
|
+
print(headers.to_str())
|
|
31
|
+
|
|
32
|
+
:mod:`Pyjo.Headers` is a container for HTTP headers, based on :rfc:`7230` and
|
|
33
|
+
:rfc:`7231`.
|
|
34
|
+
|
|
35
|
+
The values are strings; bytes from the wire are decoded as ISO-8859-1, as
|
|
36
|
+
:mod:`http.client` does.
|
|
37
|
+
|
|
38
|
+
The headers also work with the Python standard library: they take an
|
|
39
|
+
:class:`email.message.Message`, such as :class:`http.client.HTTPMessage`, a
|
|
40
|
+
``dict`` or a list of pairs, they are a mapping of the names to the values
|
|
41
|
+
without regard to case, and they turn into an :class:`email.message.Message`
|
|
42
|
+
with :meth:`to_message` or into a list of pairs with :meth:`to_list`. ::
|
|
43
|
+
|
|
44
|
+
# "text/html"
|
|
45
|
+
Pyjo.Headers.new({'Content-Type': 'text/html'})['content-type']
|
|
46
|
+
|
|
47
|
+
# [('Vary', 'Accept'), ('Vary', 'Accept-Encoding')]
|
|
48
|
+
Pyjo.Headers.new([('Vary', 'Accept'), ('Vary', 'Accept-Encoding')]).to_list()
|
|
49
|
+
|
|
50
|
+
See also
|
|
51
|
+
--------
|
|
52
|
+
|
|
53
|
+
:mod:`Pyjoyment`, https://mojolicious.org.
|
|
54
|
+
|
|
55
|
+
Ported from `Mojo::Headers <https://github.com/mojolicious/mojo/blob/v9.52/lib/Mojo/Headers.pm>`_
|
|
56
|
+
of Mojolicious 9.52.
|
|
57
|
+
|
|
58
|
+
Classes
|
|
59
|
+
-------
|
|
60
|
+
"""
|
|
61
|
+
|
|
62
|
+
import Pyjo.Base
|
|
63
|
+
import Pyjo.String.Mixin
|
|
64
|
+
|
|
65
|
+
from Pyjo.Util import header_params
|
|
66
|
+
|
|
67
|
+
import builtins
|
|
68
|
+
import os
|
|
69
|
+
import re
|
|
70
|
+
|
|
71
|
+
from collections.abc import Iterator
|
|
72
|
+
from email.message import Message
|
|
73
|
+
from typing import Self, cast, overload
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
# The module ends with `object = Pyjo_Headers`, which an annotation of
|
|
77
|
+
# `object` would mean, as annotations are evaluated lazily
|
|
78
|
+
type Anything = builtins.object
|
|
79
|
+
|
|
80
|
+
# Common headers
|
|
81
|
+
NAMES = {
|
|
82
|
+
name.lower(): name
|
|
83
|
+
for name in (
|
|
84
|
+
'Accept Accept-Charset Accept-Encoding Accept-Language Accept-Ranges Access-Control-Allow-Origin Allow '
|
|
85
|
+
'Authorization Cache-Control Connection Content-Disposition Content-Encoding Content-Language Content-Length '
|
|
86
|
+
'Content-Location Content-Range Content-Security-Policy Content-Type Cookie DNT Date ETag Expect Expires Host '
|
|
87
|
+
'If-Modified-Since If-None-Match Last-Modified Link Location Origin Proxy-Authenticate Proxy-Authorization '
|
|
88
|
+
'Range Sec-WebSocket-Accept Sec-WebSocket-Extensions Sec-WebSocket-Key Sec-WebSocket-Protocol '
|
|
89
|
+
'Sec-WebSocket-Version Server Server-Timing Set-Cookie Status Strict-Transport-Security TE Trailer '
|
|
90
|
+
'Transfer-Encoding Upgrade User-Agent Vary WWW-Authenticate'
|
|
91
|
+
).split()
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
# Hop-by-hop headers
|
|
95
|
+
HOP_BY_HOP = [
|
|
96
|
+
name.lower()
|
|
97
|
+
for name in (
|
|
98
|
+
'Connection',
|
|
99
|
+
'Keep-Alive',
|
|
100
|
+
'Proxy-Authenticate',
|
|
101
|
+
'Proxy-Authorization',
|
|
102
|
+
'TE',
|
|
103
|
+
'Trailer',
|
|
104
|
+
'Transfer-Encoding',
|
|
105
|
+
'Upgrade',
|
|
106
|
+
)
|
|
107
|
+
]
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
class _Shortcut:
|
|
111
|
+
# A property for a common header, as Mojo makes them with monkey_patch
|
|
112
|
+
def __init__(self, name: str, example: str, about: str = '') -> None:
|
|
113
|
+
attr = name.lower().replace('-', '_')
|
|
114
|
+
self.name = name
|
|
115
|
+
self.__doc__ = f"""::
|
|
116
|
+
|
|
117
|
+
{attr} = headers.{attr}
|
|
118
|
+
headers.{attr} = {example}
|
|
119
|
+
|
|
120
|
+
Get or replace current header value, shortcut for the ``{name}`` header{about}.
|
|
121
|
+
"""
|
|
122
|
+
|
|
123
|
+
@overload
|
|
124
|
+
def __get__(self, headers: None, owner: type | None = None) -> Self: ...
|
|
125
|
+
|
|
126
|
+
@overload
|
|
127
|
+
def __get__(self, headers: 'Pyjo_Headers', owner: type | None = None) -> str | None: ...
|
|
128
|
+
|
|
129
|
+
def __get__(self, headers: 'Pyjo_Headers | None', owner: type | None = None) -> Self | str | None:
|
|
130
|
+
return self if headers is None else headers.header(self.name)
|
|
131
|
+
|
|
132
|
+
def __set__(self, headers: 'Pyjo_Headers', value: Anything) -> None:
|
|
133
|
+
# Unlike Mojolicious: the value is checked for invalid characters, as add
|
|
134
|
+
# does; Mojo sets it directly
|
|
135
|
+
headers.header(self.name, value)
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
class Pyjo_Headers(Pyjo.Base.object, Pyjo.String.Mixin.object):
|
|
139
|
+
"""
|
|
140
|
+
:mod:`Pyjo.Headers` inherits all methods from :mod:`Pyjo.Base` and
|
|
141
|
+
:mod:`Pyjo.String.Mixin` and implements the following new ones.
|
|
142
|
+
"""
|
|
143
|
+
|
|
144
|
+
def __init__(
|
|
145
|
+
self,
|
|
146
|
+
headers: 'Message | dict[str, Anything] | list[tuple[str, Anything]] | Pyjo_Headers | None' = None,
|
|
147
|
+
*,
|
|
148
|
+
max_line_size: int | None = None,
|
|
149
|
+
max_lines: int | None = None,
|
|
150
|
+
) -> None:
|
|
151
|
+
"""::
|
|
152
|
+
|
|
153
|
+
headers = Pyjo.Headers.new()
|
|
154
|
+
headers = Pyjo.Headers.new({'Content-Type': 'text/html'})
|
|
155
|
+
headers = Pyjo.Headers.new([('Vary', 'Accept'), ('Vary', 'Accept-Encoding')])
|
|
156
|
+
headers = Pyjo.Headers.new(http_response.headers)
|
|
157
|
+
|
|
158
|
+
Construct a new :mod:`Pyjo.Headers` object, from headers of the standard
|
|
159
|
+
library if they are given: an :class:`email.message.Message`, such as
|
|
160
|
+
:class:`http.client.HTTPMessage`, a ``dict``, as :meth:`from_dict` takes,
|
|
161
|
+
or a list of pairs.
|
|
162
|
+
"""
|
|
163
|
+
self._max_line_size: int | None = max_line_size or None
|
|
164
|
+
self._max_lines: int | None = max_lines or None
|
|
165
|
+
|
|
166
|
+
self._headers: dict[str, list[str]] = {}
|
|
167
|
+
self._names: dict[str, str] = {}
|
|
168
|
+
self._buffer = bytearray()
|
|
169
|
+
self._cache: list[list[str]] = []
|
|
170
|
+
self._state: str | None = None
|
|
171
|
+
self._limit = False
|
|
172
|
+
|
|
173
|
+
# Not in Mojolicious: headers of the standard library
|
|
174
|
+
if isinstance(headers, Message | Pyjo_Headers | list):
|
|
175
|
+
pairs = (
|
|
176
|
+
headers.items()
|
|
177
|
+
if isinstance(headers, Message)
|
|
178
|
+
else headers.to_list()
|
|
179
|
+
if isinstance(headers, Pyjo_Headers)
|
|
180
|
+
else headers
|
|
181
|
+
)
|
|
182
|
+
for name, value in pairs:
|
|
183
|
+
self.add(name, value)
|
|
184
|
+
elif isinstance(headers, dict):
|
|
185
|
+
self.from_dict(headers)
|
|
186
|
+
|
|
187
|
+
@property
|
|
188
|
+
def max_line_size(self) -> int:
|
|
189
|
+
"""::
|
|
190
|
+
|
|
191
|
+
size = headers.max_line_size
|
|
192
|
+
headers.max_line_size = 1024
|
|
193
|
+
|
|
194
|
+
Maximum header line size in bytes, defaults to the value of the
|
|
195
|
+
``PYJO_MAX_LINE_SIZE`` environment variable or ``65536`` (64KiB).
|
|
196
|
+
"""
|
|
197
|
+
if self._max_line_size is None:
|
|
198
|
+
# Unlike Mojolicious: 64KiB, as _MAXLINE of http.client, where Mojo has 8KiB, as a header of a response, as
|
|
199
|
+
# Content-Security-Policy, can be longer
|
|
200
|
+
self._max_line_size = int(os.environ.get('PYJO_MAX_LINE_SIZE') or 65536)
|
|
201
|
+
return self._max_line_size
|
|
202
|
+
|
|
203
|
+
@max_line_size.setter
|
|
204
|
+
def max_line_size(self, value: int) -> None:
|
|
205
|
+
self._max_line_size = value
|
|
206
|
+
|
|
207
|
+
@property
|
|
208
|
+
def max_lines(self) -> int:
|
|
209
|
+
"""::
|
|
210
|
+
|
|
211
|
+
num = headers.max_lines
|
|
212
|
+
headers.max_lines = 200
|
|
213
|
+
|
|
214
|
+
Maximum number of header lines, defaults to the value of the
|
|
215
|
+
``PYJO_MAX_LINES`` environment variable or ``100``.
|
|
216
|
+
"""
|
|
217
|
+
if self._max_lines is None:
|
|
218
|
+
self._max_lines = int(os.environ.get('PYJO_MAX_LINES') or 100)
|
|
219
|
+
return self._max_lines
|
|
220
|
+
|
|
221
|
+
@max_lines.setter
|
|
222
|
+
def max_lines(self, value: int) -> None:
|
|
223
|
+
self._max_lines = value
|
|
224
|
+
|
|
225
|
+
def __bool__(self) -> bool:
|
|
226
|
+
"""::
|
|
227
|
+
|
|
228
|
+
boolean = bool(headers)
|
|
229
|
+
|
|
230
|
+
Always true, also without headers.
|
|
231
|
+
"""
|
|
232
|
+
return True
|
|
233
|
+
|
|
234
|
+
# Not in Mojolicious: the mapping protocol of the standard library
|
|
235
|
+
def __contains__(self, name: Anything) -> bool:
|
|
236
|
+
"""::
|
|
237
|
+
|
|
238
|
+
boolean = 'Content-Type' in headers
|
|
239
|
+
|
|
240
|
+
Check if a header exists, without regard to case.
|
|
241
|
+
"""
|
|
242
|
+
return isinstance(name, str) and name.lower() in self._headers
|
|
243
|
+
|
|
244
|
+
# Not in Mojolicious: the mapping protocol of the standard library
|
|
245
|
+
def __getitem__(self, name: str) -> str:
|
|
246
|
+
"""::
|
|
247
|
+
|
|
248
|
+
value = headers['Content-Type']
|
|
249
|
+
|
|
250
|
+
Same as :meth:`header`, but raises :class:`KeyError` for a header that does
|
|
251
|
+
not exist.
|
|
252
|
+
"""
|
|
253
|
+
if (value := self.header(name)) is None:
|
|
254
|
+
raise KeyError(name)
|
|
255
|
+
return value
|
|
256
|
+
|
|
257
|
+
# Not in Mojolicious: the mapping protocol of the standard library
|
|
258
|
+
def __iter__(self) -> Iterator[str]:
|
|
259
|
+
"""::
|
|
260
|
+
|
|
261
|
+
names = list(headers)
|
|
262
|
+
|
|
263
|
+
Iterate over :meth:`names`.
|
|
264
|
+
"""
|
|
265
|
+
return iter(self.names())
|
|
266
|
+
|
|
267
|
+
# Not in Mojolicious: the mapping protocol of the standard library
|
|
268
|
+
def __len__(self) -> int:
|
|
269
|
+
"""::
|
|
270
|
+
|
|
271
|
+
size = len(headers)
|
|
272
|
+
|
|
273
|
+
Number of headers, with the values of a header counting once.
|
|
274
|
+
"""
|
|
275
|
+
return len(self._headers)
|
|
276
|
+
|
|
277
|
+
def __str__(self) -> str:
|
|
278
|
+
"""::
|
|
279
|
+
|
|
280
|
+
string = str(headers)
|
|
281
|
+
|
|
282
|
+
Alias for :meth:`to_str`.
|
|
283
|
+
"""
|
|
284
|
+
return self.to_str()
|
|
285
|
+
|
|
286
|
+
accept = _Shortcut('Accept', "'*/*'")
|
|
287
|
+
accept_charset = _Shortcut('Accept-Charset', "'UTF-8'")
|
|
288
|
+
accept_encoding = _Shortcut('Accept-Encoding', "'gzip'")
|
|
289
|
+
accept_language = _Shortcut('Accept-Language', "'de, en'")
|
|
290
|
+
accept_ranges = _Shortcut('Accept-Ranges', "'bytes'")
|
|
291
|
+
access_control_allow_origin = _Shortcut(
|
|
292
|
+
'Access-Control-Allow-Origin',
|
|
293
|
+
"'*'",
|
|
294
|
+
' from `Cross-Origin Resource Sharing <https://www.w3.org/TR/cors/>`_',
|
|
295
|
+
)
|
|
296
|
+
|
|
297
|
+
def add(self, name: str, *values: Anything) -> Self:
|
|
298
|
+
"""::
|
|
299
|
+
|
|
300
|
+
headers = headers.add('Foo', 'one value')
|
|
301
|
+
headers = headers.add('Foo', 'first value', 'second value')
|
|
302
|
+
|
|
303
|
+
Add header with one or more lines. ::
|
|
304
|
+
|
|
305
|
+
# "Vary: Accept
|
|
306
|
+
# Vary: Accept-Encoding"
|
|
307
|
+
headers.add('Vary', 'Accept').add('Vary', 'Accept-Encoding').to_str()
|
|
308
|
+
"""
|
|
309
|
+
# Not in Mojolicious: a list adds its values
|
|
310
|
+
strings = [
|
|
311
|
+
str(v)
|
|
312
|
+
for value in values
|
|
313
|
+
for v in (cast(list[Anything], value) if isinstance(value, (list, tuple)) else [value])
|
|
314
|
+
]
|
|
315
|
+
for value in strings:
|
|
316
|
+
if '\x0d' in value or '\x0a' in value:
|
|
317
|
+
raise ValueError(f'Invalid characters in {name} header')
|
|
318
|
+
|
|
319
|
+
# Make sure we have a normal case entry for name
|
|
320
|
+
key = name.lower()
|
|
321
|
+
if key not in NAMES:
|
|
322
|
+
self._names.setdefault(key, name)
|
|
323
|
+
self._headers.setdefault(key, []).extend(strings)
|
|
324
|
+
|
|
325
|
+
return self
|
|
326
|
+
|
|
327
|
+
allow = _Shortcut('Allow', "'GET, POST'")
|
|
328
|
+
|
|
329
|
+
def append(self, name: str, value: Anything) -> Self:
|
|
330
|
+
"""::
|
|
331
|
+
|
|
332
|
+
headers = headers.append('Vary', 'Accept-Encoding')
|
|
333
|
+
|
|
334
|
+
Append value to header and flatten it if necessary. ::
|
|
335
|
+
|
|
336
|
+
# "Vary: Accept"
|
|
337
|
+
headers.append('Vary', 'Accept').to_str()
|
|
338
|
+
|
|
339
|
+
# "Vary: Accept, Accept-Encoding"
|
|
340
|
+
headers.set(vary='Accept').append('Vary', 'Accept-Encoding').to_str()
|
|
341
|
+
"""
|
|
342
|
+
old = self.header(name)
|
|
343
|
+
return self.header(name, f'{old}, {value}' if old is not None else value)
|
|
344
|
+
|
|
345
|
+
authorization = _Shortcut('Authorization', "'Basic Zm9vOmJhcg=='")
|
|
346
|
+
cache_control = _Shortcut('Cache-Control', "'max-age=1, no-cache'")
|
|
347
|
+
|
|
348
|
+
def clone(self) -> Self:
|
|
349
|
+
"""::
|
|
350
|
+
|
|
351
|
+
clone = headers.clone()
|
|
352
|
+
|
|
353
|
+
Return a new :mod:`Pyjo.Headers` object cloned from these headers.
|
|
354
|
+
"""
|
|
355
|
+
clone = self.new()
|
|
356
|
+
clone._names = dict(self._names)
|
|
357
|
+
clone._headers = {key: list(values) for key, values in self._headers.items()}
|
|
358
|
+
return clone
|
|
359
|
+
|
|
360
|
+
connection = _Shortcut('Connection', "'close'")
|
|
361
|
+
content_disposition = _Shortcut('Content-Disposition', "'foo'")
|
|
362
|
+
content_encoding = _Shortcut('Content-Encoding', "'gzip'")
|
|
363
|
+
content_language = _Shortcut('Content-Language', "'en'")
|
|
364
|
+
content_length = _Shortcut('Content-Length', '4000')
|
|
365
|
+
content_location = _Shortcut('Content-Location', "'http://127.0.0.1/foo'")
|
|
366
|
+
content_range = _Shortcut('Content-Range', "'bytes 2-8/100'")
|
|
367
|
+
content_security_policy = _Shortcut(
|
|
368
|
+
'Content-Security-Policy',
|
|
369
|
+
"'default-src https:'",
|
|
370
|
+
' from `Content Security Policy 1.0 <https://www.w3.org/TR/CSP/>`_',
|
|
371
|
+
)
|
|
372
|
+
content_type = _Shortcut('Content-Type', "'text/plain'")
|
|
373
|
+
cookie = _Shortcut('Cookie', "'f=b'", ' from :rfc:`6265`')
|
|
374
|
+
date = _Shortcut('Date', "'Sun, 17 Aug 2008 16:27:35 GMT'")
|
|
375
|
+
|
|
376
|
+
def dehop(self) -> Self:
|
|
377
|
+
"""::
|
|
378
|
+
|
|
379
|
+
headers = headers.dehop()
|
|
380
|
+
|
|
381
|
+
Remove hop-by-hop headers that should not be retransmitted.
|
|
382
|
+
"""
|
|
383
|
+
for key in HOP_BY_HOP:
|
|
384
|
+
self._headers.pop(key, None)
|
|
385
|
+
return self
|
|
386
|
+
|
|
387
|
+
dnt = _Shortcut(
|
|
388
|
+
'DNT',
|
|
389
|
+
'1',
|
|
390
|
+
' (Do Not Track), which has no specification yet, but is very commonly used',
|
|
391
|
+
)
|
|
392
|
+
etag = _Shortcut('ETag', '\'"abc321"\'')
|
|
393
|
+
|
|
394
|
+
def every_header(self, name: str) -> list[str]:
|
|
395
|
+
"""::
|
|
396
|
+
|
|
397
|
+
all = headers.every_header('Location')
|
|
398
|
+
|
|
399
|
+
Similar to :meth:`header`, but returns all headers sharing the same name as a
|
|
400
|
+
list. ::
|
|
401
|
+
|
|
402
|
+
# Get first header value
|
|
403
|
+
print(headers.every_header('Location')[0])
|
|
404
|
+
"""
|
|
405
|
+
return self._headers.get(name.lower(), [])
|
|
406
|
+
|
|
407
|
+
expect = _Shortcut('Expect', "'100-continue'")
|
|
408
|
+
expires = _Shortcut('Expires', "'Thu, 01 Dec 1994 16:00:00 GMT'")
|
|
409
|
+
|
|
410
|
+
def from_dict(self, d: dict[str, Anything]) -> Self:
|
|
411
|
+
"""::
|
|
412
|
+
|
|
413
|
+
headers = headers.from_dict({'Cookie': 'a=b'})
|
|
414
|
+
headers = headers.from_dict({'Cookie': ['a=b', 'c=d']})
|
|
415
|
+
headers = headers.from_dict({})
|
|
416
|
+
|
|
417
|
+
Parse headers from a ``dict``, an empty one removes all headers.
|
|
418
|
+
"""
|
|
419
|
+
# Empty dict deletes all headers
|
|
420
|
+
if not d:
|
|
421
|
+
self._headers = {}
|
|
422
|
+
|
|
423
|
+
# Merge
|
|
424
|
+
for header, value in d.items():
|
|
425
|
+
self.add(header, value)
|
|
426
|
+
|
|
427
|
+
return self
|
|
428
|
+
|
|
429
|
+
# Not in Mojolicious: the mapping protocol of the standard library
|
|
430
|
+
def get(self, name: str, default: str | None = None) -> str | None:
|
|
431
|
+
"""::
|
|
432
|
+
|
|
433
|
+
value = headers.get('Content-Type')
|
|
434
|
+
value = headers.get('Content-Type', 'text/plain')
|
|
435
|
+
|
|
436
|
+
Same as :meth:`header`, but returns the default for a header that does not
|
|
437
|
+
exist.
|
|
438
|
+
"""
|
|
439
|
+
value = self.header(name)
|
|
440
|
+
return value if value is not None else default
|
|
441
|
+
|
|
442
|
+
@overload
|
|
443
|
+
def header(self, name: str, /) -> str | None: ...
|
|
444
|
+
|
|
445
|
+
@overload
|
|
446
|
+
def header(self, name: str, value: Anything, /, *values: Anything) -> Self: ...
|
|
447
|
+
|
|
448
|
+
def header(self, name: str, /, *values: Anything) -> Self | str | None:
|
|
449
|
+
"""::
|
|
450
|
+
|
|
451
|
+
value = headers.header('Foo')
|
|
452
|
+
headers = headers.header('Foo', 'one value')
|
|
453
|
+
headers = headers.header('Foo', 'first value', 'second value')
|
|
454
|
+
|
|
455
|
+
Get or replace the current header values.
|
|
456
|
+
"""
|
|
457
|
+
# Replace
|
|
458
|
+
if values:
|
|
459
|
+
return self.remove(name).add(name, *values)
|
|
460
|
+
|
|
461
|
+
if (headers := self._headers.get(name.lower())) is None:
|
|
462
|
+
return None
|
|
463
|
+
return ', '.join(headers)
|
|
464
|
+
|
|
465
|
+
host = _Shortcut('Host', "'127.0.0.1'")
|
|
466
|
+
if_modified_since = _Shortcut('If-Modified-Since', "'Sun, 17 Aug 2008 16:27:35 GMT'")
|
|
467
|
+
if_none_match = _Shortcut('If-None-Match', '\'"abc321"\'')
|
|
468
|
+
|
|
469
|
+
@property
|
|
470
|
+
def is_finished(self) -> bool:
|
|
471
|
+
"""::
|
|
472
|
+
|
|
473
|
+
boolean = headers.is_finished
|
|
474
|
+
|
|
475
|
+
Check if header parser is finished.
|
|
476
|
+
"""
|
|
477
|
+
return self._state == 'finished'
|
|
478
|
+
|
|
479
|
+
@property
|
|
480
|
+
def is_limit_exceeded(self) -> bool:
|
|
481
|
+
"""::
|
|
482
|
+
|
|
483
|
+
boolean = headers.is_limit_exceeded
|
|
484
|
+
|
|
485
|
+
Check if headers have exceeded :attr:`max_line_size` or :attr:`max_lines`.
|
|
486
|
+
"""
|
|
487
|
+
return self._limit
|
|
488
|
+
|
|
489
|
+
# Not in Mojolicious: the mapping protocol of the standard library
|
|
490
|
+
def items(self) -> list[tuple[str, str]]:
|
|
491
|
+
"""::
|
|
492
|
+
|
|
493
|
+
items = headers.items()
|
|
494
|
+
|
|
495
|
+
Return a list of :meth:`names` with the values that :meth:`header` gives.
|
|
496
|
+
"""
|
|
497
|
+
return [(name, ', '.join(self._headers[name.lower()])) for name in self.names()]
|
|
498
|
+
|
|
499
|
+
# Not in Mojolicious: the mapping protocol of the standard library
|
|
500
|
+
def keys(self) -> list[str]:
|
|
501
|
+
"""::
|
|
502
|
+
|
|
503
|
+
names = headers.keys()
|
|
504
|
+
|
|
505
|
+
Alias for :meth:`names`.
|
|
506
|
+
"""
|
|
507
|
+
return self.names()
|
|
508
|
+
|
|
509
|
+
last_modified = _Shortcut('Last-Modified', "'Sun, 17 Aug 2008 16:27:35 GMT'")
|
|
510
|
+
|
|
511
|
+
def leftovers(self) -> bytes:
|
|
512
|
+
"""::
|
|
513
|
+
|
|
514
|
+
data = headers.leftovers()
|
|
515
|
+
|
|
516
|
+
Get and remove leftover data from header parser.
|
|
517
|
+
"""
|
|
518
|
+
buffer, self._buffer = bytes(self._buffer), bytearray()
|
|
519
|
+
return buffer
|
|
520
|
+
|
|
521
|
+
link = _Shortcut('Link', '\'<http://127.0.0.1/foo/3>; rel="next"\'', ' from :rfc:`5988`')
|
|
522
|
+
|
|
523
|
+
@property
|
|
524
|
+
def links(self) -> dict[str, dict[str, str]]:
|
|
525
|
+
"""::
|
|
526
|
+
|
|
527
|
+
links = headers.links
|
|
528
|
+
headers.links = {'next': 'http://example.com/foo', 'prev': 'http://example.com/bar'}
|
|
529
|
+
|
|
530
|
+
Get or set web links from or to ``Link`` header according to :rfc:`5988`. ::
|
|
531
|
+
|
|
532
|
+
# Extract information about next page
|
|
533
|
+
print(headers.links['next']['link'])
|
|
534
|
+
print(headers.links['next']['title'])
|
|
535
|
+
"""
|
|
536
|
+
header = self.link or ''
|
|
537
|
+
data: dict[str, dict[str, str]] = {}
|
|
538
|
+
while m := re.match(r'[,\s]*<(.+?)>', header):
|
|
539
|
+
target = m[1]
|
|
540
|
+
params, header = header_params(header[m.end() :])
|
|
541
|
+
if (rel := params.get('rel')) is not None:
|
|
542
|
+
data.setdefault(rel, {**params, 'link': target})
|
|
543
|
+
|
|
544
|
+
return data
|
|
545
|
+
|
|
546
|
+
@links.setter
|
|
547
|
+
def links(self, links: dict[str, str]) -> None:
|
|
548
|
+
self.link = ', '.join(f'<{links[rel]}>; rel="{rel}"' for rel in sorted(links))
|
|
549
|
+
|
|
550
|
+
location = _Shortcut('Location', "'http://127.0.0.1/foo'")
|
|
551
|
+
|
|
552
|
+
def names(self) -> list[str]:
|
|
553
|
+
"""::
|
|
554
|
+
|
|
555
|
+
names = headers.names()
|
|
556
|
+
|
|
557
|
+
Return a list with all currently defined headers. ::
|
|
558
|
+
|
|
559
|
+
# Names of all headers
|
|
560
|
+
for name in headers.names():
|
|
561
|
+
print(name)
|
|
562
|
+
"""
|
|
563
|
+
return [NAMES.get(key) or self._names[key] for key in sorted(self._headers)]
|
|
564
|
+
|
|
565
|
+
origin = _Shortcut('Origin', "'http://example.com'", ' from :rfc:`6454`')
|
|
566
|
+
|
|
567
|
+
def parse(self, chunk: str | bytes | bytearray) -> Self:
|
|
568
|
+
r"""::
|
|
569
|
+
|
|
570
|
+
headers = headers.parse('Content-Type: text/plain\x0d\x0a\x0d\x0a')
|
|
571
|
+
|
|
572
|
+
Parse formatted headers, given as characters or as bytes.
|
|
573
|
+
"""
|
|
574
|
+
self._state = 'headers'
|
|
575
|
+
# Not in Mojolicious: characters, as ISO-8859-1
|
|
576
|
+
self._buffer += chunk if isinstance(chunk, (bytes, bytearray)) else chunk.encode('latin-1')
|
|
577
|
+
headers = self._cache
|
|
578
|
+
size = self.max_line_size
|
|
579
|
+
lines = self.max_lines
|
|
580
|
+
while (length := self._buffer.find(b'\x0a') + 1) > 0:
|
|
581
|
+
raw = self._buffer[: length - 1]
|
|
582
|
+
del self._buffer[:length]
|
|
583
|
+
line = (raw[:-1] if raw.endswith(b'\x0d') else raw).decode('latin-1')
|
|
584
|
+
|
|
585
|
+
# Check line size limit
|
|
586
|
+
if length > size or len(headers) >= lines:
|
|
587
|
+
self._state, self._limit = 'finished', True
|
|
588
|
+
return self
|
|
589
|
+
|
|
590
|
+
# New header
|
|
591
|
+
if header := re.match(r'^(\S[^:]*):\s*(.*)$', line):
|
|
592
|
+
headers.append([header[1], header[2]])
|
|
593
|
+
|
|
594
|
+
# Multi-line
|
|
595
|
+
elif (space := re.match(r'\s+', line)) and headers:
|
|
596
|
+
headers[-1][1] += ' ' + line[space.end() :]
|
|
597
|
+
|
|
598
|
+
# Empty line
|
|
599
|
+
else:
|
|
600
|
+
for name, value in headers:
|
|
601
|
+
self.add(name, value)
|
|
602
|
+
self._state, self._cache = 'finished', []
|
|
603
|
+
return self
|
|
604
|
+
|
|
605
|
+
# Check line size limit
|
|
606
|
+
if len(self._buffer) > size:
|
|
607
|
+
self._state, self._limit = 'finished', True
|
|
608
|
+
|
|
609
|
+
return self
|
|
610
|
+
|
|
611
|
+
proxy_authenticate = _Shortcut('Proxy-Authenticate', '\'Basic "realm"\'')
|
|
612
|
+
proxy_authorization = _Shortcut('Proxy-Authorization', "'Basic Zm9vOmJhcg=='")
|
|
613
|
+
range = _Shortcut('Range', "'bytes=2-8'")
|
|
614
|
+
|
|
615
|
+
@property
|
|
616
|
+
def referer(self) -> str | None:
|
|
617
|
+
"""::
|
|
618
|
+
|
|
619
|
+
referrer = headers.referer
|
|
620
|
+
headers.referer = 'http://example.com'
|
|
621
|
+
|
|
622
|
+
Alias for :attr:`referrer`.
|
|
623
|
+
"""
|
|
624
|
+
return self.referrer
|
|
625
|
+
|
|
626
|
+
@referer.setter
|
|
627
|
+
def referer(self, value: Anything) -> None:
|
|
628
|
+
self.referrer = value
|
|
629
|
+
|
|
630
|
+
@property
|
|
631
|
+
def referrer(self) -> str | None:
|
|
632
|
+
"""::
|
|
633
|
+
|
|
634
|
+
referrer = headers.referrer
|
|
635
|
+
headers.referrer = 'http://example.com'
|
|
636
|
+
|
|
637
|
+
Get or replace current header value, shortcut for the ``Referer`` header,
|
|
638
|
+
there was a typo in :rfc:`2068` which resulted in ``Referer`` becoming an
|
|
639
|
+
official header.
|
|
640
|
+
"""
|
|
641
|
+
return self.header('Referer')
|
|
642
|
+
|
|
643
|
+
@referrer.setter
|
|
644
|
+
def referrer(self, value: Anything) -> None:
|
|
645
|
+
self.header('Referer', value)
|
|
646
|
+
|
|
647
|
+
def remove(self, name: str) -> Self:
|
|
648
|
+
"""::
|
|
649
|
+
|
|
650
|
+
headers = headers.remove('Foo')
|
|
651
|
+
|
|
652
|
+
Remove a header.
|
|
653
|
+
"""
|
|
654
|
+
self._headers.pop(name.lower(), None)
|
|
655
|
+
return self
|
|
656
|
+
|
|
657
|
+
sec_websocket_accept = _Shortcut(
|
|
658
|
+
'Sec-WebSocket-Accept',
|
|
659
|
+
"'s3pPLMBiTxaQ9kYGzzhZRbK+xOo='",
|
|
660
|
+
' from :rfc:`6455`',
|
|
661
|
+
)
|
|
662
|
+
sec_websocket_extensions = _Shortcut('Sec-WebSocket-Extensions', "'foo'", ' from :rfc:`6455`')
|
|
663
|
+
sec_websocket_key = _Shortcut('Sec-WebSocket-Key', "'dGhlIHNhbXBsZSBub25jZQ=='", ' from :rfc:`6455`')
|
|
664
|
+
sec_websocket_protocol = _Shortcut('Sec-WebSocket-Protocol', "'sample'", ' from :rfc:`6455`')
|
|
665
|
+
sec_websocket_version = _Shortcut('Sec-WebSocket-Version', '13', ' from :rfc:`6455`')
|
|
666
|
+
server = _Shortcut('Server', "'Pyjoyment'")
|
|
667
|
+
server_timing = _Shortcut(
|
|
668
|
+
'Server-Timing',
|
|
669
|
+
"'app;desc=Pyjoyment;dur=0.0001'",
|
|
670
|
+
' from `Server Timing <https://www.w3.org/TR/server-timing/>`_',
|
|
671
|
+
)
|
|
672
|
+
set_cookie = _Shortcut('Set-Cookie', "'f=b; path=/'", ' from :rfc:`6265`')
|
|
673
|
+
status = _Shortcut('Status', "'200 OK'", ' from :rfc:`3875`')
|
|
674
|
+
strict_transport_security = _Shortcut('Strict-Transport-Security', "'max-age=31536000'", ' from :rfc:`6797`')
|
|
675
|
+
te = _Shortcut('TE', "'chunked'")
|
|
676
|
+
|
|
677
|
+
# Not in Mojolicious: bytes for the standard library and the network
|
|
678
|
+
def to_bytes(self) -> bytes:
|
|
679
|
+
"""::
|
|
680
|
+
|
|
681
|
+
data = headers.to_bytes()
|
|
682
|
+
|
|
683
|
+
Turn headers into bytes, suitable for HTTP messages, with the characters
|
|
684
|
+
encoded as ISO-8859-1.
|
|
685
|
+
"""
|
|
686
|
+
return self.to_str().encode('latin-1')
|
|
687
|
+
|
|
688
|
+
def to_dict(self, multi: bool = False) -> dict[str, str] | dict[str, list[str]]:
|
|
689
|
+
"""::
|
|
690
|
+
|
|
691
|
+
single = headers.to_dict()
|
|
692
|
+
multi = headers.to_dict(True)
|
|
693
|
+
|
|
694
|
+
Turn headers into ``dict``, lists to represent multiple headers with the same
|
|
695
|
+
name are disabled by default. ::
|
|
696
|
+
|
|
697
|
+
print(headers.to_dict()['DNT'])
|
|
698
|
+
"""
|
|
699
|
+
if multi:
|
|
700
|
+
return {name: list(self._headers[name.lower()]) for name in self.names()}
|
|
701
|
+
return dict(self.items())
|
|
702
|
+
|
|
703
|
+
# Not in Mojolicious: a list of pairs for the standard library
|
|
704
|
+
def to_list(self) -> list[tuple[str, str]]:
|
|
705
|
+
"""::
|
|
706
|
+
|
|
707
|
+
pairs = headers.to_list()
|
|
708
|
+
|
|
709
|
+
Turn headers into a list of pairs, one for each value, as WSGI and
|
|
710
|
+
:meth:`http.client.HTTPResponse.getheaders` have them.
|
|
711
|
+
"""
|
|
712
|
+
return [(name, value) for name in self.names() for value in self._headers[name.lower()]]
|
|
713
|
+
|
|
714
|
+
# Not in Mojolicious: email.message of the standard library
|
|
715
|
+
def to_message(self) -> Message:
|
|
716
|
+
"""::
|
|
717
|
+
|
|
718
|
+
message = headers.to_message()
|
|
719
|
+
|
|
720
|
+
Turn headers into an :class:`email.message.Message`, with each value as a
|
|
721
|
+
header of its own, as :mod:`http.client` has them.
|
|
722
|
+
"""
|
|
723
|
+
message = Message()
|
|
724
|
+
for name, value in self.to_list():
|
|
725
|
+
message[name] = value
|
|
726
|
+
return message
|
|
727
|
+
|
|
728
|
+
def to_str(self) -> str:
|
|
729
|
+
"""::
|
|
730
|
+
|
|
731
|
+
string = headers.to_str()
|
|
732
|
+
|
|
733
|
+
Turn headers into a string, suitable for HTTP messages.
|
|
734
|
+
"""
|
|
735
|
+
# Make sure multi-line values are formatted correctly
|
|
736
|
+
return '\x0d\x0a'.join(f'{name}: {value}' for name, value in self.to_list())
|
|
737
|
+
|
|
738
|
+
trailer = _Shortcut('Trailer', "'X-Foo'")
|
|
739
|
+
transfer_encoding = _Shortcut('Transfer-Encoding', "'chunked'")
|
|
740
|
+
upgrade = _Shortcut('Upgrade', "'websocket'")
|
|
741
|
+
user_agent = _Shortcut('User-Agent', "'Pyjoyment/1.0'")
|
|
742
|
+
|
|
743
|
+
# Not in Mojolicious: the mapping protocol of the standard library
|
|
744
|
+
def values(self) -> list[str]:
|
|
745
|
+
"""::
|
|
746
|
+
|
|
747
|
+
values = headers.values()
|
|
748
|
+
|
|
749
|
+
Return a list of the values that :meth:`header` gives, in the order of
|
|
750
|
+
:meth:`names`.
|
|
751
|
+
"""
|
|
752
|
+
return [value for _, value in self.items()]
|
|
753
|
+
|
|
754
|
+
vary = _Shortcut('Vary', "'*'")
|
|
755
|
+
www_authenticate = _Shortcut('WWW-Authenticate', '\'Basic realm="realm"\'')
|
|
756
|
+
|
|
757
|
+
|
|
758
|
+
new = Pyjo_Headers.new
|
|
759
|
+
object = Pyjo_Headers
|
|
760
|
+
Headers = Pyjo_Headers
|