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

Why does CURL's PUT fail authentication before uploading the payload, but XHR PUT only after?

提问人:Satish Gandham 提问时间:2/20/2017 最后编辑:Satish Gandham 更新时间:2/27/2017 访问量:788

问:

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

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

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

我使用有权上传的用户的令牌测试了 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 );
?>
javascript curl 文件上传 xmlhttprequest

评论

0赞 Pekka 2/20/2017
你能显示你正在使用的 CURL 命令吗?
0赞 Satish Gandham 2/20/2017
使用 CURL 命令更新了问题。
2赞 Pekka 2/22/2017
做。---------
1赞 manishg 2/23/2017
我只是在我这边尝试过这个(使用我的基于 node.js 的服务器),XHR 和 curl 都给出了类似的行为。即使是 curl 也需要时间才能抛出错误。curl 或 XHR 在收到来自服务器的 40 倍错误时都会抛出错误。我不认为 XHR 有什么问题。如果您看到此行为,请检查为什么您的服务器在使用 XHR 时会延迟发送错误。如果您确信您的服务器正在发送错误并且 XHR 报告延迟,请添加一个 tcpdump,以便我们可以在 wireshark 中看到相同的内容。
1赞 Knu 2/26/2017
curl 收到的确切错误代码是什么?

答:

2赞 Knu 2/26/2017 #1

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

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

  • XHR:Expect 是禁止的标头名称
  • curl:默认添加Expect: 100-continue
5赞 Brian Nixon 2/27/2017 #2

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

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

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

XHR 不实现此类探测,只是将所有内容与初始请求一起发送。

评论

1赞 sideshowbarker 2/27/2017
可能值得注意的是,每个 github.com/curl/curl/blob/... curl 也对 POST 请求执行这种零长度的“探测”。
0赞 Satish Gandham 2/28/2017
非常感谢@Brian,在过去的一年里,这一直困扰着我。