
    T                     ~   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	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ZdZdZdZdZdZdZ G d dej<                        Z G d dej<                        Z  G d dej<                        Z! G d dej<                        Z" G d dej<                        Z# G d dej<                        Z$ G d  d!ej<                        Z% G d" d#ej<                        Z& G d$ d%ej<                        Z' G d& d'ej<                        Z( G d( d)e)      Z*y)*z8WebSocket connection class for tunneling with Cloud IAP.    )absolute_import)division)unicode_literalsN)iap_tunnel_websocket_helper)iap_tunnel_websocket_utils)
exceptions)log)
properties)retry)queue   <   i N  i    
   i  c                       e Zd Zy)SendAckNotificationN__name__
__module____qualname__     :lib/googlecloudsdk/api_lib/compute/iap_tunnel_websocket.pyr   r   -       r   r   c                       e Zd Zy)ConnectionCreationErrorNr   r   r   r   r   r   1   r   r   r   c                       e Zd Zy)ConnectionReconnectTimeoutNr   r   r   r   r   r   5   r   r   r   c                       e Zd Zy)StoppingErrorNr   r   r   r   r    r    9   r   r   r    c                       e Zd Zy)SubprotocolEarlyAckErrorNr   r   r   r   r"   r"   =   r   r   r"   c                       e Zd Zy)SubprotocolEarlyDataErrorNr   r   r   r   r$   r$   A   r   r   r$   c                       e Zd Zy)!SubprotocolExtraConnectSuccessSidNr   r   r   r   r&   r&   E   r   r   r&   c                       e Zd Zy)#SubprotocolExtraReconnectSuccessAckNr   r   r   r   r(   r(   I   r   r   r(   c                       e Zd Zy)SubprotocolInvalidAckErrorNr   r   r   r   r*   r*   M   r   r   r*   c                       e Zd Zy)SubprotocolOutOfOrderAckErrorNr   r   r   r   r,   r,   Q   r   r   r,   c                       e Zd ZdZ	 ddZd Zd Zd Zd Zd Z	d Z
d	 Zd
 Zd Zd Zd Zd Zd Zd Zd Zd Zd Zd Zd Zd Zd Zd Zd Zd Zd Zy)IapTunnelWebSocketzCloud IAP WebSocket class for tunnelling connections.

  It takes in local data (via Send()) which it sends over the websocket. It
  takes data from the websocket and gives it to data_handler_callback.
  c                    || _         || _        || _        || _        || _        || _        d | _        d| _        d | _        d| _	        d| _
        d | _        d| _        t        j                         | _        t        j                         | _        d| _        d| _        d| _        t)        j*                  t,              | _        t1        j2                         | _        t)        j*                         | _        || _        y )NFr   )maxsize)_tunnel_target_get_access_token_callback_data_handler_callback_close_handler_callback_ignore_certs_user_agent_websocket_helper_connect_msg_received_connection_sid	_stopping_close_message_sent_send_and_reconnect_thread
