nginx + http2: conexão antecipadamente fechada upstream, PROTOCOL_ERROR

1

No meu servidor, tenho o nginx em execução usando a imagem padrão do docker nginx (versão 1.13.8) como um proxy reverso para o Lychee (usando wonderfall / lychee ), um software de galeria de fotos escrito em PHP. Até onde eu sei, a imagem do Lychee também roda nginx com php7.1-fpm.

A galeria funciona bem. Somente ao tentar baixar uma imagem ou um álbum, recebo um erro estranho no navegador. O Chromium diz "Falha - erro de rede" e outros navegadores exibem mensagens de erro semelhantes. Este erro aparece quando o download é concluído e, ao usar um navegador que mantém downloads com falha, é possível observar que o arquivo baixado está realmente completo.

O erro ocorre apenas ao acessar o Lychee através do nginx e ao usar o HTTP / 2. O log do nginx relata:

2018/01/15 15:37:34 [error] 8#8: *2960 upstream prematurely closed connection while sending to client, client: 192.168.255.1, server: example.com, request: "GET /2018/php/index.php?function=Photo::getArchive&photoID=123&password= HTTP/2.0", upstream: "http://10.93.1.23:8888/php/index.php?function=Photo::getArchive&photoID=123&password=", host: "example.com"

O log de lichia não informa nada.

Ao tentar baixar um arquivo usando curl -v, esta é a saída:

