chauvd
chauvd

Reputation: 1

Azure Web App request timeout during Nginx proxy upload

I am deploying a container to an Azure Web App for Containers instance. The container bundles an Nginx server and RClone. The Nginx reverse proxy validates the request using an auth_request directive and then proxy_pass to the RClone upstream server on localhost.

Timeout configurations

Copied to /etc/nginx/conf.d/timeout.conf:

client_header_timeout 1800;
client_body_timeout 1800;
proxy_connect_timeout 1800;
proxy_send_timeout 1800;
proxy_read_timeout 1800;
send_timeout 1800;

Server configurations

Copied to /etc/nginx/nginx.conf.

pid                 /var/run/nginx/nginx.pid;
load_module         modules/ngx_http_js_module.so;
worker_processes    auto;

events {
    worker_connections 1024;
}

http {
    resolver                127.0.0.11 ipv6=off;
    resolver_timeout        10s;

    default_type            application/json;
    keepalive_timeout       65;
    client_max_body_size    0;
    
    upstream rclone {
      server localhost:5572;
    }

    upstream validator {
      server localhost:8079;
    }

    js_path "/etc/nginx/njs/";
    js_import main from index.js;

    server {
        listen                      8000;
        listen                      [::]:8000;
        server_name                 localhost;

        sendfile                    on;
        client_header_buffer_size   32k;

        location / {
            access_log off;
            return 200;
        }

        location /healthcheck {
            access_log off;
            return 200;
        }

        location ~* /operations/uploadfile {
            limit_except                HEAD POST { deny all; }
            auth_request                /_validate;

            proxy_set_header            Content-Length $content_length;
            proxy_set_header            Host $host;
            proxy_set_header            Referer $http_referer;
            proxy_set_header            X-Real-IP $remote_addr;
            proxy_set_header            X-Forwarded-Host $host;
            proxy_set_header            X-Forwarded-Server $host;
            proxy_set_header            X-Forwarded-For $proxy_add_x_forwarded_for;

            proxy_buffering             off;
            proxy_request_buffering     off;
            proxy_intercept_errors      off;

            proxy_pass                  http://rclone;
        }       

        location = /_validate {
            internal;

            proxy_set_header        Content-Length "";
            proxy_set_header        X-Original-URI $request_uri;
            proxy_set_header        X-Original-Method $request_method;

            proxy_pass_request_body off;
            proxy_intercept_errors  off;

            proxy_pass              http://validator/validate;            
        }

        error_page 405 = @405;
        location @405 { return 405 '{"status":405,"code":"MethodNotAllowed","message":"Method Not Allowed"}\n'; } 

        error_page 500 = @500;
        location @500 { return 500 '{"status":500,"code":"InternalServerError","message":"Internal Server Error"}\n'; }   
    }
        
    server {
        listen              8079;
        listen              [::]:8079;
        server_name         localhost;

        client_header_buffer_size   32k;

        location /validate {
            js_content                      main.authorize;
            js_fetch_timeout                10;
            js_fetch_trusted_certificate    /etc/ssl/certs/ca-certificates.crt;
        }
    }
}

It was my understanding that idle timeouts don't apply to active data uploads with data in transit. Depending on network latency/bandwidth, large uploads that take longer than ~5 minutes are being canceled.

Curl:

#!/bin/bash

curl -v -X POST 'https://<redacted>.azurewebsites.net/operations/uploadfile?<rclone_query_params>' \
  -H @token.txt \
  -F file=@"../samples/test-500MB.file"

Javascript:

const fs = require('fs');
const axios = require('axios');

const domain = 'https://<redacted>.azurewebsites.net';
const file = '../samples/test-500MB.file';
const fileStream = fs.createReadStream(file);
const token = fs.readFileSync('./token.txt', 'utf8').split(':')[1].trim();

console.log(`Uploading file: ${file}`);
console.log(`Token: ${token}`);

axios({
  method: 'post',
  url: `${domain}/operations/uploadfile?<rclone_query_params>`,
  headers: {
    'Content-Type': 'application/octet-stream',
    'Content-Length': fs.statSync(file).size,
    Expect: '100-continue',
    Connection: 'keep-alive',
    'Transfer-Encoding': 'chunked',
    'User-Agent': 'axios/0.21.4',
    Authorization: `${token}`
  },
  data: fileStream,
  maxContentLength: Infinity,
  maxBodyLength: Infinity,
  onUploadProgress: (progressEvent) => {
    const percentCompleted = Math.round((progressEvent.loaded * 100) / progressEvent.total);
    console.log(`Upload progress: ${percentCompleted}%`);
  },
  validateStatus: (status) => {
    return (status >= 200 && status < 300) || status === 308;
  },
  maxRedirects: 0
})
  .then((response) => {
    console.log(`Upload completed: ${response.status} ${response.statusText}`);
  })
  .catch((error) => {
    console.error(`Upload failed: ${error.message}`);
  });

It doesn't appear to be an issue between Nginx and RClone. The request authorizes successfully and starts to upload in parts via multipart upload. After ~300 seconds the client connection is closed by Azure with the following logs.

Nginx:

2023/05/13 13:01:34 [info] 18#18: *15 client prematurely closed connection while sending request to upstream, client: <redacted>, server: localhost, request: "POST /uploadfile?<rclone_query_param> HTTP/1.1", upstream: "http://127.0.0.1:5572/operations/uploadfile?<rclone_query_param>", host: "<redacted>.azurewebsites.net"

RClone:

2023/05/13 13:01:34 ERROR : rc: "operations/uploadfile": error: multipart upload failed to upload part: context canceled

Azure Middleware

Failed to forward request to http://<redacted>. Encountered a System.Threading.Tasks.TaskCanceledException exception after 299998.262ms with message: The request was canceled due to the configured HttpClient.Timeout of 300 seconds elapsing.. Check application logs to verify the application is properly handling HTTP traffic.

Any insight is appreciated.

Upvotes: 0

Views: 666

Answers (0)

Related Questions