
    b                     4   S 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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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  \R>                  (       a  \ r! " S S\"5      r#g)ap  Boto translation layer for resumable uploads.

See https://cloud.google.com/storage/docs/resumable-uploads-xml
for details.

Resumable uploads will retry interrupted uploads, resuming at the byte
count completed by the last upload attempt. If too many retries happen with
no progress (per configurable num_retries param), the upload will be
aborted in the current process.

Unlike the boto implementation of resumable upload handler, this class does
not directly interact with tracker files.

Originally Google wrote and contributed this code to the boto project,
then copied that code back into gsutil on the release of gsutil 4.0 which
supports both boto and non-boto codepaths for resumable uploads.  Any bug
fixes made to this file should also be integrated to resumable_upload_handler.py
in boto, where applicable.

TODO: gsutil-beta: Add a similar comment to the boto code.
    )absolute_import)print_function)division)unicode_literalsN)urllib)http_client)config)	UserAgent)AWSAuthConnection)ResumableTransferDisposition)ResumableUploadException)InvalidUrlError)GetMaxRetryDelay)GetNumRetries)XML_PROGRESS_CALLBACKS)UTF8c                       \ rS rSrSrSr\R                  \\	R                  \	R                  4rSr  SS jrS rSS jrS	 rS
 rSS jrS rS rS r SS jrSS\4S jrSrg)BotoResumableUploadJ   z3Upload helper class for resumable uploads via boto.i    )r   Nc                     U(       a  U R                  U5        OSU l        X@l        SU l        SU l        Xl        X l        g)a=  Constructor. Instantiate once for each uploaded file.

Args:
  tracker_callback: Callback function that takes a string argument.  Used
                    by caller to track this upload across upload
                    interruption.
  logger: logging.logger instance to use for debug messages.
  resume_url: If present, attempt to resume the upload at this URL.
  num_retries: Number of times to retry the upload making no progress.
               This count resets every time we make progress, so the upload
               can span many more than this number of retries.
Nr   )_SetUploadUrl
upload_urlnum_retriesservice_has_bytesupload_start_pointtracker_callbacklogger)selfr   r   
resume_urlr   s        .platform/gsutil/gslib/boto_resumable_upload.py__init__BotoResumableUpload.__init__U   s?    " 
$do"D #D,K    c                 z   [         R                  R                  U5      nUR                  R	                  5       S;  d  UR
                  (       d  [        SU-  5      eXl        [        R                  " SSS5      =(       d    UR
                  U l
        UR                  < SUR                  < 3U l        SU l        g)zSaves URL and resets upload state.

Called when we start a new resumable upload or get a new tracker
URL for the upload.

Args:
  url: URL string for the upload.

Raises InvalidUrlError if URL is syntactically invalid.
)httphttpszInvalid upload URL (%s)Credentialsgs_hostN?r   )r   parseurlparseschemelowernetlocr   r   r	   getupload_url_hostpathqueryupload_url_pathr   )r   urlparse_results      r!   r   !BotoResumableUpload._SetUploadUrlr   s     <<((-L!!#+<<5;<<O"JJ}iF 0(// 	&2&7&79K9KLDDr$   c                     SU< SU< 3$ )Nzbytes / )r   
range_speclength_specs      r!   _BuildContentRangeHeader,BotoResumableUpload._BuildContentRangeHeader   s    &44r$   c           	          U R                  SU5      SS.n[        R                  " USU R                  U R                  UU R                  S9$ )a  Queries service to find out state of given upload.

Note that this method really just makes special case use of the
fact that the upload service always returns the current start/end
state whenever a PUT doesn't complete.

Args:
  conn: HTTPConnection to use for the query.
  file_length: Total length of the file.

Returns:
  HTTP response from sending request.

Raises:
  ResumableUploadException if problem querying service.
*0)Content-RangeContent-LengthPUTr2   	auth_pathheadershost)r=   r   make_requestr4   r1   )r   connfile_lengthput_headerss       r!   _QueryServiceState&BotoResumableUpload._QueryServiceState   sX    ( 77[IK ))$*//3/C/C484H4H2=/3/C/CE Er$   c                    U R                  X5      nUR                  S:X  a  SUS-
  4$ UR                  S:w  a'  [        SUR                  -  [        R                  5      eSnUR                  S5      nU(       aU  [        R                  " SU5      nU(       a6  [        UR                  S5      5      n[        UR                  S	5      5      nS
