为什么 CURL 的 PUT 在上传有效负载之前身份验证失败,而 XHR PUT 仅在上传后?



我正在使用具有基于令牌的身份验证的 rest API,其中一些用户有权上传文件,而有些则无权上传。

问题是当某些没有上传文件权限的用户尝试上传(例如 1GB 文件)时,只有在上传整个 1GB 后,我才会收到错误响应。

如果我从chrome开发人员工具中将请求复制为curl并通过终端发送,则会立即失败。

我使用有权上传的用户的令牌测试了 curl 命令,它按预期工作。

那么,卷曲与XHR有何不同?

curl 是同步的,默认情况下 XHR 不是同步的。我尝试使 XHR 同步,但它仍然必须上传整个文件才能得到响应。

function upload(file, url) {
var xhr = new XMLHttpRequest();
xhr.upload.key = xhr.key = file.key
xhr.upload.addEventListener("progress", updateProgress);
xhr.addEventListener("error", transferFailed);
xhr.addEventListener("abort", transferCanceled);
xhr.open("PUT", url);
xhr.setRequestHeader("Content-Type", "application/octet-stream");
xhr.setRequestHeader("X-Auth-Token", service.token.token);

xhr.addEventListener("readystatechange", function (e) {
if (this.readyState === 4 && this.status >= 200 && this.status < 300) {
transferComplete(e)
}
if (this.readyState === 4 && this.status === 0) {
transferFailed(e)
}
if (this.status >= 400) {
transferFailed(e)
}
});

xhr.send(file);
}

以下是确切的 curl 命令,为便于阅读而格式化:

curl 'https://my-website.com/54321/my-filename.jpg' 
-X PUT   
-H 'Pragma: no-cache' 
-H 'Origin: https://my-website.com' 
-H 'Accept-Encoding: gzip, deflate, sdch, br' 
-H 'Accept-Language: en-US,en;q=0.8' 
-H 'User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36' 
-H 'Content-Type: application/octet-stream' 
-H 'Accept: */*' 
-H 'Cache-Control: no-cache' 
-H 'X-Auth-Token: fsadgsdgs' 
-H 'Referer: https://some-allowed-origin-referrer.com/' 
-H 'Connection: keep-alive' 
-H 'Content-Length: 86815' 
-F "data=@/Users/satish/abc.jpg" --compressed --insecure 

除令牌外的标头

curl 'https://my-website.com/54321/my-filename.jpg' 
-X PUT  
-H 'X-Auth-Token: fsadsdsdaf' 
-F "data=@/Users/satish/abc.jpg" 
--compressed --insecure 

---更新 21-02-2017---

为了排除任何特定于 API 端点的行为,我编写了一个粗略的 PHP 脚本来测试这一观察结果,它仍然是正确的。下面是我尝试上传到的 php 脚本。

<?php
/**
* If I comment out the below lines, then curl is failing immediately.
* But XHR doesn't
**/
// http_response_code(400);
// return;
/* PUT data comes in on the stdin stream */
$putdata = fopen( "php://input", "r" );
/* Open a file for writing */
$file = fopen( "/Users/satish/Work/Development/htdocs/put-test/wow.jpg", "w" );
/* Read the data 1 KB at a time
and write to the file */
while ( $data = fread( $putdata, 1024 ) ) {
fwrite( $file, $data );
}
/* Close the streams */
fclose( $file );
fclose( $putdata );
?>

当curl有一个需要身份验证的PUT请求时,它首先发送一个没有内容的"探测器",以允许服务器有机会在发送任何数据之前拒绝连接。

除了lib/http.c中代码Curl_http()注释之外,这似乎没有记录在任何地方。

(请注意,在 7.53.0 之前的 curl 版本中(2017-02-22,即比问题更新),存在一个错误,即用户提供的Content‑Length标头(或无标头)被发送而不是Content‑Length: 0

XHR 不实现此类探测,只是在初始请求中发送所有内容。

尝试将-H "Expect:"添加到curl命令中。

我可能是错的,但这是我的预感:

  • XHR:期望是一个禁止的标头名称
  • curl:默认添加Expect: 100-continue

最新更新