为什么在上传有效载荷之前CURL的PUT验证失败,但XHR PUT仅在之后?

时间:2017-02-20 05:36:45

标签: javascript curl file-upload xmlhttprequest

我正在使用基于令牌的身份验证的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 );
?>

2 个答案:

答案 0 :(得分:5)

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

除了lib/http.cCurl_http()的代码中的评论之外,其他任何地方都没有记录。

(请注意,在7.53.0之前的curl版本中(2017-02-22,比问题更新),有bug用户提供的Content‑Length标题(或没有标题)而不是Content‑Length: 0。)

XHR不实施此类探测,只是使用初始请求发送所有内容。

答案 1 :(得分:2)

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

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

  • XHR:Expect是一个禁止的标题名称
  • curl:默认情况下添加Expect: 100-continue