예제 #1
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
예제 #2
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