diff --git a/moto/s3/responses.py b/moto/s3/responses.py index 3fe391cd3..adaefc230 100644 --- a/moto/s3/responses.py +++ b/moto/s3/responses.py @@ -252,13 +252,13 @@ class ResponseObject(_TemplateEnvironmentMixin): toint = lambda i: int(i) if i else None begin, end = map(toint, rspec.split('-')) if begin is not None: # byte range - end = last if end is None else end + end = last if end is None else min(end, last) elif end is not None: # suffix byte range - begin = length - end + begin = length - min(end, length) end = last else: return 400, headers, "" - if begin < 0 or end > length or begin > min(end, last): + if begin < 0 or end > last or begin > min(end, last): return 416, headers, "" headers['content-range'] = "bytes {0}-{1}/{2}".format( begin, end, length) diff --git a/tests/test_s3/test_s3.py b/tests/test_s3/test_s3.py index 662b22cbb..43f25c748 100644 --- a/tests/test_s3/test_s3.py +++ b/tests/test_s3/test_s3.py @@ -791,11 +791,35 @@ def test_ranged_get(): key.key = 'bigkey' rep = b"0123456789" key.set_contents_from_string(rep * 10) + + # Implicitly bounded range requests. key.get_contents_as_string(headers={'Range': 'bytes=0-'}).should.equal(rep * 10) - key.get_contents_as_string(headers={'Range': 'bytes=0-99'}).should.equal(rep * 10) - key.get_contents_as_string(headers={'Range': 'bytes=0-0'}).should.equal(b'0') - key.get_contents_as_string(headers={'Range': 'bytes=99-99'}).should.equal(b'9') - key.get_contents_as_string(headers={'Range': 'bytes=50-54'}).should.equal(rep[:5]) key.get_contents_as_string(headers={'Range': 'bytes=50-'}).should.equal(rep * 5) + key.get_contents_as_string(headers={'Range': 'bytes=99-'}).should.equal(b'9') + + # Explicitly bounded range requests starting from the first byte. + key.get_contents_as_string(headers={'Range': 'bytes=0-0'}).should.equal(b'0') + key.get_contents_as_string(headers={'Range': 'bytes=0-49'}).should.equal(rep * 5) + key.get_contents_as_string(headers={'Range': 'bytes=0-99'}).should.equal(rep * 10) + key.get_contents_as_string(headers={'Range': 'bytes=0-100'}).should.equal(rep * 10) + key.get_contents_as_string(headers={'Range': 'bytes=0-700'}).should.equal(rep * 10) + + # Explicitly bounded range requests starting from the / a middle byte. + key.get_contents_as_string(headers={'Range': 'bytes=50-54'}).should.equal(rep[:5]) + key.get_contents_as_string(headers={'Range': 'bytes=50-99'}).should.equal(rep * 5) + key.get_contents_as_string(headers={'Range': 'bytes=50-100'}).should.equal(rep * 5) + key.get_contents_as_string(headers={'Range': 'bytes=50-700'}).should.equal(rep * 5) + + # Explicitly bounded range requests starting from the last byte. + key.get_contents_as_string(headers={'Range': 'bytes=99-99'}).should.equal(b'9') + key.get_contents_as_string(headers={'Range': 'bytes=99-100'}).should.equal(b'9') + key.get_contents_as_string(headers={'Range': 'bytes=99-700'}).should.equal(b'9') + + # Suffix range requests. + key.get_contents_as_string(headers={'Range': 'bytes=-1'}).should.equal(b'9') key.get_contents_as_string(headers={'Range': 'bytes=-60'}).should.equal(rep * 6) + key.get_contents_as_string(headers={'Range': 'bytes=-100'}).should.equal(rep * 10) + key.get_contents_as_string(headers={'Range': 'bytes=-101'}).should.equal(rep * 10) + key.get_contents_as_string(headers={'Range': 'bytes=-700'}).should.equal(rep * 10) + key.size.should.equal(100)