예제 #1
0
def objects_get(bucket_name, object_name):
    """Implement the 'Objects: get' API.  Read objects or their metadata."""
    _, blob = testbench_utils.lookup_object(bucket_name, object_name)
    blob.check_preconditions(flask.request)
    revision = blob.get_revision(flask.request)

    media = flask.request.args.get('alt', None)
    if media is None or media == 'json':
        return testbench_utils.filtered_response(flask.request, revision.metadata)
    if media != 'media':
        raise error_response.ErrorResponse('Invalid alt=%s parameter' % media)
    revision.validate_encryption_for_read(flask.request)
    # Respect the Range: header, if present.
    range_header = flask.request.headers.get('range')
    response_payload = revision.media
    begin = 0
    end = len(response_payload)
    if range_header is not None:
        print("\n\n\nrange_header = %s\n\n" % range_header)
        m = re.match('bytes=([0-9]+)-([0-9]+)', range_header)
        if m:
            print("\n\n\nmatch = %s\n\n" % m)
            begin = int(m.group(1))
            end = int(m.group(2))
            response_payload = response_payload[begin:end + 1]
    # Process custome headers to test error conditions.
    instructions = flask.request.headers.get('x-goog-testbench-instructions')
    if instructions == 'return-corrupted-data':
        response_payload = testbench_utils.corrupt_media(response_payload)
    response = flask.make_response(response_payload)
    length = len(response_payload)
    content_range = 'bytes %d-%d/%d' % (begin, end - 1, length)
    response.headers['Content-Range'] = content_range
    response.headers['x-goog-hash'] = revision.x_goog_hash_header()
    return response
예제 #2
0
    def __init__(self, gcs_url, bucket_name, name, generation, request, media):
        """Initialize a new object revision.

        :param gcs_url:str the base URL for the GCS service.
        :param bucket_name:str the name of the bucket that contains the object.
        :param name:str the name of the object.
        :param generation:int the generation number for this object.
        :param request:flask.Request the contents of the HTTP request.
        :param media:str the contents of the object.
        """
        self.gcs_url = gcs_url
        self.bucket_name = bucket_name
        self.name = name
        self.generation = str(generation)
        self.object_id = bucket_name + "/o/" + name + "/" + str(generation)
        now = time.gmtime(time.time())
        timestamp = time.strftime("%Y-%m-%dT%H:%M:%SZ", now)
        self.media = media
        instructions = request.headers.get("x-goog-testbench-instructions")
        if instructions == "inject-upload-data-error":
            self.media = testbench_utils.corrupt_media(media)

        self.metadata = {
            "timeCreated":
            timestamp,
            "updated":
            timestamp,
            "metageneration":
            "0",
            "generation":
            str(generation),
            "location":
            "US",
            "storageClass":
            "STANDARD",
            "size":
            str(len(self.media)),
            "etag":
            "XYZ=",
            "owner": {
                "entity": "project-owners-123456789",
                "entityId": ""
            },
            "md5Hash":
            base64.b64encode(hashlib.md5(self.media).digest()).decode("utf-8"),
            "crc32c":
            base64.b64encode(struct.pack(">I", crc32c.crc32(
                self.media))).decode("utf-8"),
        }
        if request.headers.get("content-type") is not None:
            self.metadata["contentType"] = request.headers.get("content-type")
