a
    !f                     @   s,  d Z ddlmZ ddlm  mZ ddlm  m	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mZ ddlmZ ddlmZ ddlmZ ddl m!Z! dZ"dZ#dZ$dZ%G dd de&Z'G dd de'Z(G dd de'Z)dS )z)Upload and download support for apitools.    N)http_client)	_to_bytes)BufferedStream)CommunicationError)	HttpError)TransferInvalidError)TransferRetryError)get_http)make_api_request)Request)RESUME_INCOMPLETE)StreamSlice)acceptable_mime_typei  P simple	resumablei   c                   @   s   e Zd ZdZdZdedddfddZdd	 Zed
d Z	edd Z
edd Zejdd Zedd Zejdd Zedd Zedd Zdd Zedd Zdd Zdd Zd d! ZdS )"	_Transfera  Generic bits common to Uploads and Downloads.

    :type stream: file-like object
    :param stream: stream to/from which data is downloaded/uploaded.

    :type close_stream: boolean
    :param close_stream: should this instance close the stream when deleted

    :type chunksize: integer
    :param chunksize: the size of chunks used to download/upload a file.

    :type auto_transfer: boolean
    :param auto_transfer: should this instance automatically begin transfering
                          data when initialized

    :type http: :class:`httplib2.Http` (or workalike)
    :param http: Http instance used to perform requests.

    :type num_retries: integer
    :param num_retries: how many retries should the transfer attempt
    NFT   c                 C   s4   d | _ || _|| _|| _d | _|| _|| _|| _d S N)_bytes_http_close_stream_http_stream_urlnum_retriesauto_transfer	chunksize)selfstreamclose_streamr   r   httpr    r    Z/var/www/html/python-backend/venv/lib/python3.9/site-packages/gcloud/streaming/transfer.py__init__K   s    z_Transfer.__init__c                 C   s   t | S r   )strr   r    r    r!   __repr__Z   s    z_Transfer.__repr__c                 C   s   | j S )zShould this instance close the stream when deleted.

        :rtype: boolean
        :returns: Boolean indicated if the stream should be closed.
        )r   r$   r    r    r!   r   ]   s    z_Transfer.close_streamc                 C   s   | j S )zHttp instance used to perform requests.

        :rtype: :class:`httplib2.Http` (or workalike)
        :returns: The HTTP object used for requests.
        )r   r$   r    r    r!   r   f   s    z_Transfer.httpc                 C   s   | j p
| jS )zHttp instance used to perform binary requests.

        Defaults to :attr:`http`.

        :rtype: :class:`httplib2.Http` (or workalike)
        :returns: The HTTP object used for binary requests.
        )r   r   r$   r    r    r!   
bytes_httpo   s    	z_Transfer.bytes_httpc                 C   s
   || _ dS )zUpdate Http instance used to perform binary requests.

        :type value: :class:`httplib2.Http` (or workalike)
        :param value: new instance
        N)r   r   valuer    r    r!   r&   z   s    c                 C   s   | j S )zHow many retries should the transfer attempt

        :rtype: integer
        :returns: The number of retries allowed.
        )_num_retriesr$   r    r    r!   r      s    z_Transfer.num_retriesc                 C   s.   t |tjstd|dk r$td|| _dS )zZUpdate how many retries should the transfer attempt

        :type value: integer
        znum_retries: pass an integerr   z*Cannot have negative value for num_retriesN)
isinstancesixinteger_types
ValueErrorr)   r'   r    r    r!   r      s    c                 C   s   | j S )zStream to/from which data is downloaded/uploaded.

        :rtype: file-like object
        :returns: The stream that sends/receives data.
        )r   r$   r    r    r!   r      s    z_Transfer.streamc                 C   s   | j S )zURL to / from which data is downloaded/uploaded.

        :rtype: string
        :returns: The URL where data is sent/received.
        )r   r$   r    r    r!   url   s    z_Transfer.urlc                 C   s(   |    | jdu r|pt | _|| _dS )a  Initialize this download by setting :attr:`http` and :attr`url`.

        Allow the user to be able to pre-initialize :attr:`http` by setting
        the value in the constructor; in that case, we ignore the provided
        http.

        :type http: :class:`httplib2.Http` (or a worklike) or None.
        :param http: the Http instance to use to make requests.

        :type url: string
        :param url: The url for this transfer.
        N)_ensure_uninitializedr   r	   r   r   )r   r   r.   r    r    r!   _initialize   s    
