@ohos-ports/curlconverter 4.12.0-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +153 -0
- package/dist/src/Headers.d.ts +23 -0
- package/dist/src/Headers.js +287 -0
- package/dist/src/Headers.js.map +1 -0
- package/dist/src/Query.d.ts +8 -0
- package/dist/src/Query.js +136 -0
- package/dist/src/Query.js.map +1 -0
- package/dist/src/Request.d.ts +200 -0
- package/dist/src/Request.js +1191 -0
- package/dist/src/Request.js.map +1 -0
- package/dist/src/Warnings.d.ts +15 -0
- package/dist/src/Warnings.js +111 -0
- package/dist/src/Warnings.js.map +1 -0
- package/dist/src/cli.d.ts +2 -0
- package/dist/src/cli.js +333 -0
- package/dist/src/cli.js.map +1 -0
- package/dist/src/curl/auth.d.ts +12 -0
- package/dist/src/curl/auth.js +40 -0
- package/dist/src/curl/auth.js.map +1 -0
- package/dist/src/curl/form.d.ts +28 -0
- package/dist/src/curl/form.js +218 -0
- package/dist/src/curl/form.js.map +1 -0
- package/dist/src/curl/opts.d.ts +2052 -0
- package/dist/src/curl/opts.js +1000 -0
- package/dist/src/curl/opts.js.map +1 -0
- package/dist/src/curl/url.d.ts +14 -0
- package/dist/src/curl/url.js +218 -0
- package/dist/src/curl/url.js.map +1 -0
- package/dist/src/generators/ansible.d.ts +5 -0
- package/dist/src/generators/ansible.js +282 -0
- package/dist/src/generators/ansible.js.map +1 -0
- package/dist/src/generators/c.d.ts +12 -0
- package/dist/src/generators/c.js +1230 -0
- package/dist/src/generators/c.js.map +1 -0
- package/dist/src/generators/cfml.d.ts +5 -0
- package/dist/src/generators/cfml.js +134 -0
- package/dist/src/generators/cfml.js.map +1 -0
- package/dist/src/generators/clojure.d.ts +7 -0
- package/dist/src/generators/clojure.js +436 -0
- package/dist/src/generators/clojure.js.map +1 -0
- package/dist/src/generators/csharp.d.ts +8 -0
- package/dist/src/generators/csharp.js +356 -0
- package/dist/src/generators/csharp.js.map +1 -0
- package/dist/src/generators/dart.d.ts +5 -0
- package/dist/src/generators/dart.js +273 -0
- package/dist/src/generators/dart.js.map +1 -0
- package/dist/src/generators/elixir.d.ts +8 -0
- package/dist/src/generators/elixir.js +296 -0
- package/dist/src/generators/elixir.js.map +1 -0
- package/dist/src/generators/go.d.ts +5 -0
- package/dist/src/generators/go.js +228 -0
- package/dist/src/generators/go.js.map +1 -0
- package/dist/src/generators/har.d.ts +7 -0
- package/dist/src/generators/har.js +115 -0
- package/dist/src/generators/har.js.map +1 -0
- package/dist/src/generators/http.d.ts +7 -0
- package/dist/src/generators/http.js +165 -0
- package/dist/src/generators/http.js.map +1 -0
- package/dist/src/generators/httpie.d.ts +5 -0
- package/dist/src/generators/httpie.js +446 -0
- package/dist/src/generators/httpie.js.map +1 -0
- package/dist/src/generators/java/httpurlconnection.d.ts +5 -0
- package/dist/src/generators/java/httpurlconnection.js +123 -0
- package/dist/src/generators/java/httpurlconnection.js.map +1 -0
- package/dist/src/generators/java/java.d.ts +8 -0
- package/dist/src/generators/java/java.js +247 -0
- package/dist/src/generators/java/java.js.map +1 -0
- package/dist/src/generators/java/jsoup.d.ts +5 -0
- package/dist/src/generators/java/jsoup.js +152 -0
- package/dist/src/generators/java/jsoup.js.map +1 -0
- package/dist/src/generators/java/okhttp.d.ts +5 -0
- package/dist/src/generators/java/okhttp.js +232 -0
- package/dist/src/generators/java/okhttp.js.map +1 -0
- package/dist/src/generators/javascript/axios.d.ts +5 -0
- package/dist/src/generators/javascript/axios.js +332 -0
- package/dist/src/generators/javascript/axios.js.map +1 -0
- package/dist/src/generators/javascript/got.d.ts +5 -0
- package/dist/src/generators/javascript/got.js +277 -0
- package/dist/src/generators/javascript/got.js.map +1 -0
- package/dist/src/generators/javascript/http.d.ts +8 -0
- package/dist/src/generators/javascript/http.js +183 -0
- package/dist/src/generators/javascript/http.js.map +1 -0
- package/dist/src/generators/javascript/javascript.d.ts +37 -0
- package/dist/src/generators/javascript/javascript.js +676 -0
- package/dist/src/generators/javascript/javascript.js.map +1 -0
- package/dist/src/generators/javascript/jquery.d.ts +15 -0
- package/dist/src/generators/javascript/jquery.js +297 -0
- package/dist/src/generators/javascript/jquery.js.map +1 -0
- package/dist/src/generators/javascript/ky.d.ts +7 -0
- package/dist/src/generators/javascript/ky.js +245 -0
- package/dist/src/generators/javascript/ky.js.map +1 -0
- package/dist/src/generators/javascript/request.d.ts +5 -0
- package/dist/src/generators/javascript/request.js +113 -0
- package/dist/src/generators/javascript/request.js.map +1 -0
- package/dist/src/generators/javascript/superagent.d.ts +10 -0
- package/dist/src/generators/javascript/superagent.js +301 -0
- package/dist/src/generators/javascript/superagent.js.map +1 -0
- package/dist/src/generators/javascript/xhr.d.ts +8 -0
- package/dist/src/generators/javascript/xhr.js +147 -0
- package/dist/src/generators/javascript/xhr.js.map +1 -0
- package/dist/src/generators/json.d.ts +45 -0
- package/dist/src/generators/json.js +197 -0
- package/dist/src/generators/json.js.map +1 -0
- package/dist/src/generators/julia.d.ts +8 -0
- package/dist/src/generators/julia.js +365 -0
- package/dist/src/generators/julia.js.map +1 -0
- package/dist/src/generators/kotlin.d.ts +8 -0
- package/dist/src/generators/kotlin.js +325 -0
- package/dist/src/generators/kotlin.js.map +1 -0
- package/dist/src/generators/lua.d.ts +5 -0
- package/dist/src/generators/lua.js +192 -0
- package/dist/src/generators/lua.js.map +1 -0
- package/dist/src/generators/matlab/common.d.ts +16 -0
- package/dist/src/generators/matlab/common.js +238 -0
- package/dist/src/generators/matlab/common.js.map +1 -0
- package/dist/src/generators/matlab/httpinterface.d.ts +2 -0
- package/dist/src/generators/matlab/httpinterface.js +211 -0
- package/dist/src/generators/matlab/httpinterface.js.map +1 -0
- package/dist/src/generators/matlab/matlab.d.ts +5 -0
- package/dist/src/generators/matlab/matlab.js +29 -0
- package/dist/src/generators/matlab/matlab.js.map +1 -0
- package/dist/src/generators/matlab/webservices.d.ts +2 -0
- package/dist/src/generators/matlab/webservices.js +191 -0
- package/dist/src/generators/matlab/webservices.js.map +1 -0
- package/dist/src/generators/objectivec.d.ts +8 -0
- package/dist/src/generators/objectivec.js +269 -0
- package/dist/src/generators/objectivec.js.map +1 -0
- package/dist/src/generators/ocaml.d.ts +8 -0
- package/dist/src/generators/ocaml.js +150 -0
- package/dist/src/generators/ocaml.js.map +1 -0
- package/dist/src/generators/perl.d.ts +9 -0
- package/dist/src/generators/perl.js +209 -0
- package/dist/src/generators/perl.js.map +1 -0
- package/dist/src/generators/php/guzzle.d.ts +5 -0
- package/dist/src/generators/php/guzzle.js +328 -0
- package/dist/src/generators/php/guzzle.js.map +1 -0
- package/dist/src/generators/php/php.d.ts +8 -0
- package/dist/src/generators/php/php.js +217 -0
- package/dist/src/generators/php/php.js.map +1 -0
- package/dist/src/generators/php/requests.d.ts +5 -0
- package/dist/src/generators/php/requests.js +89 -0
- package/dist/src/generators/php/requests.js.map +1 -0
- package/dist/src/generators/powershell.d.ts +11 -0
- package/dist/src/generators/powershell.js +377 -0
- package/dist/src/generators/powershell.js.map +1 -0
- package/dist/src/generators/python/http.d.ts +5 -0
- package/dist/src/generators/python/http.js +128 -0
- package/dist/src/generators/python/http.js.map +1 -0
- package/dist/src/generators/python/python.d.ts +22 -0
- package/dist/src/generators/python/python.js +1693 -0
- package/dist/src/generators/python/python.js.map +1 -0
- package/dist/src/generators/r/httr.d.ts +10 -0
- package/dist/src/generators/r/httr.js +298 -0
- package/dist/src/generators/r/httr.js.map +1 -0
- package/dist/src/generators/r/httr2.d.ts +5 -0
- package/dist/src/generators/r/httr2.js +214 -0
- package/dist/src/generators/r/httr2.js.map +1 -0
- package/dist/src/generators/ruby/httparty.d.ts +6 -0
- package/dist/src/generators/ruby/httparty.js +308 -0
- package/dist/src/generators/ruby/httparty.js.map +1 -0
- package/dist/src/generators/ruby/ruby.d.ts +14 -0
- package/dist/src/generators/ruby/ruby.js +515 -0
- package/dist/src/generators/ruby/ruby.js.map +1 -0
- package/dist/src/generators/rust.d.ts +8 -0
- package/dist/src/generators/rust.js +177 -0
- package/dist/src/generators/rust.js.map +1 -0
- package/dist/src/generators/swift.d.ts +8 -0
- package/dist/src/generators/swift.js +219 -0
- package/dist/src/generators/swift.js.map +1 -0
- package/dist/src/generators/wget.d.ts +8 -0
- package/dist/src/generators/wget.js +472 -0
- package/dist/src/generators/wget.js.map +1 -0
- package/dist/src/index.d.ts +53 -0
- package/dist/src/index.js +54 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/parse.d.ts +13 -0
- package/dist/src/parse.js +43 -0
- package/dist/src/parse.js.map +1 -0
- package/dist/src/shell/Parser.d.ts +4 -0
- package/dist/src/shell/Parser.js +6 -0
- package/dist/src/shell/Parser.js.map +1 -0
- package/dist/src/shell/Word.d.ts +48 -0
- package/dist/src/shell/Word.js +486 -0
- package/dist/src/shell/Word.js.map +1 -0
- package/dist/src/shell/tokenizer.d.ts +3 -0
- package/dist/src/shell/tokenizer.js +610 -0
- package/dist/src/shell/tokenizer.js.map +1 -0
- package/dist/src/shell/webParser.d.ts +4 -0
- package/dist/src/shell/webParser.js +15 -0
- package/dist/src/shell/webParser.js.map +1 -0
- package/dist/src/utils.d.ts +6 -0
- package/dist/src/utils.js +21 -0
- package/dist/src/utils.js.map +1 -0
- package/dist/tools/compare-requests.d.ts +2 -0
- package/dist/tools/compare-requests.js +448 -0
- package/dist/tools/compare-requests.js.map +1 -0
- package/dist/tools/gen-test.d.ts +2 -0
- package/dist/tools/gen-test.js +114 -0
- package/dist/tools/gen-test.js.map +1 -0
- package/dist/tree-sitter-bash.wasm +0 -0
- package/index.d.ts +3 -0
- package/package.json +97 -0
- package/src/Headers.ts +334 -0
- package/src/Query.ts +147 -0
- package/src/Request.ts +1649 -0
- package/src/Warnings.ts +154 -0
- package/src/cli.ts +561 -0
- package/src/curl/auth.ts +45 -0
- package/src/curl/form.ts +297 -0
- package/src/curl/opts.ts +1417 -0
- package/src/curl/url.ts +241 -0
- package/src/generators/ansible.ts +364 -0
- package/src/generators/c.ts +1371 -0
- package/src/generators/cfml.ts +155 -0
- package/src/generators/clojure.ts +521 -0
- package/src/generators/csharp.ts +383 -0
- package/src/generators/dart.ts +293 -0
- package/src/generators/elixir.ts +349 -0
- package/src/generators/go.ts +269 -0
- package/src/generators/har.ts +147 -0
- package/src/generators/http.ts +194 -0
- package/src/generators/httpie.ts +506 -0
- package/src/generators/java/httpurlconnection.ts +151 -0
- package/src/generators/java/java.ts +280 -0
- package/src/generators/java/jsoup.ts +172 -0
- package/src/generators/java/okhttp.ts +257 -0
- package/src/generators/javascript/axios.ts +395 -0
- package/src/generators/javascript/got.ts +362 -0
- package/src/generators/javascript/http.ts +232 -0
- package/src/generators/javascript/javascript.ts +825 -0
- package/src/generators/javascript/jquery.ts +360 -0
- package/src/generators/javascript/ky.ts +315 -0
- package/src/generators/javascript/request.ts +152 -0
- package/src/generators/javascript/superagent.ts +373 -0
- package/src/generators/javascript/xhr.ts +192 -0
- package/src/generators/json.ts +293 -0
- package/src/generators/julia.ts +398 -0
- package/src/generators/kotlin.ts +355 -0
- package/src/generators/lua.ts +209 -0
- package/src/generators/matlab/common.ts +281 -0
- package/src/generators/matlab/httpinterface.ts +284 -0
- package/src/generators/matlab/matlab.ts +39 -0
- package/src/generators/matlab/webservices.ts +263 -0
- package/src/generators/objectivec.ts +297 -0
- package/src/generators/ocaml.ts +168 -0
- package/src/generators/perl.ts +237 -0
- package/src/generators/php/guzzle.ts +368 -0
- package/src/generators/php/php.ts +239 -0
- package/src/generators/php/requests.ts +103 -0
- package/src/generators/powershell.ts +430 -0
- package/src/generators/python/http.ts +167 -0
- package/src/generators/python/python.ts +1934 -0
- package/src/generators/r/httr.ts +328 -0
- package/src/generators/r/httr2.ts +268 -0
- package/src/generators/ruby/httparty.ts +351 -0
- package/src/generators/ruby/ruby.ts +564 -0
- package/src/generators/rust.ts +243 -0
- package/src/generators/swift.ts +235 -0
- package/src/generators/wget.ts +533 -0
- package/src/index.ts +89 -0
- package/src/parse.ts +72 -0
- package/src/shell/Parser.ts +8 -0
- package/src/shell/Word.ts +531 -0
- package/src/shell/tokenizer.ts +736 -0
- package/src/shell/webParser.ts +18 -0
- package/src/utils.ts +25 -0
- package/tools/compare-requests.ts +571 -0
- package/tools/extract_curl_args.py +511 -0
- package/tools/gen-test.ts +129 -0
package/src/curl/url.ts
ADDED
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
import { Word, eq } from "../shell/Word.js";
|
|
2
|
+
import { warnf } from "../Warnings.js";
|
|
3
|
+
import type { GlobalConfig, OperationConfig } from "./opts.js";
|
|
4
|
+
|
|
5
|
+
// https://github.com/curl/curl/blob/curl-7_87_0/lib/urlapi.c#L60
|
|
6
|
+
// https://github.com/curl/curl/blob/curl-7_87_0/lib/urldata.h#L1295
|
|
7
|
+
export interface Curl_URL {
|
|
8
|
+
scheme: Word;
|
|
9
|
+
|
|
10
|
+
auth?: Word;
|
|
11
|
+
user?: Word;
|
|
12
|
+
password?: Word;
|
|
13
|
+
|
|
14
|
+
// options: string /* IMAP only? */;
|
|
15
|
+
host: Word;
|
|
16
|
+
// zoneid: string /* for numerical IPv6 addresses */;
|
|
17
|
+
port: Word;
|
|
18
|
+
path: Word;
|
|
19
|
+
query: Word;
|
|
20
|
+
fragment: Word;
|
|
21
|
+
// portnum: number /* the numerical version */;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// https://github.com/curl/curl/blob/curl-7_88_1/src/tool_urlglob.c#L327
|
|
25
|
+
const MAX_IP6LEN = 128;
|
|
26
|
+
function isIpv6(glob: string): boolean {
|
|
27
|
+
if (glob.length > MAX_IP6LEN) {
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
// TODO: curl tries to parse the glob as a hostname.
|
|
31
|
+
return !glob.includes("-");
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function warnAboutGlobs(global_: GlobalConfig, url: string) {
|
|
35
|
+
// Find any glob expressions in the URL and underline them
|
|
36
|
+
let prev = "";
|
|
37
|
+
for (let i = 0; i < url.length; i++) {
|
|
38
|
+
const cur = url[i];
|
|
39
|
+
if (cur === "[" && prev !== "\\") {
|
|
40
|
+
let j = i + 1;
|
|
41
|
+
while (j < url.length && url[j] !== "]") {
|
|
42
|
+
j++;
|
|
43
|
+
}
|
|
44
|
+
if (j < url.length && url[j] === "]") {
|
|
45
|
+
const glob = url.slice(i, j + 1);
|
|
46
|
+
// could be ipv6 address
|
|
47
|
+
if (!isIpv6(glob)) {
|
|
48
|
+
warnf(global_, [
|
|
49
|
+
"glob-in-url",
|
|
50
|
+
`globs in the URL are not supported:\n` +
|
|
51
|
+
`${url}\n` +
|
|
52
|
+
" ".repeat(i) +
|
|
53
|
+
"^".repeat(glob.length),
|
|
54
|
+
]);
|
|
55
|
+
}
|
|
56
|
+
prev = "";
|
|
57
|
+
} else {
|
|
58
|
+
// No closing bracket
|
|
59
|
+
warnf(global_, [
|
|
60
|
+
"unbalanced-glob",
|
|
61
|
+
"bracket doesn't have a closing bracket:\n" +
|
|
62
|
+
`${url}\n` +
|
|
63
|
+
`${" ".repeat(i)}^`,
|
|
64
|
+
]);
|
|
65
|
+
return; // malformed URL, stop looking for globs
|
|
66
|
+
}
|
|
67
|
+
} else if (cur === "{" && prev !== "\\") {
|
|
68
|
+
let j = i + 1;
|
|
69
|
+
while (j < url.length && url[j] !== "}") {
|
|
70
|
+
j++;
|
|
71
|
+
}
|
|
72
|
+
if (j < url.length && url[j] === "}") {
|
|
73
|
+
const glob = url.slice(i, j + 1);
|
|
74
|
+
warnf(global_, [
|
|
75
|
+
"glob-in-url",
|
|
76
|
+
`globs in the URL are not supported:\n` +
|
|
77
|
+
`${url}\n` +
|
|
78
|
+
" ".repeat(i) +
|
|
79
|
+
"^".repeat(glob.length),
|
|
80
|
+
]);
|
|
81
|
+
prev = "";
|
|
82
|
+
} else {
|
|
83
|
+
// No closing bracket
|
|
84
|
+
warnf(global_, [
|
|
85
|
+
"unbalanced-glob",
|
|
86
|
+
"bracket doesn't have a closing bracket:\n" +
|
|
87
|
+
`${url}\n` +
|
|
88
|
+
`${" ".repeat(i)}^`,
|
|
89
|
+
]);
|
|
90
|
+
return; // malformed URL, stop looking for globs
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
prev = cur;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export function parseurl(
|
|
99
|
+
global_: GlobalConfig,
|
|
100
|
+
config: OperationConfig,
|
|
101
|
+
url: Word,
|
|
102
|
+
): Curl_URL {
|
|
103
|
+
// This is curl's parseurl()
|
|
104
|
+
// https://github.com/curl/curl/blob/curl-7_85_0/lib/urlapi.c#L1144
|
|
105
|
+
// Except we want to accept all URLs.
|
|
106
|
+
// curl further validates URLs in curl_url_get()
|
|
107
|
+
// https://github.com/curl/curl/blob/curl-7_86_0/lib/urlapi.c#L1374
|
|
108
|
+
const u: Curl_URL = {
|
|
109
|
+
scheme: new Word(),
|
|
110
|
+
host: new Word(),
|
|
111
|
+
port: new Word(),
|
|
112
|
+
path: new Word(), // with leading '/'
|
|
113
|
+
query: new Word(), // with leading '?'
|
|
114
|
+
fragment: new Word(), // with leading '#'
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
// Remove url glob escapes
|
|
118
|
+
// https://github.com/curl/curl/blob/curl-7_87_0/src/tool_urlglob.c#L395-L398
|
|
119
|
+
if (!config.globoff) {
|
|
120
|
+
if (url.isString()) {
|
|
121
|
+
warnAboutGlobs(global_, url.toString());
|
|
122
|
+
}
|
|
123
|
+
url = url.replace(/\\([[\]{}])/g, "$1");
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Prepend "http"/"https" if the scheme is missing.
|
|
127
|
+
// RFC 3986 3.1 says
|
|
128
|
+
// scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." )
|
|
129
|
+
// but curl will accept a digit/plus/minus/dot in the first character
|
|
130
|
+
// curl will also accept a url with one / like http:/localhost
|
|
131
|
+
// https://github.com/curl/curl/blob/curl-7_85_0/lib/urlapi.c#L960
|
|
132
|
+
let schemeMatch = null;
|
|
133
|
+
if (url.tokens.length && typeof url.tokens[0] === "string") {
|
|
134
|
+
schemeMatch = url.tokens[0].match(/^([a-zA-Z0-9+-.]*):\/\/*/);
|
|
135
|
+
}
|
|
136
|
+
if (schemeMatch) {
|
|
137
|
+
const [schemeAndSlashes, scheme] = schemeMatch;
|
|
138
|
+
u.scheme = new Word(scheme.toLowerCase());
|
|
139
|
+
url = url.slice(schemeAndSlashes.length);
|
|
140
|
+
} else {
|
|
141
|
+
// curl defaults to https://
|
|
142
|
+
// we don't because most libraries won't downgrade to http
|
|
143
|
+
// if you ask for https, unlike curl.
|
|
144
|
+
// TODO: handle file:// scheme
|
|
145
|
+
u.scheme = config["proto-default"] ?? new Word("http");
|
|
146
|
+
}
|
|
147
|
+
if (!eq(u.scheme, "http") && !eq(u.scheme, "https")) {
|
|
148
|
+
warnf(global_, ["bad-scheme", `Protocol "${u.scheme}" not supported`]);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// https://github.com/curl/curl/blob/curl-7_85_0/lib/urlapi.c#L992
|
|
152
|
+
const hostMatch = url.indexOfFirstChar("/?#");
|
|
153
|
+
if (hostMatch !== -1) {
|
|
154
|
+
u.host = url.slice(0, hostMatch);
|
|
155
|
+
// TODO: u.path might end up empty if indexOfFirstChar found ?#
|
|
156
|
+
u.path = url.slice(hostMatch); // keep leading '/' in .path
|
|
157
|
+
// https://github.com/curl/curl/blob/curl-7_85_0/lib/urlapi.c#L1024
|
|
158
|
+
const fragmentIndex = u.path.indexOf("#");
|
|
159
|
+
const queryIndex = u.path.indexOf("?");
|
|
160
|
+
if (fragmentIndex !== -1) {
|
|
161
|
+
u.fragment = u.path.slice(fragmentIndex);
|
|
162
|
+
if (queryIndex !== -1 && queryIndex < fragmentIndex) {
|
|
163
|
+
u.query = u.path.slice(queryIndex, fragmentIndex);
|
|
164
|
+
u.path = u.path.slice(0, queryIndex);
|
|
165
|
+
} else {
|
|
166
|
+
u.path = u.path.slice(0, fragmentIndex);
|
|
167
|
+
}
|
|
168
|
+
} else if (queryIndex !== -1) {
|
|
169
|
+
u.query = u.path.slice(queryIndex);
|
|
170
|
+
u.path = u.path.slice(0, queryIndex);
|
|
171
|
+
}
|
|
172
|
+
} else {
|
|
173
|
+
u.host = url;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// parse username:password@hostname
|
|
177
|
+
// https://github.com/curl/curl/blob/curl-7_85_0/lib/urlapi.c#L1083
|
|
178
|
+
// https://github.com/curl/curl/blob/curl-7_85_0/lib/urlapi.c#L460
|
|
179
|
+
// https://github.com/curl/curl/blob/curl-7_85_0/lib/url.c#L2827
|
|
180
|
+
const authMatch = u.host.indexOf("@");
|
|
181
|
+
if (authMatch !== -1) {
|
|
182
|
+
const auth = u.host.slice(0, authMatch);
|
|
183
|
+
u.host = u.host.slice(authMatch + 1); // throw away '@'
|
|
184
|
+
if (!config["disallow-username-in-url"]) {
|
|
185
|
+
u.auth = auth;
|
|
186
|
+
if (auth.includes(":")) {
|
|
187
|
+
[u.user, u.password] = auth.split(":", 2);
|
|
188
|
+
} else {
|
|
189
|
+
u.user = auth;
|
|
190
|
+
u.password = new Word(); // if there's no ':', curl will append it
|
|
191
|
+
}
|
|
192
|
+
} else {
|
|
193
|
+
// Curl will exit if this is the case, but we just remove it from the URL
|
|
194
|
+
warnf(global_, [
|
|
195
|
+
"login-denied",
|
|
196
|
+
`Found auth in URL but --disallow-username-in-url was passed: ${auth.toString()}`,
|
|
197
|
+
]);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// TODO: need to extract port first
|
|
202
|
+
// hostname_check()
|
|
203
|
+
// https://github.com/curl/curl/blob/curl-7_86_0/lib/urlapi.c#L572
|
|
204
|
+
// if (!u.host) {
|
|
205
|
+
// warnf(global_, [
|
|
206
|
+
// "no-host",
|
|
207
|
+
// "Found empty host in URL: " + JSON.stringify(url),
|
|
208
|
+
// ]);
|
|
209
|
+
// } else if (u.host.startsWith("[")) {
|
|
210
|
+
// if (!u.host.endsWith("]")) {
|
|
211
|
+
// warnf(global_, [
|
|
212
|
+
// "bad-host",
|
|
213
|
+
// "Found invalid IPv6 address in URL: " + JSON.stringify(url),
|
|
214
|
+
// ]);
|
|
215
|
+
// } else {
|
|
216
|
+
// const firstWeirdCharacter = u.host.match(/[^0123456789abcdefABCDEF:.]/);
|
|
217
|
+
// // %zone_id
|
|
218
|
+
// if (firstWeirdCharacter && firstWeirdCharacter[0] !== "%") {
|
|
219
|
+
// warnf(global_, [
|
|
220
|
+
// "bad-host",
|
|
221
|
+
// "Found invalid IPv6 address in URL: " + JSON.stringify(url),
|
|
222
|
+
// ]);
|
|
223
|
+
// }
|
|
224
|
+
// }
|
|
225
|
+
// } else {
|
|
226
|
+
// const firstInvalidCharacter = u.host.match(
|
|
227
|
+
// /[\r\n\t/:#?!@{}[\]\\$'"^`*<>=;,]/
|
|
228
|
+
// );
|
|
229
|
+
// if (firstInvalidCharacter) {
|
|
230
|
+
// warnf(global_, [
|
|
231
|
+
// "bad-host",
|
|
232
|
+
// "Found invalid character " +
|
|
233
|
+
// JSON.stringify(firstInvalidCharacter[0]) +
|
|
234
|
+
// " in URL: " +
|
|
235
|
+
// JSON.stringify(url),
|
|
236
|
+
// ]);
|
|
237
|
+
// }
|
|
238
|
+
// }
|
|
239
|
+
|
|
240
|
+
return u;
|
|
241
|
+
}
|
|
@@ -0,0 +1,364 @@
|
|
|
1
|
+
import { Word, eq } from "../shell/Word.js";
|
|
2
|
+
import { parse, getFirst, COMMON_SUPPORTED_ARGS } from "../parse.js";
|
|
3
|
+
import type { Request, Warnings } from "../parse.js";
|
|
4
|
+
import { parseQueryString, type QueryList, type QueryDict } from "../Query.js";
|
|
5
|
+
|
|
6
|
+
import yaml from "yamljs";
|
|
7
|
+
|
|
8
|
+
export const supportedArgs = new Set([
|
|
9
|
+
...COMMON_SUPPORTED_ARGS,
|
|
10
|
+
"compressed",
|
|
11
|
+
"no-compressed", // only explicitly disabling compression has an effect
|
|
12
|
+
|
|
13
|
+
"netrc",
|
|
14
|
+
"netrc-optional",
|
|
15
|
+
"no-netrc", // only explicitly disabling netrc has an effect
|
|
16
|
+
"no-netrc-optional",
|
|
17
|
+
|
|
18
|
+
"cacert",
|
|
19
|
+
"cert",
|
|
20
|
+
"key",
|
|
21
|
+
"ciphers",
|
|
22
|
+
"insecure",
|
|
23
|
+
"no-insecure",
|
|
24
|
+
|
|
25
|
+
"anyauth",
|
|
26
|
+
"no-anyauth",
|
|
27
|
+
"digest",
|
|
28
|
+
"no-digest",
|
|
29
|
+
"aws-sigv4",
|
|
30
|
+
"negotiate",
|
|
31
|
+
"no-negotiate",
|
|
32
|
+
// "delegation", // GSS/kerberos
|
|
33
|
+
// Not suported, just more specific warning
|
|
34
|
+
"ntlm",
|
|
35
|
+
"no-ntlm",
|
|
36
|
+
"ntlm-wb",
|
|
37
|
+
"no-ntlm-wb",
|
|
38
|
+
|
|
39
|
+
"unix-socket",
|
|
40
|
+
"abstract-unix-socket",
|
|
41
|
+
|
|
42
|
+
"max-time",
|
|
43
|
+
|
|
44
|
+
"location",
|
|
45
|
+
"no-location",
|
|
46
|
+
"location-trusted",
|
|
47
|
+
"no-location-trusted",
|
|
48
|
+
|
|
49
|
+
"upload-file",
|
|
50
|
+
"output",
|
|
51
|
+
|
|
52
|
+
"form",
|
|
53
|
+
"form-string",
|
|
54
|
+
]);
|
|
55
|
+
|
|
56
|
+
// https://docs.ansible.com/ansible/latest/collections/ansible/builtin/uri_module.html
|
|
57
|
+
type BodyFormat =
|
|
58
|
+
| "form-urlencoded"
|
|
59
|
+
| "json"
|
|
60
|
+
| "raw" // default
|
|
61
|
+
| "form-multipart";
|
|
62
|
+
type AnsibleForm = {
|
|
63
|
+
[key: string]: {
|
|
64
|
+
content?: string;
|
|
65
|
+
filename?: string;
|
|
66
|
+
mime_type?: string;
|
|
67
|
+
};
|
|
68
|
+
};
|
|
69
|
+
type Body = QueryList | QueryDict | object | string | AnsibleForm;
|
|
70
|
+
type AnsibleURI = {
|
|
71
|
+
url: string;
|
|
72
|
+
method?: string;
|
|
73
|
+
headers?: { [key: string]: string };
|
|
74
|
+
body?: Body;
|
|
75
|
+
body_format?: BodyFormat;
|
|
76
|
+
src?: string; // shouldn't have body if has src
|
|
77
|
+
dest?: string;
|
|
78
|
+
|
|
79
|
+
url_username?: string;
|
|
80
|
+
url_password?: string;
|
|
81
|
+
force_basic_auth?: boolean;
|
|
82
|
+
use_gssapi?: boolean;
|
|
83
|
+
use_netrc?: boolean; // true
|
|
84
|
+
|
|
85
|
+
// String won't work, it's in case we can't parse it as a number
|
|
86
|
+
timeout?: number | string; // 30
|
|
87
|
+
|
|
88
|
+
decompress?: boolean;
|
|
89
|
+
follow_redirects?:
|
|
90
|
+
| "all"
|
|
91
|
+
| "no"
|
|
92
|
+
| "none"
|
|
93
|
+
| "safe" // default
|
|
94
|
+
| "urllib2"
|
|
95
|
+
| "yes";
|
|
96
|
+
|
|
97
|
+
ca_path?: string;
|
|
98
|
+
ciphers?: string;
|
|
99
|
+
client_cert?: string;
|
|
100
|
+
client_key?: string;
|
|
101
|
+
validate_certs?: boolean;
|
|
102
|
+
|
|
103
|
+
unix_socket?: string;
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
function getDataString(
|
|
107
|
+
request: Request,
|
|
108
|
+
warnings: Warnings,
|
|
109
|
+
): [Body, BodyFormat] | [string, "src"] | undefined {
|
|
110
|
+
if (!request.data || !request.data.isString()) {
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
const data = request.data.toString();
|
|
114
|
+
|
|
115
|
+
const contentType = request.headers.getContentType();
|
|
116
|
+
// TODO: delete Content-Type header when it's what Ansible will send anyway
|
|
117
|
+
// const exactContentType = request.headers.get("Content-Type");
|
|
118
|
+
if (contentType === "application/json") {
|
|
119
|
+
try {
|
|
120
|
+
const dataAsJson = JSON.parse(data);
|
|
121
|
+
// TODO: if doesn't roundtrip, add commented out raw string
|
|
122
|
+
// TODO: we actually want to know how it's serialized by Ansible
|
|
123
|
+
// const roundtrips = JSON.stringify(dataAsJson) === dataStr;
|
|
124
|
+
return [dataAsJson, "json"];
|
|
125
|
+
} catch {}
|
|
126
|
+
} else if (contentType === "application/x-www-form-urlencoded") {
|
|
127
|
+
if (
|
|
128
|
+
request.dataArray &&
|
|
129
|
+
request.dataArray.length === 1 &&
|
|
130
|
+
!(request.dataArray[0] instanceof Word) &&
|
|
131
|
+
!request.dataArray[0].name
|
|
132
|
+
) {
|
|
133
|
+
const filetype = request.dataArray[0].filetype;
|
|
134
|
+
const filename = request.dataArray[0].filename.toString();
|
|
135
|
+
// TODO: and newlines have to be stripped for others
|
|
136
|
+
if (filetype === "urlencode") {
|
|
137
|
+
// TODO: or the other ones don't need to be?
|
|
138
|
+
warnings.push([
|
|
139
|
+
"urlencoded-file",
|
|
140
|
+
"the file needs to be urlencoded: " + JSON.stringify(filename),
|
|
141
|
+
]);
|
|
142
|
+
}
|
|
143
|
+
return [filename, "src"];
|
|
144
|
+
}
|
|
145
|
+
const [queryList, queryDict] = parseQueryString(request.data);
|
|
146
|
+
if (queryDict) {
|
|
147
|
+
return [
|
|
148
|
+
Object.fromEntries(
|
|
149
|
+
queryDict.map((q) => [
|
|
150
|
+
q[0].toString(),
|
|
151
|
+
Array.isArray(q[1])
|
|
152
|
+
? q[1].map((qv) => qv.toString())
|
|
153
|
+
: q[1].toString(),
|
|
154
|
+
]),
|
|
155
|
+
),
|
|
156
|
+
"form-urlencoded",
|
|
157
|
+
];
|
|
158
|
+
}
|
|
159
|
+
if (queryList) {
|
|
160
|
+
return [
|
|
161
|
+
queryList.map((q) => [q[0].toString(), q[1].toString()]),
|
|
162
|
+
"form-urlencoded",
|
|
163
|
+
];
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
export function _toAnsible(
|
|
170
|
+
requests: Request[],
|
|
171
|
+
warnings: Warnings = [],
|
|
172
|
+
): string {
|
|
173
|
+
// Only supported if there's one file and nothing else
|
|
174
|
+
const request = getFirst(requests, warnings, { dataReadsFile: true });
|
|
175
|
+
if (
|
|
176
|
+
request.dataReadsFile &&
|
|
177
|
+
request.dataArray &&
|
|
178
|
+
(request.dataArray.length > 1 ||
|
|
179
|
+
(!(request.dataArray[0] instanceof Word) && request.dataArray[0].name))
|
|
180
|
+
) {
|
|
181
|
+
warnings.push([
|
|
182
|
+
"unsafe-data",
|
|
183
|
+
"the generated body is wrong, " +
|
|
184
|
+
JSON.stringify("@" + request.dataReadsFile) +
|
|
185
|
+
" means read the file " +
|
|
186
|
+
JSON.stringify(request.dataReadsFile),
|
|
187
|
+
]);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
const r: AnsibleURI = {
|
|
191
|
+
url: request.urls[0].url.toString(),
|
|
192
|
+
method: request.urls[0].method.toString(),
|
|
193
|
+
};
|
|
194
|
+
if (request.multipartUploads) {
|
|
195
|
+
const form: AnsibleForm = {};
|
|
196
|
+
for (const m of request.multipartUploads) {
|
|
197
|
+
// TODO: can't have duplicate keys
|
|
198
|
+
const name = m.name.toString();
|
|
199
|
+
form[name] = {};
|
|
200
|
+
if ("content" in m) {
|
|
201
|
+
form[name].content = m.content.toString();
|
|
202
|
+
} else {
|
|
203
|
+
// TODO: get basename
|
|
204
|
+
form[name].filename = m.contentFile.toString();
|
|
205
|
+
if (m.filename && !eq(m.filename, m.contentFile)) {
|
|
206
|
+
warnings.push([
|
|
207
|
+
"multipart-fake-filename",
|
|
208
|
+
// TODO: check if this is true, the only source is it's not in the example
|
|
209
|
+
// https://docs.ansible.com/ansible/latest/collections/ansible/builtin/uri_module.html#examples
|
|
210
|
+
"Ansible doesn't allow reading from a file and changing the sent filename: " +
|
|
211
|
+
JSON.stringify(m.contentFile.toString()),
|
|
212
|
+
]);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
if ("contentType" in m && m.contentType) {
|
|
216
|
+
form[name].mime_type = m.contentType.toString();
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
r.body = form;
|
|
220
|
+
r.body_format = "form-multipart";
|
|
221
|
+
} else if (request.data) {
|
|
222
|
+
const d = getDataString(request, warnings);
|
|
223
|
+
if (d) {
|
|
224
|
+
const [body, format] = d;
|
|
225
|
+
if (format === "src") {
|
|
226
|
+
r.src = body;
|
|
227
|
+
} else {
|
|
228
|
+
r.body = body;
|
|
229
|
+
if (format !== "raw") {
|
|
230
|
+
r.body_format = format;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
} else {
|
|
234
|
+
r.body = request.data.toString();
|
|
235
|
+
}
|
|
236
|
+
} else if (request.urls[0].uploadFile) {
|
|
237
|
+
r.src = request.urls[0].uploadFile.toString();
|
|
238
|
+
}
|
|
239
|
+
if (request.urls[0].output) {
|
|
240
|
+
r.dest = request.urls[0].output.toString();
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
if (request.headers.length) {
|
|
244
|
+
r.headers = {};
|
|
245
|
+
for (const h of request.headers) {
|
|
246
|
+
const [k, v] = h;
|
|
247
|
+
if (v !== null) {
|
|
248
|
+
r.headers[k.toString()] = v.toString();
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
if (request.urls[0].auth) {
|
|
253
|
+
const [username, password] = request.urls[0].auth;
|
|
254
|
+
if (username.toBool()) {
|
|
255
|
+
r.url_username = username.toString();
|
|
256
|
+
}
|
|
257
|
+
if (password.toBool()) {
|
|
258
|
+
r.url_password = password.toString();
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
// No idea if this stuff is correct
|
|
262
|
+
switch (request.authType) {
|
|
263
|
+
case "basic":
|
|
264
|
+
// TODO: if --basic explicitly passed, set this?
|
|
265
|
+
// r.force_basic_auth = true;
|
|
266
|
+
break;
|
|
267
|
+
case "negotiate":
|
|
268
|
+
r.use_gssapi = true;
|
|
269
|
+
break;
|
|
270
|
+
case "ntlm":
|
|
271
|
+
case "ntlm-wb":
|
|
272
|
+
// https://docs.ansible.com/ansible/latest/collections/ansible/builtin/uri_module.html#parameter-use_gssapi
|
|
273
|
+
warnings.push(["ntlm", "Ansible doesn't support NTLM authentication."]);
|
|
274
|
+
break;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
if (request.cacert) {
|
|
278
|
+
r.ca_path = request.cacert.toString();
|
|
279
|
+
}
|
|
280
|
+
if (request.cert) {
|
|
281
|
+
// TODO: might have password
|
|
282
|
+
const [cert, password] = request.cert;
|
|
283
|
+
r.client_cert = cert.toString();
|
|
284
|
+
if (password) {
|
|
285
|
+
warnings.push([
|
|
286
|
+
"cert-password",
|
|
287
|
+
"Ansible does not support client certificate passwords: " +
|
|
288
|
+
JSON.stringify(password.toString()),
|
|
289
|
+
]);
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
if (request.key) {
|
|
293
|
+
r.client_key = request.key.toString();
|
|
294
|
+
}
|
|
295
|
+
if (request.ciphers) {
|
|
296
|
+
r.ciphers = request.ciphers.toString();
|
|
297
|
+
}
|
|
298
|
+
if (request.insecure) {
|
|
299
|
+
r.validate_certs = false;
|
|
300
|
+
}
|
|
301
|
+
if (request.netrc === "ignored") {
|
|
302
|
+
r.use_netrc = false;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
if (request.compressed === false) {
|
|
306
|
+
r.decompress = false;
|
|
307
|
+
}
|
|
308
|
+
// curl defaults to not following redirects, Ansible defaults to following
|
|
309
|
+
// safe redirects. Don't change that default unless it's explicitly set.
|
|
310
|
+
if (request.followRedirects === false) {
|
|
311
|
+
r.follow_redirects = "none";
|
|
312
|
+
} else if (request.followRedirects === true) {
|
|
313
|
+
// curl will follow redirects to a different host but not send credentials
|
|
314
|
+
// Ansible will only follow redirects for GET or HEAD to the same host
|
|
315
|
+
r.follow_redirects = request.followRedirectsTrusted ? "all" : "safe";
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
if (request.timeout) {
|
|
319
|
+
const timeoutStr = request.timeout.toString();
|
|
320
|
+
const timeoutInt = parseInt(timeoutStr); // TODO: warn if wasn't int
|
|
321
|
+
|
|
322
|
+
if (isNaN(timeoutInt)) {
|
|
323
|
+
r.timeout = timeoutStr;
|
|
324
|
+
warnings.push([
|
|
325
|
+
"ansible-timeout-not-int",
|
|
326
|
+
"couldn't parse timeout as an integer: " + JSON.stringify(timeoutStr),
|
|
327
|
+
]);
|
|
328
|
+
} else {
|
|
329
|
+
r.timeout = timeoutInt;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
warnings.push([
|
|
333
|
+
"ansible-timeout",
|
|
334
|
+
"Ansible's timeout is socket-level but curl's is for the entire request.",
|
|
335
|
+
]);
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
if (request.unixSocket) {
|
|
339
|
+
r.unix_socket = request.unixSocket.toString();
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
return yaml.stringify(
|
|
343
|
+
[
|
|
344
|
+
{
|
|
345
|
+
name: request.urls[0].urlWithoutQueryList.toString(),
|
|
346
|
+
uri: r,
|
|
347
|
+
register: "result",
|
|
348
|
+
},
|
|
349
|
+
],
|
|
350
|
+
100,
|
|
351
|
+
2,
|
|
352
|
+
);
|
|
353
|
+
}
|
|
354
|
+
export function toAnsibleWarn(
|
|
355
|
+
curlCommand: string | string[],
|
|
356
|
+
warnings: Warnings = [],
|
|
357
|
+
): [string, Warnings] {
|
|
358
|
+
const requests = parse(curlCommand, supportedArgs, warnings);
|
|
359
|
+
const ansible = _toAnsible(requests, warnings);
|
|
360
|
+
return [ansible, warnings];
|
|
361
|
+
}
|
|
362
|
+
export function toAnsible(curlCommand: string | string[]): string {
|
|
363
|
+
return toAnsibleWarn(curlCommand)[0];
|
|
364
|
+
}
|