rustfs icon indicating copy to clipboard operation
rustfs copied to clipboard

Mishandling Content-Encoding?

Open enuuros opened this issue 4 weeks ago • 2 comments

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?

enuuros avatar Dec 08 '25 10:12 enuuros

< x-amz-meta-content-type: application/json < x-amz-meta-content-encoding: br

@weisd

I can see x-amz-meta-content-encoding in the user's logs, but I can't find why it's stored in user_metadata. please take a look at this.

reatang avatar Dec 08 '25 14:12 reatang

Should be fixed by: https://github.com/rustfs/rustfs/pull/1089

Mic92 avatar Dec 10 '25 08:12 Mic92

Should be fixed by: #1089

I can confirm this fix included in 1.0.0-alpha.73 resolves the issue.

enuuros avatar Dec 12 '25 06:12 enuuros