flutterdarthttp-headersaqueducthttp-content-length

Files served with Aqueduct don't have a Content-Length header


I am writing a backend for my Flutter app using Aqueduct. I have Aqueduct set up so that Nginx proxies requests to it like this:

server {

    root /home/web/my_server/web;
    index index.html index.htm;

    server_name example.com www.example.com;

    location / {
        try_files $uri $uri/ =404;
    }

    location /api {
        proxy_set_header X-Real-IP $remote_addr;
        proxy_pass http://127.0.0.1:8888;
        proxy_http_version 1.1;
    }

    ...
}

In Aqueduct I serve the files using a FileController:

router.route("/api/v1/app/*")
    .link(() => LogController(context))
    .link(() => FileController("public/app/")
  ..setContentTypeForExtension('apk', ContentType('application', 'vnd.android.package-archive')));

However, any files that it returns don't include the Content-Length header. That means that I can't show download progress.

I tried creating a custom FileController where I added the headers in manually:

final contentLengthValue = file.lengthSync();

return Response.ok(byteStream,
    headers: {HttpHeaders.lastModifiedHeader: lastModifiedDateStringValue,
      HttpHeaders.contentLengthHeader: contentLengthValue,
      'x-decompressed-content-length': contentLengthValue,
      HttpHeaders.cacheControlHeader: 'no-transform',
      HttpHeaders.acceptRangesHeader: 'bytes'})
  ..cachePolicy = _policyForFile(file)
  ..encodeBody = false
  ..contentType = contentType;

The Content-Length header was still removed, but the x-decompressed-content-length header remained so this is a possible workaround. It just doesn't play nicely with some Flutter plugins that look for the Content-Length header and don't have a convenient way to check other headers.

Is this an Aqueduct problem or an Nginx problem? How do I solve it?


Solution

  • This solution works, but it skirts around the original problem. That it, it allows you to serve files that have the Content-Length in the header, but it doesn't explain why it was getting stripped in Aqueduct. Other answers are welcome.

    Rather than have Aqueduct serve files, just have Nginx serve them directly.

    If you can't change your API route, you can just give it an alias in the Nginx config location block. Add this before the /api location block.

    location /api/v1/app/ {
        alias /home/web/my_server/public/app/;
    }
    

    Now files in the app/ folder will get served by Nginx rather than Aqueduct. Nginx includes the Content-Length header in the files it returns.