z_Transfer._initializec                 C   s   | j duo| jduS )zHas the instance been initialized

        :rtype: boolean
        :returns: Boolean indicating if the current transfer
                  has been initialized.
        N)r.   r   r$   r    r    r!   initialized   s    z_Transfer.initializedc                 C   s   | j stdt| jdS )zHelper:  assert that the instance is initialized.

        :raises: :exc:`gcloud.streaming.exceptions.TransferInvalidError`
                 if the instance is not initialized.
        zCannot use uninitialized %sNr1   r   type__name__r$   r    r    r!   _ensure_initialized   s    
z_Transfer._ensure_initializedc                 C   s   | j rtdt| jdS )zHelper:  assert that the instance is not initialized.

        :raises: :exc:`gcloud.streaming.exceptions.TransferInvalidError`
                 if the instance is already initialized.
        zCannot re-initialize %sNr2   r$   r    r    r!   r/      s    
z_Transfer._ensure_uninitializedc                 C   s   | j r| j  d S r   )r   r   closer$   r    r    r!   __del__   s    z_Transfer.__del__)r4   
__module____qualname____doc__r)   _DEFAULT_CHUNKSIZEr"   r%   propertyr   r   r&   setterr   r   r.   r0   r1   r5   r/   r7   r    r    r    r!   r   2   s:   










	

r   c                       s   e Zd ZdZeejejejej	fZ
 fddZed'ddZed(d	d
Zedd Zedd Zedd Zdd Zdd Zdd Zdd Zd)ddZed*ddZd+ddZdd  Zd!d" Zd,d#d$Zd-d%d&Z  ZS ).Downloada&  Represent a single download.

    :type stream: file-like object
    :param stream: stream to/from which data is downloaded/uploaded.

    :type kwds: dict
    :param kwds:  keyword arguments:  all except ``total_size`` are passed
                  through to :meth:`_Transfer.__init__()`.
    c                    s@   | dd }tt| j|fi | d | _d| _|| _d | _d S )N
total_sizer   )popsuperr>   r"   _initial_response	_progress_total_size	_encoding)r   r   kwdsr?   	__class__r    r!   r"      s    zDownload.__init__FTc                 K   sD   t j|}t j|r(|s(td| | t|dfd|d|S )aO  Create a new download object from a filename.

        :type filename: string
        :param filename: path/filename for the target file

        :type overwrite: boolean
        :param overwrite: should an existing file be overwritten

        :type auto_transfer: boolean
        :param auto_transfer: should the transfer be started immediately

        :type kwds: dict
        :param kwds:  keyword arguments:  passed
                      through to :meth:`_Transfer.__init__()`.

        :rtype: :class:`Download`
        :returns: The download initiated from the file passed.
        z*File %s exists and overwrite not specifiedwbT)r   r   )ospath
expanduserexistsr-   open)clsfilename	overwriter   rF   rK   r    r    r!   	from_file   s    zDownload.from_fileNc                 K   s   | |f||d|S )aW  Create a new Download object from a stream.

        :type stream: writable file-like object
        :param stream: the target file

        :type total_size: integer or None
        :param total_size: total size of the file to be downloaded

        :type auto_transfer: boolean
        :param auto_transfer: should the transfer be started immediately

        :type kwds: dict
        :param kwds:  keyword arguments:  passed
                      through to :meth:`_Transfer.__init__()`.

        :rtype: :class:`Download`
        :returns: The download initiated from the stream passed.
        )r   r?   r    )rO   r   r   r?   rF   r    r    r!   from_stream  s    zDownload.from_streamc                 C   s   | j S )z~Number of bytes have been downloaded.

        :rtype: integer >= 0
        :returns: The number of downloaded bytes.
        rC   r$   r    r    r!   progress,  s    zDownload.progressc                 C   s   | j S )zTotal number of bytes to be downloaded.

        :rtype: integer or None
        :returns: The total number of bytes to download.
        rD   r$   r    r    r!   r?   5  s    zDownload.total_sizec                 C   s   | j S )z'Content-Encoding' used to transfer the file

        :rtype: string or None
        :returns: The encoding of the downloaded content.
        )rE   r$   r    r    r!   encoding>  s    zDownload.encodingc                 C   s"   | j s
