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
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")
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
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
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
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
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
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)
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 )
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
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
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