Skip to content

Commit

Permalink
Add curl option aliases and all boolean options (#47)
Browse files Browse the repository at this point in the history
* Update list of boolean curl options

* Add all removed boolean options since 1999

* store flags in a Set

* add curl option aliases

* add '-' as a boolean option
  • Loading branch information
verhovsky authored Jul 14, 2021
1 parent 31a063d commit 57203b1
Showing 1 changed file with 173 additions and 33 deletions.
206 changes: 173 additions & 33 deletions resources/js/curl-to-go.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,26 +17,160 @@ function curlToGo(curl) {
// List of curl flags that are boolean typed; this helps with parsing
// a command like `curl -abc value` to know whether 'value' belongs to '-c'
// or is just a positional argument instead.
var boolOptions = ['#', 'progress-bar', '-', 'next', '0', 'http1.0', 'http1.1', 'http2',
'no-npn', 'no-alpn', '1', 'tlsv1', '2', 'sslv2', '3', 'sslv3', '4', 'ipv4', '6', 'ipv6',
'a', 'append', 'anyauth', 'B', 'use-ascii', 'basic', 'compressed', 'create-dirs',
'crlf', 'digest', 'disable-eprt', 'disable-epsv', 'environment', 'cert-status',
'false-start', 'f', 'fail', 'ftp-create-dirs', 'ftp-pasv', 'ftp-skip-pasv-ip',
'ftp-pret', 'ftp-ssl-ccc', 'ftp-ssl-control', 'g', 'globoff', 'G', 'get',
'ignore-content-length', 'i', 'include', 'I', 'head', 'j', 'junk-session-cookies',
'J', 'remote-header-name', 'k', 'insecure', 'l', 'list-only', 'L', 'location',
'location-trusted', 'metalink', 'n', 'netrc', 'N', 'no-buffer', 'netrc-file',
'netrc-optional', 'negotiate', 'no-keepalive', 'no-sessionid', 'ntlm', 'O',
'remote-name', 'oauth2-bearer', 'p', 'proxy-tunnel', 'path-as-is', 'post301', 'post302',
'post303', 'proxy-anyauth', 'proxy-basic', 'proxy-digest', 'proxy-negotiate',
'proxy-ntlm', 'q', 'raw', 'remote-name-all', 's', 'silent', 'sasl-ir', 'S', 'show-error',
'ssl', 'ssl-reqd', 'ssl-allow-beast', 'ssl-no-revoke', 'socks5-gssapi-nec', 'tcp-nodelay',
'tlsv1.0', 'tlsv1.1', 'tlsv1.2', 'tr-encoding', 'trace-time', 'v', 'verbose', 'xattr',
'h', 'help', 'M', 'manual', 'V', 'version'];
// https://github.com/curl/curl/blob/f410b9e538129e77607fef1894f96c684a7c8c3b/src/tool_getparam.c#L73-L341
var boolOptions = new Set([
'disable-epsv', 'no-disable-epsv', 'disallow-username-in-url', 'no-disallow-username-in-url',
'epsv', 'no-epsv', 'npn', 'no-npn', 'alpn', 'no-alpn', 'compressed', 'no-compressed',
'tr-encoding', 'no-tr-encoding', 'digest', 'no-digest', 'negotiate', 'no-negotiate',
'ntlm', 'no-ntlm', 'ntlm-wb', 'no-ntlm-wb', 'basic', 'no-basic', 'anyauth', 'no-anyauth',
'wdebug', 'no-wdebug', 'ftp-create-dirs', 'no-ftp-create-dirs',
'create-dirs', 'no-create-dirs', 'proxy-ntlm', 'no-proxy-ntlm', 'crlf', 'no-crlf',
'haproxy-protocol', 'no-haproxy-protocol', 'disable-eprt', 'no-disable-eprt',
'eprt', 'no-eprt', 'xattr', 'no-xattr', 'ftp-ssl', 'no-ftp-ssl', 'ssl', 'no-ssl',
'ftp-pasv', 'no-ftp-pasv', 'tcp-nodelay', 'no-tcp-nodelay', 'proxy-digest', 'no-proxy-digest',
'proxy-basic', 'no-proxy-basic', 'retry-connrefused', 'no-retry-connrefused',
'proxy-negotiate', 'no-proxy-negotiate', 'proxy-anyauth', 'no-proxy-anyauth',
'trace-time', 'no-trace-time', 'ignore-content-length', 'no-ignore-content-length',
'ftp-skip-pasv-ip', 'no-ftp-skip-pasv-ip', 'ftp-ssl-reqd', 'no-ftp-ssl-reqd',
'ssl-reqd', 'no-ssl-reqd', 'sessionid', 'no-sessionid', 'ftp-ssl-control', 'no-ftp-ssl-control',
'ftp-ssl-ccc', 'no-ftp-ssl-ccc', 'raw', 'no-raw', 'post301', 'no-post301',
'keepalive', 'no-keepalive', 'post302', 'no-post302',
'socks5-gssapi-nec', 'no-socks5-gssapi-nec', 'ftp-pret', 'no-ftp-pret', 'post303', 'no-post303',
'metalink', 'no-metalink', 'sasl-ir', 'no-sasl-ir', 'test-event', 'no-test-event',
'path-as-is', 'no-path-as-is', 'tftp-no-options', 'no-tftp-no-options',
'suppress-connect-headers', 'no-suppress-connect-headers', 'compressed-ssh', 'no-compressed-ssh',
'retry-all-errors', 'no-retry-all-errors',
'http1.0', 'http1.1', 'http2', 'http2-prior-knowledge', 'http3', 'http0.9', 'no-http0.9',
'tlsv1', 'tlsv1.0', 'tlsv1.1', 'tlsv1.2', 'tlsv1.3', 'sslv2', 'sslv3',
'ipv4', 'ipv6',
'append', 'no-append', 'use-ascii', 'no-use-ascii', 'ssl-allow-beast', 'no-ssl-allow-beast',
'ssl-auto-client-cert', 'no-ssl-auto-client-cert',
'proxy-ssl-auto-client-cert', 'no-proxy-ssl-auto-client-cert', 'cert-status', 'no-cert-status',
'doh-cert-status', 'no-doh-cert-status', 'false-start', 'no-false-start',
'ssl-no-revoke', 'no-ssl-no-revoke', 'ssl-revoke-best-effort', 'no-ssl-revoke-best-effort',
'tcp-fastopen', 'no-tcp-fastopen', 'proxy-ssl-allow-beast', 'no-proxy-ssl-allow-beast',
'proxy-insecure', 'no-proxy-insecure', 'proxy-tlsv1', 'socks5-basic', 'no-socks5-basic',
'socks5-gssapi', 'no-socks5-gssapi', 'fail', 'no-fail', 'fail-early', 'no-fail-early',
'styled-output', 'no-styled-output', 'mail-rcpt-allowfails', 'no-mail-rcpt-allowfails',
'fail-with-body', 'no-fail-with-body', 'globoff', 'no-globoff', 'get', 'help', 'no-help',
'include', 'no-include', 'head', 'no-head', 'junk-session-cookies', 'no-junk-session-cookies',
'remote-header-name', 'no-remote-header-name', 'insecure', 'no-insecure',
'doh-insecure', 'no-doh-insecure', 'list-only', 'no-list-only', 'location', 'no-location',
'location-trusted', 'no-location-trusted', 'manual', 'no-manual', 'netrc', 'no-netrc',
'netrc-optional', 'no-netrc-optional', 'buffer', 'no-buffer', 'remote-name',
'remote-name-all', 'no-remote-name-all', 'proxytunnel', 'no-proxytunnel', 'disable', 'no-disable',
'remote-time', 'no-remote-time', 'silent', 'no-silent', 'show-error', 'no-show-error',
'verbose', 'no-verbose', 'version', 'no-version', 'parallel', 'no-parallel',
'parallel-immediate', 'no-parallel-immediate', 'progress-bar', 'no-progress-bar',
'progress-meter', 'no-progress-meter', 'next',
// renamed to --http3 in https://github.com/curl/curl/commit/026840e3
'http3-direct',
// replaced by --request-target in https://github.com/curl/curl/commit/9b167fd0
'strip-path-slash', 'no-strip-path-slash',
// removed in https://github.com/curl/curl/commit/a8e388dd
'environment', 'no-environment',
// curl technically accepted these non-sensical options, they were removed in
// https://github.com/curl/curl/commit/913c3c8f
'no-http1.0', 'no-http1.1', 'no-http2', 'no-http2-prior-knowledge',
'no-tlsv1', 'no-tlsv1.0', 'no-tlsv1.1', 'no-tlsv1.2', 'no-tlsv1.3', 'no-sslv2', 'no-sslv3',
'no-ipv4', 'no-ipv6', 'no-proxy-tlsv1', 'no-get', 'no-remote-name', 'no-next',
// removed in https://github.com/curl/curl/commit/720ea577
'proxy-sslv2', 'no-proxy-sslv2', 'proxy-sslv3', 'no-proxy-sslv3',
// removed in https://github.com/curl/curl/commit/388c6b5e
// I don't think this was ever a real short option
// '~',
// renamed to --http2 in https://github.com/curl/curl/commit/0952c9ab
'http2.0', 'no-http2.0',
// removed in https://github.com/curl/curl/commit/ebf31389
// I don't think this option was ever released, it was renamed the same day
// it was introduced
// 'ssl-no-empty-fragments', 'no-ssl-no-empty-fragments',
// renamed to --ntlm-wb in https://github.com/curl/curl/commit/b4f6319c
'ntlm-sso', 'no-ntlm-sso',
// all options got "--no-" versions in https://github.com/curl/curl/commit/5abfdc01
// renamed to --no-keepalive in https://github.com/curl/curl/commit/f866af91
'no-keep-alive',
// may've been short for --crlf until https://github.com/curl/curl/commit/16643faa
// '9',
// removed in https://github.com/curl/curl/commit/07660eea
// -@ used to be short for --create-dirs
'ftp-ascii', // '@',
// removed in https://github.com/curl/curl/commit/c13dbf7b
// 'c', 'continue',
// removed in https://github.com/curl/curl/commit/a1d6ad26
// -t used to be short for --upload
// 't', 'upload',
// https://github.com/mholt/curl-to-go/pull/47#issuecomment-879485938
'-',
]);

// all of curl's short options have a long form
var optionAliases = {
'0': 'http1.0',
'1': 'tlsv1',
'2': 'sslv2',
'3': 'sslv3',
'4': 'ipv4',
'6': 'ipv6',
'a': 'append',
'A': 'user-agent',
'b': 'cookie',
'B': 'use-ascii',
'c': 'cookie-jar',
'C': 'continue-at',
'd': 'data',
'D': 'dump-header',
'e': 'referer',
'E': 'cert',
'f': 'fail',
'F': 'form',
'g': 'globoff',
'G': 'get',
'h': 'help',
'H': 'header',
'i': 'include',
'I': 'head',
'j': 'junk-session-cookies',
'J': 'remote-header-name',
'k': 'insecure',
'K': 'config',
'l': 'list-only',
'L': 'location',
'm': 'max-time',
'M': 'manual',
'n': 'netrc',
// N is an alias for --no-buffer, not --buffer
'N': 'no-buffer',
'o': 'output',
'O': 'remote-name',
'p': 'proxytunnel',
'P': 'ftp-port',
'q': 'disable',
'Q': 'quote',
'r': 'range',
'R': 'remote-time',
's': 'silent',
'S': 'show-error',
't': 'telnet-option',
'T': 'upload-file',
'u': 'user',
'U': 'proxy-user',
'v': 'verbose',
'V': 'version',
'w': 'write-out',
'x': 'proxy',
'X': 'request',
'Y': 'speed-limit',
'y': 'speed-time',
'z': 'time-cond',
'Z': 'parallel',
'#': 'progress-bar',
':': 'next',
};

if (!curl.trim())
return;
var cmd = parseCommand(curl, { boolFlags: boolOptions });
var cmd = parseCommand(curl, { boolFlags: boolOptions, aliases: optionAliases });

if (cmd._[0] != "curl")
throw "Not a curl command";
Expand Down Expand Up @@ -196,21 +330,16 @@ function curlToGo(curl) {
relevant.url = cmd._[1]; // position 1 because index 0 is the curl command itself

// gather the headers together
if (cmd.H)
relevant.headers = relevant.headers.concat(cmd.H);
if (cmd.header)
relevant.headers = relevant.headers.concat(cmd.header);
relevant.headers = parseHeaders(relevant.headers)

// set method to HEAD?
if (cmd.I || cmd.head)
if (cmd.head)
relevant.method = "HEAD";

// between -X and --request, prefer the long form I guess
if (cmd.request && cmd.request.length > 0)
relevant.method = cmd.request[cmd.request.length-1].toUpperCase();
else if (cmd.X && cmd.X.length > 0)
relevant.method = cmd.X[cmd.X.length-1].toUpperCase(); // if multiple, use last (according to curl docs)
relevant.method = cmd.request[cmd.request.length-1].toUpperCase(); // if multiple, use last (according to curl docs)
else if (
(cmd["data-binary"] && cmd["data-binary"].length > 0)
|| (cmd["data-raw"] && cmd["data-raw"].length > 0)
Expand Down Expand Up @@ -243,8 +372,6 @@ function curlToGo(curl) {
}
}
};
if (cmd.d)
loadData(cmd.d);
if (cmd.data)
loadData(cmd.data);
if (cmd["data-binary"])
Expand All @@ -256,12 +383,9 @@ function curlToGo(curl) {
if (dataFiles.length > 0)
relevant.data.files = dataFiles;

// between -u and --user, choose the long form...
var basicAuthString = "";
if (cmd.user && cmd.user.length > 0)
basicAuthString = cmd.user[cmd.user.length-1];
else if (cmd.u && cmd.u.length > 0)
basicAuthString = cmd.u[cmd.u.length-1];
// if the -u or --user flags haven't been set then don't set the
// basicauth property.
if (basicAuthString) {
Expand All @@ -281,7 +405,7 @@ function curlToGo(curl) {
if (!relevant.method)
relevant.method = "GET";

if (cmd.k || cmd.insecure) {
if (cmd.insecure) {
relevant.insecure = true;
}

Expand Down Expand Up @@ -378,13 +502,13 @@ function parseCommand(input, options) {
cursor++; // skip leading dash
while (cursor < input.length && !whitespace(input[cursor]))
{
var flagName = input[cursor];
var flagName = fullName(input[cursor]);
if (typeof result[flagName] == 'undefined') {
result[flagName] = [];
}
cursor++; // skip the flag name
if (boolFlag(flagName))
result[flagName] = true;
result[flagName] = toBool(flagName);
else if (Array.isArray(result[flagName]))
result[flagName].push(nextString());
}
Expand All @@ -396,7 +520,7 @@ function parseCommand(input, options) {
cursor += 2; // skip leading dashes
var flagName = nextString("=");
if (boolFlag(flagName))
result[flagName] = true;
result[flagName] = toBool(flagName);
else {
if (typeof result[flagName] == 'undefined') {
result[flagName] = [];
Expand All @@ -413,8 +537,17 @@ function parseCommand(input, options) {
result._.push(nextString());
}

// fullName returns the long name of a short flag
function fullName(flag) {
var alias = options.aliases[flag]
return alias ? alias : flag;
}

// boolFlag returns whether a flag is known to be boolean type
function boolFlag(flag) {
if (options.boolFlags instanceof Set) {
return options.boolFlags.has(flag);
}
if (Array.isArray(options.boolFlags)) {
for (var i = 0; i < options.boolFlags.length; i++) {
if (options.boolFlags[i] == flag)
Expand All @@ -424,6 +557,13 @@ function parseCommand(input, options) {
return false;
}

// toBool converts a long flag name to a boolean value.
// --verbose -> true
// --no-verbose -> false
function toBool(flag) {
return !(flag.startsWith('no-') || flag.startsWith('disable-'));
}

// nextString skips any leading whitespace and consumes the next
// space-delimited string value and returns it. If endChar is set,
// it will be used to determine the end of the string. Normally just
Expand Down

0 comments on commit 57203b1

Please sign in to comment.