Problem z przesyłaniem nginx client_max_body_size

117

Używam nginx / ruby-on-rails i mam prosty formularz wieloczęściowy do przesyłania plików. Wszystko działa dobrze, dopóki nie zdecyduję się ograniczyć maksymalnego rozmiaru plików, które chcę przesłać. Aby to zrobić, ustawiłem nginx client_max_body_sizena 1 m (1 MB) i oczekuję statusu HTTP 413 (żądanie zbyt dużej jednostki) w odpowiedzi, gdy ta reguła zostanie złamana.

Problem polega na tym, że kiedy przesyłam plik o rozmiarze 1,2 MB, zamiast wyświetlać stronę błędu HTTP 413, przeglądarka trochę się zawiesza, a następnie umiera z komunikatem „Połączenie zostało zresetowane podczas ładowania strony”.

Wypróbowałem prawie każdą opcję, którą oferuje nginx, nic nie wydaje się działać. Czy ktoś ma jakieś pomysły na ten temat?

Oto mój 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;    
    }    
}

Dzięki.


**Edit**

Środowisko / UA: Windows XP / Firefox 3.6.13

krukid
źródło

Odpowiedzi:

128

nginx „zawodzi szybko”, gdy klient informuje go, że zamierza wysłać treść większą niż client_max_body_sizetreść, 413 wysyłając odpowiedź i zamykając połączenie.

Większość klientów nie czyta odpowiedzi, dopóki nie zostanie wysłana cała treść żądania. Ponieważ nginx zamyka połączenie, klient wysyła dane do zamkniętego gniazda, powodując TCP RST.

Jeśli twój klient HTTP to obsługuje, najlepszym sposobem na to jest wysłanie Expect: 100-Continuenagłówka. Nginx obsługuje to poprawnie jak z 1.2.7 i będzie odpowiadać z 413 Request Entity Too Largeodpowiedzią, a nie 100 Continuejeśli Content-Lengthprzekroczy maksymalny rozmiar ciała.

Joe Shaw
źródło
1
Och, powinienem zaznaczyć, że ta odpowiedź zakłada, że ​​klient Content-Lengthraczej wysyła niż robi Transfer-Encoding: chunked.
Joe Shaw
2
Autor nginx opublikował na liście dyskusyjnej poprawkę, która ma to naprawić: nginx.2469901.n2.nabble.com/ ... Nie wiadomo, czy zostanie dodana do stabilnej gałęzi 1.2.x.
Joe Shaw
Dzięki, to właściwie wiele wyjaśnia. Z pewnością wygląda na Expectto, że jest to dobry sposób na duże żądania.
krukid
Zaktualizowałem moją odpowiedź, aby zauważyć, że łatka, o której wspomniałem wcześniej, została zatwierdzona i włączona do wydania 1.2.7.
Joe Shaw
Żeby zaoszczędzić czas na szukanie ładnej składni (tak jak spędziłem): request.setHeader(HttpHeaders.EXPECT, CONTINUE);z import org.apache.http.HttpHeaders;iimport static org.jboss.netty.handler.codec.http.HttpHeaders.Values.CONTINUE;
Erez Cohen
48

Czy przesyłanie umiera na samym końcu? 99% przed awarią? Treść klienta i bufory są kluczem, ponieważ nginx musi buforować przychodzące dane. Konfiguracje treści (dane treści żądania) określają, w jaki sposób nginx obsługuje zbiorczy przepływ danych binarnych z klientów wieloczęściowych do logiki aplikacji.

To cleanustawienie zwalnia pamięć i limity zużycia, instruując nginx, aby przechowywał przychodzący bufor w pliku, a następnie wyczyścił ten plik później z dysku, usuwając go.

Ustaw body_in_file_onlysię cleani dostosować buforów dla client_max_body_size. Konfiguracja pierwotnego pytania miała już włączony plik send, zwiększ również limity czasu. Używam poniższych ustawień, aby to naprawić, odpowiednie w kontekście lokalnej konfiguracji, serwera i http.

client_body_in_file_only clean;
client_body_buffer_size 32K;

