Skip to content

Commit 7b0f8b5

Browse files
authored
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.
1 parent b44c93b commit 7b0f8b5

File tree

2 files changed

+33
-12
lines changed

2 files changed

+33
-12
lines changed

pywb/warcserver/inputrequest.py

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import base64
1212
import cgi
1313
import json
14+
import math
1415
import sys
1516

1617

@@ -328,7 +329,22 @@ def _parser(json_obj, name=""):
328329
_parser(v, name)
329330

330331
elif name:
331-
data[get_key(name)] = str(json_obj)
332+
if isinstance(json_obj, bool) and json_obj:
333+
data[get_key(name)] = "true"
334+
elif isinstance(json_obj, bool):
335+
data[get_key(name)] = "false"
336+
elif json_obj is None:
337+
data[get_key(name)] = "null"
338+
elif isinstance(json_obj, float):
339+
# Treat floats like JavaScript's Number.prototype.toString(),
340+
# drop decimal if float represents a whole number.
341+
fraction, _ = math.modf(json_obj)
342+
if fraction == 0.0:
343+
data[get_key(name)] = str(int(json_obj))
344+
else:
345+
data[get_key(name)] = str(json_obj)
346+
else:
347+
data[get_key(name)] = str(json_obj)
332348

333349
_parser(json.loads(string))
334350
return urlencode(data)

pywb/warcserver/test/test_inputreq.py

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -82,44 +82,49 @@ def test_post_req(self):
8282
class TestPostQueryExtract(object):
8383
@classmethod
8484
def setup_class(cls):
85-
cls.post_data = b'foo=bar&dir=%2Fbaz'
85+
cls.post_data = b'foo=bar&dir=%2Fbaz&do=true&re=false&re=null'
8686
cls.binary_post_data = b'\x816l`L\xa04P\x0e\xe0r\x02\xb5\x89\x19\x00fP\xdb\x0e\xb0\x02,'
8787

8888
def test_post_extract_1(self):
8989
mq = MethodQueryCanonicalizer('POST', 'application/x-www-form-urlencoded',
9090
len(self.post_data), BytesIO(self.post_data))
9191

92-
assert mq.append_query('http://example.com/') == 'http://example.com/?__wb_method=POST&foo=bar&dir=/baz'
92+
assert mq.append_query('http://example.com/') == 'http://example.com/?__wb_method=POST&foo=bar&dir=/baz&do=true&re=false&re=null'
9393

94-
assert mq.append_query('http://example.com/?123=ABC') == 'http://example.com/?123=ABC&__wb_method=POST&foo=bar&dir=/baz'
94+
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'
9595

9696
def test_post_extract_json(self):
97-
post_data = b'{"a": "b", "c": {"a": 2}, "d": "e"}'
97+
post_data = b'{"a": "b", "c": {"a": 2}, "d": "e", "f": true, "g": [false, null]}'
9898
mq = MethodQueryCanonicalizer('POST', 'application/json',
9999
len(post_data), BytesIO(post_data))
100100

101-
assert mq.append_query('http://example.com/') == 'http://example.com/?__wb_method=POST&a=b&a.2_=2&d=e'
101+
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'
102102

103+
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]}}'
104+
mq = MethodQueryCanonicalizer('POST', 'application/json',
105+
len(post_data), BytesIO(post_data))
106+
107+
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'
103108

104109
def test_put_extract_method(self):
105110
mq = MethodQueryCanonicalizer('PUT', 'application/x-www-form-urlencoded',
106111
len(self.post_data), BytesIO(self.post_data))
107112

108-
assert mq.append_query('http://example.com/') == 'http://example.com/?__wb_method=PUT&foo=bar&dir=/baz'
113+
assert mq.append_query('http://example.com/') == 'http://example.com/?__wb_method=PUT&foo=bar&dir=/baz&do=true&re=false&re=null'
109114

110115
def test_post_extract_non_form_data_1(self):
111116
mq = MethodQueryCanonicalizer('POST', 'application/octet-stream',
112117
len(self.post_data), BytesIO(self.post_data))
113118

114119
#base64 encoded data
115-
assert mq.append_query('http://example.com/') == 'http://example.com/?__wb_method=POST&__wb_post_data=Zm9vPWJhciZkaXI9JTJGYmF6'
120+
assert mq.append_query('http://example.com/') == 'http://example.com/?__wb_method=POST&__wb_post_data=Zm9vPWJhciZkaXI9JTJGYmF6JmRvPXRydWUmcmU9ZmFsc2UmcmU9bnVsbA=='
116121

117122
def test_post_extract_non_form_data_2(self):
118123
mq = MethodQueryCanonicalizer('POST', 'text/plain',
119124
len(self.post_data), BytesIO(self.post_data))
120125

121126
#base64 encoded data
122-
assert mq.append_query('http://example.com/pathbar?id=123') == 'http://example.com/pathbar?id=123&__wb_method=POST&__wb_post_data=Zm9vPWJhciZkaXI9JTJGYmF6'
127+
assert mq.append_query('http://example.com/pathbar?id=123') == 'http://example.com/pathbar?id=123&__wb_method=POST&__wb_post_data=Zm9vPWJhciZkaXI9JTJGYmF6JmRvPXRydWUmcmU9ZmFsc2UmcmU9bnVsbA=='
123128

124129
def test_post_extract_length_invalid_ignore(self):
125130
mq = MethodQueryCanonicalizer('POST', 'application/x-www-form-urlencoded',
@@ -136,13 +141,13 @@ def test_post_extract_length_too_short(self):
136141
mq = MethodQueryCanonicalizer('POST', 'application/x-www-form-urlencoded',
137142
len(self.post_data) - 4, BytesIO(self.post_data))
138143

139-
assert mq.append_query('http://example.com/') == 'http://example.com/?__wb_method=POST&foo=bar&dir=%2'
144+
assert mq.append_query('http://example.com/') == 'http://example.com/?__wb_method=POST&foo=bar&dir=/baz&do=true&re=false&re='
140145

141146
def test_post_extract_length_too_long(self):
142147
mq = MethodQueryCanonicalizer('POST', 'application/x-www-form-urlencoded',
143148
len(self.post_data) + 4, BytesIO(self.post_data))
144149

145-
assert mq.append_query('http://example.com/') == 'http://example.com/?__wb_method=POST&foo=bar&dir=/baz'
150+
assert mq.append_query('http://example.com/') == 'http://example.com/?__wb_method=POST&foo=bar&dir=/baz&do=true&re=false&re=null'
146151

147152
def test_post_extract_malformed_form_data(self):
148153
mq = MethodQueryCanonicalizer('POST', 'application/x-www-form-urlencoded',
@@ -155,7 +160,7 @@ def test_post_extract_no_boundary_in_multipart_form_mimetype(self):
155160
mq = MethodQueryCanonicalizer('POST', 'multipart/form-data',
156161
len(self.post_data), BytesIO(self.post_data))
157162

158-
assert mq.append_query('http://example.com/') == 'http://example.com/?__wb_method=POST&__wb_post_data=Zm9vPWJhciZkaXI9JTJGYmF6'
163+
assert mq.append_query('http://example.com/') == 'http://example.com/?__wb_method=POST&__wb_post_data=Zm9vPWJhciZkaXI9JTJGYmF6JmRvPXRydWUmcmU9ZmFsc2UmcmU9bnVsbA=='
159164

160165

161166
def test_options(self):

0 commit comments

Comments
 (0)