Repository: localstack/localstack

Issue:
Regression: listObjects does not work with phpsdk
# Type of request: This is a ...

[x] bug report
[ ] feature request

# Detailed description

If I upload objects to my bucket I don't get a list of my objects back, using the listObjects method of the phpsdk.
This was fixed previously in 0.11 but it's broken again in 0.12.x [#3021](https://github.com/localstack/localstack/issues/3021)

## Expected behavior

listObjects should return the objects in my bucket/folder.

## Actual behavior

Only metadata is returned.

# Steps to reproduce

- docker-compose.yml

```
version: '3.7'

services:
  localstack:
    #image: localstack/localstack:0.11.0
    image: localstack/localstack:latest
    ports:
      - "4566:4566"
      - "8080:8080"
    environment:
      - SERVICES=s3
      - DATA_DIR=/tmp/localstack-test/data
      - DEBUG=1
      - USE_LIGHT_IMAGE=1
      - DOCKER_HOST=unix:///var/run/docker.sock
      - DEFAULT_REGION=eu-central-1
    volumes:
      - "${TMPDIR:-/tmp/localstack-test}:/tmp/localstack-test"
```

- test.php

```
<?php
require 'vendor/aws-autoload.php';

use Aws\S3\S3Client;

$config = [
	'credentials' => [
		'key'    => 'test-id',
		'secret' => 'test-secret',
	],
	'region' => 'eu-central-1',
	'version' => 'latest',
	'endpoint' => 'http://localhost:4566',
	'use_path_style_endpoint' => true,
];
$s3client = new S3Client($config);
$buckets = $s3client->listBuckets();
if (empty($buckets['Buckets'])) {
	$s3client->createBucket([
		'Bucket' => 'test',
	]);
	echo 'Bucket test created' . PHP_EOL;
} else {
	echo 'Bucket test exists' . PHP_EOL;
}

$s3client->upload('test', 'test.txt', 'test');
var_dump($s3client->listObjectsV2(['Bucket' => 'test']));
```
## Expected example

```
> php test.php
Bucket test exists

class Aws\Result#245 (2) {
  private $data =>
  array(7) {
    'IsTruncated' =>
    bool(false)
    'Contents' =>
    array(1) {
      [0] =>
      array(5) {
        ...
      }
    }
    'Name' =>
    string(4) "test"
    'Prefix' =>
    string(0) ""
    'MaxKeys' =>
    int(1000)
    'KeyCount' =>
    int(1)
    '@metadata' =>
    array(4) {
      'statusCode' =>
      int(200)
      'effectiveUri' =>
      string(38) "http://localhost:4566/test?list-type=2"
      'headers' =>
      array(9) {
        ...
      }
      'transferStats' =>
      array(1) {
        ...
      }
    }
  }
  private $monitoringEvents =>
  array(0) {
  }
}
```

## Actual

```
> php test.php
Bucket test exists

class Aws\Result#245 (2) {
  private $data =>
  array(1) {
    '@metadata' =>
    array(4) {
      'statusCode' =>
      int(200)
      'effectiveUri' =>
      string(38) "http://localhost:4566/test?list-type=2"
      'headers' =>
      array(16) {
        ...
      }
      'transferStats' =>
      array(1) {
        ...
      }
    }
  }
  private $monitoringEvents =>
  array(0) {
  }
}
```



Discussion:
OK I have traced the issue down to `guzzlehttp/guzzle` - it works with v7 (the latest), but does not work with v6.

`aws/aws-sdk-php` lists the dependency as `"guzzlehttp/guzzle": "^5.3.3|^6.2.1|^7.0"`, which is incorrect when used with localstack ^0.12 as only ^7.0 works.

To re-iterate, the following combinations DO work:

- localstack 0.12.6
- aws/aws-sdk-php ^3
- guzzlehttp/guzzle ^7

and

- localstack 0.11.0
- aws/aws-sdk-php ^3
- guzzlehttp/guzzle ^6

However, this one DOES NOT work:

- localstack 0.12.6
- aws/aws-sdk-php ^3
- guzzlehttp/guzzle ^6

I guess the issue needs fixing in localstack, or aws-sdk-php should drop guzzle 6 from its dependencies.
Hi @devkokov I tested your issue against the lates version of LocalStack, and the `ListObjectsV2(['Bucket' => 'test']` function does return the list of objects inside the bucket. 