dS d| j| j| jf S d S )NzDownload (uninitialized)z1Download with %d/%s bytes transferred from url %sr1   rU   r?   r.   r$   r    r    r!   r%   G  s
    zDownload.__repr__c                 C   s$   d|j d< d| jd f |jd< dS )a[  Update http_request/url_builder with download-appropriate values.

        :type http_request: :class:`gcloud.streaming.http_wrapper.Request`
        :param http_request: the request to be updated

        :type url_builder: instance with settable 'query_params' attribute.
        :param url_builder: transfer policy object to be updated
        mediaZaltz
bytes=0-%d   RangeN)query_paramsr   headers)r   http_requesturl_builderr    r    r!   configure_requestN  s    	
zDownload.configure_requestc                 C   sB   d|v r.|d  d\}}}|dkr.t|| _| jdu r>d| _dS )z~Update 'total_size' based on data from a response.

        :type info: mapping
        :param info: response headers
        zcontent-range/*Nr   )
rpartitionintrD   r?   )r   info_totalr    r    r!   
_set_totalZ  s    

zDownload._set_totalc                 C   s   |    |j}| jrt| d}| |d| t| jp6||}|j| jvrRt	
||| _| |j |jd|j}| || | jr| jdd dS )a  Initialize this download.

        If the instance has :attr:`auto_transfer` enabled, begins the
        download immediately.

        :type http_request: :class:`gcloud.streaming.http_wrapper.Request`
        :param http_request: the request to use to initialize this download.

        :type http: :class:`httplib2.Http` (or workalike)
        :param http: Http instance for this request.
        r   zcontent-locationT
use_chunksN)r/   r.   r   _compute_end_byte_set_range_headerr
   r&   status_code_ACCEPTABLE_STATUSESr   from_responserB   rh   re   getrequest_urlr0   stream_file)r   r^   r   r.   end_byteresponser    r    r!   initialize_downloadk  s     


zDownload.initialize_downloadc                 C   s   |dur\|dk rt dn|| jkr,t dt|| jd }||k rTt d||f ||fS |dk rttd|| j }|| jd fS dS )a  Validate / fix up byte range.

        :type start: integer
        :param start: start byte of the range:  if negative, used as an
                      offset from the end.

        :type end: integer
        :param end: end byte of the range.

        :rtype: tuple, (start, end)
        :returns:  the normalized start, end pair.
        :raises: :exc:`gcloud.streaming.exceptions.TransferInvalidError`
                 for invalid combinations of start, end.
        Nr   z/Cannot have end index with negative start indexz/Cannot have start index greater than total sizerZ   z(Range requested with end[%s] < start[%s])r   r?   minmax)r   startendr    r    r!   _normalize_start_end  s$    

zDownload._normalize_start_endc                 C   sF   |dk rd| | j d< n*|du r0d| | j d< nd||f | j d< dS )a  Update the 'Range' header in a request to match a byte range.

        :type request: :class:`gcloud.streaming.http_wrapper.Request`
        :param request: the request to update

        :type start: integer
        :param start: start byte of the range:  if negative, used as an
                      offset from the end.

        :type end: integer
        :param end: end byte of the range.
        r   zbytes=%drangeNz	bytes=%d-zbytes=%d-%d)r]   )requestrx   ry   r    r    r!   rl     s
    zDownload._set_range_headerc                 C   sl   |}|dk r| j s|S |r@|| j d }|dur<t||}n|}| j rh| j d }|durdt||}n|}|S )a  Compute the last byte to fetch for this request.

        Based on the HTTP spec for Range and Content-Range.

        .. note::
           This is potentially confusing in several ways:
           - the value for the last byte is 0-based, eg "fetch 10 bytes
             from the beginning" would return 9 here.
           - if we have no information about size, and don't want to
             use the chunksize, we'll return None.

        :type start: integer
        :param start: start byte of the range.

        :type end: integer or None
        :param end: suggested last byte of the range.

        :type use_chunks: boolean
        :param use_chunks: If False, ignore :attr:`chunksize`.

        :rtype: str
        :returns: Last byte to use in a 'Range' header, or None.
        r   rZ   N)r?   r   rv   )r   rx   ry   rj   rs   	alternater    r    r!   rk     s    
zDownload._compute_end_bytec                 C   s6   |    t| jd}| j|||d t| j|| jdS )a6  Retrieve a chunk of the file.

        :type start: integer
        :param start: start byte of the range.

        :type end: integer or None
        :param end: end byte of the range.

        :rtype: :class:`gcloud.streaming.http_wrapper.Response`
        :returns: response from the chunk request.
        )r.   )ry   retries)r5   r   r.   rl   r
   r&   r   )r   rx   ry   r|   r    r    r!   
_get_chunk  s    
zDownload._get_chunkc                 C   s   |j | jvr4|j tjtjfv r*t|n
t|j|j tj	tj
fv r| j|j |  j|j7  _|jrd|jv r|jd | _n|j tjkr| jd |S )a  Update attribtes and writing stream, based on response.

        :type response: :class:`gcloud.streaming.http_wrapper.Response`
        :param response: response from a download request.

        :rtype: :class:`gcloud.streaming.http_wrapper.Response`
        :returns: the response
        :raises: :exc:`gcloud.streaming.exceptions.HttpError` for
                 missing / unauthorized responses;
                 :exc:`gcloud.streaming.exceptions.TransferRetryError`
                 for other error responses.
        zcontent-encoding )rm   rn   r   	FORBIDDEN	NOT_FOUNDr   ro   r   contentOKPARTIAL_CONTENTr   writerC   lengthre   rE   
NO_CONTENT)r   rt   r    r    r!   _process_response  s     
zDownload._process_responsec                 C   s   |    d}| jdur,| ||\}}d}n|}|}|rH|du sH||kr| j|||d}| ||}|s| |j | ||\}}d}| |}||j7 }|jdkr4t	dq4dS )a/  Retrieve a given byte range from this download, inclusive.

        Writes retrieved bytes into :attr:`stream`.

        Range must be of one of these three forms:
        * 0 <= start, end = None: Fetch from start to the end of the file.
        * 0 <= start <= end: Fetch the bytes from start to end.
        * start < 0, end = None: Fetch the last -start bytes of the file.

        (These variations correspond to those described in the HTTP 1.1
        protocol for range headers in RFC 2616, sec. 14.35.1.)

        :type start: integer
        :param start: Where to start fetching bytes. (See above.)

        :type end: integer or ``None``
        :param end: Where to stop fetching bytes. (See above.)

        :type use_chunks: boolean
        :param use_chunks: If False, ignore :attr:`chunksize`
                           and fetch this range in a single request.
                           If True, streams via chunks.

        :raises: :exc:`gcloud.streaming.exceptions.TransferRetryError`
                 if a request returns an empty response.
        FNT)ry   rj   r   z5Zero bytes unexpectedly returned in download response)
r5   r?   rz   rk   r   rh   re   r   r   r   )r   rx   ry   rj   Zprogress_end_normalizedrU   rs   rt   r    r    r!   	get_range  s0    



zDownload.get_rangec                 C   s~   |    | jdur | j}d| _n| j| j|d}| | j|}| jdu rT| |j | |}|j	t
jksz| j| jkrqzqdS )a;  Stream the entire download.

        Writes retrieved bytes into :attr:`stream`.

        :type use_chunks: boolean
        :param use_chunks: If False, ignore :attr:`chunksize`
                           and stream this download in a single request.
                           If True, streams via chunks.
        Nri   )r5   rB   rk   rU   r   r?   rh   re   r   rm   r   r   )r   rj   rt   rs   r    r    r!   rr   Q  s    




zDownload.stream_file)FT)TN)N)N)NT)NT)T) r4   r8   r9   r:   setr   r   r   r   REQUESTED_RANGE_NOT_SATISFIABLErn   r"   classmethodrR   rS   r<   rU   r?   rW   r%   r`   rh   ru   rz   staticmethodrl   rk   r   r   r   rr   __classcell__r    r    rG   r!   r>      s<   	



 
-"
2r>   c                       s"  e Zd ZdZedZd6 fdd	Zed7dd	Zed8d
dZ	e
dd Ze
dd Ze
dd Ze
dd Zejdd Ze
dd Zejdd Zdd Zdd Zdd Zdd Zd d! Zd"d# Zd$d% Zed&d' Zd(d) Zed*d+ Zd9d,d-Zd:d.d/Zd0d1 Zd2d3 Zd4d5 Z   Z!S );UploadaO  Represent a single Upload.

    :type stream: file-like object
    :param stream: stream to/from which data is downloaded/uploaded.

    :type mime_type: string:
    :param mime_type: MIME type of the upload.

    :type total_size: integer or None
    :param total_size: Total upload size for the stream.

    :type http: :class:`httplib2.Http` (or workalike)
    :param http: Http instance used to perform requests.

    :type close_stream: boolean
    :param close_stream: should this instance close the stream when deleted

    :type auto_transfer: boolean
    :param auto_transfer: should this instance automatically begin transfering
                          data when initialized

    :type kwds: dict
    :param kwds:  keyword arguments:  all except ``total_size`` are passed
                  through to :meth:`_Transfer.__init__()`.
    )r   	mime_typer?   r.   NFTc                    sN   t t| j|f|||d| d | _d | _d| _|| _d| _d | _|| _	d S )N)r   r   r   Fr   )
rA   r   r"   _final_response_server_chunk_granularity	_complete
_mime_typerC   	_strategyrD   )r   r   r   r?   r   r   r   rF   rG   r    r!   r"     s    
zUpload.__init__c                 K   s^   t j|}|s2t|\}}|du r2td| t |j}| t|d|f|d|d|S )aU  Create a new Upload object from a filename.

        :type filename: string
        :param filename: path/filename to the file being uploaded

        :type mime_type: string
        :param mime_type:  MIMEtype of the file being uploaded

        :type auto_transfer: boolean or None
        :param auto_transfer: should the transfer be started immediately

        :type kwds: dict
        :param kwds:  keyword arguments:  passed
                      through to :meth:`_Transfer.__init__()`.

        :rtype: :class:`Upload`
        :returns: The upload initiated from the file passed.
        Nz Could not guess mime type for %srbTr?   r   r   )	rJ   rK   rL   	mimetypes
guess_typer-   statst_sizerN   )rO   rP   r   r   rF   rK   rf   sizer    r    r!   rR     s    zUpload.from_filec                 K   s*   |du rt d| ||f|d|d|S )a  Create a new Upload object from a stream.

        :type stream: writable file-like object
        :param stream: the target file

        :type mime_type: string
        :param mime_type:  MIMEtype of the file being uploaded

        :type total_size: integer or None
        :param total_size:  Size of the file being uploaded

        :type auto_transfer: boolean or None
        :param auto_transfer: should the transfer be started immediately

        :type kwds: dict
        :param kwds:  keyword arguments:  passed
                      through to :meth:`_Transfer.__init__()`.

        :rtype: :class:`Upload`
        :returns: The upload initiated from the stream passed.
        Nz!No mime_type specified for streamFr   )r-   )rO   r   r   r?   r   rF   r    r    r!   rS     s    
zUpload.from_streamc                 C   s   | j S )zHas the entire stream been uploaded.

        :rtype: boolean
        :returns: Boolean indicated if the upload is complete.
        )r   r$   r    r    r!   complete  s    zUpload.completec                 C   s   | j S )ztMIMEtype of the file being uploaded.

        :rtype: string
        :returns: The mime-type of the upload.
        )r   r$   r    r    r!   r     s    zUpload.mime_typec                 C   s   | j S )zeBytes uploaded so far

        :rtype: integer
        :returns: The amount uploaded so far.
        rT   r$   r    r    r!   rU     s    zUpload.progressc                 C   s   | j S )zwUpload strategy to use

        :rtype: string or None
        :returns: The strategy used to upload the data.
        )r   r$   r    r    r!   strategy  s    zUpload.strategyc                 C   s"   |t tfvrtd| || _dS )zUpdate upload strategy to use

        :type value: string (one of :data:`SIMPLE_UPLOAD` or
                :data:`RESUMABLE_UPLOAD`)

        :raises: :exc:`ValueError` if value is not one of the two allowed
                 strings.
        zOInvalid value "%s" for upload strategy, must be one of "simple" or "resumable".N)SIMPLE_UPLOADRESUMABLE_UPLOADr-   r   r'   r    r    r!   r     s    
c                 C   s   | j S )zTotal size of the stream to be uploaded.

        :rtype: integer or None
        :returns: The total size to be uploaded.
        rV   r$   r    r    r!   r?     s    zUpload.total_sizec                 C   s   |    || _dS )z}Update total size of the stream to be uploaded.

        :type value: integer or None
        :param value: the size
        N)r/   rD   r'   r    r    r!   r?     s    c                 C   s&   | j s
dS d| j| jpd| jf S d S )NzUpload (uninitialized)z.Upload with %d/%s bytes transferred for url %sz???rX   r$   r    r    r!   r%     s
    zUpload.__repr__c                 C   s^   |j du rt| _| jdurdS t}| jdur:| jtkr:t}|jrJ|jsJt}|jsTt}|| _dS )a  Determine and set the default upload strategy for this upload.

        We generally prefer simple or multipart, unless we're forced to
        use resumable. This happens when any of (1) the upload is too
        large, (2) the simple endpoint doesn't support multipart requests
        and we have metadata, or (3) there is no simple upload endpoint.

        :type upload_config: instance w/ ``max_size`` and ``accept``
                             attributes
        :param upload_config: Configuration for the upload endpoint.

        :type http_request: :class:`gcloud.streaming.http_wrapper.Request`
        :param http_request: The associated http request.
        N)	resumable_pathr   r   r?   RESUMABLE_UPLOAD_THRESHOLDr   bodyZsimple_multipartsimple_path)r   upload_configr^   r   r    r    r!   _set_default_strategy!  s    


zUpload._set_default_strategyc                 C   s   | j r,|jr,| j |jkr,td| j |jf t|j| jsNtd| j|jf | || | jtkr|j	|_
|jrd|jd< | | qd|jd< | | n|j|_
d|jd< | | dS )a  Configure the request and url for this upload.

        :type upload_config: instance w/ ``max_size`` and ``accept``
                             attributes
        :param upload_config: transfer policy object to be queried

        :type http_request: :class:`gcloud.streaming.http_wrapper.Request`
        :param http_request: the request to be updated

        :type url_builder: instance with settable 'relative_path' and
                           'query_params' attributes.
        :param url_builder: transfer policy object to be updated

        :raises: :exc:`ValueError` if the requested upload is too big,
                  or does not have an acceptable MIME type.
        z*Upload too big: %s larger than max size %sz7MIME type %s does not match any accepted MIME ranges %s	multipartZ
uploadTyperY   r   N)r?   max_sizer-   r   acceptr   r   r   r   r   relative_pathr   r\   _configure_multipart_request_configure_media_requestr   _configure_resumable_request)r   r   r^   r_   r    r    r!   r`   >  s2    




zUpload.configure_requestc                 C   s"   | j |jd< | j |_d|_dS )z6Helper for 'configure_request': set up simple request.content-type<media body>N)r   r]   r   readr   loggable_bodyr   r^   r    r    r!   r   i  s    zUpload._configure_media_requestc                 C   s  t d}t|ddd  tj|jd d }||j |	| tj| j
d }d|d< || j  |	| t }tjrtj}ntj}||d	d
}|j|d	d | |_| }d| |jd< t|}|j|}	|	d d\}
}}d|
dg|	d< ||	|_dS )z9Helper for 'configure_request': set up multipart request.relatedZ_write_headersc                 S   s   d S r   r    r$   r    r    r!   <lambda>t      z5Upload._configure_multipart_request.<locals>.<lambda>r   ra   binaryzContent-Transfer-EncodingF)mangle_from_)unixfromz multipart/related; boundary="%s"s   

s   <media body>

--N)mime_multipartZMIMEMultipartsetattrmime_nonmultipartZMIMENonMultipartr]   splitset_payloadr   attachr   r   r   r+   BytesIOPY3email_generatorBytesGenerator	Generatorflattengetvalueget_boundaryr   	partitionjoinr   )r   r^   Zmsg_rootmsgr   Zgenerator_class	generatormultipart_boundaryZboundary_bytesZbody_componentsr]   rf   r    r    r!   r   o  s4    



z#Upload._configure_multipart_requestc                 C   s*   | j |jd< | jdur&t| j|jd< dS )z9Helper for 'configure_request': set up resumable request.zX-Upload-Content-TypeNzX-Upload-Content-Length)r   r]   r?   r#   r   r    r    r!   r     s    
z#Upload._configure_resumable_requestc                 C   s   | j tkrdS |   t| jdddid}t| j|d| jd}| |}|j	t
jt
jfv r~d| _| j| _| j| j || _nD|j	tkr|du rd| _n| |d	 | _| j| j n
t|dS )
zKRefresh the state of a resumable upload via query to the back-end.
        NPUTContent-Rangez	bytes */*)r.   http_methodr]   r   )Zredirectionsr   TrZ   )r   r   r5   r   r.   r
   r   r   _get_range_headerrm   r   r   CREATEDr   r?   rC   r   seekrU   r   r   
