Tìm kiếm trang web

javascript - Tại sao PUT của CURL lại thất bại xác thực trước khi tải payload lên, nhưng PUT của XHR chỉ thất bại sau?


Tôi đang làm việc với một API REST sử dụng xác thực dựa trên token, trong đó một số người dùng có quyền tải lên tệp và một số người thì không.

Vấn đề là khi một số người dùng không có quyền tải lên tệp cố gắng tải lên (ví dụ một tệp 1GB), tôi chỉ nhận được phản hồi lỗi sau khi toàn bộ 1GB đã được tải lên.

Nếu tôi sao chép yêu cầu dưới dạng curl từ công cụ phát triển Chrome và gửi nó qua terminal, nó sẽ thất bại ngay lập tức.

Tôi đã thử lệnh curl với token của người dùng có quyền tải lên, nó hoạt động như mong đợi.

Vậy, curl khác XHR như thế nào?

Curl là đồng bộ, và XHR không đồng bộ theo mặc định. Tôi đã cố gắng làm cho XHR đồng bộ, nhưng nó vẫn phải tải lên toàn bộ tệp trước khi nhận được phản hồi.

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);
}

Đây là lệnh curl chính xác, được định dạng để dễ đọc:

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 

//Các tiêu đề bị loại trừ ngoại trừ mã thông báo

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

--- Cập nhật 21-02-2017 ---

Để loại trừ bất kỳ hành vi cụ thể nào của điểm cuối API, tôi đã viết một script PHP thô để kiểm tra quan sát này và nó vẫn đúng. Dưới đây là script PHP mà tôi đã thử tải lên.

<?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 );
?>

Câu trả lời #1

Khi curl có một yêu cầu PUT cần xác thực, nó sẽ gửi trước một "probe" không có nội dung để cho phép máy chủ cơ hội từ chối kết nối trước khi bất kỳ dữ liệu nào được gửi đi.

Điều này dường như không được ghi chép ở đâu khác ngoài một chú thích trong mã ở Curl_http() trong lib/http.c.

(Lưu ý rằng trong các phiên bản của curl trước 7.53.0 (22‑02‑2017, tức là mới hơn câu hỏi), đã có một lỗi mà trong đó tiêu đề Content‑Length do người dùng cung cấp (hoặc không có tiêu đề) được gửi thay vì Content‑Length: 0.)

XHR không thực hiện bất kỳ phép dò nào như vậy và chỉ đơn giản gửi tất cả nội dung cùng với yêu cầu ban đầu.

Câu trả lời #2

Hãy thử thêm -H "Expect:" vào lệnh curl của bạn.

Tôi có thể sai nhưng đây là linh cảm của tôi:

  • XHR: Expect là một tên header bị cấm
  • curl: mặc định sẽ thêm Expect: 100-continue