问题描述
我在 Nginx 和 PUT
重定向方面遇到了一些问题:
假设我有一个位于 Nginx 服务器后面的 HTTP 服务(假设 HTTP 1.1)
客户端使用 PUT /my/api
执行 Expect: 100-continue
。
我的服务没有发送 100-continue
,而是发送 307
重定向到另一个端点(在本例中为 S3)。
但是,Nginx
出于某种未知原因在提供重定向服务之前发送了 100-continue
- 客户端在提供重定向服务之前继续将整个正文上传到 Nginx。这会导致客户端两次有效地传输正文 - 这对于多 GB 的上传来说不是很好
我想知道是否有办法:
- 防止
Nginx
发送100-continue
,除非服务确实发送。 - 允许具有任意大
Content-Length
的请求,而不必将client_max_body_size
设置为大值(以避免413 Entity too large
)。
由于我的服务仅发送重定向而不发送 100-Continue
,因此请求正文永远不会到达 Nginx。必须设置 client_max_body_size
并等待 Nginx 缓冲整个主体以提供重定向服务是非常不理想的。
我已经能够用 Apache 做到这一点,但不能用 Nginx。 Apache 在修复之前曾经有相同的行为:https://bz.apache.org/bugzilla/show_bug.cgi?id=60330 - 想知道 Nginx 是否有同样的问题
任何指针表示赞赏:)
编辑 1:这是重现问题的示例设置:
- Nginx.conf
worker_rlimit_nofile 261120;
worker_shutdown_timeout 10s ;
events {
multi_accept on;
worker_connections 16384;
use epoll;
}
http {
server {
listen 80;
server_name frontend;
keepalive_timeout 75s;
keepalive_requests 100;
location / {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass http://127.0.0.1:9999/;
}
}
}
我在上面运行
docker run --rm --name Nginx --net=host -v /path/to/Nginx.conf:/etc/Nginx/Nginx.conf:ro Nginx:1.21.1
- 简单的 python3 HTTP 服务器。
#!/usr/bin/env python3
import sys
from http.server import HTTPServer,BaseHTTPRequestHandler
class Redirect(BaseHTTPRequestHandler):
def do_PUT(self):
self.send_response(307)
self.send_header('Location','https://s3.amazonaws.com/test')
self.end_headers()
HTTPServer(("",9999),Redirect).serve_forever()
测试结果:
$ curl -sv -L -X PUT -T /some/very/large/file 127.0.0.1:9999/test
> PUT /test HTTP/1.1
> Host: 127.0.0.1:9999
> User-Agent: curl/7.74.0
> Accept: */*
> Content-Length: 531202949
> Expect: 100-continue
>
* Mark bundle as not supporting multiuse
* HTTP 1.0,assume close after body
< HTTP/1.0 307 Temporary Redirect
< Server: BaseHTTP/0.6 Python/3.9.2
< Date: Thu,15 Jul 2021 10:16:44 GMT
< Location: https://s3.amazonaws.com/test
<
* Closing connection 0
* Issue another request to this URL: 'https://s3.amazonaws.com/test'
* Trying 52.216.129.157:443...
* Connected to s3.amazonaws.com (52.216.129.157) port 443 (#1)
> PUT /test HTTP/1.0
> Host: s3.amazonaws.com
> User-Agent: curl/7.74.0
> Accept: */*
> Content-Length: 531202949
>
- 通过 Nginx 执行相同的操作失败并显示
413 Entity too large
- 即使主体不应通过 Nginx。 - 在配置中添加
client_max_body_size 1G;
后,结果不同,除了Nginx尝试缓冲整个主体:
$ curl -sv -L -X PUT -T /some/very/large/file 127.0.0.1:80/test
* Trying 127.0.0.1:80...
* Connected to 127.0.0.1 (127.0.0.1) port 80 (#0)
> PUT /test HTTP/1.1
> Host: 127.0.0.1
> User-Agent: curl/7.74.0
> Accept: */*
> Content-Length: 531202949
> Expect: 100-continue
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 100 Continue
} [65536 bytes data]
* We are completely uploaded and fine
* Mark bundle as not supporting multiuse
< HTTP/1.1 502 Bad Gateway
< Server: Nginx/1.21.1
< Date: Thu,15 Jul 2021 10:22:08 GMT
< Content-Type: text/html
< Content-Length: 157
< Connection: keep-alive
<
{ [157 bytes data]
<html>
<head><title>502 Bad Gateway</title></head>
<body>
<center><h1>502 Bad Gateway</h1></center>
<hr><center>Nginx/1.21.1</center>
</body>
</html>
注意 Nginx 如何发送 HTTP/1.1 100 Continue
使用这个简单的 python 服务器,请求随后失败,因为 python 服务器在服务重定向后立即关闭连接,这导致 Nginx 由于管道损坏而服务 502:
127.0.0.1 - - [15/Jul/2021:10:22:08 +0000] "PUT /test HTTP/1.1" 502 182 "-" "curl/7.74.0"
2021/07/15 10:22:08 [error] 31#31: *1 writev() Failed (32: broken pipe) while sending request to upstream,client: 127.0.0.1,server: frontend,request: "PUT /test HTTP/1.1",upstream: "http://127.0.0.1:9999/test",host: "127.0.0.1"
据我所知,这似乎与下面的 Apache 问题 https://bz.apache.org/bugzilla/show_bug.cgi?id=60330 完全一样(现在在较新的版本中已解决)。我不知道如何用 Nginx 来规避这个
解决方法
暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!
如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。
小编邮箱:dio#foxmail.com (将#修改为@)