15
15
16
16
from __future__ import absolute_import
17
17
18
+ from typing import Union
19
+
18
20
import json
19
21
import logging
20
22
from pipes import quote as pquote
@@ -78,6 +80,27 @@ def get_url_without_trailing_slash(value):
78
80
return result
79
81
80
82
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
+
81
104
class HTTPClient (object ):
82
105
def __init__ (self , root , cacert = None , debug = False ):
83
106
self .root = get_url_without_trailing_slash (root )
@@ -87,6 +110,7 @@ def __init__(self, root, cacert=None, debug=False):
87
110
@add_ssl_verify_to_kwargs
88
111
@add_auth_token_to_headers
89
112
def get (self , url , ** kwargs ):
113
+ url = sanitize_url (url )
90
114
response = requests .get (self .root + url , ** kwargs )
91
115
response = self ._response_hook (response = response )
92
116
return response
@@ -95,13 +119,15 @@ def get(self, url, **kwargs):
95
119
@add_auth_token_to_headers
96
120
@add_json_content_type_to_headers
97
121
def post (self , url , data , ** kwargs ):
122
+ url = sanitize_url (url )
98
123
response = requests .post (self .root + url , json .dumps (data ), ** kwargs )
99
124
response = self ._response_hook (response = response )
100
125
return response
101
126
102
127
@add_ssl_verify_to_kwargs
103
128
@add_auth_token_to_headers
104
129
def post_raw (self , url , data , ** kwargs ):
130
+ url = sanitize_url (url )
105
131
response = requests .post (self .root + url , data , ** kwargs )
106
132
response = self ._response_hook (response = response )
107
133
return response
@@ -110,6 +136,7 @@ def post_raw(self, url, data, **kwargs):
110
136
@add_auth_token_to_headers
111
137
@add_json_content_type_to_headers
112
138
def put (self , url , data , ** kwargs ):
139
+ url = sanitize_url (url )
113
140
response = requests .put (self .root + url , json .dumps (data ), ** kwargs )
114
141
response = self ._response_hook (response = response )
115
142
return response
@@ -118,13 +145,15 @@ def put(self, url, data, **kwargs):
118
145
@add_auth_token_to_headers
119
146
@add_json_content_type_to_headers
120
147
def patch (self , url , data , ** kwargs ):
148
+ url = sanitize_url (url )
121
149
response = requests .patch (self .root + url , data , ** kwargs )
122
150
response = self ._response_hook (response = response )
123
151
return response
124
152
125
153
@add_ssl_verify_to_kwargs
126
154
@add_auth_token_to_headers
127
155
def delete (self , url , ** kwargs ):
156
+ url = sanitize_url (url )
128
157
response = requests .delete (self .root + url , ** kwargs )
129
158
response = self ._response_hook (response = response )
130
159
return response
0 commit comments