Skip to content

Mishandling Content-Encoding? #1062

@enuuros

Description

@enuuros

Describe the bug

If I manually compress a JSON file with brotli and upload it to RustFS bucket with "Content-Encoding: br", "Content-Type: application/json" the object no longer has Content-Encoding when I download it (GET) from the bucket. This behaviour is different from MinIO and AWS S3.

To Reproduce

Reproduction using docker compose:

services:
  rustfs:
    image: rustfs/rustfs:latest
    environment:
      RUSTFS_ACCESS_KEY: rustfs-user
      RUSTFS_SECRET_KEY: rustfs-password
    ports:
      - "9000:9000"
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:9000/health"]
      interval: 2s
      timeout: 2s
      retries: 10

  minio:
    image: minio/minio:latest
    environment:
      MINIO_ROOT_USER: rustfs-user
      MINIO_ROOT_PASSWORD: rustfs-password
    command: server /data
    ports:
      - "9000:9000"
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:9000/minio/health/live"]
      interval: 2s
      timeout: 2s
      retries: 10

  tester:
    image: alpine:latest
    environment:
      RUSTFS_ACCESS_KEY: rustfs-user
      RUSTFS_SECRET_KEY: rustfs-password
      ENDPOINT: http://rustfs:9000
    entrypoint: ["/bin/sh", "-c"]
    command:
      - |
        set -e
        apk add --no-cache curl brotli

        echo Ensure bucket exists...
        curl -sf --aws-sigv4 "aws:amz:us-east-1:s3" -u "$$RUSTFS_ACCESS_KEY:$$RUSTFS_SECRET_KEY" -X PUT "$$ENDPOINT/bucket" || true

        echo Applying public policy to bucket...
        curl -sf --aws-sigv4 "aws:amz:us-east-1:s3" -u "$$RUSTFS_ACCESS_KEY:$$RUSTFS_SECRET_KEY" -X PUT "$$ENDPOINT/bucket?policy" -H "Content-Type: application/json" -d '{"Version":"2012-10-17","Statement":[{"Effect":"Allow","Principal":{"AWS":["*"]},"Action":["s3:GetObject","s3:PutObject"],"Resource":["arn:aws:s3:::bucket/*"]}]}'

        echo Creating JSON file...
        printf '{"msg":"hello"}' > data.json

        echo Compressing with brotli...
        brotli -f data.json

        echo Uploading to pre-compressed file to bucket via PUT...
        curl -v -X PUT -H "content-encoding: br" -H "content-type: application/json" --data-binary @data.json.br "$$ENDPOINT/bucket/data.json"

        echo Downloading file back...
        curl -v -s --compressed -o downloaded.json "$$ENDPOINT/bucket/data.json"

        echo Show original file...
        cat data.json
        printf "\n"

        echo Show downloaded file...
        cat downloaded.json
        printf "\n"

        echo Comparing raw bytes...
        if cmp -s data.json downloaded.json; then
          echo 'SUCCESS: Files match';
          exit 0;
        else
          echo 'ERROR: Files differ (mishandling Content-Encoding?).';
          exit 1;
        fi
  1. docker compose up rustfs -d
  2. docker compose run -e ENDPOINT=http://rustfs:9000 tester

You can see that Content-Encoding was present when uploading the object, but it is missing when downloading the object (there is some user-defined meta data x-amz-meta-content-encoding: br instead):

