From 26c7e0f5f8fc4554c4ae4b97f0a2b6fbdaa37510 Mon Sep 17 00:00:00 2001 From: debugtalk Date: Fri, 30 Nov 2018 14:20:44 +0800 Subject: [PATCH] bugfix: POST content-type is application/x-www-form-urlencoded --- httprunner/built_in.py | 22 ++++++++++++++++------ httprunner/client.py | 8 +++++--- httprunner/utils.py | 27 +++++++++++++++++++++++++++ tests/test_client.py | 18 ++++++++++++++++++ 4 files changed, 66 insertions(+), 9 deletions(-) diff --git a/httprunner/built_in.py b/httprunner/built_in.py index e2fb0e69..ff36dcfd 100644 --- a/httprunner/built_in.py +++ b/httprunner/built_in.py @@ -14,6 +14,7 @@ import time from httprunner.compat import basestring, builtin_str, integer_types, str from httprunner.exceptions import ParamsError +from httprunner.utils import convert_dict_to_params, lower_dict_keys from requests_toolbelt import MultipartEncoder @@ -134,14 +135,23 @@ def endswith(check_value, expect_value): """ def setup_hook_prepare_kwargs(request): if request["method"] == "POST": - content_type = request.get("headers", {}).get("content-type") + req_headers = lower_dict_keys(request.get("headers", {})) + content_type = req_headers.get("content-type") if content_type and "data" in request: - # if request content-type is application/json, request data should be dumped - if content_type.startswith("application/json") and isinstance(request["data"], (dict, list)): - request["data"] = json.dumps(request["data"]) + req_data = request["data"] - if isinstance(request["data"], str): - request["data"] = request["data"].encode('utf-8') + # if request content-type is application/json, request data should be dumped + if content_type.startswith("application/json") and isinstance(req_data, (dict, list)): + req_data = json.dumps(req_data) + + # if request content-type is application/x-www-form-urlencoded, request data should be in params format + elif content_type.startswith("application/x-www-form-urlencoded") and isinstance(req_data, dict): + req_data = convert_dict_to_params(req_data) + + if isinstance(req_data, str): + req_data = req_data.encode('utf-8') + + request["data"] = req_data def sleep_N_secs(n_secs): """ sleep n seconds diff --git a/httprunner/client.py b/httprunner/client.py index d8c12b89..7bd12b07 100644 --- a/httprunner/client.py +++ b/httprunner/client.py @@ -5,7 +5,7 @@ import time import requests import urllib3 from httprunner import logger -from httprunner.utils import build_url +from httprunner.utils import build_url, lower_dict_keys from requests import Request, Response from requests.exceptions import (InvalidSchema, InvalidURL, MissingSchema, RequestException) @@ -138,11 +138,13 @@ class HttpSession(requests.Session): self.meta_data["response"]["url"] = response.url self.meta_data["response"]["status_code"] = response.status_code self.meta_data["response"]["reason"] = response.reason - self.meta_data["response"]["headers"] = dict(response.headers) self.meta_data["response"]["cookies"] = response.cookies or {} self.meta_data["response"]["encoding"] = response.encoding + resp_headers = dict(response.headers) + self.meta_data["response"]["headers"] = resp_headers - content_type = response.headers.get("Content-Type", "") + lower_resp_headers = lower_dict_keys(resp_headers) + content_type = lower_resp_headers.get("content-type", "") self.meta_data["response"]["content_type"] = content_type if "image" in content_type: diff --git a/httprunner/utils.py b/httprunner/utils.py index 3af67f43..586e57fc 100644 --- a/httprunner/utils.py +++ b/httprunner/utils.py @@ -152,6 +152,7 @@ def get_uniform_comparator(comparator): else: return comparator + def deep_update_dict(origin_dict, override_dict): """ update origin dict with override dict recursively e.g. origin_dict = {'a': 1, 'b': {'c': 2, 'd': 4}} @@ -173,6 +174,31 @@ def deep_update_dict(origin_dict, override_dict): return origin_dict + +def convert_dict_to_params(src_dict): + """ convert dict to params string + + Args: + src_dict (dict): source mapping data structure + + Returns: + str: string params data + + Examples: + >>> src_dict = { + "a": 1, + "b": 2 + } + >>> convert_dict_to_params(src_dict) + >>> "a=1&b=2" + + """ + return "&".join([ + "{}={}".format(key, value) + for key, value in src_dict.items() + ]) + + def lower_dict_keys(origin_dict): """ convert keys in dict to lower case @@ -210,6 +236,7 @@ def lower_dict_keys(origin_dict): for key, value in origin_dict.items() } + def lower_test_dict_keys(test_dict): """ convert keys in test_dict to lower case, convertion will occur in two places: 1, all keys in test_dict; diff --git a/tests/test_client.py b/tests/test_client.py index f756a4b9..1facac54 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -70,3 +70,21 @@ class TestHttpClient(ApiServerUnittest): } setup_hook_prepare_kwargs(request) self.assertIsInstance(request["data"], bytes) + + def test_prepare_kwargs_content_type_x_www_form_urlencoded(self): + request = { + "url": "/path", + "method": "POST", + "headers": { + "content-type": "application/x-www-form-urlencoded; charset=utf-8" + }, + "data": { + "a": 1, + "b": 2 + } + } + setup_hook_prepare_kwargs(request) + self.assertIsInstance(request["data"], bytes) + self.assertIn(b'a=1', request["data"]) + self.assertIn(b'&', request["data"]) + self.assertIn(b'b=2', request["data"])