Repository: getmoto/moto

Issue:
Error when listing objects with page size 0
Error when running this command with Localstack. Empty bucket works fine.
`aws s3api list-objects --bucket=mybucket --endpoint-url http://localhost:4572 --page-size 0`

Ref:
https://github.com/spulec/moto/blob/850496f29a66b109b87f9d26d4c5b8456b3b195f/moto/s3/responses.py#L382

Error logs:
```
localstack                       | 127.0.0.1 - - [16/Jan/2019 00:50:16] "GET /mybucket?max-keys=0&encoding-type=url HTTP/1.1" 500 -
localstack                       | Error on request:
localstack                       | Traceback (most recent call last):
localstack                       | File "/opt/code/localstack/.venv/lib/python2.7/site-packages/werkzeug/serving.py", line 270, in run_wsgi
localstack                       | execute(self.server.app)
localstack                       | File "/opt/code/localstack/.venv/lib/python2.7/site-packages/werkzeug/serving.py", line 258, in execute
localstack                       | application_iter = app(environ, start_response)
localstack                       | File "/opt/code/localstack/.venv/lib/python2.7/site-packages/moto/server.py", line 101, in __call__
localstack                       | return backend_app(environ, start_response)
localstack                       | File "/opt/code/localstack/.venv/lib/python2.7/site-packages/flask/app.py", line 1997, in __call__
localstack                       | return self.wsgi_app(environ, start_response)
localstack                       | File "/opt/code/localstack/.venv/lib/python2.7/site-packages/flask/app.py", line 1985, in wsgi_app
localstack                       | response = self.handle_exception(e)
localstack                       | File "/opt/code/localstack/.venv/lib/python2.7/site-packages/flask/app.py", line 1540, in handle_exception
localstack                       | reraise(exc_type, exc_value, tb)
localstack                       | File "/opt/code/localstack/.venv/lib/python2.7/site-packages/flask/app.py", line 1982, in wsgi_app
localstack                       | response = self.full_dispatch_request()
localstack                       | File "/opt/code/localstack/.venv/lib/python2.7/site-packages/flask/app.py", line 1614, in full_dispatch_request
localstack                       | rv = self.handle_user_exception(e)
localstack                       | File "/opt/code/localstack/.venv/lib/python2.7/site-packages/flask/app.py", line 1517, in handle_user_exception
localstack                       | reraise(exc_type, exc_value, tb)
localstack                       | File "/opt/code/localstack/.venv/lib/python2.7/site-packages/flask/app.py", line 1612, in full_dispatch_request
localstack                       | rv = self.dispatch_request()
localstack                       | File "/opt/code/localstack/.venv/lib/python2.7/site-packages/flask/app.py", line 1598, in dispatch_request
localstack                       | return self.view_functions[rule.endpoint](**req.view_args)
localstack                       | File "/opt/code/localstack/.venv/lib/python2.7/site-packages/moto/core/utils.py", line 140, in __call__
localstack                       | result = self.callback(request, request.url, {})
localstack                       | File "/opt/code/localstack/.venv/lib/python2.7/site-packages/moto/s3/responses.py", line 112, in ambiguous_response
localstack                       | return self.bucket_response(request, full_url, headers)
localstack                       | File "/opt/code/localstack/.venv/lib/python2.7/site-packages/moto/s3/responses.py", line 116, in bucket_response
localstack                       | response = self._bucket_response(request, full_url, headers)
localstack                       | File "/opt/code/localstack/.venv/lib/python2.7/site-packages/moto/s3/responses.py", line 155, in _bucket_response
localstack                       | return self._bucket_response_get(bucket_name, querystring, headers)
localstack                       | File "/opt/code/localstack/.venv/lib/python2.7/site-packages/moto/s3/responses.py", line 310, in _bucket_response_get
localstack                       | result_keys, is_truncated, _ = self._truncate_result(result_keys, max_keys)
localstack                       | File "/opt/code/localstack/.venv/lib/python2.7/site-packages/moto/s3/responses.py", line 370, in _truncate_result
localstack                       | next_continuation_token = result_keys[-1].name
localstack                       | IndexError: list index out of range
```


Discussion:
Any update?
Thanks for opening. I'm setting this as a feature request for us to implement the logic to handle this.

Resolving patch:
diff --git a/moto/s3/responses.py b/moto/s3/responses.py
--- a/moto/s3/responses.py
+++ b/moto/s3/responses.py
@@ -628,7 +628,11 @@ def _get_results_from_token(self, result_keys, token):
         return result_keys[continuation_index:]
 
     def _truncate_result(self, result_keys, max_keys):
-        if len(result_keys) > max_keys:
+        if max_keys == 0:
+            result_keys = []
+            is_truncated = True
+            next_continuation_token = None
+        elif len(result_keys) > max_keys:
             is_truncated = "true"
             result_keys = result_keys[:max_keys]
             item = result_keys[-1]


Test patch:
diff --git a/tests/test_s3/test_s3.py b/tests/test_s3/test_s3.py
--- a/tests/test_s3/test_s3.py
+++ b/tests/test_s3/test_s3.py
@@ -2851,6 +2851,17 @@ def test_delimiter_optional_in_response():
     assert resp.get("Delimiter") == "/"
 
 
+@mock_s3
+def test_list_objects_with_pagesize_0():
+    s3 = boto3.client("s3", region_name=DEFAULT_REGION_NAME)
+    s3.create_bucket(Bucket="mybucket")
+    resp = s3.list_objects(Bucket="mybucket", MaxKeys=0)
+    resp["Name"].should.equal("mybucket")
+    resp["MaxKeys"].should.equal(0)
+    resp["IsTruncated"].should.equal(False)
+    resp.shouldnt.have.key("Contents")
+
+
 @mock_s3
 def test_boto3_list_objects_truncated_response():
     s3 = boto3.client("s3", region_name=DEFAULT_REGION_NAME)