Skip to content

Commit 04a94c0

Browse files
committed
fix handling of body parameters in Context.post(...)
1 parent 26d6156 commit 04a94c0

File tree

2 files changed

+89
-11
lines changed

2 files changed

+89
-11
lines changed

splunklib/binding.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -726,8 +726,10 @@ def post(self, path_segment, owner=None, app=None, sharing=None, headers=None, *
726726
parameters.
727727
:param body: Parameters to be used in the post body. If specified,
728728
any parameters in the query will be applied to the URL instead of
729-
the body.
730-
:type body: dict
729+
the body. If a dict is supplied, the key-value pairs will be form
730+
encoded. If a string is supplied, the body will be passed through
731+
in the request unchanged.
732+
:type body: ``dict`` or ``str``
731733
:return: The response from the server.
732734
:rtype: ``dict`` with keys ``body``, ``headers``, ``reason``,
733735
and ``status``
@@ -1226,6 +1228,8 @@ def post(self, url, headers=None, **kwargs):
12261228
headers.append(("Content-Type", "application/x-www-form-urlencoded"))
12271229

12281230
body = kwargs.pop('body')
1231+
if isinstance(body, dict):
1232+
body = _encode(**body).encode('utf-8')
12291233
if len(kwargs) > 0:
12301234
url = url + UrlEncoded('?' + _encode(**kwargs), skip_encode=True)
12311235
else:

tests/test_binding.py

Lines changed: 83 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,16 @@
1616

1717

1818
from __future__ import absolute_import
19-
import time
2019
from io import BytesIO
20+
from threading import Thread
2121

22+
from splunklib.six.moves import BaseHTTPServer
2223
from splunklib.six.moves.urllib.request import Request, urlopen
2324
from splunklib.six.moves.urllib.error import HTTPError
24-
from splunklib.six import StringIO
25+
import splunklib.six as six
2526
from xml.etree.ElementTree import XML
2627

28+
import json
2729
import logging
2830
from tests import testlib
2931
import unittest
@@ -104,7 +106,7 @@ def test_read_partial(self):
104106

105107
def test_readable(self):
106108
txt = "abcd"
107-
response = binding.ResponseReader(StringIO(txt))
109+
response = binding.ResponseReader(six.StringIO(txt))
108110
self.assertTrue(response.readable())
109111

110112
def test_readinto_bytearray(self):
@@ -813,8 +815,8 @@ class TestPostWithBodyParam(unittest.TestCase):
813815

814816
def test_post(self):
815817
def handler(url, message, **kwargs):
816-
assert url == "https://localhost:8089/servicesNS/testowner/testapp/foo/bar"
817-
assert message["body"]["testkey"] == "testvalue"
818+
assert six.ensure_str(url) == "https://localhost:8089/servicesNS/testowner/testapp/foo/bar"
819+
assert six.ensure_str(message["body"]) == "testkey=testvalue"
818820
return splunklib.data.Record({
819821
"status": 200,
820822
"headers": [],
@@ -825,7 +827,7 @@ def handler(url, message, **kwargs):
825827
def test_post_with_params_and_body(self):
826828
def handler(url, message, **kwargs):
827829
assert url == "https://localhost:8089/servicesNS/testowner/testapp/foo/bar?extrakey=extraval"
828-
assert message["body"]["testkey"] == "testvalue"
830+
assert six.ensure_str(message["body"]) == "testkey=testvalue"
829831
return splunklib.data.Record({
830832
"status": 200,
831833
"headers": [],
@@ -836,18 +838,90 @@ def handler(url, message, **kwargs):
836838
def test_post_with_params_and_no_body(self):
837839
def handler(url, message, **kwargs):
838840
assert url == "https://localhost:8089/servicesNS/testowner/testapp/foo/bar"
839-
assert message["body"] == "extrakey=extraval"
841+
assert six.ensure_str(message["body"]) == "extrakey=extraval"
840842
return splunklib.data.Record({
841843
"status": 200,
842844
"headers": [],
843845
})
844846
ctx = binding.Context(handler=handler)
845847
ctx.post("foo/bar", extrakey="extraval", owner="testowner", app="testapp")
846848

847-
def test_with_body_with_full_request(self):
848-
class TestRequestHandler(BaseHTTPRequestHandler):
849+
850+
def _wrap_handler(func, response_code=200, body=""):
851+
def wrapped(handler_self):
852+
result = func(handler_self)
853+
if result is None:
854+
handler_self.send_response(response_code)
855+
handler_self.end_headers()
856+
handler_self.wfile.write(body)
857+
return wrapped
858+
859+
860+
class MockServer(object):
861+
def __init__(self, port=9093, **handlers):
862+
methods = {"do_" + k: _wrap_handler(v) for (k, v) in handlers.items()}
863+
864+
def init(handler_self, socket, address, server):
865+
BaseHTTPServer.BaseHTTPRequestHandler.__init__(handler_self, socket, address, server)
866+
867+
def log(*args): # To silence server access logs
849868
pass
850869

870+
methods["__init__"] = init
871+
methods["log_message"] = log
872+
Handler = type("Handler",
873+
(BaseHTTPServer.BaseHTTPRequestHandler, object),
874+
methods)
875+
self._svr = BaseHTTPServer.HTTPServer(("localhost", port), Handler)
876+
877+
def run():
878+
self._svr.handle_request()
879+
self._thread = Thread(target=run)
880+
self._thread.daemon = True
881+
882+
def __enter__(self):
883+
self._thread.start()
884+
return self._svr
885+
886+
def __exit__(self, typ, value, traceback):
887+
self._thread.join(10)
888+
self._svr.server_close()
889+
890+
891+
class TestFullPost(unittest.TestCase):
892+
893+
def test_post_with_body_urlencoded(self):
894+
def check_response(handler):
895+
length = int(handler.headers.get('content-length', 0))
896+
body = handler.rfile.read(length)
897+
assert six.ensure_str(body) == "foo=bar"
898+
899+
with MockServer(POST=check_response):
900+
ctx = binding.connect(port=9093, scheme='http', token="waffle")
901+
ctx.post("/", foo="bar")
902+
903+
def test_post_with_body_string(self):
904+
def check_response(handler):
905+
length = int(handler.headers.get('content-length', 0))
906+
body = handler.rfile.read(length)
907+
assert six.ensure_str(handler.headers['content-type']) == 'application/json'
908+
assert json.loads(body)["baz"] == "baf"
909+
910+
with MockServer(POST=check_response):
911+
ctx = binding.connect(port=9093, scheme='http', token="waffle", headers=[("Content-Type", "application/json")])
912+
ctx.post("/", foo="bar", body='{"baz": "baf"}')
913+
914+
def test_post_with_body_dict(self):
915+
def check_response(handler):
916+
length = int(handler.headers.get('content-length', 0))
917+
body = handler.rfile.read(length)
918+
assert six.ensure_str(handler.headers['content-type']) == 'application/x-www-form-urlencoded'
919+
assert six.ensure_str(body) == 'baz=baf&hep=cat'
920+
921+
with MockServer(POST=check_response):
922+
ctx = binding.connect(port=9093, scheme='http', token="waffle")
923+
ctx.post("/", foo="bar", body={"baz": "baf", "hep": "cat"})
924+
851925

852926
if __name__ == "__main__":
853927
try:

0 commit comments

Comments
 (0)