nOU R                  $ U(       d4  [        S[        UR                  5       5      -  [        R                  5      eUR                  S:  a  U R                  R                  SWW5        WW4$ )al  Queries service to find out what bytes it currently has.

Args:
  conn: HTTPConnection to use for the query.
  file_length: Total length of the file.

Returns:
  (service_start, service_end), where the values are inclusive.
  For example, (0, 2) would mean that the service has bytes 0, 1, *and* 2.

Raises:
  ResumableUploadException if problem querying service.
   r      i4  z2Got non-308 response (%s) from service state queryFrangezbytes=(\d+)-(\d+)   Tz7Couldn't parse upload service state query response (%s)zService has: Range: %d - %d.)rM   statusr   r   
START_OVER	getheaderresearchlonggroupSERVICE_HAS_NOTHINGstr
getheadersdebugr   )	r   rJ   rK   respgot_valid_responser;   mservice_startservice_ends	            r!   _QueryServicePos$BotoResumableUpload._QueryServicePos   s+    ""45D{{c q!!{{c %
>
L
&
1
13 3 (J
))(*
5a	
QWWQZ(1771:&! %%%$
D
doo
 !">"I"IK K zzQ
kk6#%;''r$   c                 p   UR                   R                  nUR                  S:  a  U R                  R                  S5        SU l        0 nU H7  nUR                  5       S:X  a  [        S[        R                  5      eX%   XE'   M9     SXCR                  R                  '   UR                  SUR                   R                  UR                  U5      nUR                  5       nUR                  S;   a'  [        S	UR                  -  [        R                  5      eUR                  S
:w  a7  UR                  S:w  a'  [        SUR                  -  [        R                  5      eUR!                  S5      nU(       d  [        SU-  [        R                  5      eU R#                  U5        U R%                  U5        g)zStarts a new resumable upload.

Args:
  key: Boto Key representing the object to upload.
  headers: Headers to use in the upload requests.

Raises:
  ResumableUploadException if any errors occur.
rQ   zStarting new resumable upload.r   zcontent-lengthz5Attempt to specify Content-Length header (disallowed)startPOST)      zEGot status %d from attempt to start resumable upload. Will wait/retryrP      z>Got status %d from attempt to start resumable upload. AbortingLocationzHNo resumable upload URL found in resumable initiation POST response (%s)N)bucket
connectionr^   r   r   r.   r   r   ABORTproviderresumable_upload_headerrI   namereadrT   WAIT_BEFORE_RETRYrV   r   r   )	r   keyrG   rJ   post_headerskr_   bodyr   s	            r!   _StartNewResumableUpload,BotoResumableUpload._StartNewResumableUpload   s    ::  DzzQ
kk89D L	
&	&&C(..0 	0  
lo  ;BL667VSZZ__chhMD99;D {{o%$"kk*
&
8
8: : 
	s 2${{#$@$F$FH H 
+J$!%&
&
8
8: : 	z"*%r$   c	           	      >   UR                  U R                  5      n	U(       a0  US:  a  X@R                  -  US-
  -  n
OUS:  a  Sn
OSn
SnU" XT5        U(       a  UR                  5       O0 nU(       a5  XT:X  a  U R                  SU5      nOU R                  SXTS-
  4-  U5      nXS'   [	        XE-
  5      US'   [
        R                  " US	U R                  S
UU R                  S9nUR                  S	UR                  5        U H  nUR                  XU   5        M     UR                  5         UR                  S5        U	(       a  [        R                  (       a   UR!                  U	5        U[#        U	5      -  nOi[%        U	[&        5      (       a   UR!                  U	5        U[#        U	5      -  nO4U	R)                  [*        5      nUR!                  U5        U[#        U5      -  nU(       a  WS-  nUW
:X  d  U
S:X  a
  U" XT5        SnUR                  U R                  5      n	U	(       a  M  UR                  UR,                  5        U(       a  U" XT5        XT:w  a  [/        SXT4-  [0        R2                  5      eUR5                  5       nUR6                  S:X  a2  UR9                  S5      UR9                  S5      UR9                  S5      4$ UR6                  S;   a  [0        R:                  nO[0        R2                  n[/        SUR6                  UR<                  4-  U5      e)a   Attempts to upload file bytes.

Makes a single attempt using an existing resumable upload connection.

Args:
  conn: HTTPConnection from the boto Key.
  http_conn: Separate HTTPConnection for the transfer.
  fp: File pointer containing bytes to upload.
  file_length: Total length of the file.
  total_bytes_uploaded: The total number of bytes uploaded.
  cb: Progress callback function that takes (progress, total_size).
  num_cb: Granularity of the callback (maximum number of times the
          callback will be called during the file transfer). If negative,
          perform callback with each buffer read.
  headers: Headers to be used in the upload requests.

Returns:
  (etag, generation, metageneration) from service upon success.

Raises:
  ResumableUploadException if any problems occur.
rS   r   r   r@   z%d-%drQ   rB   rC   rD   NrE   z<File changed during upload: EOF at %d bytes of %d byte file.rP   etagzx-goog-generationzx-goog-metageneration)i  ri   rj   rk   z1Got response code %d while attempting upload (%s))rt   BUFFER_SIZEcopyr=   r\   r   build_base_http_requestr4   r1   
putrequestr2   	putheader
endheadersset_debuglevelsixPY2sendlen
isinstancebytesencoder   r^   r   r   rp   getresponserT   rV   ru   reason)r   rJ   	http_connfprK   total_bytes_uploadedcbnum_cbrG   bufcb_countirL   range_headerhttp_requestrx   	buf_bytesr_   dispositions                      r!   _UploadFileBytes$BotoResumableUpload._UploadFileBytes  s   0 ''$""
#C	 
!!1!11VaZ@A:
a	+ %,',,.K		,44S+F44+1_=={L%1/"$'(J$KK !$<<!!!!#L  1 12!^,  Q
 
sC(c5!!
..

#c(
*
 jj&)
..
#
#i.
0
		Q=HN
!
/!GGD$$%c- #2 TZZ(		+* %
H
-.
&
,
,. .
   "D{{cnnV$dnn5H&Inn457 7 
,	,0BBk 166k
"	dkk2	34?A Ar$   c           
      "   U R                   u  pxUR                  R                  n	U R                  (       aG   U R	                  X5      u  nnXpl        U	R                  S:  a  U R                  R                  S5        OU R                  X5        U R                  c  Xl        US-   nX:  a  UR                  U5        UR                  R                  n	U	R                  U R                  U	R                  U	R                   5      nUR#                  U	R                  5         U R%                  XX#XXd5      UR'                  5         $ ! [         aR  n
U	R                  S:  a&  U R                  R                  SU
R                  5        U R                  X5         Sn
A
GNSn
A
ff = f! [        [(        R*                  4 a=    U R-                  X5      nUR.                  S:X  a  [        S[0        R2                  5      ee f = f! UR'                  5         f = f)ad  Attempts a resumable upload.

Args:
  key: Boto key representing object to upload.
  fp: File pointer containing upload bytes.
  file_length: Total length of the upload.
  headers: Headers to be used in upload requests.
  cb: Progress callback function that takes (progress, total_size).
  num_cb: Granularity of the callback (maximum number of times the
          callback will be called during the file transfer). If negative,
          perform callback with each buffer read.

Returns:
  (etag, generation, metageneration) from service upon success.

Raises:
  ResumableUploadException if any problems occur.
rQ   zResuming transfer.zUnable to resume transfer (%s).Ni  zGot 400 response from service state query after failed resumable upload attempt. This can happen for various reasons, including specifying an invalid request (e.g., an invalid canned ACL) or if the file size changed between upload attempts)r[   rn   ro   r   rd   r   r^   r   r   messagerz   r   seeknew_http_connectionr1   port	is_securer   r   closesocketerrorrM   rT   r   rp   )r   rv   r   rK   rG   r   r   rb   rc   rJ   er   r   r_   s                 r!   _AttemptResumableUpload+BotoResumableUpload._AttemptResumableUpload  s   & $(#;#; ]::  D	4..tA		!.::?
++

0
1 ##C1
 & +&? )gg"#::  D (()=)=tyy)-9ITZZ(
""4B#7VN ooa & 4::?
++

=qyy
I%%c334H %fll3 
$$T7d		&? )..0 	0 	
 oos2   AE F" 
FAFF"AG99G< <Hc                    UR                   [        R                  :X  a-  US:  a&  U R                  R	                  SUR
                  5        e UR                   [        R                  :X  a-  US:  a&  U R                  R	                  SUR
                  5        e UR                   [        R                  :X  a  e US:  a'  U R                  R	                  SUR
                  5        g g )NrQ   zWCaught non-retryable ResumableUploadException (%s); aborting but retaining tracker filezVCaught non-retryable ResumableUploadException (%s); aborting and removing tracker filez1Caught ResumableUploadException (%s) - will retry)r   r   ABORT_CUR_PROCESSr   r^   r   rp   rU   )r   r   r^   s      r!   HandleResumableUploadException2BotoResumableUpload.HandleResumableUploadException  s    }}4FFF	!23499	> 	
6<<	<	!123))	= 	
6AA	A	!M))	% 
r$   c                    U R                   U:  a  SU l        OU =R                  S-  sl        U R                  U R                  :  a  [        S[        R
                  5      e[        [        R                  " 5       SU R                  -  -  [        5       5      nUS:  a'  U R                  R                  SU R                  U5        [        R                  " U5        g)a  Tracks the number of iterations without progress.

Performs randomized exponential backoff.

Args:
  service_had_bytes_before_attempt: Number of bytes the service had prior
                                   to this upload attempt.
  debug: debug level 0..3
r   rQ   zaToo many resumable upload attempts failed without progress. You might try this upload again laterrS   zZGot retryable failure (%d progress-less in a row).
Sleeping %3.1f seconds before re-tryingN)r   progress_less_iterationsr   r   r   r   minrandomr   r   r^   timesleep)r    service_had_bytes_before_attemptr^   sleep_time_secss       r!   TrackProgressLessIterations/BotoResumableUpload.TrackProgressLessIterations  s      @@&'d#
##q(#$$t'7'77$<
&
8
8: : &--/Q0M0M-MN*,.Oz
kk4

'
': 	JJr$   c                    U(       d  0 nSnX;   a  XH   c  XH	 U(       a  XTUR                   R                  '   [        US'   Un	UR                  R                  R
                  n
U R                  c  [        5       U l        SU l         U R                  n U R                  XXXg5      u  ol        U l        U R                  Ul        U
S:  a  U R                  R                  S5        g! U R                   a  nU
S:  a*  U R                  R                  SUR                  5       5        [!        U["        5      (       aL  UR$                  [$        R&                  :X  a.  UR                  R                  R                  R)                  5          SnAO+SnAf[*         a  nU R-                  X5         SnAOSnAff = fU R/                  UU
S9  GMD  )	a>  Upload a file to a key into a bucket on GS, resumable upload protocol.

Args:
  key: `boto.s3.key.Key` or subclass representing the upload destination.
  fp: File pointer to upload
  size: Size of the file to upload.
  headers: The headers to pass along with the PUT request
  canned_acl: Optional canned ACL to apply to object.
  cb: Callback function that will be called to report progress on
      the upload.  The callback should accept two integer parameters, the
      first representing the number of bytes that have been successfully
      transmitted to GS, and the second representing the total number of
      bytes that need to be transmitted.
  num_cb: (optional) If a callback is specified with the cb parameter, this
          parameter determines the granularity of the callback by defining
          the maximum number of times the callback will be called during the
          file transfer. Providing a negative integer will cause your
          callback to be called with each buffer read.

Raises:
  ResumableUploadException if a problem occurs during the transfer.
zContent-TypeNz
User-Agentr   rQ   zResumable upload complete.zCaught exception (%s))r^   )rq   
acl_headerr
   rn   ro   r^   r   r   r   r   r   
generationmetagenerationr   RETRYABLE_EXCEPTIONS__repr__r   IOErrorerrnoEPIPEr   r   r   r   )r   rv   r   sizerG   
canned_aclr   r   content_typerK   r^   r   _r   s                 r!   SendFileBotoResumableUpload.SendFile  s   > g "L7#8#@

)3cll%%&%GLKJJ!!''E &d$%D!
)-)?)?&6 372N2N[237/?D/ A:
++

8
9&& 	3A:
++

3QZZ\
Ba!!agg&< **


*
*
0
0
2% 6++A556 &&'G-2 ' 45 s&   AC& &F46BFF4F//F4)r   r   r   r   r   r   r   r   r   r1   r4   )NN)r@   r@   )N)r   )__name__
__module____qualname____firstlineno____doc__r~   r   HTTPExceptionr   r   r   gaierrorr   r[   r"   r   r=   rM   rd   rz   r   r   r   r   r   r   __static_attributes__r:   r$   r!   r   r   J   s    ;+%33Wfll //+
  
 	:*5E<8(t:&xuAnM^%, )*! P ,Q4r$   r   )$r   
__future__r   r   r   r   r   r   rW   r   r   r   	six.movesr   r   botor	   r
   boto.connectionr   boto.exceptionr   r   gslib.exceptionr   gslib.utils.boto_utilr   r   gslib.utils.constantsr   r   PY3intrY   objectr   r:   r$   r!   <module>r      sh   ,, ' %  '   	   
  !   - 7 3 + 2 / 8 &77	$b4& b4r$   