> PUT /bucket/data.json HTTP/1.1
> Host: rustfs:9000
> User-Agent: curl/8.17.0
> Accept: */*
> content-encoding: br
> content-type: application/json
> Content-Length: 20
>
* upload completely sent off: 20 bytes
< HTTP/1.1 200 OK
< etag: "46d9a8f8877d8ad8ee4b8758b6e0719f"
< vary: accept-encoding
< vary: origin, access-control-request-method, access-control-request-headers
< access-control-allow-origin: *
< access-control-expose-headers: *
< x-request-id: 8c870519-9f85-4c3e-b54c-0d95921c75c5
< content-length: 0
< date: Mon, 08 Dec 2025 09:53:52 GMT
<
* Connection #0 to host rustfs:9000 left intact
Downloading file back...
* Host rustfs:9000 was resolved.
* IPv6: (none)
* IPv4: 172.18.0.2
*   Trying 172.18.0.2:9000...
* Established connection to rustfs (172.18.0.2 port 9000) from 172.18.0.3 port 34272
* using HTTP/1.x
> GET /bucket/data.json HTTP/1.1
> Host: rustfs:9000
> User-Agent: curl/8.17.0
> Accept: */*
> Accept-Encoding: deflate, gzip, br, zstd
>
* Request completely sent off
< HTTP/1.1 200 OK
< accept-ranges: bytes
< content-length: 20
< content-type: application/json
< etag: "46d9a8f8877d8ad8ee4b8758b6e0719f"
< last-modified: Mon, 08 Dec 2025 09:53:52 GMT
< x-amz-meta-content-type: application/json
< x-amz-meta-content-encoding: br
< vary: origin, access-control-request-method, access-control-request-headers
< access-control-allow-origin: *
< access-control-expose-headers: *
< x-request-id: 2cdb586f-c368-4614-b930-6ecf2ea6dc14
< date: Mon, 08 Dec 2025 09:53:52 GMT
<
{ [20 bytes data]
* Connection #0 to host rustfs:9000 left intact
Show original file...
{"msg":"hello"}
Show downloaded file...
!8{"msg":"hello"}
Comparing raw bytes...
ERROR: Files differ (mishandling Content-Encoding?).

Expected behavior

Downloading the object should have Content-Encoding: br.

This is how MinIO and AWS S3 work. This allows a web browser to download the display JSON file correctly if you just point the URL to the object and browser would automatically decompress it. With RustFS this doesn't work at the moment, browser will just display the uncompressed file.

You can compare behavior to MinIO by running:

docker compose down
docker-compose up minio -d
docker compose run -e ENDPOINT=http://minio:9000 tester
> PUT /bucket/data.json HTTP/1.1
> Host: minio:9000
> User-Agent: curl/8.17.0
> Accept: */*
> content-encoding: br
> content-type: application/json
> Content-Length: 20
>
* upload completely sent off: 20 bytes
< HTTP/1.1 200 OK
< Accept-Ranges: bytes
< Content-Length: 0
< ETag: "46d9a8f8877d8ad8ee4b8758b6e0719f"
< Server: MinIO
< Strict-Transport-Security: max-age=31536000; includeSubDomains
< Vary: Origin
< Vary: Accept-Encoding
< X-Amz-Id-2: dd9025bab4ad464b049177c95eb6ebf374d3b3fd1af9251148b658df7ac2e3e8
< X-Amz-Request-Id: 187F359E3401C054
< X-Content-Type-Options: nosniff
< X-Ratelimit-Limit: 4482
< X-Ratelimit-Remaining: 4482
< X-Xss-Protection: 1; mode=block
< Date: Mon, 08 Dec 2025 10:08:52 GMT
<
* Connection #0 to host minio:9000 left intact
Downloading file back...
* Host minio:9000 was resolved.
* IPv6: (none)
* IPv4: 172.18.0.2
*   Trying 172.18.0.2:9000...
* Established connection to minio (172.18.0.2 port 9000) from 172.18.0.3 port 38650
* using HTTP/1.x
> GET /bucket/data.json HTTP/1.1
> Host: minio:9000
> User-Agent: curl/8.17.0
> Accept: */*
> Accept-Encoding: deflate, gzip, br, zstd
>
* Request completely sent off
< HTTP/1.1 200 OK
< Accept-Ranges: bytes
< Content-Encoding: br
< Content-Length: 20
< Content-Type: application/json
< ETag: "46d9a8f8877d8ad8ee4b8758b6e0719f"
< Last-Modified: Mon, 08 Dec 2025 10:08:52 GMT
< Server: MinIO
< Strict-Transport-Security: max-age=31536000; includeSubDomains
< Vary: Origin
< Vary: Accept-Encoding
< X-Amz-Id-2: dd9025bab4ad464b049177c95eb6ebf374d3b3fd1af9251148b658df7ac2e3e8
< X-Amz-Request-Id: 187F359E3473EB85
< X-Content-Type-Options: nosniff
< X-Ratelimit-Limit: 4482
< X-Ratelimit-Remaining: 4482
< X-Xss-Protection: 1; mode=block
< Date: Mon, 08 Dec 2025 10:08:52 GMT
<
{ [20 bytes data]
* Connection #0 to host minio:9000 left intact
Show original file...
{"msg":"hello"}
Show downloaded file...
{"msg":"hello"}
Comparing raw bytes...
SUCCESS: Files match

Desktop (please complete the following information):

Rancher Desktop (on Windows host) running rustfs docker image:

rustfs 1.0.0-alpha.72
build time   : 2025-12-05 05:18:43 +00:00
build profile: release
build os     : linux-x86_64
rust version : rustc 1.91.1 (ed61e7d7e 2025-11-07)
rust channel : stable-x86_64-unknown-linux-gnu
git branch   :
git commit   : 63d846ed145cbab606437ac6f6d5f38ceae24bcb
git tag      : 1.0.0-alpha.72
git status   :

Additional context

Is there some setting we need to adjust in RustFS to make it behave the same way as MinIO and AWS S3 when using Content-Encoding for uploaded objects?

Metadata

Metadata

Assignees

Labels

S-reproducingStatus: Reproducing a bug reports3s

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions