I have a Flask server that downloads files using chunks of 4096 bytes.
between the flask and the client, we use a Nginx server.
downloads fail after about more than 1 GB, with this error in the Ngnix proxy:
024/01/07 15:03:45 [error] 23#23: *4891 readv() failed (104: Connection reset by
peer) while reading upstream, client: *.*.*.*, server: , request: "GET /api/asset-
service/v1/recordings/68f38d34-9e10-4bd1-a173-5b2217b70818/download?
connector_uuid=f7e88796-4d87-4143-9c6c-54a8d154d9f6 HTTP/1.1", upstream:
"http://*.*.*.*:8080/v1/api/recordings/68f38d34-9e10-4bd1-a173-
5b2217b70818/download?connector_uuid=f7e88796-4d87-4143-9c6c-54a8d154d9f6", host:
"*.*.*.*:8080", referrer: "https://localhost:8000/recordings"
I refactor the ngnix.conf file with all the solutions I found, but still getting the same error.
This is the ngnix conf:
load_module /usr/lib/nginx/modules/ndk_http_module.so;
load_module /usr/lib/nginx/modules/ngx_http_lua_module.so;
pcre_jit on;
worker_processes 1;
events {
worker_connections 1024;
}
env STACK_NAME;
env NAMESPACE;
http {
server_tokens off;
client_body_timeout 3600;
gzip on;
gzip_static on;
gzip_min_length 500;
gzip_proxied any;
gzip_comp_level 4;
gzip_types text/css text/javascript text/xml text/plain text/x-component application/javascript application/json application/xml application/rss+xml font/truetype font/opentype application/vnd.ms-fontobject image/svg+xml;
gzip_vary on;
gzip_disable "msie6";
resolver kube-dns.kube-system.svc.cluster.local ipv6=off;
# Increased timeouts for large file transfers
proxy_read_timeout 3600s;
proxy_send_timeout 3600s;
proxy_connect_timeout 3600s;
# Buffer size adjustments
proxy_buffer_size 128k;
proxy_buffers 4 256k;
proxy_busy_buffers_size 256k;
types_hash_max_size 4096;
# Keepalive connections
keepalive_timeout 300s;
keepalive_requests 10000;
# resolver 127.0.0.11 ipv6=off;
client_max_body_size 0;
proxy_max_temp_file_size 0;
proxy_buffering off;
server_names_hash_bucket_size 256;
log_format trace '$remote_addr $remote_user [$time_local] "$request" $status $body_bytes_sent "$http_referer" "$http_user_agent" "$http_x_forwarded_for" ($request_id)';
server {
set_by_lua $kn 'return os.getenv("NAMESPACE")' ;
listen 443 ssl;
listen 8080 ssl;
# redirect http to https
error_page 497 https://$host:$server_port$request_uri;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;
access_log /var/log/nginx/access.log trace;
add_header X-Frame-Options “DENY”; #Prevent ClickJacking
add_header X-XSS-Protection "1; mode=block"; #XSS Filter
add_header X-Content-Type-Options nosniff; #Prevent Mime-Type Risks
location /ws {
location ~ /ws/socket.io/(.*) {
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
proxy_pass http://notifications-service.$kn.svc.cluster.local:8080/socket.io/$1$is_args$args;
}
location ~ ^/ws/wsproxy-service/(.*) {
proxy_read_timeout 3600s;
proxy_send_timeout 3600s;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Request-ID $request_id;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
proxy_pass http://wsproxy-service.$kn.svc.cluster.local:8080/api/$1$is_args$args;
}
}
location /api {
proxy_http_version 1.1;
proxy_set_header Connection "";
location ~ ^/api/([w-]*)/v(d+)/swagger.json {
proxy_pass http://$1.$kn.svc.cluster.local:8080/v$2/swagger.json;
}
location ~ ^/api/([w-]*)/v(d+)/ui {
proxy_pass http://$1.$kn.svc.cluster.local:8080/v$2/ui;
}
location ~* ^/api/([w-]*)/v(d+)/(.*) {
# Adjusted timeouts for large file transfers
proxy_read_timeout 3600s;
proxy_send_timeout 3600s;
if ($request_method = OPTIONS ) {
add_header Access-Control-Allow-Origin *;
add_header Access-Control-Allow-Methods "GET, OPTIONS, PUT, DELETE";
add_header Access-Control-Allow-Headers "origin, authorization, accept, access-code, aad-token, Content-type";
add_header Access-Control-Allow-Credentials "true";
add_header Content-Length 0;
add_header Content-Type text/plain;
return 200;
}
rewrite_by_lua '
if string.match(ngx.var.request_uri, "/health$") == nil
and string.match(ngx.var.request_uri, "/beat_health$") == nil
and string.match(ngx.var.request_uri, "/celery_health/.*") == nil
and string.match(ngx.var.request_uri, "/admin/create_db$") == nil
and ngx.var.request_uri ~= "/api/user-service/v1/decrypt-token" then
local res = ngx.location.capture("/check_license_expiration")
if (res) and res.status ~= 200 then
ngx.exit(ngx.HTTP_PAYMENT_REQUIRED)
end
end
';
proxy_http_version 1.1;
proxy_set_header HOST $host;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Request-ID $request_id;
proxy_send_timeout 1200;
proxy_read_timeout 1200;
proxy_pass http://$1.$kn.svc.cluster.local:8080/v$2/api/$3$is_args$args;
}
}
location /tests {
location ~* ^/tests/([w-]*)/(.*) {
proxy_http_version 1.1;
proxy_set_header HOST $host;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Request-ID $request_id;
proxy_send_timeout 1200;
proxy_read_timeout 1200;
proxy_pass http://$1.$kn.svc.cluster.local:8080/tests/$2$is_args$args;
}
}
location / {
proxy_pass http://frontend.$kn.svc.cluster.local:8080;
}
location /nginx_status {
stub_status;
allow 127.0.0.1;
deny all;
}
}
}
This is the client code (download_file function):
import { action, observable } from 'mobx';
import axios from 'axios';
import BaseStore from './baseStore';
import { getAuthHeaders } from '../utils/tokenRequest';
import MessageService from '../utils/MessageService';
class RecordingsStore extends BaseStore {
@observable downloadingFiles = new Map();
downloadBlob(blob, originalFilename) {
const url = window.URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = url;
link.download = originalFilename;
// Append to html link element page
document.body.appendChild(link);
// Start download
link.click();
setTimeout(() => {
// Clean up and remove the link
link.parentNode.removeChild(link);
window.URL.revokeObjectURL(url);
}, 100);
}
@action
async downloadFile(recording, messages, i18next) {
try {
const cancelToken = axios.CancelToken.source();
this.downloadingFiles.set(recording.id, { progress: '0', cancelToken });
const authHeaders = await getAuthHeaders();
const headers = {
...authHeaders,
};
const response = await axios.get(`api/asset-service/v1/recordings/${recording.id}/download?connector_uuid=${recording.connector_uuid}`, {
headers,
withCredentials: true,
responseType: 'blob',
onDownloadProgress: (progressEvent) => {
const percentage = ((progressEvent.loaded * 100) / progressEvent.total).toFixed(1);
this.downloadingFiles.set(recording.id, { progress: percentage, cancelToken });
},
cancelToken: cancelToken.token,
});
const contentDisposition = response.headers.get('content-disposition');
const filenameMatch = contentDisposition && contentDisposition.match(/filename="?([^";]+)"?/);
const originalFilename = filenameMatch ? filenameMatch[1] : 'unknown_filename';
this.downloadBlob(response.data, originalFilename);
} catch (error) {
if (!axios.isCancel(error)) {
console.error('failed to download file', error);
MessageService.showMessage(messages, error?.response?.status || 500, {}, i18next);
}
} finally {
this.downloadingFiles.delete(recording.id);
}
}
@action
cancelDownload(id) {
const downloadingFile = this.downloadingFiles.get(id);
if (downloadingFile) {
downloadingFile.cancelToken.cancel();
}
}
}
export default new RecordingsStore();
Thanks