Nodejs S3 SDK 上传文件大于 1MB 时超时的问题解决
问题来源:私有化的COS存储、基于S3协议进行读写使用。
问题表现:小于 1MB 的文件可以正常上传,大于1MB 必定超时
问题原因:
网关/Nginx 未对 SDK 发出的请求中携带的 expect:100-continue 请求头进行响应。
Nodejs 的 S3 SDK 在实现上需要等待服务端确认 100-continue 才会继续发送数据,服务端不响应 expect:100-continue 的情况下表现为连接超时。
详情可以看 S3 js SDK 源码:
https://github.com/aws/aws-sdk-js/blob/82d8972c3a9a859437dbf42d81b20f07cf86dd58/lib/services/s3.js#L400-L409
/**
* Adds Expect: 100-continue header if payload is greater-or-equal 1MB
* @api private
*/
addExpect100Continue: function addExpect100Continue(req) {
var len = req.httpRequest.headers['Content-Length'];
if (AWS.util.isNode() && (len >= 1024 * 1024 || req.params.Body instanceof AWS.util.stream.Stream)) {
req.httpRequest.headers['Expect'] = '100-continue';
}
},
var expect = httpRequest.headers.Expect || httpRequest.headers.expect;
if (expect === '100-continue') {
stream.once('continue', function() {
self.writeBody(stream, httpRequest);
});
} else {
this.writeBody(stream, httpRequest);
}
RFC 对 Expect:100-continue 的描述
https://www.rfc-editor.org/rfc/rfc2616#section-8.2.3
客户端其实不应该假定服务器一定会响应 100-continue,因为中间可以有若干代理服务不一定能正确完成该响应。
解决方法:
- 服务端修复这个问题。
- SDK 屏蔽 expect Header,不向服务端发送该头部。
由于 S3 SDK 并未暴露该配置,因此无法在SDK端进行屏蔽,属于是底层机制了, 因此目前只能要求服务端进行适配解决。
换 aws-sdk-js-v3
也就是 @aws-sdk/client-s3
能否解决呢?
不行,v3 版本对于所有携带 Body 的请求都添加了 expect: 100-continue
,没有判断 1MB 。
https://github.com/aws/aws-sdk-js-v3/blob/f4c8f09cb89fe34f5deabcdec3936df69a5edb60/packages/middleware-expect-continue/src/index.ts#L14-L31
关于 Nodejs 中对 Header : expect 的处理
https://nodejs.org/api/http.html#http_event_continue
当设置 Expect 请求头时,该请求头会被立即发送。
https://nodejs.org/api/http.html#http_event_continue
当服务器对 Expect 请求头确认时,会触发 continue 事件。