예제 #3
0
def objects_get_common(bucket_name, object_name, revision):
    # Respect the Range: header, if present.
    range_header = flask.request.headers.get('range')
    response_payload = revision.media
    begin = 0
    end = len(response_payload)
    if range_header is not None:
        m = re.match('bytes=([0-9]+)-([0-9]+)', range_header)
        if m:
            begin = int(m.group(1))
            end = int(m.group(2))
            response_payload = response_payload[begin:end + 1]
        m = re.match('bytes=([0-9]+)-$', range_header)
        if m:
            begin = int(m.group(1))
            response_payload = response_payload[begin:]
    # Process custom headers to test error conditions.
    instructions = flask.request.headers.get('x-goog-testbench-instructions')
    if instructions == 'return-broken-stream':

        def streamer():
            chunk_size = 128 * 1024
            for r in range(0, len(response_payload) / 2, chunk_size):
                if r > 1024 * 1024:
                    print("\n\n###### EXIT to simulate crash\n")
                    sys.exit(1)
                time.sleep(0.1)
                chunk_end = min(r + chunk_size, len(response_payload))
                yield response_payload[r:chunk_end - 1]

        length = len(response_payload)
        content_range = 'bytes %d-%d/%d' % (begin, end - 1, length)
        headers = {
            'Content-Range': content_range,
            'x-goog-hash': revision.x_goog_hash_header(),
            'x-goog-generation': revision.generation
        }
        return flask.Response(streamer(), status=200, headers=headers)

    if instructions == 'return-corrupted-data':
        response_payload = testbench_utils.corrupt_media(response_payload)
    response = flask.make_response(response_payload)
    length = len(response_payload)
    content_range = 'bytes %d-%d/%d' % (begin, end - 1, length)
    response.headers['Content-Range'] = content_range
    response.headers['x-goog-hash'] = revision.x_goog_hash_header()
    response.headers['x-goog-generation'] = revision.generation
    return response
예제 #4
0
def objects_get(bucket_name, object_name):
    """Implement the 'Objects: get' API.  Read objects or their metadata."""
    _, blob = testbench_utils.lookup_object(bucket_name, object_name)
    blob.check_preconditions(flask.request)
    revision = blob.get_revision(flask.request)

    media = flask.request.args.get('alt', None)
    if media is None or media == 'json':
        return testbench_utils.filtered_response(flask.request,
                                                 revision.metadata)
    if media != 'media':
        raise error_response.ErrorResponse('Invalid alt=%s parameter' % media)
    revision.validate_encryption_for_read(flask.request)
    instructions = flask.request.headers.get('x-goog-testbench-instructions')
    if instructions == 'return-corrupted-data':
        response_payload = testbench_utils.corrupt_media(revision.media)
    else:
        response_payload = revision.media
    response = flask.make_response(response_payload)
    length = len(response_payload)
    response.headers['Content-Range'] = 'bytes 0-%d/%d' % (length - 1, length)
    response.headers['x-goog-hash'] = revision.x_goog_hash_header()
    return response
예제 #5
0
def xmlapi_get_object(bucket_name, object_name):
    """Implement the 'Objects: insert' API.  Insert a new GCS Object."""
    object_path, blob = testbench_utils.lookup_object(bucket_name, object_name)
    if flask.request.args.get('acl') is not None:
        raise error_response.ErrorResponse(
            'ACL query not supported in XML API', status_code=500)
    if flask.request.args.get('encryption') is not None:
        raise error_response.ErrorResponse(
            'Encryption query not supported in XML API', status_code=500)
    generation_match = flask.request.headers.get('if-generation-match')
    metageneration_match = flask.request.headers.get('if-metageneration-match')
    blob.check_preconditions_by_value(generation_match, None,
                                      metageneration_match, None)
    revision = blob.get_revision(flask.request)
    # Respect the Range: header, if present.
    range_header = flask.request.headers.get('range')
    response_payload = revision.media
    begin = 0
    end = len(response_payload)
    if range_header is not None:
        print("\n\n\nrange_header = %s\n\n" % range_header)
        m = re.match('bytes=([0-9]+)-([0-9]+)', range_header)
        if m:
            print("\n\n\nmatch = %s\n\n" % m)
            begin = int(m.group(1))
            end = int(m.group(2))
            response_payload = response_payload[begin:end + 1]
    # Process custome headers to test error conditions.
    instructions = flask.request.headers.get('x-goog-testbench-instructions')
    if instructions == 'return-corrupted-data':
        response_payload = testbench_utils.corrupt_media(response_payload)
    response = flask.make_response(response_payload)
    length = len(response_payload)
    content_range = 'bytes %d-%d/%d' % (begin, end - 1, length)
    response.headers['Content-Range'] = content_range
    response.headers['x-goog-hash'] = revision.x_goog_hash_header()
    return response
