skip to Main Content

I have a server implemented in Dart and want to parse POST request body. I am using Postman / curl (and Flutter app) to test the server. When I access server using localhost, the POST request body is processed correctly and server works as expected. However, when I try to access it using a domain name and Nginx handling as a reverse proxy, the server crashes trying to process the request body.

Using the same nginx configuration but NodeJS as a server (in some other project) with body-parser package, the NodeJS server is able to read body.

The curl version of the request:

curl --location --request POST 'https://insanichess.com/api/auth/register' 
--header 'Content-Type: application/json; charset=utf-8' 
--data-raw '{
    "email": "[email protected]",
    "password": "test_pwd"
}'

Nginx conf file (generated with Certbot):

server {
    server_name insanichess.com www.insanichess.com;

    location / {
        proxy_pass http://localhost:4040;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host;
        proxy_cache_bypass $http_upgrade;
    }

    listen [::]:443 ssl; # managed by Certbot
    listen 443 ssl; # managed by Certbot
    ssl_certificate /etc/letsencrypt/live/insanichess.com/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/insanichess.com/privkey.pem; # managed by Certbot
    include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot


}server {
    if ($host = www.insanichess.com) {
        return 301 https://$host$request_uri;
    } # managed by Certbot


    if ($host = insanichess.com) {
        return 301 https://$host$request_uri;
    } # managed by Certbot


    listen 80;
    listen [::]:80;

    server_name insanichess.com www.insanichess.com;
    return 404; # managed by Certbot
}

Important parts of Dart code:

// insanichess_server.dart

  /// Starts the server.
  Future<void> start() async {
    final InternetAddress address = InternetAddress.loopbackIPv4;
    final int port =
        int.parse(Platform.environment['INSANICHESS_PORT'] ?? '4040');

    final HttpServer server = await HttpServer.bind(address, port);
    _logger.info('InsanichessServer.create', 'Server listening on port $port');

    await _handleRequests(onServer: server);
  }

  /// Passes every request [onServer] to [_router].
  Future<void> _handleRequests({required HttpServer onServer}) async {
    await for (final HttpRequest request in onServer) {
      await _router.handle(request);
    }
  }

// auth_controller.dart
  Future<void> handleRegistration(HttpRequest request) async {
    if (request.method != 'POST' ||
        request.contentLength <= 0 ||
        request.headers.contentType?.value != ContentType.json.value) {
      return respondWithBadRequest(request);
    }

    final String content = await utf8.decodeStream(request);
    final Map<String, dynamic> body = jsonDecode(content);
    //...
  }

The server crashes with

Unhandled exception:
FormatException: Unexpected end of input (at character 1)

^

#0      _ChunkedJsonParser.fail (dart:convert-patch/convert_patch.dart:1405:5)
#1      _ChunkedJsonParser.close (dart:convert-patch/convert_patch.dart:523:7)
#2      _parseJson (dart:convert-patch/convert_patch.dart:41:10)
#3      JsonDecoder.convert (dart:convert/json.dart:506:36)
#4      JsonCodec.decode (dart:convert/json.dart:157:41)
#5      jsonDecode (dart:convert/json.dart:96:10)
#6      AuthController.handleRegistration (package:insanichess_server/src/controller/api/auth/auth.dart:79:39)
<asynchronous suspension>
#7      ApiRouter.handle (package:insanichess_server/src/router/api/api.dart:29:16)
<asynchronous suspension>
#8      ICRouter.handle (package:insanichess_server/src/router/ic_router.dart:27:16)
<asynchronous suspension>
#9      InsanichessServer._handleRequests (package:insanichess_server/src/insanichess_server.dart:38:7)
<asynchronous suspension>
#10     InsanichessServer.start (package:insanichess_server/src/insanichess_server.dart:32:5)
<asynchronous suspension>
#11     main (file:///home/campovski/insanichess/server/bin/main.dart:17:3)
<asynchronous suspension>

I have tried setting CORS and X-** headers in nginx as suggested in similar StackOverflow answers, however the error was always the same. x-www-form-urlencoded version did not help either.

Printing content.length before jsonDecode gives 0, however printing Content-Length header value in handler gives correct value (I think it is 63 or something).

This are all the headers that handler receives in request:

user-agent: curl/7.64.1
content-type: application/json; charset=utf-8
connection: upgrade
accept: */*
content-length: 63
host: insanichess.com

I assume the issue is in proxying with nginx, since querying directly on localhost works, however it seems strange that almost identical setup worked with NodeJS as backend. The full source code of the project is available on GitHub in case you want to take a closer look.

2

Answers


  1. Chosen as BEST ANSWER

    For some reason deleting everything from location / block in nginx config except for proxy_pass helped. This config is now ok:

    server {
        server_name insanichess.com www.insanichess.com;
    
        location / {
            proxy_pass http://localhost:4040;
        }
    
        listen [::]:443 ssl; # managed by Certbot
        listen 443 ssl; # managed by Certbot
        ssl_certificate /etc/letsencrypt/live/insanichess.com/fullchain.pem; # managed by Certbot
        ssl_certificate_key /etc/letsencrypt/live/insanichess.com/privkey.pem; # managed by Certbot
        include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
        ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
    
    
    }server {
        if ($host = www.insanichess.com) {
            return 301 https://$host$request_uri;
        } # managed by Certbot
    
    
        if ($host = insanichess.com) {
            return 301 https://$host$request_uri;
        } # managed by Certbot
    
    
        listen 80;
        listen [::]:80;
    
        server_name insanichess.com www.insanichess.com;
        return 404; # managed by Certbot
    }
    

  2. It seems like a problem with

    final Map<String, dynamic> body = jsonDecode(content);

    Or with redirect status on NGINX. You should use 307 response code. Source

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search