_input_eof	threadingEvent	_sent_all_cant_send_ack_total_bytes_confirmed_total_bytes_received_total_bytes_received_and_ackedr   QueueMAX_UNSENT_QUEUE_LENGTH_unsent_datacollectionsdeque_unconfirmed_data_data_to_resend_conn_id)selftunnel_targetget_access_token_callbackdata_handler_callbackclose_handler_callback
user_agentconn_idignore_certss           r   __init__zIapTunnelWebSocket.__init__\   s     (D&?D#"7D#9D %D!D!D!&DDDN$D&*D#DO __&DN#//+D"#D!"D+,D(,CDD(..0D !;;=DDMr   c                 R    | j                   r| j                   j                          y y N)r7   CloserM   s    r   __del__zIapTunnelWebSocket.__del__~   s"    
""$ r   c                 (   d| _         | j                  j                  t               	 | j	                          | j
                  rH| j                  s!| j
                  j                          d| _        | j
                  j                          yy#  Y YxY w)z5Close down local connection and WebSocket connection.TN)	r:   rG   putr    r4   r7   r;   	SendCloserX   rY   s    r   rX   zIapTunnelWebSocket.Close   s    DN 	-(
""$ %%((*#' 
""$	 
s   B Bc                 `   t        j                  | j                         t        j                  | j                         | j                          | j                          t        j                  | j                        | _
        d| j                  _        | j                  j                          y)z"Initiate the WebSocket connection.)targetTN)utilsCheckPythonVersionr5   ValidateParametersr1   _StartNewWebSocket_WaitForOpenOrRaiseErrorr>   Thread_SendDataAndReconnectWebSocketr<   daemonstartrY   s    r   InitiateConnectionz%IapTunnelWebSocket.InitiateConnection   s~    	T//0	T001!!#&/&6&622'4D#-1D##*##))+r   c                     |r=|dt         j                   }|t         j                  d }|r| j                  |       |r<yy)zSend bytes over WebSocket connection.

    Args:
      bytes_to_send: The bytes to send. Must not be empty.

    Raises:
      ConnectionReconnectTimeout: If something is preventing data from being
        sent.
    N)r`   SUBPROTOCOL_MAX_DATA_FRAME_SIZE!_EnqueueBytesWithWaitForReconnect)rM   bytes_to_sendfirst_to_sends      r   SendzIapTunnelWebSocket.Send   sC     #$JU%J%JKm#E$I$I$JKm	..}=	 r   c                 B    | j                   j                  t               y)zIndicate that the local input gave an EOF.

    This should always be called after finishing sending data, as to stop the
    sending thread.
    N)rG   r\   EOFErrorrY   s    r   LocalEOFzIapTunnelWebSocket.LocalEOF   s     	(#r   c                 @    | j                   j                  t              S )a  Wait until all local data has been sent on the websocket.

    Blocks until either all data from Send() has been sent, or it times out
    waiting. Once true, always returns true. Even if this returns true, a
    reconnect could occur causing previously sent data to be resent. Must only
    be called after an EOF has been given to Send().

    Returns:
      True on success, False on timeout.
    )r@   waitALL_DATA_SENT_WAIT_TIME_SECrY   s    r   WaitForAllSentz!IapTunnelWebSocket.WaitForAllSent   s    " >>:;;r   c                    t        j                  t        dt              }	 |j	                  |t
               y# t         j                  $ r: t        j                  d| j                  t        d       | j                          Y yw xY w)z*Attempt to reconnect with a new WebSocket.g?)max_wait_msexponential_sleep_multiplierwait_ceiling_ms)funcsleep_msz'[%d] Unable to reconnect within [%d] msTexc_infoN)r   RetryerMAX_RECONNECT_WAIT_TIME_MSMAX_RECONNECT_SLEEP_TIME_MSRetryOnExceptionRECONNECT_INITIAL_SLEEP_MSRetryExceptionr	   warningrL   _StopConnectionAsync)rM   reconnect_funcrs      r   _AttemptReconnectz$IapTunnelWebSocket._AttemptReconnect   sv     	"<36&A	CA"n"<  > "	kk;--!;dL
!"s   : A
BBc                 0   t        j                          t        dz  z   }t        j                          |k  r| j                  sv	 | j                  j	                  |t
               t        j                         t        j                  k(  r.t        j                  d| j                  t        |      |dd        y| j                  rt        d      t!               # t        j                  $ r Y nw xY wt        j                          |k  sR| j                  sՌ`)a  Add bytes to the queue; block waiting for reconnect if queue is full.

    Args:
      bytes_to_send: The local bytes to send over the websocket. At most
        utils.SUBPROTOCOL_MAX_DATA_FRAME_SIZE.

    Raises:
      ConnectionReconnectTimeout: If something is preventing data from being
        sent.
      ConnectionCreationError: If the connection was closed and no more
        reconnect retries will be performed.
    g     @@timeoutz3[%d] ENQUEUED data_len [%d] bytes_to_send[:20] [%r]N   zAUnexpected error while reconnecting. Check logs for more details.)timer   r:   rG   r\    MAX_WEBSOCKET_SEND_WAIT_TIME_SECr	   GetVerbosityloggingDEBUGdebugrL   lenr   Fullr   r   )rM   rm   end_times      r   rl   z4IapTunnelWebSocket._EnqueueBytesWithWaitForReconnect   s     yy{7&@@H
))+
  	m> 	 	@
 .
))IMM3}#5}Sb7IK
 ~~# %D E E
