o
    WZ                     @   s  d Z ddlmZ ddlmZ ddlmZ ddlZddlZddlZddlZddl	Z	ddl
Z
ddlmZ ddlmZ ddlmZ dd	lmZ dd
lmZ ddlmZ ddlmZ ddlmZ ddlZddlmZ ddlmZ ejrqdndZe
 jZg dZ G dd de!ej"e#Z$G dd de!ej"e#Z%G dd de!ej"e#Z&G dd de#Z'dd Z(dd Z)dd Z*d d! Z+d"d# Z,d<d%d&Z-d'd( Z.d=d*d+Z/d,d- Z0d.d/ Z1d0d1 Z2d>d2d3Z3d4d5 Z4d6d7 Z5d8d9 Z6d:d; Z7dS )?z@Module for common transport utilities, such as request wrapping.    )absolute_import)division)unicode_literalsN)
exceptions)config)log)metrics)
properties)console_attr)
console_io)	platforms)urllib)ziputf-8)z*https://accounts.google.com/o/oauth2/tokenz*https://www.googleapis.com/oauth2/v3/tokenz*https://www.googleapis.com/oauth2/v4/tokenz#https://oauth2.googleapis.com/tokenz-https://oauth2.googleapis.com/oauth2/v4/tokenc                   @   s8   e Zd ZdZdd Zeejdd Zejdd Z	dS )	Requesta  Encapsulates parameters for making a general HTTP request.

  Attributes:
    uri: URI of the HTTP resource.
    method: HTTP method to perform, such as GET, POST, DELETE, etc.
    headers: Additional headers to include in the request.
    body: Body of the request.
  c                 C   s   || _ || _|| _|| _dS )a  Instantiates a Request object.

    Args:
      uri: URI of the HTTP resource.
      method: HTTP method to perform, such as GET, POST, DELETE, etc.
      headers: Additional headers to include in the request.
      body: Body of the request.

    Returns:
      Request
    N)urimethodheadersbody)selfr   r   r   r    r   :/tmp/google-cloud-sdk/lib/googlecloudsdk/core/transport.py__init__>   s   
zRequest.__init__c                 O      dS )zReturns a Request object.

    Args:
      *args: args to be passed into http.request
      **kwargs: dictionary of kwargs to be passed into http.request

    Returns:
      Request
    Nr   )clsargskwargsr   r   r   FromRequestArgsO       zRequest.FromRequestArgsc                 C   r   )zAReturns the args and kwargs to be used when calling http.request.Nr   )r   r   r   r   ToRequestArgs\   r   zRequest.ToRequestArgsN)
__name__
__module____qualname____doc__r   classmethodabcabstractmethodr   r   r   r   r   r   r   4   s    	r   c                   @   s*   e Zd ZdZdd Zeejdd ZdS )ResponsezEncapsulates responses from making a general HTTP request.

  Attributes:
    status_code:
    headers: Headers of the response.
    body: Body of the response.
  c                 C   s   || _ || _|| _dS )zInstantiates a Response object.

    Args:
      status_code:
      headers: Headers of the response.
      body: Body of the response.

    Returns:
      Response
    N)status_coder   r   )r   r(   r   r   r   r   r   r   j   s   
zResponse.__init__c                 C   r   )zReturns a Response object.

    Args:
      response: raw response from calling http.request.

    Returns:
      Response
    Nr   )r   responser   r   r   FromResponsey   r   zResponse.FromResponseN)	r    r!   r"   r#   r   r$   r%   r&   r*   r   r   r   r   r'   a   s    r'   c                   @   sF   e Zd ZdZeZeZej	dd Z
			d
ddZdedfdd	ZdS )RequestWrapperaK  Class for wrapping http requests.

  The general process is that you can define a series of handlers that get
  executed before and after the original http request you are mapping. All the
  request handlers are executed in the order provided. Request handlers must
  return a result that is used when invoking the corresponding response handler.
  Request handlers don't actually execute the request but rather just modify the
  request arguments. After all request handlers are executed, the original http
  request is executed. Finally, all response handlers are executed in order,
  getting passed both the http response as well as the result from their
  corresponding request handler.

  Attributes:
    request_class: Class used to represent a generic HTTP request.
    response_class: Class used to represent a generic HTTP request.
  c                 C   r   )z9Decodes the response body according to response_encoding.Nr   )r   r)   response_encodingr   r   r   DecodeResponse   r   zRequestWrapper.DecodeResponseNFc                 C   s<  t tjjj }tt t tt	dt