예제 #6
0
def xmlapi_get_object(bucket_name, object_name):
    """Implement the 'Objects: insert' API.  Insert a new GCS Object."""
    object_path, blob = testbench_utils.lookup_object(bucket_name, object_name)
    if flask.request.args.get('acl') is not None:
        raise error_response.ErrorResponse(
            'ACL query not supported in XML API', status_code=500)
    if flask.request.args.get('encryption') is not None:
        raise error_response.ErrorResponse(
            'Encryption query not supported in XML API', status_code=500)
    generation_match = flask.request.headers.get('if-generation-match')
    metageneration_match = flask.request.headers.get('if-metageneration-match')
    blob.check_preconditions_by_value(generation_match, None,
                                      metageneration_match, None)
    revision = blob.get_revision(flask.request)
    instructions = flask.request.headers.get('x-goog-testbench-instructions')
    if instructions == 'return-corrupted-data':
        response_payload = testbench_utils.corrupt_media(revision.media)
    else:
        response_payload = revision.media
    response = flask.make_response(response_payload)
    length = len(response_payload)
    response.headers['Content-Range'] = 'bytes 0-%d/%d' % (length - 1, length)
    response.headers['x-goog-hash'] = revision.x_goog_hash_header()
    return response
예제 #7
0
    def receive_upload_chunk(self, gcs_url, request):
        """Receive a new upload chunk.

        :param gcs_url: str the base URL for the service.
        :param request: flask.Request the original http request.
        :return: the HTTP response.
        """
        upload_id = request.args.get('upload_id')
        if upload_id is None:
            raise error_response.ErrorResponse(
                'Missing upload_id in resumable_upload_chunk', status_code=400)
        upload = self.resumable_uploads.get(upload_id)
        if upload is None:
            raise error_response.ErrorResponse(
                'Cannot find resumable upload %s' % upload_id, status_code=404)
        # Be gracious in what you accept, if the Content-Range header is not
        # set we assume it is a good header and it is the end of the file.
        next_byte = upload['next_byte']
        begin = next_byte
        end = next_byte + len(request.data)
        total = end
        final_chunk = False
        content_range = request.headers.get('content-range')
        if content_range is not None:
            if content_range.startswith('bytes */*'):
                # This is just a query to resume an upload, if it is done, return
                # an empty
                response = flask.make_response('')
                if next_byte > 1:
                    response.headers['Range'] = 'bytes=0-%d' % (next_byte - 1)
                response.status_code = 308
                return response
            match = re.match('bytes \*/(\\*|[0-9]+)', content_range)
            if match:
                if match.group(1) == '*':
                    total = 0
                else:
                    total = int(match.group(1))
                    final_chunk = True
            else:
                match = re.match('bytes ([0-9]+)-([0-9]+)/(\\*|[0-9]+)', content_range)
                if not match:
                    raise error_response.ErrorResponse(
                        'Invalid Content-Range in upload %s' % content_range,
                        status_code=400)
                begin = int(match.group(1))
                end = int(match.group(2))
                if match.group(3) == '*':
                    total = 0
                else:
                    total = int(match.group(3))
                    final_chunk = True

                if begin != next_byte:
                    raise error_response.ErrorResponse(
                        'Mismatched data range, expected data at %d, got %d' % (
                            next_byte, begin), status_code=400)
                if len(request.data) != end - begin + 1:
                    raise error_response.ErrorResponse(
                        'Mismatched data range (%d) vs. content-length (%d)' % (
                            end - begin + 1, len(request.data)), status_code=400)

        upload['media'] = upload.get('media', '') + request.data
        next_byte = len(upload.get('media', ''))
        upload['next_byte'] = next_byte
        response_payload = ''
        if final_chunk and next_byte >= total:
            upload['done'] = True
            object_name = upload.get('object_name')
            object_path, blob = testbench_utils.get_object(
                self.name, object_name,
                gcs_object.GcsObject(self.name, object_name))
            # Release a few resources to control memory usage.
            original_metadata = upload.pop('metadata', None)
            media = upload.pop('media', None)
            blob.check_preconditions_by_value(
                upload.get('ifGenerationMatch'),
                upload.get('ifGenerationNotMatch'),
                upload.get('ifMetagenerationMatch'),
                upload.get('ifMetagenerationNotMatch')
            )
            if upload.pop('instructions', None) == 'inject-upload-data-error':
                media = testbench_utils.corrupt_media(media)
            revision = blob.insert_resumable(
                gcs_url, request, media,
                original_metadata)
            response_payload = testbench_utils.filter_fields_from_response(
                upload.get('fields'), revision.metadata)
            testbench_utils.insert_object(object_path, blob)

        response = flask.make_response(response_payload)
        if next_byte == 0:
            response.headers['Range'] = 'bytes=0-0'
        else:
            response.headers['Range'] = 'bytes=0-%d' % (next_byte - 1)
        if upload.get('done', False):
            response.status_code = 200
        else:
            response.status_code = 308
        return response