client_max_body_size 300M;

sendfile on;
send_timeout 300s;
Bent Cardan
źródło
Nawet jeśli spowoduje to, że nginx zwróci prawidłowy HTTP 413, UA i tak wyśle ​​całą treść żądania, prawda? W takim razie myślę, że warto wypróbować podejście zasugerowane przez @ joe-shaw.
krukid
@krukid, kiedy wygląda na to, że wysyłanie w 99% zostało ukończone, zanim NGINX „szybko się nie powiedzie”, zgadzam się z tobą. W tym przypadku wszystkie znaki otaczające obiekt żądania są pozytywne, tj. Diagnoza jest taka, że ​​logika aplikacji serwera wewnętrznego jest w porządku - cokolwiek działa za Nginx. Tak więc, chociaż jest prawdopodobne, że żądanie zostało dobrze sformułowane, musimy rozważyć, dlaczego NGINX dławi się odpowiedzią. client_max_body_size powinno być pierwszą opcją konfiguracyjną, na którą patrzymy, a następnie rozważ bufory, ponieważ przy wystarczająco dużym uploadu prawidłowe rozwiązanie zależy od tego, ile pamięci może obsłużyć nasz serwer.
Bent Cardan,
@Bent Cardan. To podejście wydawało się lepsze i spróbowałem. Ale nadal otrzymuję błąd 413 po około 20 sekundach dla pliku 4 MB. Moje przyspieszenia nie są w stanie obsłużyć 4 MB w 20 sekund, więc dzieje się tak po dłuższym przepływie danych. Myśli?
Jerome
Dodałem zmiany w pliku nginx.conf _client_max_body_size 300M; sendfile on; send_timeout 300s; _ Działa idealnie dla mnie Dzięki
Ramesh Chand
Rozwiązanie działa dla mnie dalej openshift php7 nginx.
marlo
7

Z dokumentacji :

Należy pamiętać, że przeglądarki nie wiedzą, jak poprawnie wyświetlić ten błąd.

Podejrzewam, że tak właśnie się dzieje, jeśli sprawdzisz HTTP tam iz powrotem za pomocą narzędzi takich jak Firebug lub Live HTTP Headers (oba rozszerzenia Firefox), będziesz mógł zobaczyć, co naprawdę się dzieje.

ZoFreX
źródło
1
Natknąłem się na to również tutaj: forum.nginx.org/read.php?2,2620 Gdzie autor nginx mówi, że ludzie mogą próbować zmienić lingering_time / lingering_timeout - z których oba nie przyniosły efektu w moim przypadku. Poza tym po prostu nie rozumiem, w jaki sposób może wystąpić trwały problem z przekroczeniem limitu czasu, gdy przesyłam plik 1,2 MB z limitem 1 MB, łatwo mając stałe połączenie 5 Mb / s. Wąchałem odpowiedź i wysyła stronę 413 z nagłówkiem „Connection: close”, ale połączenie nie wydaje się być zamknięte.
krukid
Wydaje mi się, że trudno mi uwierzyć, że chociaż istnieje doskonale poprawny stan 413 HTTP, to nie uruchamia się on w przeglądarkach. Przeszukałem wiele miejsc, w których ludzie nie mogą się pozbyć tej strony, i nawet jej nie widziałem.
krukid
Jeśli wyłączysz pasażera, czy spowoduje to zamknięcie połączenia?
Mark Rose
Cóż, porównałem odpowiedzi z pasażerem i bez. Kiedy wszystko działa normalnie i przesyłam plik kilka razy większy (~ 14 MB) niż moje ograniczenie do 1 MB, wielokrotnie otrzymuję odpowiedź 413 (ponieważ klient wysyła fragmenty), a ostateczne „Resetowanie połączenia” wygląda na przekroczenie limitu czasu. Bez pasażera otrzymuję natychmiastową odpowiedź 413 i wszystkie postępy się zatrzymują, ale nadal widzę stronę „Reset połączenia”, a nie mój statyczny plik 413.html lub cokolwiek, co sugeruje „Jednostka zbyt duża”
krukid