Please, try again and let me know if you still have issues.
[reproducer.zip](https://github.com/localstack/localstack/files/6230349/reproducer.zip)

@pinzon Please reopen this as I am able to duplciate.

I've tested against the latest localstack docker image.

As noted when the AWS client uses Guzzle 6 the client does not return a proper result set, however when the AWS Client connects to an official AWS S3 bucket using Guzzle 6 the aws client returns successfully.

To reiterate were talking about the guzzle version on the client side which is not in any way linked to the guzzle version running on the localstack server, this appears to indicate there is an issue inside of LocalStack parsing REMOTE CLIENT connections.  (LocalStack should not be in any way dependent upon what guzzle version a client is using as it is essentially a web server that needs to serve all clients)

Seeing as AWS supports their official AWS SDK PHP using guzzle 6 that means localstack should be able to process the requests.

To reproduce:
composer.json
```
{
    "require": {
        "aws/aws-sdk-php": "^3.174",
        "guzzlehttp/guzzle": "6.5.5"
    }
}
```

test.php
```
<?php
ini_set('display_errors', 1);
error_reporting(E_ALL);

use Aws\S3\S3Client;

require './vendor/autoload.php';
$credentials = new Aws\Credentials\Credentials('Access_Key', 'Access_Secret');
$s3 = new S3Client([
    'version'     => '2006-03-01',
    'region'      => 'us-east-1',
    'credentials' => $credentials,
    'endpoint' => 'http://localstack:4566',
]);
$result = $s3->listObjectsV2(['Bucket' => 'cmlaratest-us-east-2', 'MaxKeys' => 1000]);
var_dump($result);
```

I'll add on a note, I do appear to see LocalStack trying to provide the data in a TCP dump, but it doesn't make it all the way to the S3Client.  I suspect this means its going to be some sort of missing (or extra) response header from localstack, or maybe some sort of encoding problem, but I need to do more diagnostics into that still.
On further testing this appears to be caused by 0.11.1 (and beyond) adding the `content-encoding: identity` response header.

In Guzzle 6 this appears to cause an issue where Guzzle is unable to return the response stream which means the XML is never parsed.

AWS  Buckets do NOT return this response header which is why Guzzle 6 clients work against AWS but not localstack.

While on initial glance this does appear to be a valid and mandatory content-encoding (meaning no encoding)  and its presence breaking Guzzle 6 could indicate a Guzzle 6 bug it is still a disconnect between the official AWS servers and breaks known clients that would work against AWS. I would therefor contend it would be appropriate to remove this header.
Hi @cmlara of course. I'll take a look again with you feedback.
Thank you for your help.

Resolving patch:
diff --git a/localstack/services/s3/s3_listener.py b/localstack/services/s3/s3_listener.py
--- a/localstack/services/s3/s3_listener.py
+++ b/localstack/services/s3/s3_listener.py
@@ -29,7 +29,6 @@
     short_uid, timestamp_millis, to_str, to_bytes, clone, md5, get_service_protocol, now_utc, is_base64
 )
 from localstack.utils.analytics import event_publisher
-from localstack.utils.http_utils import uses_chunked_encoding
 from localstack.utils.persistence import PersistingProxyListener
 from localstack.utils.aws.aws_responses import requests_response
 from localstack.services.cloudformation.service_models import S3Bucket
@@ -424,9 +423,6 @@ def add_response_metadata_headers(response):
         response.headers['content-language'] = 'en-US'
     if response.headers.get('cache-control') is None:
         response.headers['cache-control'] = 'no-cache'
-    if response.headers.get('content-encoding') is None:
-        if not uses_chunked_encoding(response):
-            response.headers['content-encoding'] = 'identity'
 
 
 def append_last_modified_headers(response, content=None):


Test patch:
diff --git a/tests/integration/test_s3.py b/tests/integration/test_s3.py
--- a/tests/integration/test_s3.py
+++ b/tests/integration/test_s3.py
@@ -558,7 +558,8 @@ def test_s3_get_get_object_headers(self):
         self.assertIn('x-amz-id-2', resp['ResponseMetadata']['HTTPHeaders'])
         self.assertIn('content-language', resp['ResponseMetadata']['HTTPHeaders'])
         self.assertIn('cache-control', resp['ResponseMetadata']['HTTPHeaders'])
-        self.assertIn('content-encoding', resp['ResponseMetadata']['HTTPHeaders'])
+        # Do not send a content-encoding header as discussed in Issue #3608
+        self.assertNotIn('content-encoding', resp['ResponseMetadata']['HTTPHeaders'])
 
         # clean up
         self._delete_bucket(bucket_name, [object_key])