예제 #8
0
    def __init__(self, gcs_url, bucket_name, name, generation, request, media):
        """Initialize a new object revision.

        :param gcs_url:str the base URL for the GCS service.
        :param bucket_name:str the name of the bucket that contains the object.
        :param name:str the name of the object.
        :param generation:int the generation number for this object.
        :param request:flask.Request the contents of the HTTP request.
        :param media:str the contents of the object.
        """
        self.gcs_url = gcs_url
        self.bucket_name = bucket_name
        self.name = name
        self.generation = generation
        self.object_id = bucket_name + '/o/' + name + '/' + str(generation)
        now = time.gmtime(time.time())
        timestamp = time.strftime('%Y-%m-%dT%H:%M:%SZ', now)
        self.media = media
        instructions = request.headers.get('x-goog-testbench-instructions')
        if instructions == 'inject-upload-data-error':
            self.media = testbench_utils.corrupt_media(media)

        self.metadata = {
            'timeCreated': timestamp,
            'updated': timestamp,
            'metageneration': 0,
            'generation': generation,
            'location': 'US',
            'storageClass': 'STANDARD',
            'size': len(self.media),
            'etag': 'XYZ=',
            'owner': {
                'entity': 'project-owners-123456789',
                'entityId': '',
            },
            'md5Hash': base64.b64encode(hashlib.md5(self.media).digest()),
            'crc32c': base64.b64encode(struct.pack('>I', crc32c.crc32(self.media)))
        }
        if request.headers.get('content-type') is not None:
            self.metadata['contentType'] = request.headers.get('content-type')
        # Update the derived metadata attributes (e.g.: id, kind, selfLink)
        self.update_from_metadata({})
        # Capture any encryption key headers.
        self._capture_customer_encryption(request)
        self._update_predefined_acl(request.args.get('predefinedAcl'))
        acl2json_mapping = {
            'authenticated-read': 'authenticatedRead',
            'bucket-owner-full-control': 'bucketOwnerFullControl',
            'bucket-owner-read': 'bucketOwnerRead',
            'private': 'private',
            'project-private': 'projectPrivate',
            'public-read': 'publicRead',
        }
        if request.headers.get('x-goog-acl') is not None:
            acl = request.headers.get('x-goog-acl')
            predefined = acl2json_mapping.get(acl)
            if predefined is not None:
                self._update_predefined_acl(predefined)
            else:
                raise error_response.ErrorResponse(
                    'Invalid predefinedAcl value %s' % acl, status_code=400)
