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/Message/Response.py
ADDED
|
@@ -0,0 +1,476 @@
|
|
|
1
|
+
r"""
|
|
2
|
+
Pyjo.Message.Response - HTTP response
|
|
3
|
+
=====================================
|
|
4
|
+
::
|
|
5
|
+
|
|
6
|
+
from Pyjo.Message.Response import Response
|
|
7
|
+
|
|
8
|
+
# Parse
|
|
9
|
+
res = Response()
|
|
10
|
+
res.parse(b'HTTP/1.0 200 OK\x0d\x0a')
|
|
11
|
+
res.parse(b'Content-Length: 12\x0d\x0a')
|
|
12
|
+
res.parse(b'Content-Type: text/plain\x0d\x0a\x0d\x0a')
|
|
13
|
+
res.parse(b'Hello World!')
|
|
14
|
+
print(res.code)
|
|
15
|
+
print(res.headers.content_type)
|
|
16
|
+
print(res.body)
|
|
17
|
+
|
|
18
|
+
# Or import the module, as in Google Python Style Guide
|
|
19
|
+
|
|
20
|
+
import Pyjo.Message.Response
|
|
21
|
+
|
|
22
|
+
# Parse
|
|
23
|
+
res = Pyjo.Message.Response.new()
|
|
24
|
+
res.parse(b'HTTP/1.0 200 OK\x0d\x0a')
|
|
25
|
+
res.parse(b'Content-Length: 12\x0d\x0a')
|
|
26
|
+
res.parse(b'Content-Type: text/plain\x0d\x0a\x0d\x0a')
|
|
27
|
+
res.parse(b'Hello World!')
|
|
28
|
+
print(res.code)
|
|
29
|
+
print(res.headers.content_type)
|
|
30
|
+
print(res.body)
|
|
31
|
+
|
|
32
|
+
# Build
|
|
33
|
+
res = Pyjo.Message.Response.new()
|
|
34
|
+
res.code = 200
|
|
35
|
+
res.headers.content_type = 'text/plain'
|
|
36
|
+
res.body = b'Hello World!'
|
|
37
|
+
print(res.to_bytes())
|
|
38
|
+
|
|
39
|
+
:mod:`Pyjo.Message.Response` is a container for HTTP responses, based on
|
|
40
|
+
`RFC 7230 <https://tools.ietf.org/html/rfc7230>`_ and
|
|
41
|
+
`RFC 7231 <https://tools.ietf.org/html/rfc7231>`_.
|
|
42
|
+
|
|
43
|
+
Events
|
|
44
|
+
------
|
|
45
|
+
|
|
46
|
+
:mod:`Pyjo.Message.Response` inherits all events from :mod:`Pyjo.Message`.
|
|
47
|
+
|
|
48
|
+
See also
|
|
49
|
+
--------
|
|
50
|
+
|
|
51
|
+
:mod:`Pyjoyment`, https://mojolicious.org.
|
|
52
|
+
|
|
53
|
+
Ported from `Mojo::Message::Response <https://github.com/mojolicious/mojo/blob/v9.49/lib/Mojo/Message/Response.pm>`_
|
|
54
|
+
of Mojolicious 9.49.
|
|
55
|
+
"""
|
|
56
|
+
|
|
57
|
+
import Pyjo.Cookie
|
|
58
|
+
import Pyjo.Cookie.Response
|
|
59
|
+
import Pyjo.Date
|
|
60
|
+
import Pyjo.Message
|
|
61
|
+
|
|
62
|
+
import builtins
|
|
63
|
+
import http.client
|
|
64
|
+
import io
|
|
65
|
+
import re
|
|
66
|
+
import urllib.response
|
|
67
|
+
|
|
68
|
+
from collections.abc import Mapping
|
|
69
|
+
from typing import Any, Self, cast, overload
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
type Anything = builtins.object
|
|
73
|
+
|
|
74
|
+
# Unmarked codes are from RFC 7231
|
|
75
|
+
MESSAGES = {
|
|
76
|
+
100: 'Continue',
|
|
77
|
+
101: 'Switching Protocols',
|
|
78
|
+
102: 'Processing', # RFC 2518 (WebDAV)
|
|
79
|
+
103: 'Early Hints', # RFC 8297
|
|
80
|
+
200: 'OK',
|
|
81
|
+
201: 'Created',
|
|
82
|
+
202: 'Accepted',
|
|
83
|
+
203: 'Non-Authoritative Information',
|
|
84
|
+
204: 'No Content',
|
|
85
|
+
205: 'Reset Content',
|
|
86
|
+
206: 'Partial Content',
|
|
87
|
+
207: 'Multi-Status', # RFC 2518 (WebDAV)
|
|
88
|
+
208: 'Already Reported', # RFC 5842
|
|
89
|
+
226: 'IM Used', # RFC 3229
|
|
90
|
+
300: 'Multiple Choices',
|
|
91
|
+
301: 'Moved Permanently',
|
|
92
|
+
302: 'Found',
|
|
93
|
+
303: 'See Other',
|
|
94
|
+
304: 'Not Modified',
|
|
95
|
+
305: 'Use Proxy',
|
|
96
|
+
307: 'Temporary Redirect',
|
|
97
|
+
308: 'Permanent Redirect', # RFC 7538
|
|
98
|
+
400: 'Bad Request',
|
|
99
|
+
401: 'Unauthorized',
|
|
100
|
+
402: 'Payment Required',
|
|
101
|
+
403: 'Forbidden',
|
|
102
|
+
404: 'Not Found',
|
|
103
|
+
405: 'Method Not Allowed',
|
|
104
|
+
406: 'Not Acceptable',
|
|
105
|
+
407: 'Proxy Authentication Required',
|
|
106
|
+
408: 'Request Timeout',
|
|
107
|
+
409: 'Conflict',
|
|
108
|
+
410: 'Gone',
|
|
109
|
+
411: 'Length Required',
|
|
110
|
+
412: 'Precondition Failed',
|
|
111
|
+
413: 'Request Entity Too Large',
|
|
112
|
+
414: 'Request-URI Too Long',
|
|
113
|
+
415: 'Unsupported Media Type',
|
|
114
|
+
416: 'Request Range Not Satisfiable',
|
|
115
|
+
417: 'Expectation Failed',
|
|
116
|
+
418: "I'm a teapot", # RFC 2324 :)
|
|
117
|
+
421: 'Misdirected Request', # RFC 7540
|
|
118
|
+
422: 'Unprocessable Entity', # RFC 2518 (WebDAV)
|
|
119
|
+
423: 'Locked', # RFC 2518 (WebDAV)
|
|
120
|
+
424: 'Failed Dependency', # RFC 2518 (WebDAV)
|
|
121
|
+
425: 'Too Early', # RFC 8470
|
|
122
|
+
426: 'Upgrade Required', # RFC 2817
|
|
123
|
+
428: 'Precondition Required', # RFC 6585
|
|
124
|
+
429: 'Too Many Requests', # RFC 6585
|
|
125
|
+
431: 'Request Header Fields Too Large', # RFC 6585
|
|
126
|
+
451: 'Unavailable For Legal Reasons', # RFC 7725
|
|
127
|
+
500: 'Internal Server Error',
|
|
128
|
+
501: 'Not Implemented',
|
|
129
|
+
502: 'Bad Gateway',
|
|
130
|
+
503: 'Service Unavailable',
|
|
131
|
+
504: 'Gateway Timeout',
|
|
132
|
+
505: 'HTTP Version Not Supported',
|
|
133
|
+
506: 'Variant Also Negotiates', # RFC 2295
|
|
134
|
+
507: 'Insufficient Storage', # RFC 2518 (WebDAV)
|
|
135
|
+
508: 'Loop Detected', # RFC 5842
|
|
136
|
+
509: 'Bandwidth Limit Exceeded', # Unofficial
|
|
137
|
+
510: 'Not Extended', # RFC 2774
|
|
138
|
+
511: 'Network Authentication Required', # RFC 6585
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
class Pyjo_Message_Response(Pyjo.Message.object):
|
|
143
|
+
"""
|
|
144
|
+
:mod:`Pyjo.Message.Response` inherits all methods from :mod:`Pyjo.Message`
|
|
145
|
+
and implements the following new ones.
|
|
146
|
+
"""
|
|
147
|
+
|
|
148
|
+
# The default of max_message_size, which a response raises
|
|
149
|
+
_MAX_MESSAGE_SIZE = 2147483648
|
|
150
|
+
|
|
151
|
+
def __init__(
|
|
152
|
+
self,
|
|
153
|
+
response: http.client.HTTPResponse | urllib.response.addinfourl | None = None,
|
|
154
|
+
/,
|
|
155
|
+
*,
|
|
156
|
+
code: int | None = None,
|
|
157
|
+
message: str | None = None,
|
|
158
|
+
**kwargs: Any,
|
|
159
|
+
) -> None:
|
|
160
|
+
"""::
|
|
161
|
+
|
|
162
|
+
res = Pyjo.Message.Response.new()
|
|
163
|
+
res = Pyjo.Message.Response.new(code=200, message='OK')
|
|
164
|
+
res = Pyjo.Message.Response.new(urllib.request.urlopen('http://127.0.0.1/'))
|
|
165
|
+
|
|
166
|
+
Construct a new :mod:`Pyjo.Message.Response` object, from a response
|
|
167
|
+
of urllib if it is given: an :class:`http.client.HTTPResponse`, as
|
|
168
|
+
:func:`urllib.request.urlopen` returns for HTTP, or a
|
|
169
|
+
:class:`urllib.response.addinfourl`, as it returns for other URLs and
|
|
170
|
+
:class:`urllib.error.HTTPError` is: its status, ``200`` for a response
|
|
171
|
+
without one, as of a ``file:`` or ``data:`` URL, its headers, and the
|
|
172
|
+
body without the transfer encoding, as :mod:`http.client` decodes it.
|
|
173
|
+
It reads the response and closes it.
|
|
174
|
+
"""
|
|
175
|
+
super().__init__(**kwargs)
|
|
176
|
+
|
|
177
|
+
self.code = code
|
|
178
|
+
"""::
|
|
179
|
+
|
|
180
|
+
code = res.code
|
|
181
|
+
res.code = 200
|
|
182
|
+
|
|
183
|
+
HTTP response status code.
|
|
184
|
+
"""
|
|
185
|
+
|
|
186
|
+
self.message = message
|
|
187
|
+
"""::
|
|
188
|
+
|
|
189
|
+
message = res.message
|
|
190
|
+
res.message = 'OK'
|
|
191
|
+
|
|
192
|
+
HTTP response status message.
|
|
193
|
+
"""
|
|
194
|
+
|
|
195
|
+
self._start_buffer: bytes | None = None
|
|
196
|
+
|
|
197
|
+
# Not in Mojolicious: a response of urllib
|
|
198
|
+
if response is not None:
|
|
199
|
+
self._from_urllib(response)
|
|
200
|
+
|
|
201
|
+
@overload
|
|
202
|
+
def cookies(self) -> list[Pyjo.Cookie.Response.object]: ...
|
|
203
|
+
|
|
204
|
+
@overload
|
|
205
|
+
def cookies(self, cookie: Pyjo.Cookie.object | Mapping[str, Any], /, *cookies: Anything) -> Self: ...
|
|
206
|
+
|
|
207
|
+
def cookies(self, *cookies: Anything) -> list[Pyjo.Cookie.Response.object] | Self:
|
|
208
|
+
"""::
|
|
209
|
+
|
|
210
|
+
cookies = res.cookies()
|
|
211
|
+
res = res.cookies(Pyjo.Cookie.Response.new())
|
|
212
|
+
res = res.cookies({'name': 'foo', 'value': 'bar'})
|
|
213
|
+
|
|
214
|
+
Access response cookies, usually :mod:`Pyjo.Cookie.Response` objects. ::
|
|
215
|
+
|
|
216
|
+
# Names of all cookies
|
|
217
|
+
for cookie in res.cookies():
|
|
218
|
+
print(cookie.name)
|
|
219
|
+
"""
|
|
220
|
+
# Parse cookies
|
|
221
|
+
headers = self.headers
|
|
222
|
+
if not cookies:
|
|
223
|
+
return Pyjo.Cookie.Response.parse(headers.set_cookie)
|
|
224
|
+
|
|
225
|
+
# Add cookies
|
|
226
|
+
for cookie in cookies:
|
|
227
|
+
if isinstance(cookie, Mapping):
|
|
228
|
+
cookie = Pyjo.Cookie.Response.new(**cast(Mapping[str, Any], cookie))
|
|
229
|
+
headers.add('Set-Cookie', str(cookie))
|
|
230
|
+
|
|
231
|
+
return self
|
|
232
|
+
|
|
233
|
+
def default_message(self, code: int | None = None) -> str:
|
|
234
|
+
"""::
|
|
235
|
+
|
|
236
|
+
message = res.default_message()
|
|
237
|
+
message = res.default_message(418)
|
|
238
|
+
|
|
239
|
+
Generate default response message for status code, defaults to using
|
|
240
|
+
:attr:`code`.
|
|
241
|
+
"""
|
|
242
|
+
code = code or self.code
|
|
243
|
+
return MESSAGES.get(code if code is not None else 404, '')
|
|
244
|
+
|
|
245
|
+
@property
|
|
246
|
+
def max_message_size(self) -> int:
|
|
247
|
+
"""::
|
|
248
|
+
|
|
249
|
+
size = res.max_message_size
|
|
250
|
+
res.max_message_size = 1024
|
|
251
|
+
|
|
252
|
+
Maximum message size in bytes, defaults to the value of the
|
|
253
|
+
``PYJO_MAX_MESSAGE_SIZE`` environment variable or ``2147483648``
|
|
254
|
+
(2GiB). Setting the value to ``0`` will allow messages of indefinite
|
|
255
|
+
size.
|
|
256
|
+
"""
|
|
257
|
+
return super().max_message_size
|
|
258
|
+
|
|
259
|
+
@max_message_size.setter
|
|
260
|
+
def max_message_size(self, value: int) -> None:
|
|
261
|
+
self._max_message_size = value
|
|
262
|
+
|
|
263
|
+
def extract_start_line(self, buffer: bytearray) -> bool | None:
|
|
264
|
+
"""::
|
|
265
|
+
|
|
266
|
+
boolean = res.extract_start_line(buffer)
|
|
267
|
+
|
|
268
|
+
Extract status-line from a :class:`bytearray`, which loses it.
|
|
269
|
+
"""
|
|
270
|
+
# We have a full response line
|
|
271
|
+
if not (m := re.match(rb'(.*?)\x0d?\x0a', bytes(buffer))):
|
|
272
|
+
return None
|
|
273
|
+
del buffer[: m.end()]
|
|
274
|
+
if not (m := re.fullmatch(rb'\s*HTTP/(\d\.\d)\s+(\d\d\d)\s*(.+)?', m[1])):
|
|
275
|
+
self.error = {'message': 'Bad response start-line'}
|
|
276
|
+
return False
|
|
277
|
+
|
|
278
|
+
content = self.content
|
|
279
|
+
self.code = int(m[2])
|
|
280
|
+
if self.is_empty:
|
|
281
|
+
content.skip_body = True
|
|
282
|
+
if content.auto_decompress is None:
|
|
283
|
+
content.auto_decompress = True
|
|
284
|
+
if content.auto_relax is None:
|
|
285
|
+
content.auto_relax = True
|
|
286
|
+
self.version = m[1].decode('latin-1')
|
|
287
|
+
self.message = m[3].decode('latin-1') if m[3] is not None else None
|
|
288
|
+
return True
|
|
289
|
+
|
|
290
|
+
def fix_headers(self) -> Self:
|
|
291
|
+
"""::
|
|
292
|
+
|
|
293
|
+
res = res.fix_headers()
|
|
294
|
+
|
|
295
|
+
Make sure response has all required headers.
|
|
296
|
+
"""
|
|
297
|
+
if self._fix:
|
|
298
|
+
return self
|
|
299
|
+
super().fix_headers()
|
|
300
|
+
|
|
301
|
+
# Date
|
|
302
|
+
headers = self.headers
|
|
303
|
+
if not headers.date:
|
|
304
|
+
headers.date = Pyjo.Date.new().to_str()
|
|
305
|
+
|
|
306
|
+
# RFC 7230 3.3.2
|
|
307
|
+
if self.is_empty:
|
|
308
|
+
headers.remove('Content-Length')
|
|
309
|
+
|
|
310
|
+
return self
|
|
311
|
+
|
|
312
|
+
def get_start_line_chunk(self, offset: int) -> bytes:
|
|
313
|
+
"""::
|
|
314
|
+
|
|
315
|
+
data = res.get_start_line_chunk(offset)
|
|
316
|
+
|
|
317
|
+
Get a chunk of status-line data starting from a specific position.
|
|
318
|
+
Note that this method finalizes the response.
|
|
319
|
+
"""
|
|
320
|
+
buffer = self._start_line()
|
|
321
|
+
self.emit('progress', 'start_line', offset)
|
|
322
|
+
return buffer[offset : offset + 131072]
|
|
323
|
+
|
|
324
|
+
@property
|
|
325
|
+
def is_client_error(self) -> bool:
|
|
326
|
+
"""::
|
|
327
|
+
|
|
328
|
+
boolean = res.is_client_error
|
|
329
|
+
|
|
330
|
+
Check if this response has a ``4xx`` status :attr:`code`.
|
|
331
|
+
"""
|
|
332
|
+
return self._status_class(400)
|
|
333
|
+
|
|
334
|
+
@property
|
|
335
|
+
def is_empty(self) -> bool:
|
|
336
|
+
"""::
|
|
337
|
+
|
|
338
|
+
boolean = res.is_empty
|
|
339
|
+
|
|
340
|
+
Check if this response has a ``1xx``, ``204`` or ``304`` status
|
|
341
|
+
:attr:`code`.
|
|
342
|
+
"""
|
|
343
|
+
if not (code := self.code):
|
|
344
|
+
return False
|
|
345
|
+
return self.is_info or code == 204 or code == 304
|
|
346
|
+
|
|
347
|
+
@property
|
|
348
|
+
def is_error(self) -> bool:
|
|
349
|
+
"""::
|
|
350
|
+
|
|
351
|
+
boolean = res.is_error
|
|
352
|
+
|
|
353
|
+
Check if this response has a ``4xx`` or ``5xx`` status :attr:`code`.
|
|
354
|
+
"""
|
|
355
|
+
return self._status_class(400, 500)
|
|
356
|
+
|
|
357
|
+
@property
|
|
358
|
+
def is_info(self) -> bool:
|
|
359
|
+
"""::
|
|
360
|
+
|
|
361
|
+
boolean = res.is_info
|
|
362
|
+
|
|
363
|
+
Check if this response has a ``1xx`` status :attr:`code`.
|
|
364
|
+
"""
|
|
365
|
+
return self._status_class(100)
|
|
366
|
+
|
|
367
|
+
@property
|
|
368
|
+
def is_redirect(self) -> bool:
|
|
369
|
+
"""::
|
|
370
|
+
|
|
371
|
+
boolean = res.is_redirect
|
|
372
|
+
|
|
373
|
+
Check if this response has a ``3xx`` status :attr:`code`.
|
|
374
|
+
"""
|
|
375
|
+
return self._status_class(300)
|
|
376
|
+
|
|
377
|
+
@property
|
|
378
|
+
def is_server_error(self) -> bool:
|
|
379
|
+
"""::
|
|
380
|
+
|
|
381
|
+
boolean = res.is_server_error
|
|
382
|
+
|
|
383
|
+
Check if this response has a ``5xx`` status :attr:`code`.
|
|
384
|
+
"""
|
|
385
|
+
return self._status_class(500)
|
|
386
|
+
|
|
387
|
+
@property
|
|
388
|
+
def is_success(self) -> bool:
|
|
389
|
+
"""::
|
|
390
|
+
|
|
391
|
+
boolean = res.is_success
|
|
392
|
+
|
|
393
|
+
Check if this response has a ``2xx`` status :attr:`code`.
|
|
394
|
+
"""
|
|
395
|
+
return self._status_class(200)
|
|
396
|
+
|
|
397
|
+
@property
|
|
398
|
+
def start_line_size(self) -> int:
|
|
399
|
+
"""::
|
|
400
|
+
|
|
401
|
+
size = res.start_line_size
|
|
402
|
+
|
|
403
|
+
Size of the status-line in bytes. Note that this method finalizes the
|
|
404
|
+
response.
|
|
405
|
+
"""
|
|
406
|
+
return len(self._start_line())
|
|
407
|
+
|
|
408
|
+
# Not in Mojolicious: a response of urllib
|
|
409
|
+
def to_urllib(self, *, method: str | None = None, url: str | None = None) -> http.client.HTTPResponse:
|
|
410
|
+
"""::
|
|
411
|
+
|
|
412
|
+
response = res.to_urllib()
|
|
413
|
+
response = res.to_urllib(method='HEAD', url='http://127.0.0.1/')
|
|
414
|
+
|
|
415
|
+
Turn response into an :class:`http.client.HTTPResponse`, as
|
|
416
|
+
:func:`urllib.request.urlopen` returns for HTTP, parsed by
|
|
417
|
+
:mod:`http.client` from :meth:`~Pyjo.Message.Pyjo_Message.to_bytes`.
|
|
418
|
+
The method of the request tells whether the response has a body, and
|
|
419
|
+
the URL is what :meth:`~http.client.HTTPResponse.geturl` returns. ::
|
|
420
|
+
|
|
421
|
+
# A response for the code that uses urllib
|
|
422
|
+
response = Pyjo.Message.Response.new(code=200).set(body=b'Hi!').to_urllib()
|
|
423
|
+
print(response.read())
|
|
424
|
+
"""
|
|
425
|
+
response = http.client.HTTPResponse(cast(Any, _Socket(self.to_bytes())), method=method)
|
|
426
|
+
response.begin()
|
|
427
|
+
|
|
428
|
+
# The URL, as urlopen sets it
|
|
429
|
+
if url is not None:
|
|
430
|
+
response.url = url
|
|
431
|
+
return response
|
|
432
|
+
|
|
433
|
+
# Not in Mojolicious: a response of urllib
|
|
434
|
+
def _from_urllib(self, response: http.client.HTTPResponse | urllib.response.addinfourl) -> None:
|
|
435
|
+
# A response without a status, as of a file: or data: URL, is a success
|
|
436
|
+
self.code = response.status or 200
|
|
437
|
+
self.message = getattr(response, 'reason', None) or None
|
|
438
|
+
self.version = {9: '0.9', 10: '1.0'}.get(getattr(response, 'version', 11), '1.1')
|
|
439
|
+
headers = self.headers
|
|
440
|
+
for name, value in response.headers.items():
|
|
441
|
+
headers.add(name, value)
|
|
442
|
+
body = response.read()
|
|
443
|
+
response.close()
|
|
444
|
+
|
|
445
|
+
# The body without the transfer encoding
|
|
446
|
+
if headers.transfer_encoding:
|
|
447
|
+
headers.remove('Transfer-Encoding')
|
|
448
|
+
headers.content_length = len(body)
|
|
449
|
+
self.body = body
|
|
450
|
+
|
|
451
|
+
def _start_line(self) -> bytes:
|
|
452
|
+
if self._start_buffer is not None:
|
|
453
|
+
return self._start_buffer
|
|
454
|
+
code = self.code or 404
|
|
455
|
+
message = self.message or self.default_message()
|
|
456
|
+
self._start_buffer = f'HTTP/{self.version} {code} {message}\x0d\x0a'.encode('latin-1')
|
|
457
|
+
return self._start_buffer
|
|
458
|
+
|
|
459
|
+
def _status_class(self, *classes: int) -> bool:
|
|
460
|
+
if not (code := self.code):
|
|
461
|
+
return False
|
|
462
|
+
return any(code >= c and code < c + 100 for c in classes)
|
|
463
|
+
|
|
464
|
+
|
|
465
|
+
# Not in Mojolicious: a socket that http.client reads a response from
|
|
466
|
+
class _Socket:
|
|
467
|
+
def __init__(self, data: bytes) -> None:
|
|
468
|
+
self._data = data
|
|
469
|
+
|
|
470
|
+
def makefile(self, mode: str, *args: Any, **kwargs: Any) -> io.BytesIO:
|
|
471
|
+
return io.BytesIO(self._data)
|
|
472
|
+
|
|
473
|
+
|
|
474
|
+
new = Pyjo_Message_Response.new
|
|
475
|
+
object = Pyjo_Message_Response
|
|
476
|
+
Response = Pyjo_Message_Response
|