jttd|g}tjjj }|r1|ttd| tjjj }|rD|ttd| tjjj }	|	rW|ttd|	 tjjj r}tjjj }
tjjj }|tt|
|su|ndt| dtjv rtjd dkr|tt dd	  | j|||d
 |S )a'  Wraps request with user-agent, and trace reporting.

    Args:
      http_client: The original http client to be wrapped.
      response_encoding: str, the encoding to use to decode the response.
      streaming_response_body: bool, True indicates that the response body will
          be a streaming body.
      redact_request_body_reason: str, the reason why the request body must be
          redacted if --log-http is used. If None, the body is not redacted.

    Returns:
      http, The same http object but with the request method wrapped.
    z
user-agentCookiezX-Goog-Request-ReasonzX-Goog-Allowed-ResourcesNCLOUDSDK_CORE_DRY_RUN1c                  W   s   d S Nr   )r   r   r   r   <lambda>   r   z1RequestWrapper.WrapWithDefaults.<locals>.<lambda>)r,   )MakeUserAgentStringr	   VALUESr   command_nameGetHandlerRecordStartTimeReportDurationMaybePrependToHeaderr   CLOUDSDK_USER_AGENTAppendToHeadercoretrace_tokenappend	SetHeaderrequest_reasonresource_policyorg_restriction_headerlog_httpGetBoollog_http_redact_tokenlog_http_show_request_body
LogRequestLogResponseosenvironLogRequestDryRunWrapRequest)r   http_clientr,   streaming_response_bodyredact_request_body_reason	gcloud_uahandlerstrace_valuerA   request_org_restriction_headersredact_tokenshow_request_bodyr   r   r   WrapWithDefaults   s`   	


zRequestWrapper.WrapWithDefaultsc                    s&   |j  fdd}||_ dS )aX  Wraps an http client with request modifiers.

    Args:
      http_client: The original http client to be wrapped.
      handlers: [Handler], The handlers to execute before and after the original
        request.
      exc_handler: f(e), A function that takes an exception and handles it. It
        should also throw an exception if you don't want it to be swallowed.
      exc_type: The type of exception that should be caught and given to the
        handler. It could be a tuple to catch more than one exception type.
      response_encoding: str, the encoding to use to decode the response.
    c               
      s  j j| i |}dd t|jD }i |_t|D ]\}}t||\}}||j|< qg }D ]}||}|| q1z| \}	}
|	i |
}W n yi } zd} rd | W Y d}~dS  d}~ww durt	|}j
|}t|D ]\}}|jr||| q|S )z)Replacement http_client.request() method.c                 S   s   i | ]\}}||qS r   r   ).0hvr   r   r   
<dictcomp>  s    zFRequestWrapper.WrapRequest.<locals>.WrappedRequest.<locals>.<dictcomp>N)request_classr   six	iteritemsr   _EncodeHeaderrequestr?   r   r-   response_classr*   r   r)   )r   r   handler_requestr   rY   rZ   modifier_datahandlermodifier_resultmodified_argsmodified_kwargsr)   ehandler_responsedataexc_handlerexc_typerR   orig_requestr,   r   r   r   WrappedRequest  s:   
z2RequestWrapper.WrapRequest.<locals>.WrappedRequestNr`   )r   rN   rR   rl   rm   r,   ro   r   rk   r   rM      s   
%zRequestWrapper.WrapRequest)NFN)r    r!   r"   r#   r   r\   r'   ra   r%   r&   r-   rW   	ExceptionrM   r   r   r   r   r+      s    

Wr+   c                   @   s   e Zd ZdZdddZdS )r7   zA holder object for a pair of request and response handlers.

  Request handlers are invoked before the original http request, response
  handlers are invoked after.
  Nc                 C   s   || _ || _dS )aD  Creates a new Handler.

    Args:
      request: f(request) -> data, A function that gets called before the
        original http request gets called. It is passed a Request object that
        encapsulates the parameters of an http request. It returns data to be
        passed to its corresponding response hander.
      response: f(response, data), A function that gets called after the
        original http request. It is passed a Response object that encapsulates
        the response of an http request as well as whatever the request handler
        returned as data.
    N)r`   r)   )r   r`   r)   r   r   r   r   6  s   
zHandler.__init__r1   )r    r!   r"   r#   r   r   r   r   r   r7   /  s    r7   c                 C   s4   t | tjr| d} t |tjr|d}| |fS )Nr   )
isinstancer]   	text_typeencodeheadervaluer   r   r   r_   G  s
   

r_   c                        t  \  fdd}|S )a  Prepends the given value if the existing header does not start with it.

  Args:
    header: str, The name of the header to prepend to.
    value: str, The value to prepend to the existing header value.

  Returns:
    A function that can be used in a Handler.request.
  c                    sb   | j }d}t|D ]\}}|   kr|}||=  nq