$
&& ZZ  ))+
 s   A4C C/.C/c                     | j                   S )z.Returns true if we received a connect message.)r8   rY   s    r   _HasConnectedz IapTunnelWebSocket._HasConnected   s    %%%r   c                     | j                   xr | j                   j                         xs) | j                  xr | j                  j                          S rW   )r7   IsClosedr<   is_aliverY   s    r   	_IsClosedzIapTunnelWebSocket._IsClosed  sL    ##I(>(>(G(G(I =,, <0099;;>r   c           
         d| j                   z   g}t        j                  d| j                  | j                          t        j
                  j                  j                  j                         }|r	|d|z   gz  }| j                  r|d| j                         z   gz  }t        j                  d| j                         | j                  rYt        j                  | j                  | j                  | j                  d      }t        j                  d| j                  |       nBt        j                   | j                  d      }t        j                  d	| j                  |       d
| _        t%        j&                  ||| j(                  | j                  j*                  | j,                  | j.                  d| j                        | _        | j0                  j3                          y)z=Start a new WebSocket and thread to listen for incoming data.zUser-Agent: z[%d] user-agent [%s]zX-Goog-Request-Reason: zAuthorization: Bearer z [%d] Using new websocket libraryT)should_use_new_websocketz[%d] Reconnecting with URL [%r]z[%d] Connecting with URL [%r]F)r   rS   N)r6   r	   r   rL   r
   VALUEScorerequest_reasonGetr2   r9   r`   CreateWebSocketReconnectUrlr1   rC   infoCreateWebSocketConnectUrlr8   helperIapTunnelWebSocketHelperr5   
proxy_info_OnData_OnCloser7   StartReceivingThread)rM   headersr   urls       r   rc   z%IapTunnelWebSocket._StartNewWebSocket  s    0 001GII$dmmT5E5EF&&++::>>@N+n<==g&&*T-L-L-NNOOgII0$--@--







$
$#'	)c
 
hh0$--E++


>c	hh.sC!&D#<<&&!%D 	//1r   c                 Z   | j                   | j                  kD  r_| j                   }	 t        j                  |      }| j                  j                  |       || _        | j                  j!                          yy# t        j                  $ r  t        $ r>}t        j                  d| j                  t        j                  |             Y d}~qd}~w | j                         s$t        j                  d| j                  |d       n Y xY w# | j                  j!                          w xY w)zSend an ACK back to server.z&[%d] Unable to send WebSocket ack [%s]Nz-[%d] Error while attempting to ack [%d] bytesTr}   )rC   rD   r`   CreateSubprotocolAckFramer7   ro   r   WebSocketConnectionClosedEnvironmentErrorr	   r   rL   six	text_typer   rA   clear)rM   bytes_receivedack_dataes       r   _SendAckzIapTunnelWebSocket._SendAck-  s    !!D$H$HH11n$22>B##H-/=, 	!!#' I --  29a 0	2 	2~~
((B==.4A A
 	!!#s)   7A: :D4C
D :D	D D*c                    | j                   j                         ry| j                  }| j                  }t        j
                  }||z
  d|z  kD  r:| j                   j                          | j                  j                  t               yy)z3Decide if an ACK should be sent back to the server.Nr   )
rA   is_setrC   rD   r`   rk   setrG   r\   r   )rM   total_bytesbytes_recv_and_ackdwindow_sizes       r   _MaybeSendAckz IapTunnelWebSocket._MaybeSendAckD  s{    !!#,,K>>77K ((1{?:
 /0	 ;r   c                 ^     fd} fd}	  j                   s	  |         j                   s j                          y# t        $ rO}t        j                  d j                  t        j                  |              j                  |       Y d}~qd}~ww xY w#  j                          w xY w)z,Main function for send_and_reconnect_thread.c                  `     j                   s! j                           j                          y y rW   )r:   _SendQueuedDatar   rY   s   r   SendDatazCIapTunnelWebSocket._SendDataAndReconnectWebSocket.<locals>.SendDataW  s$    ^^ r   c                  `     j                   s! j                           j                          y y rW   )r:   rc   rd   rY   s   r   	ReconnectzDIapTunnelWebSocket._SendDataAndReconnectWebSocket.<locals>.Reconnect\  s'    ^^!%%' r   z7[%d] Error while sending data, trying to reconnect [%s]N)	r:   	Exceptionr	   r   rL   r   r   r   rX   )rM   r   r   r   s   `   r   rf   z1IapTunnelWebSocket._SendDataAndReconnectWebSocketU  s    