_last_byter   ro   )r   Zrefresh_requestZrefresh_responserange_headerr    r    r!   refresh_upload_state  s2    


zUpload.refresh_upload_statec                 C   s   | j d| j dS )a  Return a 'Range' header from a response.

        :type response: :class:`gcloud.streaming.http_wrapper.Response`
        :param response: response to be queried

        :rtype: string
        :returns: The header used to determine the bytes range.
        r[   r{   )re   rp   )rt   r    r    r!   r     s    zUpload._get_range_headerc                 C   s   | j du rtd| j tkr dS |   t||| jd}|jtjkrNt	
||jd}|durjt|}|| _|jd }| || | jr| jddS |S dS )aF  Initialize this upload from the given http_request.

        :type http_request: :class:`gcloud.streaming.http_wrapper.Request`
        :param http_request: the request to be used

        :type http: :class:`httplib2.Http` (or workalike)
        :param http: Http instance for this request.

        :raises: :exc:`ValueError` if the instance has not been configured
                 with a strategy.
        :rtype: :class:`~gcloud.streaming.http_wrapper.Response`
        :returns: The response if the upload is resumable and auto transfer
                  is not used.
        Nz7No upload strategy set; did you call configure_request?r~   zX-Goog-Upload-Chunk-GranularitylocationTri   )r   r-   r   r/   r
   r   rm   r   r   r   ro   re   rp   rd   r   r0   r   rr   )r   r^   r   http_responseZgranularityr.   r    r    r!   initialize_upload  s*    



