/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict" ;
/**
* Tests utility functions contained in `source-utils.js`
*/
const curl = require(
"resource://devtools/client/shared/curl.js");
const Curl = curl.Curl;
const CurlUtils = curl.CurlUtils;
// Test `Curl.generateCommand` headers forwarding/filtering
add_task(async
function () {
const request = {
url:
"https://example.com/form/ ",
method:
"GET" ,
headers: [
{ name:
"Host" , value:
"example.com" },
{
name:
"User-Agent" ,
value:
"Mozilla/5.0 (X11; Linux x86_64; rv:68.0) Gecko/20100101 Firefox/68.0" ,
},
{ name:
"Accept" , value:
"*/*" },
{ name:
"Accept-Language" , value:
"en-US,en;q=0.5" },
{ name:
"Accept-Encoding" , value:
"gzip, deflate, br" },
{ name:
"Origin" , value:
"https://example.com " },
{ name:
"Connection" , value:
"keep-alive" },
{ name:
"Referer" , value:
"https://example.com/home/ " },
{ name:
"Content-Type" , value:
"text/plain" },
],
responseHeaders: [],
httpVersion:
"HTTP/2.0" ,
};
const cmd = Curl.generateCommand(request);
const curlParams = parseCurl(cmd);
ok(
!headerTypeInParams(curlParams,
"Host" ),
"host header ignored - to be generated from url"
);
ok(
exactHeaderInParams(curlParams,
"Accept: */*"),
"accept header present in curl command"
);
ok(
exactHeaderInParams(
curlParams,
"User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:68.0) Gecko/20100101 Firefox/68.0"
),
"user-agent header present in curl command"
);
ok(
exactHeaderInParams(curlParams,
"Accept-Language: en-US,en;q=0.5" ),
"accept-language header present in curl output"
);
ok(
exactHeaderInParams(curlParams,
"Accept-Encoding: gzip, deflate, br" ),
"accept-encoding header present in curl output"
);
ok(
exactHeaderInParams(curlParams,
"Origin: https://example.com "),
"origin header present in curl output"
);
ok(
exactHeaderInParams(curlParams,
"Connection: keep-alive" ),
"connection header present in curl output"
);
ok(
exactHeaderInParams(curlParams,
"Referer: https://example.com/home/ "),
"referer header present in curl output"
);
ok(
exactHeaderInParams(curlParams,
"Content-Type: text/plain" ),
"content-type header present in curl output"
);
ok(!inParams(curlParams,
"--data" ),
"no data param in GET curl output" );
ok(
!inParams(curlParams,
"--data-raw" ),
"no raw data param in GET curl output"
);
});
// Test `Curl.generateCommand` URL glob handling
add_task(async
function () {
let request = {
url:
"https://example.com/ ",
method:
"GET" ,
headers: [],
responseHeaders: [],
httpVersion:
"HTTP/2.0" ,
};
let cmd = Curl.generateCommand(request);
let curlParams = parseCurl(cmd);
ok(
!inParams(curlParams,
"--globoff" ),
"no globoff param in curl output when not needed"
);
request = {
url:
"https://example.com/[] ",
method:
"GET" ,
headers: [],
responseHeaders: [],
httpVersion:
"HTTP/2.0" ,
};
cmd = Curl.generateCommand(request);
curlParams = parseCurl(cmd);
ok(
inParams(curlParams,
"--globoff" ),
"globoff param present in curl output when needed"
);
});
// Test `Curl.generateCommand` data POSTing
add_task(async
function () {
const request = {
url:
"https://example.com/form/ ",
method:
"POST" ,
headers: [
{ name:
"Content-Length" , value:
"1000" },
{ name:
"Content-Type" , value:
"text/plain" },
],
responseHeaders: [],
httpVersion:
"HTTP/2.0" ,
postDataText:
"A piece of plain payload text" ,
};
const cmd = Curl.generateCommand(request);
const curlParams = parseCurl(cmd);
ok(
!headerTypeInParams(curlParams,
"Content-Length" ),
"content-length header ignored - curl generates new one"
);
ok(
exactHeaderInParams(curlParams,
"Content-Type: text/plain" ),
"content-type header present in curl output"
);
ok(
inParams(curlParams,
"--data-raw" ),
'"--data-raw" param present in curl output'
);
ok(
inParams(curlParams, `--data-raw ${quote(request.postDataText)}`),
"proper payload data present in output"
);
});
// Test `Curl.generateCommand` data POSTing - not post data
add_task(async
function () {
const request = {
url:
"https://example.com/form/ ",
method:
"POST" ,
headers: [
{ name:
"Content-Length" , value:
"1000" },
{ name:
"Content-Type" , value:
"text/plain" },
],
responseHeaders: [],
httpVersion:
"HTTP/2.0" ,
};
const cmd = Curl.generateCommand(request);
const curlParams = parseCurl(cmd);
ok(
!inParams(curlParams,
"--data-raw" ),
'"--data-raw" param not present in curl output'
);
const methodIndex = curlParams.indexOf(
"-X" );
ok(
methodIndex !== -1 && curlParams[methodIndex + 1] ===
"POST" ,
"request method explicit is POST"
);
});
// Test `Curl.generateCommand` multipart data POSTing
add_task(async
function () {
const boundary =
"----------14808" ;
const request = {
url:
"https://example.com/form/ ",
method:
"POST" ,
headers: [
{
name:
"Content-Type" ,
value: `multipart/form-data; boundary=${boundary}`,
},
],
responseHeaders: [],
httpVersion:
"HTTP/2.0" ,
postDataText: [
`--${boundary}`,
'Content-Disposition: form-data; name="field_one"' ,
"" ,
"value_one" ,
`--${boundary}`,
'Content-Disposition: form-data; name="field_two"' ,
"" ,
"value two" ,
`--${boundary}--`,
"" ,
].join(
"\r\n" ),
};
const cmd = Curl.generateCommand(request);
// Check content type
const contentTypePos = cmd.indexOf(headerParamPrefix(
"Content-Type" ));
const contentTypeParam = headerParam(
`Content-Type: multipart/form-data; boundary=${boundary}`
);
Assert .notStrictEqual(
contentTypePos,
-1,
"content type header present in curl output"
);
equal(
cmd.substr(contentTypePos, contentTypeParam.length),
contentTypeParam,
"proper content type header present in curl output"
);
// Check binary data
const dataBinaryPos = cmd.indexOf(
"--data-binary" );
const dataBinaryParam = `--data-binary ${isWin() ?
"" :
"$" }${escapeNewline(
quote(request.postDataText)
)}`;
Assert .notStrictEqual(
dataBinaryPos,
-1,
"--data-binary param present in curl output"
);
equal(
cmd.substr(dataBinaryPos, dataBinaryParam.length),
dataBinaryParam,
"proper multipart data present in curl output"
);
});
// Test `CurlUtils.removeBinaryDataFromMultipartText` doesn't change text data
add_task(async
function () {
const boundary =
"----------14808" ;
const postTextLines = [
`--${boundary}`,
'Content-Disposition: form-data; name="field_one"' ,
"" ,
"value_one" ,
`--${boundary}`,
'Content-Disposition: form-data; name="field_two"' ,
"" ,
"value two" ,
`--${boundary}--`,
"" ,
];
const cleanedText = CurlUtils.removeBinaryDataFromMultipartText(
postTextLines.join(
"\r\n" ),
boundary
);
equal(
cleanedText,
postTextLines.join(
"\r\n" ),
"proper non-binary multipart text unchanged"
);
});
// Test `CurlUtils.removeBinaryDataFromMultipartText` removes binary data
add_task(async
function () {
const boundary =
"----------14808" ;
const postTextLines = [
`--${boundary}`,
'Content-Disposition: form-data; name="field_one"' ,
"" ,
"value_one" ,
`--${boundary}`,
'Content-Disposition: form-data; name="field_two"; filename="file_field_two.txt"' ,
"" ,
"file content" ,
`--${boundary}--`,
"" ,
];
const cleanedText = CurlUtils.removeBinaryDataFromMultipartText(
postTextLines.join(
"\r\n" ),
boundary
);
postTextLines.splice(7, 1);
equal(
cleanedText,
postTextLines.join(
"\r\n" ),
"file content removed from multipart text"
);
});
// Test `Curl.generateCommand` add --compressed flag
add_task(async
function () {
let request = {
url:
"https://example.com/ ",
method:
"GET" ,
headers: [],
responseHeaders: [],
httpVersion:
"HTTP/2.0" ,
};
let cmd = Curl.generateCommand(request);
let curlParams = parseCurl(cmd);
ok(
!inParams(curlParams,
"--compressed" ),
"no compressed param in curl output when not needed"
);
request = {
url:
"https://example.com/ ",
method:
"GET" ,
headers: [],
responseHeaders: [{ name:
"Content-Encoding" , value:
"gzip" }],
httpVersion:
"HTTP/2.0" ,
};
cmd = Curl.generateCommand(request);
curlParams = parseCurl(cmd);
ok(
inParams(curlParams,
"--compressed" ),
"compressed param present in curl output when needed"
);
});
function isWin() {
return Services.appinfo.OS ===
"WINNT" ;
}
const QUOTE = isWin() ?
'"' :
"'" ;
// Quote a string, escape the quotes inside the string
function quote(str) {
let escaped;
if (isWin()) {
escaped = str.replace(
new RegExp(QUOTE,
"g" ), `${QUOTE}${QUOTE}`);
}
else {
escaped = str.replace(
new RegExp(QUOTE,
"g" ), `\\${QUOTE}`);
}
return QUOTE + escaped + QUOTE;
}
function escapeNewline(txt) {
if (isWin()) {
// Add `"` to close quote, then escape newline outside of quote, then start new quote
return txt.replace(/[\r\n]{1,2}/g,
'"^$&$&"' );
}
return txt.replace(/\r/g,
"\\r" ).replace(/\n/g,
"\\n" );
}
// Header param is formatted as -H "Header: value" or -H 'Header: value'
function headerParam(h) {
return "-H " + quote(h);
}
// Header param prefix is formatted as `-H "HeaderName` or `-H 'HeaderName`
function headerParamPrefix(headerName) {
return `-H ${QUOTE}${headerName}`;
}
// If any params startswith `-H "HeaderName` or `-H 'HeaderName`
function headerTypeInParams(curlParams, headerName) {
return curlParams.some(param =>
param.toLowerCase().startsWith(headerParamPrefix(headerName).toLowerCase())
);
}
function exactHeaderInParams(curlParams, header) {
return curlParams.some(param => param === headerParam(header));
}
function inParams(curlParams, param) {
return curlParams.some(p => p.startsWith(param));
}
// Parse complete curl command to array of params. Can be applied to simple headers/data,
// but will not on WIN with sophisticated values of --data-binary with e.g. escaped quotes
function parseCurl(curlCmd) {
// This monster regexp parses the command line into an array of arguments,
// recognizing quoted args with matching quotes and escaped quotes inside:
// [ "curl 'url'", "--standalone-arg", "-arg-with-quoted-string 'value\'s'" ]
const matchRe = /[-A-Za-z1-9]+(?: \$?([\
"'])(?:\\\1|.)*?\1)?/g;
return curlCmd.match(matchRe);
}
Messung V0.5 C=93 H=90 G=91
¤ Dauer der Verarbeitung: 0.3 Sekunden
¤
*© Formatika GbR, Deutschland