PoorWSGI 2.6.1__tar.gz → 2.6.3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {PoorWSGI-2.6.1 → poorwsgi-2.6.3}/PKG-INFO +1 -1
- {PoorWSGI-2.6.1 → poorwsgi-2.6.3}/PoorWSGI.egg-info/PKG-INFO +1 -1
- {PoorWSGI-2.6.1 → poorwsgi-2.6.3}/PoorWSGI.egg-info/SOURCES.txt +4 -4
- {PoorWSGI-2.6.1 → poorwsgi-2.6.3}/doc/ChangeLog +5 -0
- {PoorWSGI-2.6.1 → poorwsgi-2.6.3}/doc/documentation.rst +1 -1
- poorwsgi-2.6.3/examples/http_digest.py +200 -0
- poorwsgi-2.6.3/examples/jwt_example.py.skip +86 -0
- {PoorWSGI-2.6.1 → poorwsgi-2.6.3}/examples/large_file.py +9 -7
- {PoorWSGI-2.6.1 → poorwsgi-2.6.3}/examples/metrics.py +1 -0
- {PoorWSGI-2.6.1 → poorwsgi-2.6.3}/examples/openapi3.py +2 -2
- {PoorWSGI-2.6.1 → poorwsgi-2.6.3}/examples/put_file.py +6 -4
- {PoorWSGI-2.6.1 → poorwsgi-2.6.3}/examples/simple.py +163 -121
- {PoorWSGI-2.6.1 → poorwsgi-2.6.3}/examples/simple_json.py +4 -4
- poorwsgi-2.6.3/examples/websocket-frames.py.skip +80 -0
- {PoorWSGI-2.6.1 → poorwsgi-2.6.3}/examples/websocket.py +10 -2
- {PoorWSGI-2.6.1 → poorwsgi-2.6.3}/poorwsgi/openapi_wrapper.py +1 -1
- {PoorWSGI-2.6.1 → poorwsgi-2.6.3}/poorwsgi/request.py +2 -2
- {PoorWSGI-2.6.1 → poorwsgi-2.6.3}/poorwsgi/response.py +11 -0
- {PoorWSGI-2.6.1 → poorwsgi-2.6.3}/poorwsgi/results.py +10 -10
- {PoorWSGI-2.6.1 → poorwsgi-2.6.3}/poorwsgi/session.py +1 -1
- {PoorWSGI-2.6.1 → poorwsgi-2.6.3}/poorwsgi/state.py +2 -2
- poorwsgi-2.6.3/setup.py +206 -0
- {PoorWSGI-2.6.1 → poorwsgi-2.6.3}/tests/test_digest.py +17 -8
- {PoorWSGI-2.6.1 → poorwsgi-2.6.3}/tests/test_header.py +4 -2
- {PoorWSGI-2.6.1 → poorwsgi-2.6.3}/tests/test_responses.py +56 -8
- {PoorWSGI-2.6.1 → poorwsgi-2.6.3}/tests/test_session.py +4 -0
- PoorWSGI-2.6.1/examples/http_digest.py +0 -237
- PoorWSGI-2.6.1/setup.py +0 -229
- PoorWSGI-2.6.1/test_hidden.py +0 -58
- PoorWSGI-2.6.1/websockets_chat_async.py +0 -95
- {PoorWSGI-2.6.1 → poorwsgi-2.6.3}/CONTRIBUTION.rst +0 -0
- {PoorWSGI-2.6.1 → poorwsgi-2.6.3}/MANIFEST.in +0 -0
- {PoorWSGI-2.6.1 → poorwsgi-2.6.3}/PoorWSGI.egg-info/dependency_links.txt +0 -0
- {PoorWSGI-2.6.1 → poorwsgi-2.6.3}/PoorWSGI.egg-info/requires.txt +0 -0
- {PoorWSGI-2.6.1 → poorwsgi-2.6.3}/PoorWSGI.egg-info/top_level.txt +0 -0
- {PoorWSGI-2.6.1 → poorwsgi-2.6.3}/README.rst +0 -0
- {PoorWSGI-2.6.1 → poorwsgi-2.6.3}/doc/_documentation.html +0 -0
- {PoorWSGI-2.6.1 → poorwsgi-2.6.3}/doc/_footer.html +0 -0
- {PoorWSGI-2.6.1 → poorwsgi-2.6.3}/doc/_header.html +0 -0
- {PoorWSGI-2.6.1 → poorwsgi-2.6.3}/doc/_install.html +0 -0
- {PoorWSGI-2.6.1 → poorwsgi-2.6.3}/doc/_licence.html +0 -0
- {PoorWSGI-2.6.1 → poorwsgi-2.6.3}/doc/_poorwsgi.html +0 -0
- {PoorWSGI-2.6.1 → poorwsgi-2.6.3}/doc/_poorwsgi_api.html +0 -0
- {PoorWSGI-2.6.1 → poorwsgi-2.6.3}/doc/_reference.html +0 -0
- {PoorWSGI-2.6.1 → poorwsgi-2.6.3}/doc/_text.html +0 -0
- {PoorWSGI-2.6.1 → poorwsgi-2.6.3}/doc/about.rst +0 -0
- {PoorWSGI-2.6.1 → poorwsgi-2.6.3}/doc/install.rst +0 -0
- {PoorWSGI-2.6.1 → poorwsgi-2.6.3}/doc/licence.txt +0 -0
- {PoorWSGI-2.6.1 → poorwsgi-2.6.3}/doc/small-logo.png +0 -0
- {PoorWSGI-2.6.1 → poorwsgi-2.6.3}/doc/style.css +0 -0
- {PoorWSGI-2.6.1 → poorwsgi-2.6.3}/doc/web.css +0 -0
- /PoorWSGI-2.6.1/examples/async.py → /poorwsgi-2.6.3/examples/async.py.skip +0 -0
- /PoorWSGI-2.6.1/examples/encoding.py → /poorwsgi-2.6.3/examples/encoding.py.skip +0 -0
- {PoorWSGI-2.6.1 → poorwsgi-2.6.3}/examples/openapi.json +0 -0
- {PoorWSGI-2.6.1 → poorwsgi-2.6.3}/examples/test.digest +0 -0
- {PoorWSGI-2.6.1 → poorwsgi-2.6.3}/poorwsgi/__init__.py +0 -0
- {PoorWSGI-2.6.1 → poorwsgi-2.6.3}/poorwsgi/digest.py +0 -0
- {PoorWSGI-2.6.1 → poorwsgi-2.6.3}/poorwsgi/headers.py +0 -0
- {PoorWSGI-2.6.1 → poorwsgi-2.6.3}/poorwsgi/py.typed +0 -0
- {PoorWSGI-2.6.1 → poorwsgi-2.6.3}/poorwsgi/wsgi.py +0 -0
- {PoorWSGI-2.6.1 → poorwsgi-2.6.3}/setup.cfg +0 -0
- {PoorWSGI-2.6.1 → poorwsgi-2.6.3}/tests/test_request.py +0 -0
|
@@ -2,8 +2,6 @@ CONTRIBUTION.rst
|
|
|
2
2
|
MANIFEST.in
|
|
3
3
|
README.rst
|
|
4
4
|
setup.py
|
|
5
|
-
test_hidden.py
|
|
6
|
-
websockets_chat_async.py
|
|
7
5
|
PoorWSGI.egg-info/PKG-INFO
|
|
8
6
|
PoorWSGI.egg-info/SOURCES.txt
|
|
9
7
|
PoorWSGI.egg-info/dependency_links.txt
|
|
@@ -26,9 +24,10 @@ doc/licence.txt
|
|
|
26
24
|
doc/small-logo.png
|
|
27
25
|
doc/style.css
|
|
28
26
|
doc/web.css
|
|
29
|
-
examples/async.py
|
|
30
|
-
examples/encoding.py
|
|
27
|
+
examples/async.py.skip
|
|
28
|
+
examples/encoding.py.skip
|
|
31
29
|
examples/http_digest.py
|
|
30
|
+
examples/jwt_example.py.skip
|
|
32
31
|
examples/large_file.py
|
|
33
32
|
examples/metrics.py
|
|
34
33
|
examples/openapi.json
|
|
@@ -37,6 +36,7 @@ examples/put_file.py
|
|
|
37
36
|
examples/simple.py
|
|
38
37
|
examples/simple_json.py
|
|
39
38
|
examples/test.digest
|
|
39
|
+
examples/websocket-frames.py.skip
|
|
40
40
|
examples/websocket.py
|
|
41
41
|
poorwsgi/__init__.py
|
|
42
42
|
poorwsgi/digest.py
|
|
@@ -795,7 +795,7 @@ CachedInput
|
|
|
795
795
|
~~~~~~~~~~~
|
|
796
796
|
|
|
797
797
|
When HTTP Forms are base64 encoded, FieldStorage use readline on request input
|
|
798
|
-
file. This is not so optimal. So there is CachedInput class, which is returned
|
|
798
|
+
file. This is not so optimal. So there is CachedInput class, which is returned
|
|
799
799
|
|
|
800
800
|
Proccess variables
|
|
801
801
|
~~~~~~~~~~~~~~~~~~
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
"""HTTP WWW-Authenticate Digest Example."""
|
|
2
|
+
import logging
|
|
3
|
+
from hashlib import sha256
|
|
4
|
+
from os import path
|
|
5
|
+
from sys import path as python_path
|
|
6
|
+
from time import time
|
|
7
|
+
from wsgiref.simple_server import make_server
|
|
8
|
+
|
|
9
|
+
python_path.insert(
|
|
10
|
+
0, path.abspath(path.join(path.dirname(__file__), path.pardir)))
|
|
11
|
+
|
|
12
|
+
# pylint: disable=wrong-import-position
|
|
13
|
+
from poorwsgi import Application, state # noqa
|
|
14
|
+
from poorwsgi.digest import PasswordMap, check_digest, hexdigest # noqa
|
|
15
|
+
from poorwsgi.response import EmptyResponse, redirect # noqa
|
|
16
|
+
|
|
17
|
+
FILE = path.join(path.dirname(__file__), 'test.digest')
|
|
18
|
+
|
|
19
|
+
logging.getLogger().setLevel("DEBUG")
|
|
20
|
+
app = application = Application(__name__) # pylint: disable=invalid-name
|
|
21
|
+
# application = app
|
|
22
|
+
app.debug = True
|
|
23
|
+
app.secret_key = sha256(str(time()).encode()).hexdigest()
|
|
24
|
+
# app.auth_algorithm = 'SHA-256-sess'
|
|
25
|
+
app.auth_type = 'Digest'
|
|
26
|
+
app.auth_timeout = 60
|
|
27
|
+
|
|
28
|
+
ADMIN = 'Admin Zone'
|
|
29
|
+
USER = 'User Zone'
|
|
30
|
+
# user/looser, foo/bar, admin/admin, Ondřej/heslíčko
|
|
31
|
+
app.auth_map = PasswordMap(FILE)
|
|
32
|
+
app.auth_map.load()
|
|
33
|
+
|
|
34
|
+
# pylint: disable=unused-argument
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def get_header(title):
|
|
38
|
+
"""Return HTML header list of lines."""
|
|
39
|
+
return (
|
|
40
|
+
"<html>", "<head>",
|
|
41
|
+
'<meta http-equiv="content-type" content="text/html; charset=utf-8"/>',
|
|
42
|
+
f"<title>{__file__} - {title}</title>", "</head>", "<body>",
|
|
43
|
+
f"<h1>{__file__} - {title}</h1>")
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def get_footer():
|
|
47
|
+
"""Return HTML footer list of lines."""
|
|
48
|
+
return ("<hr>", "<small>Copyright (c) 2020 Ondřej Tůma. See ",
|
|
49
|
+
'<a href="http://poorhttp.zeropage.cz">poorhttp.zeropage.cz</a>'
|
|
50
|
+
'</small>.', "</body>", "</html>")
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def get_link(href, text=None, title=None):
|
|
54
|
+
"""Return HTML anchor."""
|
|
55
|
+
text = text or title or href
|
|
56
|
+
title = title or text
|
|
57
|
+
return f'<a href="{href}" title="{title}">{text}</a>'
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
@app.route('/')
|
|
61
|
+
def root(req):
|
|
62
|
+
"""Return Root (Index) page."""
|
|
63
|
+
body = ('<ul>', "<li>" + get_link('/admin_zone') +
|
|
64
|
+
" - admin zone (admin/admin)</li>", "<li>" +
|
|
65
|
+
get_link('/user_zone') + " - user zone (user/looser;sha/sha)</li>",
|
|
66
|
+
"<li>" + get_link('/user') + " - user (user/looser)</li>", "<li>" +
|
|
67
|
+
get_link('/user/utf-8') + " - utf-8 (Ondřej/heslíčko)</li>",
|
|
68
|
+
"<li>" + get_link('/foo') + " - foo (foo/bar)</li>",
|
|
69
|
+
"<li>" + get_link('/unknown') + " - unknown</li>",
|
|
70
|
+
"<li>" + get_link('/spaces in url') + " - spaces in url</li>",
|
|
71
|
+
"<li>" + get_link('/čeština v url') + " - diacitics in url</li>",
|
|
72
|
+
"<li>" + get_link('/crazy in url 🤪') + " - unicode in url</li>",
|
|
73
|
+
'</ul>')
|
|
74
|
+
|
|
75
|
+
for line in get_header("Root") + body + get_footer():
|
|
76
|
+
yield line.encode() + b'\n'
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
@app.route('/admin_zone')
|
|
80
|
+
@check_digest(ADMIN)
|
|
81
|
+
def admin_zone(req):
|
|
82
|
+
"""Page only for ADMIN realm."""
|
|
83
|
+
body = (f'<h2>{ADMIN} test for {app.auth_algorithm} algorithm.</h2>',
|
|
84
|
+
'<ul>', '<li>' + get_link('/', 'Root') + '</li>',
|
|
85
|
+
'<li>' + get_link('/admin_zone?arg=42', 'one more time') + '</li>',
|
|
86
|
+
'</ul>')
|
|
87
|
+
|
|
88
|
+
for line in get_header("Root") + body + get_footer():
|
|
89
|
+
yield line.encode() + b'\n'
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
@app.route('/user_zone')
|
|
93
|
+
@check_digest(USER)
|
|
94
|
+
def user_zone(req):
|
|
95
|
+
"""Page for USER realm."""
|
|
96
|
+
body = (f'<h2>{USER} test for {app.auth_algorithm} algorithm.</h2>',
|
|
97
|
+
f'User: {req.user}', '<ul>',
|
|
98
|
+
'<li>' + get_link('/', 'Root') + '</li>', '<li>' +
|
|
99
|
+
get_link('/user_zone?param=text', 'one more time') + '</li>',
|
|
100
|
+
'</ul>')
|
|
101
|
+
|
|
102
|
+
for line in get_header("Root") + body + get_footer():
|
|
103
|
+
yield line.encode() + b'\n'
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
@app.route('/user')
|
|
107
|
+
@check_digest(USER, 'user')
|
|
108
|
+
def user_only(req):
|
|
109
|
+
"""Page for user only."""
|
|
110
|
+
body = (f'<h2>User test for {app.auth_algorithm} algorithm.</h2>',
|
|
111
|
+
f'User: {req.user}', '<ul>',
|
|
112
|
+
'<li>' + get_link('/', 'Root') + '</li>', '<li>' +
|
|
113
|
+
get_link('/user?param=1234', 'one more time') + '</li>', '</ul>')
|
|
114
|
+
|
|
115
|
+
for line in get_header("Root") + body + get_footer():
|
|
116
|
+
yield line.encode() + b'\n'
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
@app.route('/foo')
|
|
120
|
+
@check_digest(USER, 'foo')
|
|
121
|
+
def foo_only(req):
|
|
122
|
+
"""Page for foo user only."""
|
|
123
|
+
body = (f'<h2>Foo test for {app.auth_algorithm} algorithm.</h2>',
|
|
124
|
+
f'User: {req.user}', '<ul>', '<li>' + get_link('/', 'Root') +
|
|
125
|
+
'</li>', '</ul>', '<form method="post" action="/foo/passwd">',
|
|
126
|
+
'<label>Enter new password:',
|
|
127
|
+
'<input type="password" name="password"/>', '</label>',
|
|
128
|
+
'<button type="submit">Change Password</button>', '</form>')
|
|
129
|
+
|
|
130
|
+
for line in get_header("Root") + body + get_footer():
|
|
131
|
+
yield line.encode() + b'\n'
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
@app.route('/user/utf-8')
|
|
135
|
+
@check_digest(USER, 'Ondřej')
|
|
136
|
+
def utf8_chars(req):
|
|
137
|
+
"""Page for user only."""
|
|
138
|
+
body = (f'<h2>User test for {app.auth_algorithm} algorithm.</h2>',
|
|
139
|
+
f'User: {req.user}', '<ul>',
|
|
140
|
+
'<li>' + get_link('/', 'Root') + '</li>', '<li>' +
|
|
141
|
+
get_link('/user/utf-8?param=1234', 'one more time') + '</li>',
|
|
142
|
+
'</ul>')
|
|
143
|
+
|
|
144
|
+
for line in get_header("Root") + body + get_footer():
|
|
145
|
+
yield line.encode() + b'\n'
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
@app.route('/foo/passwd', method=state.METHOD_POST)
|
|
149
|
+
@check_digest(USER, 'foo')
|
|
150
|
+
def foo_password(req):
|
|
151
|
+
"""Change foo's password."""
|
|
152
|
+
digest = hexdigest(req.user, USER, req.form.get('password'), app.auth_hash)
|
|
153
|
+
app.auth_map.set(USER, req.user, digest)
|
|
154
|
+
redirect('/foo')
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
@app.route('/unknown')
|
|
158
|
+
@check_digest(USER, 'unknown')
|
|
159
|
+
def unknown_endpoint(req):
|
|
160
|
+
"""Page for digest test."""
|
|
161
|
+
return EmptyResponse()
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
def generic_response(url, user):
|
|
165
|
+
"""Return generic response"""
|
|
166
|
+
body = (f'<h2>{USER} test for {app.auth_algorithm} algorithm.</h2>',
|
|
167
|
+
f'User: {user}', '<ul>', '<li>' + get_link('/', 'Root') + '</li>',
|
|
168
|
+
'<li>' + get_link(url + '?param=text', 'one more time') + '</li>',
|
|
169
|
+
'</ul>')
|
|
170
|
+
|
|
171
|
+
for line in get_header("Root") + body + get_footer():
|
|
172
|
+
yield line.encode() + b'\n'
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
@app.route('/spaces in url')
|
|
176
|
+
@check_digest(USER)
|
|
177
|
+
def spaces_in_url(req):
|
|
178
|
+
"""URL with spaces in path."""
|
|
179
|
+
return generic_response(req.path, req.user)
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
@app.route('/čeština v url')
|
|
183
|
+
@check_digest(USER)
|
|
184
|
+
def diacritics_in_url(req):
|
|
185
|
+
"""URL with diacritics in path."""
|
|
186
|
+
return generic_response(req.path, req.user)
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
@app.route('/crazy in url 🤪')
|
|
190
|
+
@check_digest(USER)
|
|
191
|
+
def crazy_in_url(req):
|
|
192
|
+
"""URL with unicode in path."""
|
|
193
|
+
return generic_response(req.path, req.user)
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
if __name__ == '__main__':
|
|
197
|
+
# pylint: disable=invalid-name
|
|
198
|
+
httpd = make_server('127.0.0.1', 8080, app)
|
|
199
|
+
logging.info("Starting to serve on http://127.0.0.1:8080")
|
|
200
|
+
httpd.serve_forever()
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
"""JWT example"""
|
|
2
|
+
import logging
|
|
3
|
+
from functools import wraps
|
|
4
|
+
from hashlib import sha256
|
|
5
|
+
from http.cookies import SimpleCookie
|
|
6
|
+
from os import path, urandom
|
|
7
|
+
from sys import path as python_path
|
|
8
|
+
from time import time
|
|
9
|
+
from wsgiref.simple_server import make_server
|
|
10
|
+
|
|
11
|
+
import jwt
|
|
12
|
+
|
|
13
|
+
python_path.insert(
|
|
14
|
+
0, path.abspath(path.join(path.dirname(__file__), path.pardir)))
|
|
15
|
+
|
|
16
|
+
# pylint: disable=wrong-import-position
|
|
17
|
+
from poorwsgi import Application, state # noqa
|
|
18
|
+
from poorwsgi.response import RedirectResponse, abort, redirect # noaq
|
|
19
|
+
|
|
20
|
+
app = Application('test')
|
|
21
|
+
app.secret_key = urandom(32) # random secret_key
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def check_login(fn):
|
|
25
|
+
|
|
26
|
+
@wraps(fn) # using wraps make right/better /debug-info page
|
|
27
|
+
def handler(req):
|
|
28
|
+
try:
|
|
29
|
+
encoded = req.cookies["JWT"].value
|
|
30
|
+
data = jwt.decode(encoded, app.secret_key, algorithms="HS256")
|
|
31
|
+
if 'login' not in data:
|
|
32
|
+
raise RuntimeError("loggin not in data")
|
|
33
|
+
except:
|
|
34
|
+
redirect("/login", message=b"Login required")
|
|
35
|
+
|
|
36
|
+
return fn(req)
|
|
37
|
+
|
|
38
|
+
return handler
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
@app.route('/login', method=state.METHOD_GET_POST)
|
|
42
|
+
def login(req):
|
|
43
|
+
if req.method == 'POST':
|
|
44
|
+
passwd = req.form.getfirst('passwd', fce=str)
|
|
45
|
+
if passwd != 'SecretPasswds':
|
|
46
|
+
redirect('/login', message='Bad password')
|
|
47
|
+
|
|
48
|
+
response = RedirectResponse("/private/path")
|
|
49
|
+
cookie = SimpleCookie()
|
|
50
|
+
data = {'login': True}
|
|
51
|
+
encoded = jwt.encode(data, app.secret_key, algorithm="HS256")
|
|
52
|
+
cookie["JWT"] = encoded
|
|
53
|
+
for header in cookie.output().split("\r\n"):
|
|
54
|
+
var = header[:10] # Set-Cookie
|
|
55
|
+
val = header[12:] # SID=###; expires=###; Path=/
|
|
56
|
+
response.add_header(var, val)
|
|
57
|
+
|
|
58
|
+
abort(response)
|
|
59
|
+
|
|
60
|
+
return 'some html login form'
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
@app.route('/private/path')
|
|
64
|
+
@check_login
|
|
65
|
+
def private_path(req):
|
|
66
|
+
return 'Some private data'
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
@app.route('/logout')
|
|
70
|
+
def logout(req):
|
|
71
|
+
response = RedirectResponse("/login")
|
|
72
|
+
cookie = SimpleCookie()
|
|
73
|
+
cookie["JWT"]["expires"] = -1
|
|
74
|
+
for header in cookie.output().split("\r\n"):
|
|
75
|
+
var = header[:10] # Set-Cookie
|
|
76
|
+
val = header[12:] # SID=###; expires=###; Path=/
|
|
77
|
+
response.add_header(var, val)
|
|
78
|
+
|
|
79
|
+
return response
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
if __name__ == '__main__':
|
|
83
|
+
# pylint: disable=invalid-name
|
|
84
|
+
httpd = make_server('127.0.0.1', 8080, app)
|
|
85
|
+
logging.info("Starting to serve on http://127.0.0.1:8080")
|
|
86
|
+
httpd.serve_forever()
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
"""Large file upload test."""
|
|
2
|
+
# pylint: disable=duplicate-code
|
|
2
3
|
|
|
3
4
|
from wsgiref.simple_server import make_server, WSGIServer
|
|
4
5
|
from socketserver import ThreadingMixIn
|
|
@@ -14,11 +15,11 @@ EXAMPLES_PATH = os.path.dirname(__file__)
|
|
|
14
15
|
sys.path.insert(0, os.path.abspath(
|
|
15
16
|
os.path.join(EXAMPLES_PATH, os.path.pardir)))
|
|
16
17
|
|
|
17
|
-
# pylint: disable=
|
|
18
|
+
# pylint: disable=wrong-import-position
|
|
18
19
|
from poorwsgi import Application, state # noqa
|
|
19
20
|
from poorwsgi.request import FieldStorage # noqa
|
|
20
21
|
from poorwsgi.response import HTTPException # noqa
|
|
21
|
-
from poorwsgi.results import hbytes
|
|
22
|
+
from poorwsgi.results import hbytes # noqa
|
|
22
23
|
|
|
23
24
|
logger = log.getLogger()
|
|
24
25
|
logger.setLevel("DEBUG")
|
|
@@ -31,6 +32,7 @@ app.auto_form = False
|
|
|
31
32
|
|
|
32
33
|
class Blackhole:
|
|
33
34
|
"""Dummy File Object"""
|
|
35
|
+
|
|
34
36
|
def __init__(self, filename):
|
|
35
37
|
log.debug("Start uploading file: %s", filename)
|
|
36
38
|
self.uploaded = 0
|
|
@@ -56,6 +58,7 @@ class Blackhole:
|
|
|
56
58
|
|
|
57
59
|
class Temporary:
|
|
58
60
|
"""Temporary file"""
|
|
61
|
+
|
|
59
62
|
def __init__(self, filename):
|
|
60
63
|
log.debug("Start uploading file: %s", filename)
|
|
61
64
|
self.uploaded = 0
|
|
@@ -152,11 +155,10 @@ def html_form(req, file_callback):
|
|
|
152
155
|
hexdigest = form['file'].file.hexdigest()
|
|
153
156
|
|
|
154
157
|
end = time() - start
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
stats = ("Upload: %.2f%s in %ds -> %.2f%sps SHA256: %s" % args)
|
|
158
|
+
size = hbytes(bytes_read)
|
|
159
|
+
speed = hbytes(bytes_read / end)
|
|
160
|
+
stats = (f"Upload: {size[0]:.2f}{size[1]} in {end}s -> "
|
|
161
|
+
f"{speed[0]:.2f}{speed[1]}ps SHA256: {hexdigest}")
|
|
160
162
|
log.info(stats)
|
|
161
163
|
|
|
162
164
|
if bytes_read != req.content_length:
|
|
@@ -9,6 +9,7 @@ EXAMPLES_PATH = os.path.dirname(__file__)
|
|
|
9
9
|
python_path.insert(0, os.path.abspath(
|
|
10
10
|
os.path.join(EXAMPLES_PATH, os.path.pardir)))
|
|
11
11
|
|
|
12
|
+
# pylint: disable=wrong-import-position
|
|
12
13
|
from poorwsgi import Application, state # noqa
|
|
13
14
|
from poorwsgi.response import JSONResponse # noqa
|
|
14
15
|
|
|
@@ -31,7 +31,7 @@ python_path.insert(0, path.abspath(
|
|
|
31
31
|
from poorwsgi import Application, state # noqa
|
|
32
32
|
from poorwsgi.response import Response, abort, HTTPException, \
|
|
33
33
|
JSONResponse # noqa
|
|
34
|
-
from poorwsgi.request import Request
|
|
34
|
+
from poorwsgi.request import Request # noqa
|
|
35
35
|
from poorwsgi.openapi_wrapper import OpenAPIRequest, \
|
|
36
36
|
OpenAPIResponse # noqa
|
|
37
37
|
from poorwsgi.session import PoorSession # noqa
|
|
@@ -111,7 +111,7 @@ def after_each_response(req, res):
|
|
|
111
111
|
return res
|
|
112
112
|
except OpenAPIError as error:
|
|
113
113
|
log.error("API output error: %s", str(error))
|
|
114
|
-
|
|
114
|
+
raise
|
|
115
115
|
return res
|
|
116
116
|
|
|
117
117
|
|
|
@@ -15,16 +15,18 @@ sys.path.insert(0, os.path.abspath(
|
|
|
15
15
|
|
|
16
16
|
# pylint: disable=import-error, disable=wrong-import-position
|
|
17
17
|
from poorwsgi import Application, state # noqa
|
|
18
|
-
from poorwsgi.response import JSONResponse
|
|
18
|
+
from poorwsgi.response import JSONResponse # noqa
|
|
19
19
|
|
|
20
20
|
logger = log.getLogger()
|
|
21
21
|
logger.setLevel("DEBUG")
|
|
22
22
|
app = application = Application("large_file")
|
|
23
23
|
app.debug = True
|
|
24
24
|
|
|
25
|
+
# pylint: disable=duplicate-code
|
|
26
|
+
|
|
25
27
|
|
|
26
28
|
@app.route('/blackhole/<filename>', method=state.METHOD_PUT)
|
|
27
|
-
def blackhole_put(req, filename:str):
|
|
29
|
+
def blackhole_put(req, filename: str):
|
|
28
30
|
"""Upload file via PUT method like in webdav"""
|
|
29
31
|
checksum = sha256()
|
|
30
32
|
uploaded = 0
|
|
@@ -46,7 +48,7 @@ def blackhole_put(req, filename:str):
|
|
|
46
48
|
|
|
47
49
|
|
|
48
50
|
@app.route('/temporary/<filename>', method=state.METHOD_PUT)
|
|
49
|
-
def temporary_put(req, filename:str):
|
|
51
|
+
def temporary_put(req, filename: str):
|
|
50
52
|
"""Upload file via PUT method like in webdav"""
|
|
51
53
|
checksum = sha256()
|
|
52
54
|
uploaded = 0
|
|
@@ -143,5 +145,5 @@ class ThreadingWSGIServer(ThreadingMixIn, WSGIServer):
|
|
|
143
145
|
if __name__ == '__main__':
|
|
144
146
|
ADDRESS = sys.argv[1] if len(sys.argv) > 1 else '127.0.0.1'
|
|
145
147
|
httpd = make_server(ADDRESS, 8080, app, ThreadingWSGIServer)
|
|
146
|
-
print("Starting to serve on http
|
|
148
|
+
print(f"Starting to serve on http://{ADDRESS}:8080")
|
|
147
149
|
httpd.serve_forever()
|