1
0
mirror of https://github.com/webrecorder/pywb.git synced 2025-03-15 08:04:49 +01:00

Compare commits

..

6 Commits

Author SHA1 Message Date
Tessa Walsh
7b0f8b5860
Use JSON values in query string for JSON request bodies (#893)
This commit also adds a more complicated JSON test case that is
also in warcio.js to ensure parity.

Treat numbers like JavaScript's Number.prototype.toString() by
dropping decimal from floats if they represent whole number.
2024-11-13 14:07:35 -08:00
Hellseher
b44c93bf6e
requirements: Adjust installation of Py3AMF module. (#920)
Move Py3AMF from setup.py load_requirements to requirements.txt

---------

Co-authored-by: Tessa Walsh <tessa@bitarchivist.net>
2024-11-07 12:09:35 -05:00
Tessa Walsh
97fffe3a34 Once more, now 2.8.3 2024-04-26 10:32:43 +02:00
Tessa Walsh
6205646b9b Bump version to 2.8.2 2024-04-26 10:26:56 +02:00
Tessa Walsh
23891be2f1 Bump version 2024-04-26 10:21:03 +02:00
Ed Summers
b190dddee9
Pin redis for fakeredis (#904)
It looks like `poetry install` will install the latest version of redis
(v5.0.4) instead of what pip installs (v2.10.6). Unfortunately this
means that the old version of fakeredis that is pinned in the
requirements.txt will not work properly.

Fixes #903
2024-04-26 04:03:27 -04:00
6 changed files with 37 additions and 19 deletions

View File

@ -13,7 +13,7 @@ Web Archiving Tools for All
`View the full pywb documentation <https://pywb.readthedocs.org>`_ `View the full pywb documentation <https://pywb.readthedocs.org>`_
**pywb** is a Python (2 and 3) web archiving toolkit for replaying web archives large and small as accurately as possible. **pywb** is a Python 3 web archiving toolkit for replaying web archives large and small as accurately as possible.
The toolkit now also includes new features for creating high-fidelity web archives. The toolkit now also includes new features for creating high-fidelity web archives.
This toolset forms the foundation of Webrecorder project, but also provides a generic web archiving toolkit This toolset forms the foundation of Webrecorder project, but also provides a generic web archiving toolkit

View File

@ -1,4 +1,4 @@
__version__ = '2.8.0' __version__ = '2.8.3'
if __name__ == '__main__': if __name__ == '__main__':
print(__version__) print(__version__)

View File

@ -11,6 +11,7 @@ from io import BytesIO
import base64 import base64
import cgi import cgi
import json import json
import math
import sys import sys
@ -328,6 +329,21 @@ class MethodQueryCanonicalizer(object):
_parser(v, name) _parser(v, name)
elif name: elif name:
if isinstance(json_obj, bool) and json_obj:
data[get_key(name)] = "true"
elif isinstance(json_obj, bool):
data[get_key(name)] = "false"
elif json_obj is None:
data[get_key(name)] = "null"
elif isinstance(json_obj, float):
# Treat floats like JavaScript's Number.prototype.toString(),
# drop decimal if float represents a whole number.
fraction, _ = math.modf(json_obj)
if fraction == 0.0:
data[get_key(name)] = str(int(json_obj))
else:
data[get_key(name)] = str(json_obj)
else:
data[get_key(name)] = str(json_obj) data[get_key(name)] = str(json_obj)
_parser(json.loads(string)) _parser(json.loads(string))

View File

@ -82,44 +82,49 @@ Foo: Bar\r\n\
class TestPostQueryExtract(object): class TestPostQueryExtract(object):
@classmethod @classmethod
def setup_class(cls): def setup_class(cls):
cls.post_data = b'foo=bar&dir=%2Fbaz' cls.post_data = b'foo=bar&dir=%2Fbaz&do=true&re=false&re=null'
cls.binary_post_data = b'\x816l`L\xa04P\x0e\xe0r\x02\xb5\x89\x19\x00fP\xdb\x0e\xb0\x02,' cls.binary_post_data = b'\x816l`L\xa04P\x0e\xe0r\x02\xb5\x89\x19\x00fP\xdb\x0e\xb0\x02,'
def test_post_extract_1(self): def test_post_extract_1(self):
mq = MethodQueryCanonicalizer('POST', 'application/x-www-form-urlencoded', mq = MethodQueryCanonicalizer('POST', 'application/x-www-form-urlencoded',
len(self.post_data), BytesIO(self.post_data)) len(self.post_data), BytesIO(self.post_data))
assert mq.append_query('http://example.com/') == 'http://example.com/?__wb_method=POST&foo=bar&dir=/baz' assert mq.append_query('http://example.com/') == 'http://example.com/?__wb_method=POST&foo=bar&dir=/baz&do=true&re=false&re=null'
assert mq.append_query('http://example.com/?123=ABC') == 'http://example.com/?123=ABC&__wb_method=POST&foo=bar&dir=/baz' assert mq.append_query('http://example.com/?123=ABC') == 'http://example.com/?123=ABC&__wb_method=POST&foo=bar&dir=/baz&do=true&re=false&re=null'
def test_post_extract_json(self): def test_post_extract_json(self):
post_data = b'{"a": "b", "c": {"a": 2}, "d": "e"}' post_data = b'{"a": "b", "c": {"a": 2}, "d": "e", "f": true, "g": [false, null]}'
mq = MethodQueryCanonicalizer('POST', 'application/json', mq = MethodQueryCanonicalizer('POST', 'application/json',
len(post_data), BytesIO(post_data)) len(post_data), BytesIO(post_data))
assert mq.append_query('http://example.com/') == 'http://example.com/?__wb_method=POST&a=b&a.2_=2&d=e' assert mq.append_query('http://example.com/') == 'http://example.com/?__wb_method=POST&a=b&a.2_=2&d=e&f=true&g=false&g.2_=null'
post_data = b'{"type": "event", "id": 44.0, "float": 35.7, "values": [true, false, null], "source": {"type": "component", "id": "a+b&c= d", "values": [3, 4]}}'
mq = MethodQueryCanonicalizer('POST', 'application/json',
len(post_data), BytesIO(post_data))
assert mq.append_query('http://example.com/events') == 'http://example.com/events?__wb_method=POST&type=event&id=44&float=35.7&values=true&values.2_=false&values.3_=null&type.2_=component&id.2_=a%2Bb%26c%3D+d&values.4_=3&values.5_=4'
def test_put_extract_method(self): def test_put_extract_method(self):
mq = MethodQueryCanonicalizer('PUT', 'application/x-www-form-urlencoded', mq = MethodQueryCanonicalizer('PUT', 'application/x-www-form-urlencoded',
len(self.post_data), BytesIO(self.post_data)) len(self.post_data), BytesIO(self.post_data))
assert mq.append_query('http://example.com/') == 'http://example.com/?__wb_method=PUT&foo=bar&dir=/baz' assert mq.append_query('http://example.com/') == 'http://example.com/?__wb_method=PUT&foo=bar&dir=/baz&do=true&re=false&re=null'
def test_post_extract_non_form_data_1(self): def test_post_extract_non_form_data_1(self):
mq = MethodQueryCanonicalizer('POST', 'application/octet-stream', mq = MethodQueryCanonicalizer('POST', 'application/octet-stream',
len(self.post_data), BytesIO(self.post_data)) len(self.post_data), BytesIO(self.post_data))
#base64 encoded data #base64 encoded data
assert mq.append_query('http://example.com/') == 'http://example.com/?__wb_method=POST&__wb_post_data=Zm9vPWJhciZkaXI9JTJGYmF6' assert mq.append_query('http://example.com/') == 'http://example.com/?__wb_method=POST&__wb_post_data=Zm9vPWJhciZkaXI9JTJGYmF6JmRvPXRydWUmcmU9ZmFsc2UmcmU9bnVsbA=='
def test_post_extract_non_form_data_2(self): def test_post_extract_non_form_data_2(self):
mq = MethodQueryCanonicalizer('POST', 'text/plain', mq = MethodQueryCanonicalizer('POST', 'text/plain',
len(self.post_data), BytesIO(self.post_data)) len(self.post_data), BytesIO(self.post_data))
#base64 encoded data #base64 encoded data
assert mq.append_query('http://example.com/pathbar?id=123') == 'http://example.com/pathbar?id=123&__wb_method=POST&__wb_post_data=Zm9vPWJhciZkaXI9JTJGYmF6' assert mq.append_query('http://example.com/pathbar?id=123') == 'http://example.com/pathbar?id=123&__wb_method=POST&__wb_post_data=Zm9vPWJhciZkaXI9JTJGYmF6JmRvPXRydWUmcmU9ZmFsc2UmcmU9bnVsbA=='
def test_post_extract_length_invalid_ignore(self): def test_post_extract_length_invalid_ignore(self):
mq = MethodQueryCanonicalizer('POST', 'application/x-www-form-urlencoded', mq = MethodQueryCanonicalizer('POST', 'application/x-www-form-urlencoded',
@ -136,13 +141,13 @@ class TestPostQueryExtract(object):
mq = MethodQueryCanonicalizer('POST', 'application/x-www-form-urlencoded', mq = MethodQueryCanonicalizer('POST', 'application/x-www-form-urlencoded',
len(self.post_data) - 4, BytesIO(self.post_data)) len(self.post_data) - 4, BytesIO(self.post_data))
assert mq.append_query('http://example.com/') == 'http://example.com/?__wb_method=POST&foo=bar&dir=%2' assert mq.append_query('http://example.com/') == 'http://example.com/?__wb_method=POST&foo=bar&dir=/baz&do=true&re=false&re='
def test_post_extract_length_too_long(self): def test_post_extract_length_too_long(self):
mq = MethodQueryCanonicalizer('POST', 'application/x-www-form-urlencoded', mq = MethodQueryCanonicalizer('POST', 'application/x-www-form-urlencoded',
len(self.post_data) + 4, BytesIO(self.post_data)) len(self.post_data) + 4, BytesIO(self.post_data))
assert mq.append_query('http://example.com/') == 'http://example.com/?__wb_method=POST&foo=bar&dir=/baz' assert mq.append_query('http://example.com/') == 'http://example.com/?__wb_method=POST&foo=bar&dir=/baz&do=true&re=false&re=null'
def test_post_extract_malformed_form_data(self): def test_post_extract_malformed_form_data(self):
mq = MethodQueryCanonicalizer('POST', 'application/x-www-form-urlencoded', mq = MethodQueryCanonicalizer('POST', 'application/x-www-form-urlencoded',
@ -155,7 +160,7 @@ class TestPostQueryExtract(object):
mq = MethodQueryCanonicalizer('POST', 'multipart/form-data', mq = MethodQueryCanonicalizer('POST', 'multipart/form-data',
len(self.post_data), BytesIO(self.post_data)) len(self.post_data), BytesIO(self.post_data))
assert mq.append_query('http://example.com/') == 'http://example.com/?__wb_method=POST&__wb_post_data=Zm9vPWJhciZkaXI9JTJGYmF6' assert mq.append_query('http://example.com/') == 'http://example.com/?__wb_method=POST&__wb_post_data=Zm9vPWJhciZkaXI9JTJGYmF6JmRvPXRydWUmcmU9ZmFsc2UmcmU9bnVsbA=='
def test_options(self): def test_options(self):

View File

@ -1,7 +1,7 @@
six six
warcio>=1.7.1 warcio>=1.7.1
requests requests
redis redis==2.10.6
jinja2>=3.1.2 jinja2>=3.1.2
surt>=0.3.1 surt>=0.3.1
brotlipy brotlipy
@ -18,3 +18,4 @@ tldextract
python-dateutil python-dateutil
markupsafe>=2.1.1 markupsafe>=2.1.1
ua_parser ua_parser
py3AMF

View File

@ -62,10 +62,6 @@ def generate_git_hash_py(pkg, filename='git_hash.py'):
def load_requirements(filename): def load_requirements(filename):
with open(filename, 'rt') as fh: with open(filename, 'rt') as fh:
requirements = fh.read().rstrip().split('\n') requirements = fh.read().rstrip().split('\n')
if sys.version_info > (3, 0):
requirements.append("py3AMF")
else:
requirements.append("pyAMF")
return requirements return requirements