javascript - Varför misslyckas CURL:s PUT med autentisering innan payloaden laddas upp, men XHR PUT först efter?
Jag arbetar med ett REST-API med tokenbaserad autentisering där vissa användare har behörighet att ladda upp en fil och vissa inte har det.
Problemet är när någon användare utan tillstånd att ladda upp en fil försöker ladda upp (säg en 1GB fil), får jag felmeddelandet först efter att hela 1GB har laddats upp.
Om jag kopierar förfrågan som curl från Chrome-utvecklarverktygen och skickar den via terminalen misslyckas den omedelbart.
Jag testade curl-kommandot med token från en användare som har rättigheter att ladda upp, det fungerar som förväntat.
Så, hur skiljer sig curl från XHR?
Curl är synkront, och XHR är inte det som standard. Jag försökte göra XHR synkront, men det måste ändå ladda upp hela filen innan det fick ett svar.
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);
}
Här är det exakta curl-kommandot, formaterat för läsbarhet:
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
//Header tas bort förutom token
curl 'https://my-website.com/54321/my-filename.jpg'
-X PUT
-H 'X-Auth-Token: fsadsdsdaf'
-F "data=@/Users/satish/abc.jpg"
--compressed --insecure
--- Uppdatering 21-02-2017 ---
För att utesluta något API-slutpunktspecifikt beteende skrev jag ett enkelt PHP-skript för att testa denna observation och det är fortfarande sant. Nedan är PHP-skriptet jag försökte ladda upp till.
<?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 );
?>
Svar #1
När curl har en PUT-förfrågan med autentisering som krävs, skickar det först en "probe" utan innehåll för att ge servern möjlighet att neka anslutningen innan några data skickas.
Det verkar inte vara dokumenterat någon annanstans än i en kommentar i koden i Curl_http() i lib/http.c.
(Observera att i versioner av curl före 7.53.0 (2017‑02‑22, dvs. nyare än frågan), fanns en bugg där den användarangivna Content‑Length-huvudet (eller inget huvud) skickades istället för Content‑Length: 0.)
XHR implementerar ingen sådan test och skickar helt enkelt allt innehåll med den första förfrågan.
Svar #2
Försök att lägga till -H "Expect:" till ditt curl-kommando.
Jag kan ha fel, men här är min magkänsla:
- XHR: Expect är ett förbjudet rubriknamn
- curl: lägger till
Expect: 100-continuesom standard