zUpload.initialize_uploadc                 C   s   |  d\}}}t|S )zParse the last byte from a 'Range' header.

        :type range_header: string
        :param range_header: 'Range' header value per RFC 2616/7233

        :rtype: int
        :returns: The last byte from a range header.
        -)r   rd   )r   rf   ry   r    r    r!   r   
  s    
zUpload._last_bytec                 C   s2   | j du rdS |p| j}|| j  r.td| j dS )aG  Validate chunksize against server-specified granularity.

        Helper for :meth:`stream_file`.

        :type chunksize: integer or None
        :param chunksize: the chunk size to be tested.

        :raises: :exc:`ValueError` if ``chunksize`` is not a multiple
                 of the server-specified granulariy.
        Nz0Server requires chunksize to be a multiple of %d)r   r   r-   )r   r   r    r    r!   _validate_chunksize  s    


zUpload._validate_chunksizec                 C   s(  | j tkrtd| j}|r"| jn| j}|r8| | j |   | j	s|| j
 }|jtjtjfv rnd| _q| |jd | _| jd | j
 kr@td| j q@| j	r$t| j
dr$t| j
dr| j
 r$| j
 }| j
dtj | j
 }| j
| ||kr$td	t|t|  |S )
a=  Upload the stream.

        :type use_chunks: boolean
        :param use_chunks: If False, send the stream in a single request.
                           Otherwise, send it in chunks.

        :rtype: :class:`gcloud.streaming.http_wrapper.Response`
        :returns: The response for the final request made.
        z"Cannot stream non-resumable uploadTr{   rZ   z?Failed to transfer all bytes in chunk, upload paused at byte %dr   seekabler   z7Upload complete with %s additional bytes left in stream)r   r   r-   r   _send_chunk_send_media_bodyr   r   r5   r   r   tellrm   r   r   r   r   r   re   rC   rU   r   hasattrr   r   rJ   SEEK_ENDr   rd   )r   rj   rt   Z	send_funcZcurrent_posend_posr    r    r!   rr   *  sD    