|s+d |  }|| < dS )z0Maybe prepends a value to a header on a request.        N)r   r]   r^   lower
startswithstripr`   r   current_valuehdrrZ   ru   r   r   _MaybePrependToHeader[  s   
z3MaybePrependToHeader.<locals>._MaybePrependToHeaderr_   )rv   rw   r   r   ru   r   r:   O  s   
r:   c                    rx   )a  Appends the given value to the existing value in the http request.

  Args:
    header: str, The name of the header to append to.
    value: str, The value to append to the existing header value.

  Returns:
    A function that can be used in a Handler.request.
  c                    s\   | j }d}t|D ]\}}|   kr|}||=  nq
|r(|d   n| < dS )z)Appends a value to a header on a request.ry   rz   N)r   r]   r^   r{   r}   r~   ru   r   r   _AppendToHeaderx  s   
z'AppendToHeader.<locals>._AppendToHeaderr   )rv   rw   r   r   ru   r   r<   l  s   
r<   c                    rx   )zSets the given header value in the http request.

  Args:
    header: str, The name of the header to set to.
    value: str, The new value of the header.

  Returns:
    A function that can be used in a Handler.request.
  c                    s<   | j }t|D ]}|   kr||=  nq| < dS )zSets a header on a request.N)r   r]   iterkeysr{   )r`   r   r   ru   r   r   
_SetHeader  s   zSetHeader.<locals>._SetHeaderr   )rv   rw   r   r   ru   r   r@     s   

r@   c                        fdd}|S )zAdds the given query parameter to the http request.

  Args:
    param: str, The name of the parameter.
    value: str, The value of the parameter.

  Returns:
    A function that can be used in a Handler.request.
  c                    sV   t j| j}t j|j}| < t|}t jj|dd|d< t j|}|| _dS )z$Sets a query parameter on a request.T)doseq   N)	r   parseurlsplitr   parse_qsquerylist	urlencode
urlunsplit)r`   	url_partsquery_paramsnew_urlparamrw   r   r   _AddQueryParam  s   
z%AddQueryParam.<locals>._AddQueryParamr   )r   rw   r   r   r   r   AddQueryParam  s   r   Tc                    r   )a@  Logs the contents of the http request.

  Args:
    redact_token: bool, True to redact auth tokens.
    redact_request_body_reason: str, the reason why the request body must be
        redacted if --log-http is used. If None, the body is not redacted.

  Returns:
    A function that can be used in a Handler.request.
  c           	         s8  | j }| j}| j}| jpd}d}d}rt|rd}d}n dur# }tjd tjd tjdj|d tjd	j|d
 tjd t	t
|D ]\}}r^| dv r^d}tjd|| qPtjd tjd |du rtj| n	tjd| tjd tjd t |dS )zLogs a request. NzdContains oauth token. Set log_http_redact_token property to false to print the body of this request.zeContains oauth token. Set log_http_redact_token property to false to print the body of this response.z=======================z==== request start ====z
uri: {uri})r   zmethod: {method})r   z== headers start ==)s   authorizations   x-goog-iam-authorization-tokenz--- Token Redacted ---{0}: {1}z== headers end ==z== body start ==Body redacted: {}z== body end ==z==== request end ====)
start_timeredact_resp_body_reason)r   r   r   r   
IsTokenUrir   statusPrintformatsortedr]   r^   r{   time)	r`   r   r   r   r   redact_req_body_reasonr   rY   rZ   rP   rU   r   r   _LogRequest  s@   
zLogRequest.<locals>._LogRequestr   )rU   rP   r   r   r   r   rH     s   .rH   c                  C      dd } | S )z^Dry run the http request.

  Returns:
    A function that can be used in a Handler.request.
  c                 S   s
   t | )zBlocks a dry-run request.)r   DryRunErrorrp   r   r   r   r     s   
z%LogRequestDryRun.<locals>._LogRequestr   )r   r   r   r   rL     s   rL   Fc                    s    fdd}|S )zLogs the contents of the http response.

  Args:
    streaming_response_body: bool, True indicates that the response body will be
      a streaming body.

  Returns:
    A function that can be used in a Handler.response.
  c                    s   |d }t   |d  }tjd tjd| j tjd tt| j	D ]\}}tjd|| q*tjd tjd  rNtjd	 n|d
u rZtj| j
 n	tjd| tjd tjd| tjd tjd d
S )zLogs a response.r   r   z---- response start ----zstatus: {0}z-- headers start --r   z-- headers end --z-- body start --z<streaming body>Nr   z-- body end --z6total round trip time (request+response): {0:.3f} secsz---- response end ----z----------------------)r   r   r   r   r   r(   r   r]   r^   r   r   )r)   rj   r   
time_takenrY   rZ   rO   r   r   _LogResponse  s,   z!LogResponse.<locals>._LogResponser   )rO   r   r   r   r   rI   	  s   rI   c                  C   r   )zwRecords the time at which the request was started.

  Returns:
    A function that can be used in a Handler.request.
  c                 S   s   ~ dt   iS )z$Records the start time of a request.r   )r   rp   r   r   r   _RecordStartTime6  s   z)RecordStartTime.<locals>._RecordStartTimer   )r   r   r   r   r8   /  s   r8   c                  C   r   )z}Reports the duration of response to the metrics module.

  Returns:
    A function that can be used in a Handler.response.
  c                 S   s    ~ t   |d  }t| dS )z"Records the duration of a request.r   N)r   r   RPCDuration)r)   rj   durationr   r   r   _ReportDurationE  s   z'ReportDuration.<locals>._ReportDurationr   )r   r   r   r   r9   >  s   r9   c                 C   s   t  }z|d}W n ty   d}Y nw |r|r|S | jtjjkr;| jtj	j
