55from typing import Any , Optional , Dict , Iterable , List , Union , cast , Tuple , Iterator
66from urllib .parse import urlparse , ParseResult , urlencode , quote
77from xml .etree import ElementTree as ET
8+ import json
89from . import retry
910from . import transport
1011from . import exceptions
2829 BodyType ,
2930 OperationInput ,
3031 OperationOutput ,
32+ EndpointProvider ,
3133)
3234
3335
@@ -109,6 +111,7 @@ def __init__(
109111 feature_flags : Optional [int ] = None ,
110112 additional_headers : Optional [List [str ]] = None ,
111113 operation_timeout : Optional [Union [int , float ]] = None ,
114+ endpoint_provider : Optional [EndpointProvider ] = None ,
112115 ) -> None :
113116 self .product = product
114117 self .region = region
@@ -126,7 +129,7 @@ def __init__(
126129 self .feature_flags = feature_flags or defaults .FF_DEFAULT
127130 self .additional_headers = additional_headers
128131 self .operation_timeout = operation_timeout
129-
132+ self . endpoint_provider = endpoint_provider
130133
131134class _InnerOptions :
132135 """client runtime's information."""
@@ -137,7 +140,6 @@ def __init__(
137140 self .user_agent = user_agent
138141
139142
140-
141143class _ClientImplMixIn :
142144 """Client implement"""
143145
@@ -219,7 +221,10 @@ def build_request_context(self, op_input: OperationInput, options: _Options, inn
219221 """build request context
220222 """
221223 # host & path
222- url = _build_url (op_input , options )
224+ if options .endpoint_provider is not None :
225+ url = options .endpoint_provider .build_url (op_input )
226+ else :
227+ url = _build_url (op_input , options )
223228
224229 # queries
225230 if op_input .parameters is not None :
@@ -368,7 +373,10 @@ def service_error_response_handler(response: HttpResponse) -> None:
368373 if not response .is_stream_consumed :
369374 _ = response .read ()
370375
371- raise _to_service_error (response )
376+ if response .headers .get ('Content-Type' , '' ) == 'application/json' :
377+ raise _to_service_error_json (response )
378+ else :
379+ raise _to_service_error (response )
372380
373381 # insert service error responsed handler first
374382 handlers .append (service_error_response_handler )
@@ -533,6 +541,7 @@ def _resolve_endpoint(config: Config, options: _Options) -> None:
533541 options .endpoint = urlparse (endpoint )
534542
535543
544+
536545def _resolve_retryer (_ : Config , options : _Options ) -> None :
537546 """retryer"""
538547 if options .retryer :
@@ -677,6 +686,61 @@ def _to_service_error(response: HttpResponse) -> exceptions.ServiceError:
677686 error_fileds = error_fileds
678687 )
679688
689+ def _to_service_error_json (response : HttpResponse ) -> exceptions .ServiceError :
690+ timestamp = serde .deserialize_httptime (response .headers .get ('Date' ))
691+ content = response .content or b''
692+ response .close ()
693+
694+ error_fileds = {}
695+ code = 'BadErrorResponse'
696+ message = ''
697+ ec = ''
698+ request_id = ''
699+ err_body = b''
700+ try :
701+ err_body = content
702+ if len (err_body ) == 0 :
703+ err_body = base64 .b64decode (
704+ response .headers .get ('x-oss-err' , '' ))
705+ root = json .loads (err_body )
706+ errElem = root .get ('Error' , None )
707+ if errElem is not None :
708+ for k , v in errElem .items ():
709+ if isinstance (v , str ):
710+ error_fileds [k ] = v
711+ message = error_fileds .get ('Message' , '' )
712+ code = error_fileds .get ('Code' , '' )
713+ ec = error_fileds .get ('EC' , '' )
714+ request_id = error_fileds .get ('RequestId' , '' )
715+ else :
716+ message = f'Expect root node Error, but get { root .keys ()} .'
717+ except json .JSONDecodeError as e :
718+ errstr = err_body .decode ()
719+ if '"Error":' in errstr :
720+ m = re .search ('"Code":(.*),' , errstr )
721+ if m :
722+ code = m .group (1 ).strip (' "' )
723+ m = re .search ('<Message>(.*)</Message>' , errstr )
724+ if m :
725+ message = m .group (1 ).strip (' "' )
726+ if len (message ) == 0 :
727+ message = f'Failed to parse json from response body due to: { str (e )} . With part response body { err_body [:256 ]} .'
728+ except Exception as e :
729+ message = f'The body of the response was not readable, due to : { str (e )} .'
730+
731+ return exceptions .ServiceError (
732+ status_code = response .status_code ,
733+ code = code ,
734+ message = message ,
735+ request_id = request_id or response .headers .get ('x-oss-request-id' , '' ),
736+ ec = ec or response .headers .get ('x-oss-ec' , '' ),
737+ timestamp = timestamp ,
738+ request_target = f'{ response .request .method } { response .request .url } ' ,
739+ snapshot = content ,
740+ headers = response .headers ,
741+ error_fileds = error_fileds
742+ )
743+
680744def _build_user_agent (config : Config ) -> str :
681745 if config .user_agent :
682746 return f'{ utils .get_default_user_agent ()} /{ config .user_agent } '
0 commit comments