@@ -29,6 +29,7 @@ def __init__(self, resp):
2929 self .reason = getattr (resp , 'reason_phrase' , '' ) or self ._get_reason_phrase (resp .status_code )
3030 self .data = resp .text # eagerly read body
3131 self .headers = resp .headers
32+ self .request_body_size = 0
3233 # Break httpx Response <-> BoundSyncStream reference cycle (issue #395)
3334 resp .stream = None
3435 try :
@@ -219,36 +220,42 @@ def request(self, method, url, query_params=None, headers=None,
219220 "httpx client was closed before request; re-established a fresh client"
220221 )
221222
223+ # Serialize the request body once, before the retry loop.
224+ # The body never changes between attempts; only the httpx client does.
225+ request_body = None
226+ request_body_size = 0
227+ request_url = url
228+ has_body = method in ['POST' , 'PUT' , 'PATCH' , 'OPTIONS' , 'DELETE' ]
229+
230+ if has_body :
231+ if query_params :
232+ request_url = url + '?' + urlencode (query_params )
233+ if re .search ('json' , headers ['Content-Type' ], re .IGNORECASE ) or isinstance (body , str ):
234+ request_body = '{}'
235+ if body is not None :
236+ request_body = json .dumps (body )
237+ if isinstance (body , str ):
238+ request_body = request_body .strip ('"' )
239+ request_body_size = len (request_body .encode ('utf-8' ))
240+ else :
241+ msg = """Cannot prepare a request message for provided
242+ arguments. Please check that your arguments match
243+ declared content type."""
244+ raise ApiException (status = 0 , reason = msg )
245+
222246 for attempt in range (2 ):
223247 # Snapshot the client we're about to use so that if the request
224248 # fails we can ask for a reset only if the client is still this
225249 # one (i.e. nobody else healed it in the meantime).
226250 client_at_send = self .connection
227251 try :
228- # For `POST`, `PUT`, `PATCH`, `OPTIONS`, `DELETE`
229- if method in ['POST' , 'PUT' , 'PATCH' , 'OPTIONS' , 'DELETE' ]:
230- request_url = url
231- if query_params :
232- request_url += '?' + urlencode (query_params )
233- if re .search ('json' , headers ['Content-Type' ], re .IGNORECASE ) or isinstance (body , str ):
234- request_body = '{}'
235- if body is not None :
236- request_body = json .dumps (body )
237- if isinstance (body , str ):
238- request_body = request_body .strip ('"' )
239- r = client_at_send .request (
240- method , request_url ,
241- content = request_body ,
242- timeout = timeout ,
243- headers = headers
244- )
245- else :
246- # Cannot generate the request from given parameters
247- msg = """Cannot prepare a request message for provided
248- arguments. Please check that your arguments match
249- declared content type."""
250- raise ApiException (status = 0 , reason = msg )
251- # For `GET`, `HEAD`
252+ if has_body :
253+ r = client_at_send .request (
254+ method , request_url ,
255+ content = request_body ,
256+ timeout = timeout ,
257+ headers = headers
258+ )
252259 else :
253260 r = client_at_send .request (
254261 method , url ,
@@ -309,6 +316,7 @@ def request(self, method, url, query_params=None, headers=None,
309316
310317 if _preload_content :
311318 r = RESTResponse (r )
319+ r .request_body_size = request_body_size
312320
313321 if r .status == 401 or r .status == 403 :
314322 raise AuthorizationException (http_resp = r )
0 commit comments