zUpload.stream_filec                 C   sn   t | j|| jd}|jtjtjtfvr8|   t	
||jtkrj| | |}|d |krj| j| |S )a  Peform API upload request.

        Helper for _send_media_body & _send_chunk:

        :type request: :class:`gcloud.streaming.http_wrapper.Request`
        :param request: the request to upload

        :type end: integer
        :param end: end byte of the to be uploaded

        :rtype: :class:`gcloud.streaming.http_wrapper.Response`
        :returns: the response
        :raises: :exc:`gcloud.streaming.exceptions.HttpError` if the status
                 code from the response indicates an error.
        r~   rZ   )r
   r&   r   rm   r   r   r   r   r   r   ro   r   r   r   r   )r   r|   ry   rt   Z	last_byter    r    r!   _send_media_requestT  s    


zUpload._send_media_requestc                 C   s   |    | jdu rtdt| j| j| }t| jd|d}| j|jd< || jkr^d| j }nd|| jd | jf }||jd	< | 	|| jS )
a)  Send the entire stream in a single request.

        Helper for :meth:`stream_file`:

        :type start: integer
        :param start: start byte of the range.

        :rtype: :class:`gcloud.streaming.http_wrapper.Response`
        :returns: The response from the media upload request.
        Nz*Total size must be known for SendMediaBodyr   r.   r   r   Content-Type