예제 #9
0
    def __init__(self, gcs_url, bucket_name, name, generation, request, media):
        """Initialize a new object revision.

        :param gcs_url:str the base URL for the GCS service.
        :param bucket_name:str the name of the bucket that contains the object.
        :param name:str the name of the object.
        :param generation:int the generation number for this object.
        :param request:flask.Request the contents of the HTTP request.
        :param media:str the contents of the object.
        """
        self.gcs_url = gcs_url
        self.bucket_name = bucket_name
        self.name = name
        self.generation = str(generation)
        self.object_id = bucket_name + "/o/" + name + "/" + str(generation)
        now = time.gmtime(time.time())
        timestamp = time.strftime("%Y-%m-%dT%H:%M:%SZ", now)
        self.media = media
        instructions = request.headers.get("x-goog-testbench-instructions")
        if instructions == "inject-upload-data-error":
            self.media = testbench_utils.corrupt_media(media)

        self.metadata = {
            "timeCreated": timestamp,
            "updated": timestamp,
            "metageneration": "0",
            "generation": str(generation),
            "location": "US",
            "storageClass": "STANDARD",
            "size": str(len(self.media)),
            "etag": "XYZ=",
            "owner": {"entity": "project-owners-123456789", "entityId": ""},
            "md5Hash": base64.b64encode(hashlib.md5(self.media).digest()).decode(
                "utf-8"
            ),
            "crc32c": base64.b64encode(
                struct.pack(">I", crc32c.crc32(self.media))
            ).decode("utf-8"),
        }
        if request.headers.get("content-type") is not None:
            self.metadata["contentType"] = request.headers.get("content-type")
        # Update the derived metadata attributes (e.g.: id, kind, selfLink)
        self.update_from_metadata({})
        # Capture any encryption key headers.
        self._capture_customer_encryption(request)
        self._update_predefined_acl(request.args.get("predefinedAcl"))
        acl2json_mapping = {
            "authenticated-read": "authenticatedRead",
            "bucket-owner-full-control": "bucketOwnerFullControl",
            "bucket-owner-read": "bucketOwnerRead",
            "private": "private",
            "project-private": "projectPrivate",
            "public-read": "publicRead",
        }
        if request.headers.get("x-goog-acl") is not None:
            acl = request.headers.get("x-goog-acl")
            predefined = acl2json_mapping.get(acl)
            if predefined is not None:
                self._update_predefined_acl(predefined)
            else:
                raise error_response.ErrorResponse(
                    "Invalid predefinedAcl value %s" % acl, status_code=400
                )
