nginx upload client_max_body_size problem

117

Ich verwende Nginx / Ruby-on-Rails und habe ein einfaches mehrteiliges Formular zum Hochladen von Dateien. Alles funktioniert einwandfrei, bis ich mich entscheide, die maximale Größe der Dateien, die ich hochladen möchte, einzuschränken. Dazu setze ich den Nginx client_max_body_sizeauf 1 MB ( 1 MB) und erwarte als Antwort einen HTTP 413-Status (Request Entity Too Large), wenn diese Regel verletzt wird.

Das Problem ist, dass beim Hochladen einer 1,2-MB-Datei anstelle der Anzeige der HTTP 413-Fehlerseite der Browser ein wenig hängt und dann mit der Meldung "Verbindung wurde zurückgesetzt, während die Seite geladen wurde" stirbt.

Ich habe fast jede Option ausprobiert, die Nginx bietet, nichts scheint zu funktionieren. Hat jemand irgendwelche Ideen dazu?

Hier ist meine nginx.conf:

worker_processes  1;
timer_resolution  1000ms;
events {
    worker_connections  1024;
}

http {
    passenger_root /the_passenger_root;
    passenger_ruby /the_ruby;

    include       mime.types;
    default_type  application/octet-stream;

    sendfile           on;
    keepalive_timeout  65;

    server {
      listen 80;
      server_name www.x.com;
      client_max_body_size 1M;
      passenger_use_global_queue on;
      root /the_root;
      passenger_enabled on;

      error_page 404 /404.html;
      error_page 413 /413.html;    
    }    
}

Vielen Dank.


**Edit**

Umgebung / UA: Windows XP / Firefox 3.6.13

krukid
quelle

Antworten:

128

nginx "schlägt schnell fehl", wenn der Client ihm mitteilt, dass er einen größeren Text als den client_max_body_sizesenden wird, indem er eine 413-Antwort sendet und die Verbindung schließt.

Die meisten Clients lesen Antworten erst, wenn der gesamte Anforderungshauptteil gesendet wurde. Da nginx die Verbindung schließt, sendet der Client Daten an den geschlossenen Socket, wodurch ein TCP-RST verursacht wird.

Wenn Ihr HTTP-Client dies unterstützt, können Sie dies am besten tun, indem Sie einen Expect: 100-ContinueHeader senden . Nginx unterstützt dies richtig ab 1.2.7 und wird mit einer Antwort 413 Request Entity Too LargeAntwort statt , 100 Continuewenn Content-Lengthdie Größe maximale Körper überschreitet.

Joe Shaw
quelle
1
Oh, ich sollte darauf hinweisen, dass diese Antwort davon ausgeht, dass der Client Content-Lengtheher sendet als tut Transfer-Encoding: chunked.
Joe Shaw
2
Der Nginx-Autor hat einen Patch veröffentlicht, um dies auf der Mailingliste zu beheben: nginx.2469901.n2.nabble.com/… Kein Wort, ob er dem stabilen Zweig 1.2.x hinzugefügt wird.
Joe Shaw
Danke, das erklärt eigentlich viel. Sicherlich sieht es so aus, als wäre Expectes der richtige Weg für große Anfragen.
Krukid
Meine Antwort wurde aktualisiert, um festzustellen, dass der zuvor erwähnte Patch festgeschrieben und in die Version 1.2.7 integriert wurde.
Joe Shaw
Nur um die Zeit zu sparen, nach einer schönen Syntax zu suchen (wie ich sie verbracht habe): request.setHeader(HttpHeaders.EXPECT, CONTINUE);mit import org.apache.http.HttpHeaders;undimport static org.jboss.netty.handler.codec.http.HttpHeaders.Values.CONTINUE;
Erez Cohen
48

Stirbt Ihr Upload ganz am Ende? 99% vor dem Absturz? Client-Body und Puffer sind der Schlüssel, da Nginx eingehende Daten puffern muss. Die Body-Konfigurationen (Daten des Anforderungs-Body) geben an, wie nginx den Massenfluss von Binärdaten von Clients mit mehreren Teilen in die Logik Ihrer App verarbeitet.

Die cleanEinstellung gibt Speicher- und Verbrauchsgrenzen frei, indem nginx angewiesen wird, den eingehenden Puffer in einer Datei zu speichern und diese Datei später durch Löschen von der Festplatte zu bereinigen.

Stellen Sie body_in_file_onlyan cleanund stellen Sie Puffer für die client_max_body_size. In der Konfiguration der ursprünglichen Frage war bereits sendfile aktiviert. Erhöhen Sie auch die Zeitüberschreitungen. Ich verwende die folgenden Einstellungen, um dies zu beheben, die für Ihre lokalen Konfigurations-, Server- und http-Kontexte geeignet sind.

