Описание ошибки
Использую Vaadin со Spring Boot, и когда я пытаюсь загрузить файл с помощью DownloadHandler.fromInputStream API, загрузка прерывается через некоторое время.
В браузере Firefox она прерывается через 30 секунд. В браузерах на базе Chromium (пробовал Chrome и Brave) загрузка прерывается после загрузки 1 ГБ данных.
Приложение развертывается на сервере Debian за прокси-сервером Nginx в виде WAR-файла. В прокси-сервере Nginx для всех тайм-аутов установлено высокое значение, поэтому тайм-аут происходит не из-за Nginx.
Код: Выделить всё
send_timeout 300;
proxy_connect_timeout 600;
proxy_send_timeout 600;
proxy_read_timeout 600;
Код: Выделить всё
tomcat:
connection-timeout: 900000 (default is 60000).
Сетевой инспектор в Firefox показывает, что статус запроса равен 200, но через 30 секунд он получает NS_BINDING_ABORTED. Загрузка не отменяется пользователем. Я также пытался увеличить время ожидания по умолчанию в Firefox, но безрезультатно:

Я также пытался использовать разные версии Java/Tomcat.
Что также интересно, загрузка большого файла с помощью компонента Upload в Vaadin завершается без проблем. Проблема возникает только во время загрузки.
Java-код Vaadin:
Код: Выделить всё
hiddenDownloadAnchor.setHrefAndDownload(DownloadHandler.fromInputStream(
downloadEvent -> new DownloadResponse(
new FileInputStream(file),
name,
type,
length))); // hiddenDownloadAnchor is hidden Anchor component from Vaadin
Глядя на трассировку стека, где в Tomcat выдается исключение: NioSocketWrapper.doWrite(NioEndpoint.java:1410), я вижу тайм-аут, но не знаю, почему. Глядя на переменную timeout в отладке, которая извлекается из getWriteTimeout(), она превышает 30 секунд (даже значение по умолчанию равно 60), поэтому я понятия не имею, почему это происходит постоянно в Firefox через 30 секунд и в браузерах на основе Chromium после загрузки 1 ГБ (превышение 30 секунд: загрузка длится около 5 минут).
Полная трассировка стека:
Код: Выделить всё
org.apache.catalina.connector.ClientAbortException: j a v a . n e t . S o c k e t T i m e o u t E x c e p t i o n < b r / > a t o r g . a p a c h e . c a t a l i n a . c o n n e c t o r . O u t p u t B u f f e r . r e a l W r i t e B y t e s ( O u t p u t B u f f e r . j a v a : 3 4 2 ) < b r / > a t o r g . a p a c h e . c a t a l i n a . c o n n e c t o r . O u t p u t B u f f e r . a p p e n d B y t e A r r a y ( O u t p u t B u f f e r . j a v a : 7 4 7 ) < b r / > a t o r g . a p a c h e . c a t a l i n a . c o n n e c t o r . O u t p u t B u f f e r . a p p e n d ( O u t p u t B u f f e r . j a v a : 6 7 5 ) < b r / > a t o r g . a p a c h e . c a t a l i n a . c o n n e c t o r . O u t p u t B u f f e r . w r i t e B y t e s ( O u t p u t B u f f e r . j a v a : 3 7 7 ) < b r / > a t o r g . a p a c h e . c a t a l i n a . c o n n e c t o r . O u t p u t B u f f e r . w r i t e ( O u t p u t B u f f e r . j a v a : 3 5 5 ) < b r / > a t o r g . a p a c h e . c a t a l i n a . c o n n e c t o r . C o y o t e O u t p u t S t r e a m . w r i t e ( C o y o t e O u t p u t S t r e a m . j a v a : 1 0 2 ) < b r / > a t c o m . v a a d i n . f l o w . s e r v e r . s t r e a m s . T r a n s f e r U t i l . t r a n s f e r ( T r a n s f e r U t i l . j a v a : 9 7 ) < b r / > a t c o m . v a a d i n . f l o w . s e r v e r . s t r e a m s . I n p u t S t r e a m D o w n l o a d H a n d l e r . h a n d l e D o w n l o a d R e q u e s t ( I n p u t S t r e a m D o w n l o a d H a n d l e r . j a v a : 1 0 1 ) < b r / > a t c o m . v a a d i n . f l o w . s e r v e r . s t r e a m s . D o w n l o a d H a n d l e r . h a n d l e R e q u e s t ( D o w n l o a d H a n d l e r . j a v a : 1 0 4 ) < b r / > a t c o m . v a a d i n . f l o w . s e r v e r . c o m m u n i c a t i o n . S t r e a m R e q u e s t H a n d l e r . c a l l E l e m e n t R e s o u r c e H a n d l e r ( S t r e a m R e q u e s t H a n d l e r . j a v a : 1 9 4 ) < b r / > a t c o m . v a a d i n . f l o w . s e r v e r . c o m m u n i c a t i o n . S t r e a m R e q u e s t H a n d l e r . h a n d l e R e q u e s t ( S t r e a m R e q u e s t H a n d l e r . j a v a : 1 1 9 ) < b r / > a t c o m . v a a d i n . f l o w . s e r v e r . V a a d i n S e r v i c e . h a n d l e R e q u e s t ( V a a d i n S e r v i c e . j a v a : 1 8 7 9 ) < b r / > a t c o m . v a a d i n . f l o w . s e r v e r . V a a d i n S e r v l e t . s e r v i c e ( V a a d i n S e r v l e t . j a v a : 3 9 8 ) < b r / > a t c o m . v a a d i n . f l o w . s p r i n g . S p r i n g S e r v l e t . s e r v i c e ( S p r i n g S e r v l e t . j a v a : 1 0 6 ) < b r / > a t j a k a r t a . s e r v l e t . h t t p . H t t p S e r v l e t . s e r v i c e ( H t t p S e r v l e t . j a v a : 6 5 8 ) < b r / > a t o r g . a p a c h e . c a t a l i n a . c o r e . A p p l i c a t i o n F i l t e r C h a i n . i n t e r n a l D o F i l t e r ( A p p l i c a t i o n F i l t e r C h a i n . j a v a : 1 9 5 ) < b r / > a t o r g . a p a c h e . c a t a l i n a . c o r e . A p p l i c a t i o n F i l t e r C h a i n . d o F i l t e r ( A p p l i c a t i o n F i l t e r C h a i n . j a v a : 1 4 0 ) < b r / > a t o r g . a p a c h e . c a t a l i n a . c o r e . A p p l i c a t i o n D i s p a t c h e r . i n v o k e ( A p p l i c a t i o n D i s p a t c h e r . j a v a : 6 1 2 ) < b r / > a t o r g . a p a c h e . c a t a l i n a . c o r e . A p p l i c a t i o n D i s p a t c h e r . p r o c e s s R e q u e s t ( A p p l i c a t i o n D i s p a t c h e r . j a v a : 3 9 4 ) < b r / > a t o r g . a p a c h e . c a t a l i n a . c o r e . A p p l i c a t i o n D i s p a t c h e r . d o F o r w a r d ( A p p l i c a t i o n D i s p a t c h e r . j a v a : 3 2 3 ) < b r / > a t o r g . a p a c h e . c a t a l i n a . c o r e . A p p l i c a t i o n D i s p a t c h e r . f o r w a r d ( A p p l i c a t i o n D i s p a t c h e r . j a v a : 2 6 8 ) < b r / > a t o r g . s p r i n g f r a m e w o r k . w e b . s e r v l e t . m v c . S e r v l e t F o r w a r d i n g C o n t r o l l e r . h a n d l e R e q u e s t I n t e r n a l ( S e r v l e t F o r w a r d i n g C o n t r o l l e r . j a v a : 1 4 2 ) < b r / > a t o r g . s p r i n g f r a m e w o r k . w e b . s e r v l e t . m v c . A b s t r a c t C o n t r o l l e r . h a n d l e R e q u e s t ( A b s t r a c t C o n t r o l l e r . j a v a : 1 7 8 )
at org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter.handle(SimpleControllerHandlerAdapter.java:51)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1089)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:979)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1014)
at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:903)
at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:564)
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:885)
at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:658)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:195)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140)
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:51)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140)
at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140)
at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140)
at org.springframework.boot.web.servlet.support.ErrorPageFilter.doFilter(ErrorPageFilter.java:124)
at org.springframework.boot.web.servlet.support.ErrorPageFilter$1.doFilterInternal(ErrorPageFilter.java:99)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)
at org.springframework.boot.web.servlet.support.ErrorPageFilter.doFilter(ErrorPageFilter.java:117)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140)
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:167)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:90)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:483)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:116)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:93)
at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:666)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:344)
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:398)
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:63)
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:903)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1776)
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:52)
at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:975)
at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:493)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:63)
at java.base/java.lang.Thread.run(Thread.java:1474)
Caused by: java.net.SocketTimeoutException
at org.apache.tomcat.util.net.NioEndpoint$NioSocketWrapper.doWrite(NioEndpoint.java:1410)
at org.apache.tomcat.util.net.SocketWrapperBase.doWrite(SocketWrapperBase.java:732)
at org.apache.tomcat.util.net.SocketWrapperBase.writeBlocking(SocketWrapperBase.java:572)
at org.apache.tomcat.util.net.SocketWrapperBase.write(SocketWrapperBase.java:520)
at org.apache.coyote.http11.Http11OutputBuffer$SocketOutputBuffer.doWrite(Http11OutputBuffer.java:548)
at org.apache.coyote.http11.filters.ChunkedOutputFilter.doWrite(ChunkedOutputFilter.java:111)
at org.apache.coyote.http11.Http11OutputBuffer.doWrite(Http11OutputBuffer.java:193)
at org.apache.coyote.Response.doWrite(Response.java:628)
at org.apache.catalina.connector.OutputBuffer.realWriteBytes(OutputBuffer.java:330)
... 70 more
Загрузка больших файлов должна завершиться без проблем и не должна истечь по тайм-ауту в браузере Firefox через 30 секунд или после загрузки 1 ГБ в браузерах на основе Chromium.
Минимальный воспроизводимый пример
Добавьте представление в vaadin с привязкой и инициируйте загрузку большого файла (превышающего размер) 1 ГБ):
Код: Выделить всё
hiddenDownloadAnchor.setHrefAndDownload(DownloadHandler.fromInputStream(
downloadEvent -> new DownloadResponse(
new FileInputStream(file),
name,
type,
length)));
Версии
- Версия Vaadin / Flow: 24.9.3
- Версия Java: 25/21
- Версия браузера: Firefox 145.0b6 или Brave 1.83.120 (Chromium: 141.0.7390.122)
- Сервер приложений: Tomcat 10.1.48 (также пробовал более старую версию: 10.1.46)
Наткнулся на эту старую проблему nginx со ссылкой:
Привет, мы пробовали nginx от версии 1.6.2 до версии 1.12.2 и столкнулись с
проблемой при использовании в качестве прокси перед артефактом. Загрузка
прерывается при достижении 1 ГБ.
Такое поведение зависит от внутренней VLAN. В одной VLAN это всегда
происходит. В другой VLAN такого никогда не происходит. Это ограничение по размеру, а не по времени. В некоторых сетях он останавливается через 30 секунд, а в других
других медленных сетях - через 13 минут.
Мы выполнили минимальную настройку прокси-сервера с помощью Apache, и это работает со всеми
VLAN. Вот почему мы ожидаем, что это как-то связано с nginx или
комбинацией nginx и TCP/IP стека Linux.
В Wireshark мы видим «TCP Dup ACK» на стороне клиента, отправленное на сервер nginx
.
Wget завершается с ошибкой, соединение закрывается в байте 1083793011, но продолжается
загрузка с частичным содержимым. docker не может справиться с этим, и наши
клиенты не могут загружать образы Docker со слоями размером более 1 ГБ.
С предложением:
Ограничение в 1 ГБ предполагает, что проблема связана с
proxy_max_temp_file_size. По умолчанию это один гигабайт, и если
предел будет достигнут, nginx прекратит чтение из серверной части до тех пор, пока все
данные, буферизованные на диске, не будут отправлены клиенту. Это, в свою очередь, может привести к
тайм-ауту отправки на стороне бэкэнда.
Пожалуйста, проверьте nginx и журналы бэкенда, чтобы увидеть, что здесь происходит.
Вероятно, в журнале ошибок nginx есть что-то вроде «преждевременно закрытое
выходное соединение», а таймауты отправки в бэкэнд
журналах. Альтернативно, просто проверьте, установлено ли proxy_max_temp_file_size 0; помогает
(это полностью отключит буферизацию диска).
Если приведенное выше предложение верно, два возможных решения:
Настройте proxy_max_temp_file_size. Рассмотрите возможность установки ограничения
выше размера всех ожидаемых ответов или достаточно маленького размера, чтобы ваш
сервер не истекал по тайм-ауту. В частности, proxy_max_temp_file_size 0;
может быть хорошим выбором при проксировании больших файлов. Настройте
тайм-ауты соответствующим образом.
Изменение этого proxy_max_temp_file_size на 0 помогает при загрузке в браузерах на базе Chrome, но эта проблема сохраняется в Firefox.
Запуск последней стабильной версии (1.28.0) nginx локально, даже с конфигурацией по умолчанию (просто установите proxy_pass и порт), обеспечивает то же самое результаты, значит, должно быть что-то между приложением nginx и vaadin.
Подробнее здесь: https://stackoverflow.com/questions/798 ... is-inconsi
Мобильная версия