예제 #10
0
def objects_get_common(bucket_name, object_name, revision):
    # Respect the Range: header, if present.
    range_header = flask.request.headers.get('range')
    response_payload = revision.media
    begin = 0
    end = len(response_payload)
    if range_header is not None:
        m = re.match('bytes=([0-9]+)-([0-9]+)', range_header)
        if m:
            begin = int(m.group(1))
            end = int(m.group(2))
            response_payload = response_payload[begin:end + 1]
        m = re.match('bytes=([0-9]+)-$', range_header)
        if m:
            begin = int(m.group(1))
            response_payload = response_payload[begin:]
        m = re.match('bytes=-([0-9]+)$', range_header)
        if m:
            last = int(m.group(1))
            response_payload = response_payload[-last:]
    # Process custom headers to test error conditions.
    instructions = flask.request.headers.get('x-goog-testbench-instructions')
    if instructions == 'return-broken-stream':

        def streamer():
            chunk_size = 64 * 1024
            for r in range(0, len(response_payload) / 2, chunk_size):
                if r > 1024 * 1024:
                    print("\n\n###### EXIT to simulate crash\n")
                    sys.exit(1)
                time.sleep(0.1)
                chunk_end = min(r + chunk_size, len(response_payload))
                yield response_payload[r:chunk_end]

        length = len(response_payload)
        content_range = 'bytes %d-%d/%d' % (begin, end - 1, length)
        headers = {
            'Content-Range': content_range,
            'x-goog-hash': revision.x_goog_hash_header(),
            'x-goog-generation': revision.generation
        }
        return flask.Response(streamer(), status=200, headers=headers)

    if instructions == 'return-corrupted-data':
        response_payload = testbench_utils.corrupt_media(response_payload)

    if instructions is not None and instructions.startswith(u'stall-always'):
        length = len(response_payload)
        content_range = 'bytes %d-%d/%d' % (begin, end - 1, length)

        def streamer():
            chunk_size = 16 * 1024
            for r in range(begin, end, chunk_size):
                chunk_end = min(r + chunk_size, end)
                if r == begin:
                    time.sleep(10)
                yield response_payload[r:chunk_end]

        headers = {
            'Content-Range': content_range,
            'x-goog-hash': revision.x_goog_hash_header(),
            'x-goog-generation': revision.generation
        }
        return flask.Response(streamer(), status=200, headers=headers)

    if instructions == 'stall-at-256KiB' and begin == 0:
        length = len(response_payload)
        content_range = 'bytes %d-%d/%d' % (begin, end - 1, length)

        def streamer():
            chunk_size = 16 * 1024
            for r in range(begin, end, chunk_size):
                chunk_end = min(r + chunk_size, end)
                if r == 256 * 1024:
                    time.sleep(10)
                yield response_payload[r:chunk_end]

        headers = {
            'Content-Range': content_range,
            'x-goog-hash': revision.x_goog_hash_header(),
            'x-goog-generation': revision.generation
        }
        return flask.Response(streamer(), status=200, headers=headers)

    if instructions is not None and instructions.startswith(
            u'return-503-after-256K'):
        length = len(response_payload)
        headers = {
            'Content-Range': 'bytes %d-%d/%d' % (begin, end - 1, length),
            'x-goog-hash': revision.x_goog_hash_header(),
            'x-goog-generation': revision.generation
        }
        if begin == 0:

            def streamer():
                chunk_size = 4 * 1024
                for r in range(0, len(response_payload), chunk_size):
                    if r >= 256 * 1024:
                        print("\n\n###### EXIT to simulate crash\n")
                        sys.exit(1)
                    time.sleep(0.01)
                    chunk_end = min(r + chunk_size, len(response_payload))
                    yield response_payload[r:chunk_end]

            return flask.Response(streamer(), status=200, headers=headers)
        if instructions.endswith(u'/retry-1'):
            print("## Return error for retry 1")
            return flask.Response('Service Unavailable', status=503)
        if instructions.endswith(u'/retry-2'):
            print("## Return error for retry 2")
            return flask.Response('Service Unavailable', status=503)
        print("## Return success for %s" % instructions)
        return flask.Response(response_payload, status=200, headers=headers)

    response = flask.make_response(response_payload)
    length = len(response_payload)
    content_range = 'bytes %d-%d/%d' % (begin, end - 1, length)
    response.headers['Content-Range'] = content_range
    response.headers['x-goog-hash'] = revision.x_goog_hash_header()
    response.headers['x-goog-generation'] = revision.generation
    return response