(
		,
*  jjl  	,
))MMM3==#35

 
 
+
+	,
 jjls2   B ? B 	BABB BB B,c                 
   	 | j                   s| j                          	 | j                  j                         s| j                  j	                         }n | j
                  j	                  t              }|t        u s|t        u rd| _         |t        u rd| _        nq|t        u r| j!                          | j"                  j%                  |       t'        j(                  |      }| j*                  j-                  |       | j                   s| j                  rQ| j                  j                         r6| j
                  j                         r| j.                  j1                          yyyy# t        j                  $ r# | j                         rt        j                  Y w xY w# | j                  rQ| j                  j                         r6| j
                  j                         r| j.                  j1                          w w w w xY w)z3Send data that is sitting in the unsent data queue.r   TN)r:   r   rK   emptygetrG   r   r   Emptyr   r   r   rq   r    r=   r   r   rJ   appendr`   CreateSubprotocolDataFramer7   ro   r@   r   )rM   data	send_datas      r   r   z"IapTunnelWebSocket._SendQueuedDatal  s   3	%%++-''++-D $$(()I ) KD 8t}4$.X"DO
&&
--/
 	%%d+44T:	##I.] ` //d2288:



!
!
# $ ;/I {{ 	^^222
		H //d2288:



!
!
# $ ;/s1   F# AE* 4BF# *3F F# F  F# #AHc                     d| _         y )NT)r:   rY   s    r   r   z'IapTunnelWebSocket._StopConnectionAsync  s	    DNr   c                 n   t        t        dz        D ];  }| j                         r n)| j                         r yt	        j
                  d       = | j                  r| j                  j                         r| j                  j                         rd}| j                  j                         j                  d      rd}nB| j                  j                         j                  d      rd| j                  j                  z  }d	| j                  j                         d
|}t        |      t        d      )z<Wait for WebSocket open confirmation or any error condition.d   Ng{Gz? zHandshake status 40z$ (May be due to missing permissions)4003z (Failed to connect to port %d)zError while connecting [].z?Unexpected error while connecting. Check logs for more details.)range MAX_WEBSOCKET_OPEN_WAIT_TIME_SECr   r   r   sleepr7   r   ErrorMsg
startswithr1   portr   )rM   _	extra_msg	error_msgs       r   rd   z+IapTunnelWebSocket._WaitForOpenOrRaiseError  s   3c9:						
jj ; 	4#9#9#B#B#D'')i 
			(	(	*	5	56K	L:	 !!**,77?58K8K8P8PP	**335yBi#I..
! #; < <r   c                 $    | j                          y rW   )r   rY   s    r   r   zIapTunnelWebSocket._OnClose  s    r   c                    t        j                  |      \  }}|t         j                  k(  r| j                  |       y|t         j                  k(  r| j                  |       y|t         j                  k(  r| j                  |       y|t         j                  k(  r| j                  |       yt        j                  d|       y)z)Receive a single message from the server.z8Unsupported subprotocol tag [%r], discarding the messageN)r`   ExtractSubprotocolTagSUBPROTOCOL_TAG_DATA_HandleSubprotocolDataSUBPROTOCOL_TAG_ACK_HandleSubprotocolAck#SUBPROTOCOL_TAG_CONNECT_SUCCESS_SID#_HandleSubprotocolConnectSuccessSid%SUBPROTOCOL_TAG_RECONNECT_SUCCESS_ACK%_HandleSubprotocolReconnectSuccessAckr	   r   )rM   binary_datatag
bytes_lefts       r   r   zIapTunnelWebSocket._OnData  s    11+>OC
e(((
!!*-	))	)
  ,	99	9
..z:	;;	;
00<	iiJCPr   c                    | j                         s| j                          t        d      t        j                  |      \  }}| j                  |       |r+t        j                  d| j                  t        |             yy)zHandle Subprotocol ACK Frame.zReceived ACK before connected.z5[%d] Discarding [%d] extra bytes after processing ACKN)
r   r   r"   r`   ExtractSubprotocolAck_ConfirmDatar	   r   rL   r   )rM   r   bytes_confirmedr   s       r   r   z(IapTunnelWebSocket._HandleSubprotocolAck  sm    
!$%EFF"'"="=k"JOZo&	iiGs:0 r   c                     | j                         r| j                          t        d      t        j                  |      \  }}|| _        d| _        |r+t        j                  d| j                  t        |             yy)z-Handle Subprotocol CONNECT_SUCCESS_SID Frame.z5Received CONNECT_SUCCESS_SID after already connected.TzE[%d] Discarding [%d] extra bytes after processing CONNECT_SUCCESS_SIDN)r   r   r&   r`   #ExtractSubprotocolConnectSuccessSidr9   r8   r	   r   rL   r   rM   r   r   r   s       r   r   z6IapTunnelWebSocket._HandleSubprotocolConnectSuccessSid  sz    
!-
AC C @@MD*D!%D	ii !%JA r   c                     | j                   D ]  }| j                  j                  |        t        j                         | _         y rW   )rJ   rK   r\   rH   rI   )rM   r   s     r   !_AddUnconfirmedDataBackToTheQueuez4IapTunnelWebSocket._AddUnconfirmedDataBackToTheQueue  s7    &&
t$ '(..0Dr   c                    | j                         r| j                          t        d      t        j                  |      \  }}|| j
                  z
  }| j                  |       t        j                  d| j                  |t        | j                               | j                          d| _        |r+t        j                  d| j                  t        |             yy)z/Handle Subprotocol RECONNECT_SUCCESS_ACK Frame.z7Received RECONNECT_SUCCESS_ACK after already connected.zE[%d] Reconnecting: confirming [%d] bytes and resending [%d] messages.TzG[%d] Discarding [%d] extra bytes after processing RECONNECT_SUCCESS_ACKN)r   r   r(   r`   %ExtractSubprotocolReconnectSuccessAckrB   r   r	   r   rL   r   rJ   r   r8   r   )rM   r   r   r   bytes_being_confirmeds        r   r   z8IapTunnelWebSocket._HandleSubprotocolReconnectSuccessAck  s    