kr;tj r;dtj	j
tj	j}nt| j}|rH|d| |S )a  Get and cache architecture of client machine.

  For M1 Macs running x86_64 Python using Rosetta, user_platform.architecture
  (from platform.machine()) returns x86_64. We can use
  IsActuallyM1ArmArchitecture() to determine the underlying hardware; however,
  it requires a system call that might take ~5ms.
  To mitigate this, we will persist this value as an internal property with
  INSTALLATION scope.

  Args:
    user_platform: platforms.Platform.Current()

  Returns:
    client machine architecture
  client_archNz{}_{})r   GetConfigStorer6   rq   operating_systemr   OperatingSystemMACOSXarchitectureArchitecturex86_64PlatformIsActuallyM1ArmArchitecturer   armstrSet)user_platformactive_config_storecached_archarchr   r   r   GetAndCacheArchitectureN  s&   
r   c                 C   s   t j }t|}djtjdd| ptj	j
j tt tj	j
j |j|jr,|jjnd|tjdddt | t t  t dS )a  Return a user-agent string for this request.

  Contains 'gcloud' in addition to several other product IDs used for tracing in
  metrics reporting.

  Args:
    cmd_path: str representing the current command for tracing.

  Returns:
    str, User Agent string.
  a5  gcloud/{version} command/{cmd} invocation-id/{inv_id} environment/{environment} environment-version/{env_version} client-os/{os} client-os-ver/{os_version} client-pltf-arch/{architecture} interactive/{is_interactive} from-script/{from_script} python/{py_version} term/{term} {gcloud_mcp_metrics} {ua_fragment} _NT)error	heuristic)versioncmdinv_idenvironmentenv_versionrJ   
os_versionr   is_interactive
py_versionua_fragmentfrom_scripttermgcloud_mcp_metrics)r   r   Currentr   r   r   CLOUD_SDK_VERSIONreplacer	   r4   r   r5   r6   INVOCATION_IDGetMetricsEnvironmentenvironment_versionr   clean_versionr   IsInteractiveplatformpython_versionUserAgentFragmentIsRunFromShellScriptr
   GetConsoleAttrGetTermIdentifierGetValidMCPMetricsString)cmd_pathr   r   r   r   r   r3   u  s*   


r3   c                   C   s   t jjj pdS )Ni,  )r	   r4   r=   http_timeoutGetIntr   r   r   r   GetDefaultTimeout  s   r   c                 C   s@   | t v rdS d}d}t|| durdS t|| durdS dS )z=Determine if the given URI is for requesting an access token.Tzb(metadata.google.internal|169.254.169.254)/computeMetadata/.*?/instance/service-accounts/.*?/tokenzUiamcredentials.googleapis.com/v.*?/projects/-/serviceAccounts/.*?:generateAccessTokenNF)
TOKEN_URISresearch)r   metadata_regexpimpersonate_service_accountr   r   r   r     s   r   c                  C   s$   dt jv rt jd } t| r| S dS )zRReturns the valid MCP metrics string if it exists, otherwise returns empty string.GCLOUD_MCP_METRICSr   )rJ   rK   ValidateMCPMetricsFormat)metrics_stringr   r   r   r     s
   

r   c                 C   s   d}t || duS )zChecks if a string follows the MCP metrics format.

  Args:
    metrics_string: The string to validate.

  Returns:
    True if the string matches the format, False otherwise.
  zZgoog-mcp/agent/[^/]+/agent-version/[^/]+/mcp-server/[^/]+/mcp-version/[^/]+/mcp-tool/[^/]+N)r   	fullmatch)r   patternr   r   r   r     s   
r   )TN)Fr1   )8r#   
__future__r   r   r   r%   rJ   r   r   r   uuidgooglecloudsdk.callioper   googlecloudsdk.corer   r   r   r	   googlecloudsdk.core.consoler
   r   googlecloudsdk.core.utilr   r]   	six.movesr   r   PY2ENCODINGuuid4hexr   r   with_metaclassABCMetaobjectr   r'   r+   r7   r_   r:   r<   r@   r   rH   rL   rI   r8   r9   r   r3   r   r   r   r   r   r   r   r   <module>   sZ   
	-% *
=
&
'2