Example #1
0
 async def _validate_checksum(self, response):
     """Check the computed checksum, if any, against the response headers.
     Args:
         response (object): The HTTP response object.
     Raises:
         ~google.resumable_media.common.DataCorruption: If the checksum
         computed locally and the checksum reported by the remote host do
         not match.
     """
     if self._checksum_type is None:
         return
     metadata_key = sync_helpers._get_metadata_key(self._checksum_type)
     metadata = await response.json()
     remote_checksum = metadata.get(metadata_key)
     if remote_checksum is None:
         raise common.InvalidResponse(
             response,
             _UPLOAD_METADATA_NO_APPROPRIATE_CHECKSUM_MESSAGE.format(
                 metadata_key),
             self._get_headers(response),
         )
     local_checksum = sync_helpers.prepare_checksum_digest(
         self._checksum_object.digest())
     if local_checksum != remote_checksum:
         raise common.DataCorruption(
             response,
             _UPLOAD_CHECKSUM_MISMATCH_MESSAGE.format(
                 self._checksum_type.upper(), local_checksum,
                 remote_checksum),
         )
Example #2
0
    async def test__validate_checksum_mismatch(self, checksum):
        data = b"All of the data goes in a stream."
        upload = self._upload_in_flight(data, checksum=checksum)
        _fix_up_virtual(upload)
        # Go ahead and process the entire data in one go for this test.
        start_byte, payload, _ = _upload.get_next_chunk(
            upload._stream, len(data), len(data))
        upload._update_checksum(start_byte, payload)
        assert upload._bytes_checksummed == len(data)

        metadata = {
            "md5Hash": "ZZZZZZZZZZZZZZZZZZZZZZ==",
            "crc32c": "ZZZZZZ==",
        }
        # This is only used by _validate_checksum for fetching headers and
        # logging, so it doesn't need to be fleshed out with a response body.
        response = _make_response(headers=metadata)
        upload._finished = True

        assert upload._checksum_object is not None
        # Test passes if it does not raise an error (no assert needed)
        with pytest.raises(common.DataCorruption) as exc_info:
            await upload._validate_checksum(response)

        error = exc_info.value
        assert error.response is response
        message = error.args[0]
        correct_checksums = {
            "crc32c": u"Qg8thA==",
            "md5": u"GRvfKbqr5klAOwLkxgIf8w=="
        }
        metadata_key = sync_helpers._get_metadata_key(checksum)
        assert message == _upload._UPLOAD_CHECKSUM_MISMATCH_MESSAGE.format(
            checksum.upper(), correct_checksums[checksum],
            metadata[metadata_key])
Example #3
0
    async def test__validate_checksum_header_no_match(self, checksum):
        data = b"All of the data goes in a stream."
        upload = self._upload_in_flight(data, checksum=checksum)
        _fix_up_virtual(upload)
        # Go ahead and process the entire data in one go for this test.
        start_byte, payload, _ = _upload.get_next_chunk(
            upload._stream, len(data), len(data))
        upload._update_checksum(start_byte, payload)
        assert upload._bytes_checksummed == len(data)

        # For this test, each checksum option will be provided with a valid but
        # mismatching remote checksum type.
        if checksum == "crc32c":
            metadata = {"md5Hash": "GRvfKbqr5klAOwLkxgIf8w=="}
        else:
            metadata = {"crc32c": "Qg8thA=="}
        # This is only used by _validate_checksum for fetching headers and
        # logging, so it doesn't need to be fleshed out with a response body.
        response = _make_response(headers=metadata)
        upload._finished = True

        assert upload._checksum_object is not None
        with pytest.raises(common.InvalidResponse) as exc_info:
            await upload._validate_checksum(response)

        error = exc_info.value
        assert error.response is response
        message = error.args[0]
        metadata_key = sync_helpers._get_metadata_key(checksum)
        assert (
            message == _upload._UPLOAD_METADATA_NO_APPROPRIATE_CHECKSUM_MESSAGE
            .format(metadata_key))
Example #4
0
    def _prepare_request(self, data, metadata, content_type):
        """Prepare the contents of an HTTP request.

        This is everything that must be done before a request that doesn't
        require network I/O (or other I/O). This is based on the `sans-I/O`_
        philosophy.

        .. note:

            This method will be used only once, so ``headers`` will be
            mutated by having a new key added to it.

        Args:
            data (bytes): The resource content to be uploaded.
            metadata (Mapping[str, str]): The resource metadata, such as an
                ACL list.
            content_type (str): The content type of the resource, e.g. a JPEG
                image has content type ``image/jpeg``.

        Returns:
            Tuple[str, str, bytes, Mapping[str, str]]: The quadruple

              * HTTP verb for the request (always POST)
              * the URL for the request
              * the body of the request
              * headers for the request

        Raises:
            ValueError: If the current upload has already finished.
            TypeError: If ``data`` isn't bytes.

        .. _sans-I/O: https://sans-io.readthedocs.io/
        """
        if self.finished:
            raise ValueError("An upload can only be used once.")

        if not isinstance(data, bytes):
            raise TypeError("`data` must be bytes, received", type(data))

        checksum_object = sync_helpers._get_checksum_object(
            self._checksum_type)

        if checksum_object is not None:
            checksum_object.update(data)
            actual_checksum = sync_helpers.prepare_checksum_digest(
                checksum_object.digest())
            metadata_key = sync_helpers._get_metadata_key(self._checksum_type)
            metadata[metadata_key] = actual_checksum

        content, multipart_boundary = construct_multipart_request(
            data, metadata, content_type)
        multipart_content_type = _RELATED_HEADER + multipart_boundary + b'"'

        self._headers[_CONTENT_TYPE_HEADER] = multipart_content_type

        return _POST, self.upload_url, content, self._headers