client_body_in_file_only clean;
client_body_buffer_size 32K;

client_max_body_size 300M;

sendfile on;
send_timeout 300s;
Gebogener Kardan
quelle
Selbst wenn dies dazu führt, dass Nginx ein ordnungsgemäßes HTTP 413 zurückgibt, sendet UA am Ende dennoch den gesamten Anfragetext, nicht wahr? In diesem Fall lohnt es sich, den von @ joe-shaw vorgeschlagenen Ansatz auszuprobieren.
Krukid
@krukid Wenn es so aussieht, haben wir 99% des Uploads abgeschlossen, bevor NGINX "schnell fehlschlägt", stimme ich Ihnen zu. In diesem Fall sind alle Vorzeichen um das Anforderungsobjekt herum positiv, dh die Diagnose lautet, dass die interne Serveranwendungslogik in Ordnung ist - was auch immer hinter Nginx läuft. Obwohl es wahrscheinlich ist, dass die Anfrage gut formuliert wurde, müssen wir uns überlegen, warum NGINX bei der Antwort erstickt. client_max_body_size sollte die erste Konfigurationsoption sein, die wir betrachten, und dann die Puffer berücksichtigen, da bei einem ausreichend großen Upload die richtige Lösung davon abhängt, wie viel Speicher unser Server ebenfalls verarbeiten kann.
Bent Cardan
@Bent Cardan. Dieser Ansatz schien der bessere zu sein, und ich habe es versucht. Aber nach ungefähr 20 Sekunden erhalte ich immer noch einen 413-Fehler für eine 4-MB-Datei. Meine Upspeeds können 4 MB nicht in 20 Sekunden verwalten, daher geschieht dies, nachdem die Daten längere Zeit geflossen sind. Gedanken?
Jerome
Ich habe die Änderungen in der Datei nginx.conf _client_max_body_size 300M hinzugefügt. sendfile on; send_timeout 300s; _ Es funktioniert perfekt für mich Danke
Ramesh Chand
Lösung funktioniert bei mir weiter openshift php7 nginx.
Marlo
7

Aus der Dokumentation :

Es ist zu beachten, dass die Browser nicht wissen, wie dieser Fehler korrekt angezeigt werden kann.

Ich vermute, dass dies der Fall ist. Wenn Sie das HTTP mit Tools wie Firebug oder Live-HTTP-Headern (beide Firefox-Erweiterungen) hin und her überprüfen , können Sie sehen, was wirklich vor sich geht.

ZoFreX
quelle
1
Das habe ich auch hier gesehen: gestoßen forum.nginx.org/read.php?2,2620 Wo der Nginx- Autor sagt, dass die Leute versuchen könnten, verweilende_Zeit / verweilende_Zeitüberschreitung zu ändern - beides hatte in meinem Fall keine Auswirkung. Außerdem sehe ich einfach nicht, wie es zu einem dauerhaften Timeout-Problem kommen kann, wenn ich eine 1,2-MB-Datei mit einem 1-MB-Limit hochlade, die leicht eine stabile 5-Mbit / s-Verbindung hat. Ich habe die Antwort beschnuppert und sie sendet die 413-Seite mit dem Header "Verbindung: Schließen", aber die Verbindung scheint nicht zu schließen.
Krukid
Ich glaube, es fällt mir nur schwer zu glauben, dass ein 413 HTTP-Status zwar einwandfrei gültig ist, in Browsern jedoch nicht ausgelöst wird. Ich habe viele Orte gegoogelt, an denen die Leute diese Seite nicht loswerden können, und ich habe sie noch nie gesehen.
Krukid
Wenn Sie den Passagier deaktivieren, wird die Verbindung geschlossen?
Mark Rose
Nun, ich habe die Antworten mit und ohne Passagier verglichen. Wenn alles normal läuft und ich eine Datei hochlade, die um ein Vielfaches größer ist (~ 14 MB) als meine 1-MB-Einschränkung, erhalte ich mehrmals 413 Antworten (da der Client weiterhin Chunks sendet) und das endgültige "Zurücksetzen der Verbindung" sieht nach einer Zeitüberschreitung aus. Ohne Passagier erhalte ich eine sofortige 413-Antwort und alle Fortschrittsstopps, aber ich sehe immer noch die Seite "Verbindung zurücksetzen", nicht meine statische 413.html oder irgendetwas, das "Entity Too Large" impliziert
krukid