!/
CE E 	33K@  OZ+d.I.IIo&HHO,c$2H2H.IK 	**,!%D	ii"#'==#j/C r   c                    | j                         s| j                          t        d      t        j                  |      \  }}| xj
                  t        |      z  c_        | j                          	 | j                  |       |r+t        j                  d| j                  t        |             yy#  | j                           xY w)zHandle Subprotocol DATA Frame.zReceived DATA before connected.z6[%d] Discarding [%d] extra bytes after processing DATAN)r   r   r$   r`   ExtractSubprotocolDatarC   r   r   r3   r	   r   rL   r   s       r   r   z)IapTunnelWebSocket._HandleSubprotocolData	  s    
!%&GHH33K@D*#d)+
!!$' 	iiHs:0 
!s   3B2 2Cc                 $   || j                   k  r| j                          t        d|z        || j                   z
  }|r| j                  r| j                  j	                         }t        |      |kD  r4| j                  j                  ||d        | xj                   |z  c_         n| xj                   t        |      z  c_         || j                   z
  }|r| j                  r|r,| j                          t        d|d| j                   d      y)zCDiscard data that has been confirmed via ACKs received from server.z)Received out-of-order Ack for [%d] bytes.NzBytes confirmed [z] were larger than bytes sent [r   )rB   r   r,   rJ   popleftr   
appendleftr*   )rM   r   bytes_to_confirm
data_chunks       r   r   zIapTunnelWebSocket._ConfirmData  s   444
!)
5
GI I ')D)DD
t55))113j	Z+	+))*5E5F*GH##'77###s:6#(4+F+FF t55 
!&D779: : r   N)r   F)r   r   r   __doc__rU   rZ   rX   ri   ro   rr   rv   r   rl   r   r   rc   r   r   rf   r   r   rd   r   r   r   r   r   r   r   r   r   r   r   r.   r.   U   s     49 D%% 
,> $<&"$'L&>
$2L$.1".5n<6 Q
0A1C*0$:r   r.   )+r   
__future__r   r   r   rH   r   r>   r   googlecloudsdk.api_lib.computer   r   r   r`   googlecloudsdk.corer   r	   r
   googlecloudsdk.core.utilr   r   	six.movesr   r   r   r   r   rF   ru   r   Errorr   r   r   r    r"   r$   r&   r(   r*   r,   objectr.   r   r   r   <module>r     s#    ? &  '     P N * # * * 
 #$  #%  ' +    ! *** j.. !1!1 J$$ z// 
 0 0 
(8(8 **:*: !1!1 J$4$4 [: [:r   