*   Trying 1.2.3.4...
* TCP_NODELAY set
* Connected to example.com (1.2.3.4) port 443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* successfully set certificate verify locations:
*   CAfile: /etc/ssl/certs/ca-certificates.crt
  CApath: none
} [5 bytes data]
* TLSv1.2 (OUT), TLS handshake, Client hello (1):
} [226 bytes data]
* TLSv1.2 (IN), TLS handshake, Server hello (2):
{ [106 bytes data]
* TLSv1.2 (IN), TLS handshake, Certificate (11):
{ [2754 bytes data]
* TLSv1.2 (IN), TLS handshake, Server key exchange (12):
{ [556 bytes data]
* TLSv1.2 (IN), TLS handshake, Server finished (14):
{ [4 bytes data]
* TLSv1.2 (OUT), TLS handshake, Client key exchange (16):
} [37 bytes data]
* TLSv1.2 (OUT), TLS change cipher, Client hello (1):
} [1 bytes data]
* TLSv1.2 (OUT), TLS handshake, Finished (20):
} [16 bytes data]
* TLSv1.2 (IN), TLS handshake, Finished (20):
{ [16 bytes data]
* SSL connection using TLSv1.2 / ECDHE-RSA-CHACHA20-POLY1305
* ALPN, server accepted to use h2
* Server certificate:
*  subject: CN=example.com
*  start date: Dec  4 10:54:54 2017 GMT
*  expire date: Mar  4 10:54:54 2018 GMT
*  subjectAltName: host "example.com" matched cert's "example.com"
*  issuer: C=US; O=Let's Encrypt; CN=Let's Encrypt Authority X3
*  SSL certificate verify ok.
* Using HTTP2, server supports multi-use
* Connection state changed (HTTP/2 confirmed)
* Copying HTTP/2 data in stream buffer to connection buffer after upgrade: len=0
} [5 bytes data]
* Using Stream ID: 1 (easy handle 0x55e8df551160)
} [5 bytes data]
> GET /2018/php/index.php?function=Photo::getArchive&photoID=123&password= HTTP/2
> Host: example.com
> User-Agent: curl/7.57.0
> Accept: */*
> 
{ [5 bytes data]
* Connection state changed (MAX_CONCURRENT_STREAMS updated)!
} [5 bytes data]
< HTTP/2 200 
< server: nginx/1.13.8
< date: Mon, 15 Jan 2018 15:37:34 GMT
< content-type: application/octet-stream
< content-length: 530149
< x-powered-by: PHP/7.1.2
< set-cookie: PHPSESSID=123; path=/2018/
< expires: Thu, 19 Nov 1981 08:52:00 GMT
< cache-control: no-store, no-cache, must-revalidate
< pragma: no-cache
< content-disposition: attachment; filename="IMG_4872.jpg"
< strict-transport-security: max-age=31536000
< 
{ [3691 bytes data]
* HTTP/2 stream 1 was not closed cleanly: PROTOCOL_ERROR (err 1)
* Closing connection 0
} [5 bytes data]
* TLSv1.2 (OUT), TLS alert, Client hello (1):
} [2 bytes data]

Ao baixar com HTTP / 1.1, tudo funciona bem:

*   Trying 1.2.3.4...
* TCP_NODELAY set
* Connected to example.com (1.2.3.4) port 443 (#0)
* ALPN, offering http/1.1
* successfully set certificate verify locations:
*   CAfile: /etc/ssl/certs/ca-certificates.crt
  CApath: none
} [5 bytes data]
* TLSv1.2 (OUT), TLS handshake, Client hello (1):
} [223 bytes data]
* TLSv1.2 (IN), TLS handshake, Server hello (2):
{ [112 bytes data]
* TLSv1.2 (IN), TLS handshake, Certificate (11):
{ [2754 bytes data]
* TLSv1.2 (IN), TLS handshake, Server key exchange (12):
{ [556 bytes data]
* TLSv1.2 (IN), TLS handshake, Server finished (14):
{ [4 bytes data]
* TLSv1.2 (OUT), TLS handshake, Client key exchange (16):
} [37 bytes data]
* TLSv1.2 (OUT), TLS change cipher, Client hello (1):
} [1 bytes data]
* TLSv1.2 (OUT), TLS handshake, Finished (20):
} [16 bytes data]
* TLSv1.2 (IN), TLS handshake, Finished (20):
{ [16 bytes data]
* SSL connection using TLSv1.2 / ECDHE-RSA-CHACHA20-POLY1305
* ALPN, server accepted to use http/1.1
* Server certificate:
*  subject: CN=example.com
*  start date: Dec  4 10:54:54 2017 GMT
*  expire date: Mar  4 10:54:54 2018 GMT
*  subjectAltName: host "example.com" matched cert's "example.com"
*  issuer: C=US; O=Let's Encrypt; CN=Let's Encrypt Authority X3
*  SSL certificate verify ok.
} [5 bytes data]
> GET /2018/php/index.php?function=Photo::getArchive&photoID=1234&password= HTTP/1.1
> Host: example.com
> User-Agent: curl/7.57.0
> Accept: */*
> 
{ [5 bytes data]
< HTTP/1.1 200 OK
< Server: nginx/1.13.8
< Date: Mon, 15 Jan 2018 15:43:36 GMT
< Content-Type: application/octet-stream
< Content-Length: 530149
< Connection: keep-alive
< X-Powered-By: PHP/7.1.2
< Set-Cookie: PHPSESSID=1234; path=/2018/
< Expires: Thu, 19 Nov 1981 08:52:00 GMT
< Cache-Control: no-store, no-cache, must-revalidate
< Pragma: no-cache
< Content-Disposition: attachment; filename="IMG_4872.jpg"
< Strict-Transport-Security: max-age=31536000
< 
{ [3691 bytes data]
* Connection #0 to host example.com left intact

Finalmente, aqui está a saída ao acessar o Lychee diretamente:

*   Trying 10.93.1.23...
* TCP_NODELAY set
* Connected to 10.93.1.23 (10.93.1.23) port 8888 (#0)
> GET /php/index.php?function=Photo::getArchive&photoID=1234&password= HTTP/1.1
> Host: 10.93.1.23:8888
> User-Agent: curl/7.56.1
> Accept: */*
> 
< HTTP/1.1 200 OK
< Server: nginx
< Date: Mon, 15 Jan 2018 15:46:06 GMT
< Content-Type: application/octet-stream
< Content-Length: 530149
< Connection: keep-alive
< X-Powered-By: PHP/7.1.2
< Set-Cookie: PHPSESSID=1234; path=/
< Expires: Thu, 19 Nov 1981 08:52:00 GMT
< Cache-Control: no-store, no-cache, must-revalidate
< Pragma: no-cache
< Content-Disposition: attachment; filename="IMG_4872.jpg"
< 
{ [14070 bytes data]
* Connection #0 to host 10.93.1.23 left intact

Não entendo por que o nginx diz que o Lychee encerrou prematuramente a conexão, quando na verdade todo o arquivo foi transferido (o Content-Length também corresponde), e a execução manual do curl também não mostra erros. Alguém tem alguma ideia?

cdauth
fonte
Estou tendo o mesmo problema - mas com o servidor express / nodejs que presta serviços a arquivos grandes (> 1,5 GB), acho que há algo errado com o nginx (http2) e o navegador que lida com E / S em buffer. Eu tive que desativar o http2 no nginx porque deste .. :(
SoichiH
Na verdade, acho que tem a ver com um comprimento de conteúdo incorreto .. Eu estava enviando um tamanho de conteúdo menor que o conteúdo real que meu servidor estava enviando .. então algo dá errado no lado do cliente - provavelmente encerrando a conexão prematuramente?
21818 SoichiH
@ SoichiH Tenho certeza de que o comprimento do conteúdo corresponde ao tamanho do meu arquivo. Vou verificar novamente embora. Lembro-me vagamente de um relatório de bug do nginx em algum lugar em que algo assim foi relatado em conexão com o sendFile (). Não consigo encontrá-lo agora e nem tenho certeza do que é exatamente sendFile (), mas o Lychee está usando. Seu aplicativo também o usa?
Cdauth