예제 #11
0
    def receive_upload_chunk(self, gcs_url, request):
        """Receive a new upload chunk.

        :param gcs_url: str the base URL for the service.
        :param request: flask.Request the original http request.
        :return: the HTTP response.
        """
        upload_id = request.args.get("upload_id")
        if upload_id is None:
            raise error_response.ErrorResponse(
                "Missing upload_id in resumable_upload_chunk", status_code=400)
        upload = self.resumable_uploads.get(upload_id)
        if upload is None:
            raise error_response.ErrorResponse(
                "Cannot find resumable upload %s" % upload_id, status_code=404)
        # Be gracious in what you accept, if the Content-Range header is not
        # set we assume it is a good header and it is the end of the file.
        next_byte = upload["next_byte"]
        begin = next_byte
        end = next_byte + len(request.data)
        total = end
        final_chunk = False
        content_range = request.headers.get("content-range")
        if content_range is not None:
            if content_range.startswith("bytes */*"):
                # This is just a query to resume an upload, if it is done, return
                # the completed upload payload and an empty range header.
                response = flask.make_response(upload.get("payload", ""))
                if next_byte > 1 and not upload["done"]:
                    response.headers["Range"] = "bytes=0-%d" % (next_byte - 1)
                response.status_code = 200 if upload["done"] else 308
                return response
            match = re.match("bytes \*/(\\*|[0-9]+)", content_range)
            if match:
                if match.group(1) == "*":
                    total = 0
                else:
                    total = int(match.group(1))
                    final_chunk = True
            else:
                match = re.match("bytes ([0-9]+)-([0-9]+)/(\\*|[0-9]+)",
                                 content_range)
                if not match:
                    raise error_response.ErrorResponse(
                        "Invalid Content-Range in upload %s" % content_range,
                        status_code=400,
                    )
                begin = int(match.group(1))
                end = int(match.group(2))
                if match.group(3) == "*":
                    total = 0
                else:
                    total = int(match.group(3))
                    final_chunk = True

                if begin != next_byte:
                    raise error_response.ErrorResponse(
                        "Mismatched data range, expected data at %d, got %d" %
                        (next_byte, begin),
                        status_code=400,
                    )
                if len(request.data) != end - begin + 1:
                    raise error_response.ErrorResponse(
                        "Mismatched data range (%d) vs. content-length (%d)" %
                        (end - begin + 1, len(request.data)),
                        status_code=400,
                    )

        upload["media"] = upload.get("media", b"") + request.data
        next_byte = len(upload.get("media", ""))
        upload["next_byte"] = next_byte
        response_payload = ""
        if final_chunk and next_byte >= total:
            upload["done"] = True
            object_name = upload.get("object_name")
            object_path, blob = testbench_utils.get_object(
                self.name, object_name,
                gcs_object.GcsObject(self.name, object_name))
            # Release a few resources to control memory usage.
            original_metadata = upload.pop("metadata", None)
            media = upload.pop("media", None)
            blob.check_preconditions_by_value(
                upload.get("ifGenerationMatch"),
                upload.get("ifGenerationNotMatch"),
                upload.get("ifMetagenerationMatch"),
                upload.get("ifMetagenerationNotMatch"),
            )
            if upload.pop("instructions", None) == "inject-upload-data-error":
                media = testbench_utils.corrupt_media(media)
            revision = blob.insert_resumable(gcs_url, request, media,
                                             original_metadata)
            response_payload = testbench_utils.filter_fields_from_response(
                upload.get("fields"), revision.metadata)
            upload["payload"] = response_payload
            testbench_utils.insert_object(object_path, blob)

        response = flask.make_response(response_payload)
        if next_byte == 0:
            response.headers["Range"] = "bytes=0-0"
        else:
            response.headers["Range"] = "bytes=0-%d" % (next_byte - 1)
        if upload.get("done", False):
            response.status_code = 200
        else:
            response.status_code = 308
        return response
