
    T                     ~   S r SSKJr  SSKJr  SSKJr  SSKrSSKrSSKrSSKrSSK	J
r  SSK	Jr  SSKJr  SS	KJr  SS
KJr  SSKJr  SSKrSSKJr  SrSrSrSrSrSrSr " S S\R<                  5      r " S S\R<                  5      r  " S S\R<                  5      r! " S S\R<                  5      r" " S S\R<                  5      r# " S S\R<                  5      r$ " S  S!\R<                  5      r% " S" S#\R<                  5      r& " S$ S%\R<                  5      r' " S& S'\R<                  5      r( " S( S)\)5      r*g)*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                       \ rS rSrSrg)SendAckNotification-    N__name__
__module____qualname____firstlineno____static_attributes__r       :lib/googlecloudsdk/api_lib/compute/iap_tunnel_websocket.pyr   r   -       r   r   c                       \ rS rSrSrg)ConnectionCreationError1   r   Nr   r   r   r   r   r   1   r   r   r   c                       \ rS rSrSrg)ConnectionReconnectTimeout5   r   Nr   r   r   r   r"   r"   5   r   r   r"   c                       \ rS rSrSrg)StoppingError9   r   Nr   r   r   r   r%   r%   9   r   r   r%   c                       \ rS rSrSrg)SubprotocolEarlyAckError=   r   Nr   r   r   r   r(   r(   =   r   r   r(   c                       \ rS rSrSrg)SubprotocolEarlyDataErrorA   r   Nr   r   r   r   r+   r+   A   r   r   r+   c                       \ rS rSrSrg)!SubprotocolExtraConnectSuccessSidE   r   Nr   r   r   r   r.   r.   E   r   r   r.   c                       \ rS rSrSrg)#SubprotocolExtraReconnectSuccessAckI   r   Nr   r   r   r   r1   r1   I   r   r   r1   c                       \ rS rSrSrg)SubprotocolInvalidAckErrorM   r   Nr   r   r   r   r4   r4   M   r   r   r4   c                       \ rS rSrSrg)SubprotocolOutOfOrderAckErrorQ   r   Nr   r   r   r   r7   r7   Q   r   r   r7   c                       \ rS rSrSr SS jrS rS rS rS r	S r
S	 rS
 rS rS rS rS rS rS rS rS rS rS rS rS rS rS rS rS rS rS rSrg) IapTunnelWebSocketU   zCloud 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                    Xl         X l        X0l        X@l        Xpl        XPl        S U l        SU l        S U l        SU l	        SU l
        S U l        SU l        [        R                  " 5       U l        [        R                  " 5       U l        SU l        SU l        SU l        [(        R*                  " [,        S9U l        [0        R2                  " 5       U l        [(        R*                  " 5       U l        X`l        g )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__IapTunnelWebSocket.__init__\   s     (&?#"7#9 %!!D!&DDDN$D&*D#DO __&DN#//+D"#D!"D+,D(,CDD(..0D !;;=DMr   c                 \    U R                   (       a  U R                   R                  5         g g N)rD   CloserZ   s    r   __del__IapTunnelWebSocket.__del__~   s"    
""$ r   c                 <   SU l         U R                  R                  [        5         U R	                  5         U R
                  (       aM  U R                  (       d!  U R
                  R                  5         SU l        U R
                  R                  5         gg!    Nc= f)z5Close down local connection and WebSocket connection.TN)	rG   rT   putr%   rA   rD   rH   	SendCloserf   rg   s    r   rf   IapTunnelWebSocket.Close   s    DN 	-(
""$ %%((*#' 
""$	 
s   B Bc                 `   [         R                  " U R                  5        [         R                  " U R                  5        U R                  5         U R                  5         [        R                  " U R                  S9U l
        SU R                  l        U R                  R                  5         g)z"Initiate the WebSocket connection.)targetTN)utilsCheckPythonVersionrB   ValidateParametersr>   _StartNewWebSocket_WaitForOpenOrRaiseErrorrK   Thread_SendDataAndReconnectWebSocketrI   daemonstartrg   s    r   InitiateConnection%IapTunnelWebSocket.InitiateConnection   s~    	T//0	T001!!#&/&6&622'4D#-1D##*##))+r   c                     U(       aH  US[         R                   nU[         R                  S nU(       a  U R                  U5        U(       a  MG  gg)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)rp   SUBPROTOCOL_MAX_DATA_FRAME_SIZE!_EnqueueBytesWithWaitForReconnect)rZ   bytes_to_sendfirst_to_sends      r   SendIapTunnelWebSocket.Send   sE     #$JU%J%JKm#E$I$I$JKm	..}=	 -r   c                 B    U R                   R                  [        5        g)zIndicate that the local input gave an EOF.

This should always be called after finishing sending data, as to stop the
sending thread.
N)rT   rk   EOFErrorrg   s    r   LocalEOFIapTunnelWebSocket.LocalEOF   s     	(#r   c                 @    U R                   R                  [        5      $ )at  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.
)rM   waitALL_DATA_SENT_WAIT_TIME_SECrg   s    r   WaitForAllSent!IapTunnelWebSocket.WaitForAllSent   s    " >>:;;r   c                    [         R                  " [        S[        S9n UR	                  U[
        S9  g! [         R                   a8    [        R                  " SU R                  [        SS9  U R                  5          gf = f)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	   warningrY   _StopConnectionAsync)rZ   reconnect_funcrs      r   _AttemptReconnect$IapTunnelWebSocket._AttemptReconnect   sv     	"<36&A	CA"n"<  > "	kk;--!;dL
!"s   5 A	B Bc                 ^   [         R                   " 5       [        S-  -   n[         R                   " 5       U:  a  U R                  (       dv   U R                  R	                  U[
        S9  [        R                  " 5       [        R                  :X  a/  [        R                  " SU R                  [        U5      USS 5        gU R                  (       a  [        S5      e[!        5       e! [        R                   a     Of = f[         R                   " 5       U:  d  M[  U R                  (       d  M  No)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   rG   rT   rk    MAX_WEBSOCKET_SEND_WAIT_TIME_SECr	   GetVerbosityloggingDEBUGdebugrY   lenr   Fullr   r"   )rZ   r~   end_times      r   r}   4IapTunnelWebSocket._EnqueueBytesWithWaitForReconnect   s     yy{7&@@H
))+
  	m> 	 	@
 .
))IMM3}#5}Sb7IK
 ~~# %D E E
$
&& ZZ  ))+
 s   A4C& &C=<C=c                     U R                   $ )z.Returns true if we received a connect message.)rE   rg   s    r   _HasConnected IapTunnelWebSocket._HasConnected   s    %%%r   c                     U R                   =(       a    U R                   R                  5       =(       d2    U R                  =(       a    U R                  R                  5       (       + $ re   )rD   IsClosedrI   is_aliverg   s    r   	_IsClosedIapTunnelWebSocket._IsClosed  sL    ##I(>(>(G(G(I =,, <0099;;>r   c                    SU R                   -   /n[        R                  " SU R                  U R                   5        [        R
                  R                  R                  R                  5       nU(       a	  USU-   /-  nU R                  (       a  USU R                  5       -   /-  n[        R                  " SU R                  5        U R                  (       aX  [        R                  " U R                  U R                  U R                  SS9n[        R                  " SU R                  U5        OA[        R                   " U R                  SS9n[        R                  " S	U R                  U5        S
U l        [$        R&                  " UUU R(                  U R                  R*                  U R,                  U R.                  SU R                  S9U l        U R0                  R3                  5         g)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   r`   N)rC   r	   r   rY   r
   VALUEScorerequest_reasonGetr?   rF   rp   CreateWebSocketReconnectUrlr>   rP   infoCreateWebSocketConnectUrlrE   helperIapTunnelWebSocketHelperrB   
proxy_info_OnData_OnCloserD   StartReceivingThread)rZ   headersr   urls       r   rs   %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                 h   U R                   U R                  :  a_  U R                   n [        R                  " U5      nU R                  R                  U5        Xl        U R                  R!                  5         gg! [        R                   a    e [         a@  n[        R                  " SU R                  [        R                  " U5      5         SnANuSnAf  U R                  5       (       d#  [        R                  " SU R                  USS9   Ne = f! U R                  R!                  5         f = f)zSend an ACK back to server.z&[%d] Unable to send WebSocket ack [%s]Nz-[%d] Error while attempting to ack [%d] bytesTr   )rP   rQ   rp   CreateSubprotocolAckFramerD   r   r   WebSocketConnectionClosedEnvironmentErrorr	   r   rY   six	text_typer   rN   clear)rZ   bytes_receivedack_dataes       r   _SendAckIapTunnelWebSocket._SendAck-  s    !!D$H$HH11n$22>B##H-/=, 	!!#' I --  29a 0	2 	2~~
((B==.4A !!#s5   7A; ;D6CD ;DD DD D1c                    U R                   R                  5       (       a  gU R                  nU R                  n[        R
                  nX-
  SU-  :  a:  U R                   R                  5         U R                  R                  [        5        gg)z3Decide if an ACK should be sent back to the server.Nr   )
rN   is_setrP   rQ   rp   r|   setrT   rk   r   )rZ   total_bytesbytes_recv_and_ackdwindow_sizes       r   _MaybeSendAck IapTunnelWebSocket._MaybeSendAckD  s{    !!##,,K>>77K (1{?:
 /0	 ;r   c                 ~  ^  U 4S jnU 4S jn T R                   (       d   U" 5         T R                   (       d  M  T R                  5         g! [         aQ  n[        R                  " ST R                  [
        R                  " U5      5        T R                  U5         SnANzSnAff = f! T R                  5         f = f)z,Main function for send_and_reconnect_thread.c                  j   > T R                   (       d!  T R                  5         T R                  5         g g re   )rG   _SendQueuedDatar   rg   s   r   SendDataCIapTunnelWebSocket._SendDataAndReconnectWebSocket.<locals>.SendDataW  s$    ^^ r   c                  j   > T R                   (       d!  T R                  5         T R                  5         g g re   )rG   rs   rt   rg   s   r   	ReconnectDIapTunnelWebSocket._SendDataAndReconnectWebSocket.<locals>.Reconnect\  s'    ^^!%%' r   z7[%d] Error while sending data, trying to reconnect [%s]N)	rG   	Exceptionr	   r   rY   r   r   r   rf   )rZ   r   r   r   s   `   r   rv   1IapTunnelWebSocket._SendDataAndReconnectWebSocketU  s    
(
		,
*  jjl  	,
))MMM3==#35

 
 
+
+	,
 jjls4   B* A B* 
B'AB"B* "B''B* *B<c                     U R                   (       Gd  U R                  5          U R                  R                  5       (       d  U R                  R	                  5       nOU R
                  R	                  [        S9n U[        L d	  U[        L a  SU l         U[        L a  SU l        O{U[        L a  U R!                  5         M  U R"                  R%                  U5        [&        R(                  " U5      nU R*                  R-                  U5        U R                   (       d  GM  U R                  (       a[  U R                  R                  5       (       a;  U R
                  R                  5       (       a  U R.                  R1                  5         gggg! [        R                   a)    U R                  5       (       a  [        R                  e M  f = f! U R                  (       a[  U R                  R                  5       (       a;  U R
                  R                  5       (       a  U R.                  R1                  5         f f f f = f)z3Send data that is sitting in the unsent data queue.r   TN)rG   r   rX   emptygetrT   r   r   Emptyr   r   r   r   r%   rJ   r   r   rW   appendrp   CreateSubprotocolDataFramerD   r   rM   r   )rZ   data	send_datas      r   r   "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::



!
!
#
# $ ;/s<   "G 9F G F <B#G 9GG GG A.H=c                     SU l         g )NT)rG   rg   s    r   r   'IapTunnelWebSocket._StopConnectionAsync  s	    DNr   c                    [        [        S-  5       HG  nU R                  5       (       a    O1U R                  5       (       a    g[        R
                  " S5        MI     U R                  (       a  U R                  R                  5       (       a  U R                  R                  5       (       a  SnU R                  R                  5       R                  S5      (       a  SnOGU R                  R                  5       R                  S5      (       a  SU R                  R                  -  nS	U R                  R                  5       < S
U< 3n[        U5      e[        S5      e)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   sleeprD   r   ErrorMsg
startswithr>   portr   )rZ   _	extra_msg	error_msgs       r   rt   +IapTunnelWebSocket._WaitForOpenOrRaiseError  s   3c9:								
jj ; 	4#9#9#B#B#D#D''))i 
			(	(	*	5	56K	L	L:	 !!**,77??58K8K8P8PP	**335yBi#I..
! #; < <r   c                 $    U R                  5         g re   )r   rg   s    r   r   IapTunnelWebSocket._OnClose  s    r   c                    [         R                  " U5      u  p#U[         R                  :X  a  U R                  U5        gU[         R                  :X  a  U R                  U5        gU[         R                  :X  a  U R                  U5        gU[         R                  :X  a  U R                  U5        g[        R                  " SU5        g)z)Receive a single message from the server.z8Unsupported subprotocol tag [%r], discarding the messageN)rp   ExtractSubprotocolTagSUBPROTOCOL_TAG_DATA_HandleSubprotocolDataSUBPROTOCOL_TAG_ACK_HandleSubprotocolAck#SUBPROTOCOL_TAG_CONNECT_SUCCESS_SID#_HandleSubprotocolConnectSuccessSid%SUBPROTOCOL_TAG_RECONNECT_SUCCESS_ACK%_HandleSubprotocolReconnectSuccessAckr	   r   )rZ   binary_datatag
bytes_lefts       r   r   IapTunnelWebSocket._OnData  s    11+>OC
e(((
!!*-	))	)
  ,	99	9
..z:	;;	;
00<	iiJCPr   c                    U R                  5       (       d  U R                  5         [        S5      e[        R                  " U5      u  p#U R                  U5        U(       a,  [        R                  " SU R                  [        U5      5        gg)zHandle Subprotocol ACK Frame.zReceived ACK before connected.z5[%d] Discarding [%d] extra bytes after processing ACKN)
r   r   r(   rp   ExtractSubprotocolAck_ConfirmDatar	   r   rY   r   )rZ   r  bytes_confirmedr  s       r   r  (IapTunnelWebSocket._HandleSubprotocolAck  sn    
!$%EFF"'"="=k"JOo&	iiGs:0 r   c                    U R                  5       (       a  U R                  5         [        S5      e[        R                  " U5      u  p#X l        SU l        U(       a,  [        R                  " SU R                  [        U5      5        gg)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.   rp   #ExtractSubprotocolConnectSuccessSidrF   rE   r	   r   rY   r   rZ   r  r   r  s       r   r  6IapTunnelWebSocket._HandleSubprotocolConnectSuccessSid  sy    
!-
AC C @@MD!%D	ii !%JA r   c                     U R                    H  nU R                  R                  U5        M      [        R                  " 5       U l         g re   )rW   rX   rk   rU   rV   )rZ   r   s     r   !_AddUnconfirmedDataBackToTheQueue4IapTunnelWebSocket._AddUnconfirmedDataBackToTheQueue  s7    &&
t$ '(..0Dr   c                    U R                  5       (       a  U R                  5         [        S5      e[        R                  " U5      u  p#X R
                  -
  nU R                  U5        [        R                  " SU R                  U[        U R                  5      5        U R                  5         SU l        U(       a,  [        R                  " SU R                  [        U5      5        gg)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   r1   rp   %ExtractSubprotocolReconnectSuccessAckrO   r  r	   r   rY   r   rW   r  rE   r   )rZ   r  r  r  bytes_being_confirmeds        r   r
  8IapTunnelWebSocket._HandleSubprotocolReconnectSuccessAck  s    
!/
CE E 	33K@  O+.I.IIo&HHO,c$2H2H.IK 	**,!%D	ii"#'==#j/C r   c                    U R                  5       (       d  U R                  5         [        S5      e[        R                  " U5      u  p#U =R
                  [        U5      -  sl        U R                  5          U R                  U5        U(       a,  [        R                  " SU R                  [        U5      5        gg!   U R                  5         e = f)zHandle Subprotocol DATA Frame.zReceived DATA before connected.z6[%d] Discarding [%d] extra bytes after processing DATAN)r   r   r+   rp   ExtractSubprotocolDatarP   r   r   r@   r	   r   rY   r  s       r   r  )IapTunnelWebSocket._HandleSubprotocolData	  s    
!%&GHH33K@D#d)+
!!$' 	iiHs:0 
!s   8B= =Cc                 X   XR                   :  a  U R                  5         [        SU-  5      eXR                   -
  nU(       a  U R                  (       a  U R                  R	                  5       n[        U5      U:  a3  U R                  R                  X2S 5        U =R                   U-  sl         OU =R                   [        U5      -  sl         XR                   -
  nU(       a  U R                  (       a  M  U(       a.  U R                  5         [        SU< SU R                   < S35      eg)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   )rO   r   r7   rW   popleftr   
appendleftr4   )rZ   r  bytes_to_confirm
data_chunks       r   r  IapTunnelWebSocket._ConfirmData  s   444
!)
5
GI I ')D)DD
t55))113j	Z+	+))*5F*GH##'77###s:6#(+F+FF t555 
!&D779: : r   )rN   rA   rH   rY   rE   rF   r@   rX   r?   rB   rJ   rI   rM   rG   rO   rP   rQ   r>   rW   rT   rC   rD   N)r   F) r   r   r   r   __doc__rb   rh   rf   ry   r   r   r   r   r}   r   r   rs   r   r   rv   r   r   rt   r   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   rU   r   rK   r   googlecloudsdk.api_lib.computer   r   r   rp   googlecloudsdk.corer   r	   r
   googlecloudsdk.core.utilr   r   	six.movesr   r   r   r   r   rS   r   r   Errorr   r   r"   r%   r(   r+   r.   r1   r4   r7   objectr:   r   r   r   <module>r0     s#    ? &  '     P N * # * * 
 #$  #%  ' +    ! *** j.. !1!1 J$$ z// 
 0 0 
(8(8 **:*: !1!1 J$4$4 [: [:r   