1515
1616from __future__ import absolute_import
1717
18+ from typing import Union
19+
1820import json
1921import logging
2022from pipes import quote as pquote
@@ -78,6 +80,27 @@ def get_url_without_trailing_slash(value):
7880 return result
7981
8082
83+ def sanitize_url (url : Union [bytes , str ]) -> str :
84+ """
85+ Sanitize the provided url and ensure it's a valid unicode string.
86+
87+ By default, sys.argv will contain a unicode string where the actual item values which contain
88+ unicode sequences are escaped using unicode surrogates.
89+
90+ For example, if "examples.test_rule_utf8_náme" value is specified as a CLI argument, sys.argv
91+ and as such url, would contain "examples.test_rule_utf8_n%ED%B3%83%ED%B2%A1me" which is not
92+ what we want.
93+
94+ This won't work correctly when sending requests to the API. As such, we correctly escape the
95+ value to the unicode string here and then let the http layer (requests) correctly url encode
96+ this value.
97+ """
98+ if isinstance (url , str ):
99+ url = url .encode ("ascii" , "surrogateescape" ).decode ("utf-8" )
100+
101+ return url
102+
103+
81104class HTTPClient (object ):
82105 def __init__ (self , root , cacert = None , debug = False ):
83106 self .root = get_url_without_trailing_slash (root )
@@ -87,6 +110,7 @@ def __init__(self, root, cacert=None, debug=False):
87110 @add_ssl_verify_to_kwargs
88111 @add_auth_token_to_headers
89112 def get (self , url , ** kwargs ):
113+ url = sanitize_url (url )
90114 response = requests .get (self .root + url , ** kwargs )
91115 response = self ._response_hook (response = response )
92116 return response
@@ -95,13 +119,15 @@ def get(self, url, **kwargs):
95119 @add_auth_token_to_headers
96120 @add_json_content_type_to_headers
97121 def post (self , url , data , ** kwargs ):
122+ url = sanitize_url (url )
98123 response = requests .post (self .root + url , json .dumps (data ), ** kwargs )
99124 response = self ._response_hook (response = response )
100125 return response
101126
102127 @add_ssl_verify_to_kwargs
103128 @add_auth_token_to_headers
104129 def post_raw (self , url , data , ** kwargs ):
130+ url = sanitize_url (url )
105131 response = requests .post (self .root + url , data , ** kwargs )
106132 response = self ._response_hook (response = response )
107133 return response
@@ -110,6 +136,7 @@ def post_raw(self, url, data, **kwargs):
110136 @add_auth_token_to_headers
111137 @add_json_content_type_to_headers
112138 def put (self , url , data , ** kwargs ):
139+ url = sanitize_url (url )
113140 response = requests .put (self .root + url , json .dumps (data ), ** kwargs )
114141 response = self ._response_hook (response = response )
115142 return response
@@ -118,13 +145,15 @@ def put(self, url, data, **kwargs):
118145 @add_auth_token_to_headers
119146 @add_json_content_type_to_headers
120147 def patch (self , url , data , ** kwargs ):
148+ url = sanitize_url (url )
121149 response = requests .patch (self .root + url , data , ** kwargs )
122150 response = self ._response_hook (response = response )
123151 return response
124152
125153 @add_ssl_verify_to_kwargs
126154 @add_auth_token_to_headers
127155 def delete (self , url , ** kwargs ):
156+ url = sanitize_url (url )
128157 response = requests .delete (self .root + url , ** kwargs )
129158 response = self ._response_hook (response = response )
130159 return response
0 commit comments