예제 #12
0
def objects_get_common(bucket_name, object_name, revision):
    # Respect the Range: header, if present.
    range_header = flask.request.headers.get("range")
    response_payload = revision.media
    begin = 0
    end = len(response_payload)
    if range_header is not None:
        m = re.match("bytes=([0-9]+)-([0-9]+)", range_header)
        if m:
            begin = int(m.group(1))
            end = int(m.group(2))
            response_payload = response_payload[begin : end + 1]
        m = re.match("bytes=([0-9]+)-$", range_header)
        if m:
            begin = int(m.group(1))
            response_payload = response_payload[begin:]
        m = re.match("bytes=-([0-9]+)$", range_header)
        if m:
            last = int(m.group(1))
            response_payload = response_payload[-last:]
    # Process custom headers to test error conditions.
    instructions = flask.request.headers.get("x-goog-testbench-instructions")
    if instructions == "return-broken-stream":

        def streamer():
            chunk_size = 64 * 1024
            for r in range(0, len(response_payload), chunk_size):
                if r > 1024 * 1024:
                    print("\n\n###### EXIT to simulate crash\n")
                    sys.exit(1)
                time.sleep(0.1)
                chunk_end = min(r + chunk_size, len(response_payload))
                yield response_payload[r:chunk_end]

        length = len(response_payload)
        content_range = "bytes %d-%d/%d" % (begin, end - 1, length)
        headers = {
            "Content-Range": content_range,
            "Content-Length": length,
            "x-goog-hash": revision.x_goog_hash_header(),
            "x-goog-generation": revision.generation,
        }
        return flask.Response(streamer(), status=200, headers=headers)

    if instructions == "return-corrupted-data":
        response_payload = testbench_utils.corrupt_media(response_payload)

    if instructions is not None and instructions.startswith(u"stall-always"):
        length = len(response_payload)
        content_range = "bytes %d-%d/%d" % (begin, end - 1, length)

        def streamer():
            chunk_size = 16 * 1024
            for r in range(begin, end, chunk_size):
                chunk_end = min(r + chunk_size, end)
                if r == begin:
                    time.sleep(10)
                yield response_payload[r:chunk_end]

        headers = {
            "Content-Range": content_range,
            "x-goog-hash": revision.x_goog_hash_header(),
            "x-goog-generation": revision.generation,
        }
        return flask.Response(streamer(), status=200, headers=headers)

    if instructions == "stall-at-256KiB" and begin == 0:
        length = len(response_payload)
        content_range = "bytes %d-%d/%d" % (begin, end - 1, length)

        def streamer():
            chunk_size = 16 * 1024
            for r in range(begin, end, chunk_size):
                chunk_end = min(r + chunk_size, end)
                if r == 256 * 1024:
                    time.sleep(10)
                yield response_payload[r:chunk_end]

        headers = {
            "Content-Range": content_range,
            "x-goog-hash": revision.x_goog_hash_header(),
            "x-goog-generation": revision.generation,
        }
        return flask.Response(streamer(), status=200, headers=headers)

    if instructions is not None and instructions.startswith(u"return-503-after-256K"):
        length = len(response_payload)
        headers = {
            "Content-Range": "bytes %d-%d/%d" % (begin, end - 1, length),
            "x-goog-hash": revision.x_goog_hash_header(),
            "x-goog-generation": revision.generation,
        }
        if begin == 0:

            def streamer():
                chunk_size = 4 * 1024
                for r in range(0, len(response_payload), chunk_size):
                    if r >= 256 * 1024:
                        print("\n\n###### EXIT to simulate crash\n")
                        sys.exit(1)
                    time.sleep(0.01)
                    chunk_end = min(r + chunk_size, len(response_payload))
                    yield response_payload[r:chunk_end]

            return flask.Response(streamer(), status=200, headers=headers)
        if instructions.endswith(u"/retry-1"):
            print("## Return error for retry 1")
            return flask.Response("Service Unavailable", status=503)
        if instructions.endswith(u"/retry-2"):
            print("## Return error for retry 2")
            return flask.Response("Service Unavailable", status=503)
        print("## Return success for %s" % instructions)
        return flask.Response(response_payload, status=200, headers=headers)
    if instructions == "connection-reset":
      testbench_utils.reset_connection(flask.request)

    response = flask.make_response(response_payload)
    length = len(response_payload)
    content_range = "bytes %d-%d/%d" % (begin, end - 1, length)
    response.headers["Content-Range"] = content_range
    response.headers["x-goog-hash"] = revision.x_goog_hash_header()
    response.headers["x-goog-generation"] = revision.generation
    return response