bytes */%sbytes %s-%s/%srZ   r   )
r5   r?   r   r   r   r   r.   r   r]   r   )r   rx   body_streamr|   range_stringr    r    r!   r   s  s    


zUpload._send_media_bodyc                 C   s   |    | jdu }| jdu rLt| j|| j}|j}|jr>|| _|| j}n"t	|| j | j}t
| j|| }t| jd|d}| j|jd< |rd|_| jdu rd||d f }n(||krd| j }nd	||d | jf }||jd
< | ||S )a  Send a chunk of the stream.

        Helper for :meth:`stream_file`:

        :type start: integer
        :param start: start byte of the range.

        :rtype: :class:`gcloud.streaming.http_wrapper.Response`
        :returns: The response from the chunked upload request.
        Nr   r   r   r   zbytes %s-%s/*rZ   r   r   r   )r5   r?   r   r   r   Zstream_end_positionZstream_exhaustedrD   r   rv   r   r   r.   r   r]   r   r   )r   rx   Zno_log_bodyr   ry   r|   r   r    r    r!   r     s.    




zUpload._send_chunk)NNFT)NT)NT)N)T)"r4   r8   r9   r:   r   Z_REQUIRED_SERIALIZATION_KEYSr"   r   rR   rS   r<   r   r   rU   r   r=   r?   r%   r   r`   r   r   r   r   r   r   r   r   r   rr   r   r   r   r   r    r    rG   r!   r   l  sR      






	+',
(


*r   )*r:   email.generatorr   r   Zemail.mime.multipartmimer   r   Zemail.mime.nonmultipartZnonmultipartr   r   rJ   r+   Z	six.movesr   Zgcloud._helpersr   Z gcloud.streaming.buffered_streamr   Zgcloud.streaming.exceptionsr   r   r   r   Zgcloud.streaming.http_wrapperr	   r
   r   r   Zgcloud.streaming.stream_slicer   Zgcloud.streaming.utilr   r   r   r   r;   objectr   r>   r   r    r    r    r!   <module>   s<    0   