@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.
Files changed (270) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +153 -0
  3. package/dist/src/Headers.d.ts +23 -0
  4. package/dist/src/Headers.js +287 -0
  5. package/dist/src/Headers.js.map +1 -0
  6. package/dist/src/Query.d.ts +8 -0
  7. package/dist/src/Query.js +136 -0
  8. package/dist/src/Query.js.map +1 -0
  9. package/dist/src/Request.d.ts +200 -0
  10. package/dist/src/Request.js +1191 -0
  11. package/dist/src/Request.js.map +1 -0
  12. package/dist/src/Warnings.d.ts +15 -0
  13. package/dist/src/Warnings.js +111 -0
  14. package/dist/src/Warnings.js.map +1 -0
  15. package/dist/src/cli.d.ts +2 -0
  16. package/dist/src/cli.js +333 -0
  17. package/dist/src/cli.js.map +1 -0
  18. package/dist/src/curl/auth.d.ts +12 -0
  19. package/dist/src/curl/auth.js +40 -0
  20. package/dist/src/curl/auth.js.map +1 -0
  21. package/dist/src/curl/form.d.ts +28 -0
  22. package/dist/src/curl/form.js +218 -0
  23. package/dist/src/curl/form.js.map +1 -0
  24. package/dist/src/curl/opts.d.ts +2052 -0
  25. package/dist/src/curl/opts.js +1000 -0
  26. package/dist/src/curl/opts.js.map +1 -0
  27. package/dist/src/curl/url.d.ts +14 -0
  28. package/dist/src/curl/url.js +218 -0
  29. package/dist/src/curl/url.js.map +1 -0
  30. package/dist/src/generators/ansible.d.ts +5 -0
  31. package/dist/src/generators/ansible.js +282 -0
  32. package/dist/src/generators/ansible.js.map +1 -0
  33. package/dist/src/generators/c.d.ts +12 -0
  34. package/dist/src/generators/c.js +1230 -0
  35. package/dist/src/generators/c.js.map +1 -0
  36. package/dist/src/generators/cfml.d.ts +5 -0
  37. package/dist/src/generators/cfml.js +134 -0
  38. package/dist/src/generators/cfml.js.map +1 -0
  39. package/dist/src/generators/clojure.d.ts +7 -0
  40. package/dist/src/generators/clojure.js +436 -0
  41. package/dist/src/generators/clojure.js.map +1 -0
  42. package/dist/src/generators/csharp.d.ts +8 -0
  43. package/dist/src/generators/csharp.js +356 -0
  44. package/dist/src/generators/csharp.js.map +1 -0
  45. package/dist/src/generators/dart.d.ts +5 -0
  46. package/dist/src/generators/dart.js +273 -0
  47. package/dist/src/generators/dart.js.map +1 -0
  48. package/dist/src/generators/elixir.d.ts +8 -0
  49. package/dist/src/generators/elixir.js +296 -0
  50. package/dist/src/generators/elixir.js.map +1 -0
  51. package/dist/src/generators/go.d.ts +5 -0
  52. package/dist/src/generators/go.js +228 -0
  53. package/dist/src/generators/go.js.map +1 -0
  54. package/dist/src/generators/har.d.ts +7 -0
  55. package/dist/src/generators/har.js +115 -0
  56. package/dist/src/generators/har.js.map +1 -0
  57. package/dist/src/generators/http.d.ts +7 -0
  58. package/dist/src/generators/http.js +165 -0
  59. package/dist/src/generators/http.js.map +1 -0
  60. package/dist/src/generators/httpie.d.ts +5 -0
  61. package/dist/src/generators/httpie.js +446 -0
  62. package/dist/src/generators/httpie.js.map +1 -0
  63. package/dist/src/generators/java/httpurlconnection.d.ts +5 -0
  64. package/dist/src/generators/java/httpurlconnection.js +123 -0
  65. package/dist/src/generators/java/httpurlconnection.js.map +1 -0
  66. package/dist/src/generators/java/java.d.ts +8 -0
  67. package/dist/src/generators/java/java.js +247 -0
  68. package/dist/src/generators/java/java.js.map +1 -0
  69. package/dist/src/generators/java/jsoup.d.ts +5 -0
  70. package/dist/src/generators/java/jsoup.js +152 -0
  71. package/dist/src/generators/java/jsoup.js.map +1 -0
  72. package/dist/src/generators/java/okhttp.d.ts +5 -0
  73. package/dist/src/generators/java/okhttp.js +232 -0
  74. package/dist/src/generators/java/okhttp.js.map +1 -0
  75. package/dist/src/generators/javascript/axios.d.ts +5 -0
  76. package/dist/src/generators/javascript/axios.js +332 -0
  77. package/dist/src/generators/javascript/axios.js.map +1 -0
  78. package/dist/src/generators/javascript/got.d.ts +5 -0
  79. package/dist/src/generators/javascript/got.js +277 -0
  80. package/dist/src/generators/javascript/got.js.map +1 -0
  81. package/dist/src/generators/javascript/http.d.ts +8 -0
  82. package/dist/src/generators/javascript/http.js +183 -0
  83. package/dist/src/generators/javascript/http.js.map +1 -0
  84. package/dist/src/generators/javascript/javascript.d.ts +37 -0
  85. package/dist/src/generators/javascript/javascript.js +676 -0
  86. package/dist/src/generators/javascript/javascript.js.map +1 -0
  87. package/dist/src/generators/javascript/jquery.d.ts +15 -0
  88. package/dist/src/generators/javascript/jquery.js +297 -0
  89. package/dist/src/generators/javascript/jquery.js.map +1 -0
  90. package/dist/src/generators/javascript/ky.d.ts +7 -0
  91. package/dist/src/generators/javascript/ky.js +245 -0
  92. package/dist/src/generators/javascript/ky.js.map +1 -0
  93. package/dist/src/generators/javascript/request.d.ts +5 -0
  94. package/dist/src/generators/javascript/request.js +113 -0
  95. package/dist/src/generators/javascript/request.js.map +1 -0
  96. package/dist/src/generators/javascript/superagent.d.ts +10 -0
  97. package/dist/src/generators/javascript/superagent.js +301 -0
  98. package/dist/src/generators/javascript/superagent.js.map +1 -0
  99. package/dist/src/generators/javascript/xhr.d.ts +8 -0
  100. package/dist/src/generators/javascript/xhr.js +147 -0
  101. package/dist/src/generators/javascript/xhr.js.map +1 -0
  102. package/dist/src/generators/json.d.ts +45 -0
  103. package/dist/src/generators/json.js +197 -0
  104. package/dist/src/generators/json.js.map +1 -0
  105. package/dist/src/generators/julia.d.ts +8 -0
  106. package/dist/src/generators/julia.js +365 -0
  107. package/dist/src/generators/julia.js.map +1 -0
  108. package/dist/src/generators/kotlin.d.ts +8 -0
  109. package/dist/src/generators/kotlin.js +325 -0
  110. package/dist/src/generators/kotlin.js.map +1 -0
  111. package/dist/src/generators/lua.d.ts +5 -0
  112. package/dist/src/generators/lua.js +192 -0
  113. package/dist/src/generators/lua.js.map +1 -0
  114. package/dist/src/generators/matlab/common.d.ts +16 -0
  115. package/dist/src/generators/matlab/common.js +238 -0
  116. package/dist/src/generators/matlab/common.js.map +1 -0
  117. package/dist/src/generators/matlab/httpinterface.d.ts +2 -0
  118. package/dist/src/generators/matlab/httpinterface.js +211 -0
  119. package/dist/src/generators/matlab/httpinterface.js.map +1 -0
  120. package/dist/src/generators/matlab/matlab.d.ts +5 -0
  121. package/dist/src/generators/matlab/matlab.js +29 -0
  122. package/dist/src/generators/matlab/matlab.js.map +1 -0
  123. package/dist/src/generators/matlab/webservices.d.ts +2 -0
  124. package/dist/src/generators/matlab/webservices.js +191 -0
  125. package/dist/src/generators/matlab/webservices.js.map +1 -0
  126. package/dist/src/generators/objectivec.d.ts +8 -0
  127. package/dist/src/generators/objectivec.js +269 -0
  128. package/dist/src/generators/objectivec.js.map +1 -0
  129. package/dist/src/generators/ocaml.d.ts +8 -0
  130. package/dist/src/generators/ocaml.js +150 -0
  131. package/dist/src/generators/ocaml.js.map +1 -0
  132. package/dist/src/generators/perl.d.ts +9 -0
  133. package/dist/src/generators/perl.js +209 -0
  134. package/dist/src/generators/perl.js.map +1 -0
  135. package/dist/src/generators/php/guzzle.d.ts +5 -0
  136. package/dist/src/generators/php/guzzle.js +328 -0
  137. package/dist/src/generators/php/guzzle.js.map +1 -0
  138. package/dist/src/generators/php/php.d.ts +8 -0
  139. package/dist/src/generators/php/php.js +217 -0
  140. package/dist/src/generators/php/php.js.map +1 -0
  141. package/dist/src/generators/php/requests.d.ts +5 -0
  142. package/dist/src/generators/php/requests.js +89 -0
  143. package/dist/src/generators/php/requests.js.map +1 -0
  144. package/dist/src/generators/powershell.d.ts +11 -0
  145. package/dist/src/generators/powershell.js +377 -0
  146. package/dist/src/generators/powershell.js.map +1 -0
  147. package/dist/src/generators/python/http.d.ts +5 -0
  148. package/dist/src/generators/python/http.js +128 -0
  149. package/dist/src/generators/python/http.js.map +1 -0
  150. package/dist/src/generators/python/python.d.ts +22 -0
  151. package/dist/src/generators/python/python.js +1693 -0
  152. package/dist/src/generators/python/python.js.map +1 -0
  153. package/dist/src/generators/r/httr.d.ts +10 -0
  154. package/dist/src/generators/r/httr.js +298 -0
  155. package/dist/src/generators/r/httr.js.map +1 -0
  156. package/dist/src/generators/r/httr2.d.ts +5 -0
  157. package/dist/src/generators/r/httr2.js +214 -0
  158. package/dist/src/generators/r/httr2.js.map +1 -0
  159. package/dist/src/generators/ruby/httparty.d.ts +6 -0
  160. package/dist/src/generators/ruby/httparty.js +308 -0
  161. package/dist/src/generators/ruby/httparty.js.map +1 -0
  162. package/dist/src/generators/ruby/ruby.d.ts +14 -0
  163. package/dist/src/generators/ruby/ruby.js +515 -0
  164. package/dist/src/generators/ruby/ruby.js.map +1 -0
  165. package/dist/src/generators/rust.d.ts +8 -0
  166. package/dist/src/generators/rust.js +177 -0
  167. package/dist/src/generators/rust.js.map +1 -0
  168. package/dist/src/generators/swift.d.ts +8 -0
  169. package/dist/src/generators/swift.js +219 -0
  170. package/dist/src/generators/swift.js.map +1 -0
  171. package/dist/src/generators/wget.d.ts +8 -0
  172. package/dist/src/generators/wget.js +472 -0
  173. package/dist/src/generators/wget.js.map +1 -0
  174. package/dist/src/index.d.ts +53 -0
  175. package/dist/src/index.js +54 -0
  176. package/dist/src/index.js.map +1 -0
  177. package/dist/src/parse.d.ts +13 -0
  178. package/dist/src/parse.js +43 -0
  179. package/dist/src/parse.js.map +1 -0
  180. package/dist/src/shell/Parser.d.ts +4 -0
  181. package/dist/src/shell/Parser.js +6 -0
  182. package/dist/src/shell/Parser.js.map +1 -0
  183. package/dist/src/shell/Word.d.ts +48 -0
  184. package/dist/src/shell/Word.js +486 -0
  185. package/dist/src/shell/Word.js.map +1 -0
  186. package/dist/src/shell/tokenizer.d.ts +3 -0
  187. package/dist/src/shell/tokenizer.js +610 -0
  188. package/dist/src/shell/tokenizer.js.map +1 -0
  189. package/dist/src/shell/webParser.d.ts +4 -0
  190. package/dist/src/shell/webParser.js +15 -0
  191. package/dist/src/shell/webParser.js.map +1 -0
  192. package/dist/src/utils.d.ts +6 -0
  193. package/dist/src/utils.js +21 -0
  194. package/dist/src/utils.js.map +1 -0
  195. package/dist/tools/compare-requests.d.ts +2 -0
  196. package/dist/tools/compare-requests.js +448 -0
  197. package/dist/tools/compare-requests.js.map +1 -0
  198. package/dist/tools/gen-test.d.ts +2 -0
  199. package/dist/tools/gen-test.js +114 -0
  200. package/dist/tools/gen-test.js.map +1 -0
  201. package/dist/tree-sitter-bash.wasm +0 -0
  202. package/index.d.ts +3 -0
  203. package/package.json +97 -0
  204. package/src/Headers.ts +334 -0
  205. package/src/Query.ts +147 -0
  206. package/src/Request.ts +1649 -0
  207. package/src/Warnings.ts +154 -0
  208. package/src/cli.ts +561 -0
  209. package/src/curl/auth.ts +45 -0
  210. package/src/curl/form.ts +297 -0
  211. package/src/curl/opts.ts +1417 -0
  212. package/src/curl/url.ts +241 -0
  213. package/src/generators/ansible.ts +364 -0
  214. package/src/generators/c.ts +1371 -0
  215. package/src/generators/cfml.ts +155 -0
  216. package/src/generators/clojure.ts +521 -0
  217. package/src/generators/csharp.ts +383 -0
  218. package/src/generators/dart.ts +293 -0
  219. package/src/generators/elixir.ts +349 -0
  220. package/src/generators/go.ts +269 -0
  221. package/src/generators/har.ts +147 -0
  222. package/src/generators/http.ts +194 -0
  223. package/src/generators/httpie.ts +506 -0
  224. package/src/generators/java/httpurlconnection.ts +151 -0
  225. package/src/generators/java/java.ts +280 -0
  226. package/src/generators/java/jsoup.ts +172 -0
  227. package/src/generators/java/okhttp.ts +257 -0
  228. package/src/generators/javascript/axios.ts +395 -0
  229. package/src/generators/javascript/got.ts +362 -0
  230. package/src/generators/javascript/http.ts +232 -0
  231. package/src/generators/javascript/javascript.ts +825 -0
  232. package/src/generators/javascript/jquery.ts +360 -0
  233. package/src/generators/javascript/ky.ts +315 -0
  234. package/src/generators/javascript/request.ts +152 -0
  235. package/src/generators/javascript/superagent.ts +373 -0
  236. package/src/generators/javascript/xhr.ts +192 -0
  237. package/src/generators/json.ts +293 -0
  238. package/src/generators/julia.ts +398 -0
  239. package/src/generators/kotlin.ts +355 -0
  240. package/src/generators/lua.ts +209 -0
  241. package/src/generators/matlab/common.ts +281 -0
  242. package/src/generators/matlab/httpinterface.ts +284 -0
  243. package/src/generators/matlab/matlab.ts +39 -0
  244. package/src/generators/matlab/webservices.ts +263 -0
  245. package/src/generators/objectivec.ts +297 -0
  246. package/src/generators/ocaml.ts +168 -0
  247. package/src/generators/perl.ts +237 -0
  248. package/src/generators/php/guzzle.ts +368 -0
  249. package/src/generators/php/php.ts +239 -0
  250. package/src/generators/php/requests.ts +103 -0
  251. package/src/generators/powershell.ts +430 -0
  252. package/src/generators/python/http.ts +167 -0
  253. package/src/generators/python/python.ts +1934 -0
  254. package/src/generators/r/httr.ts +328 -0
  255. package/src/generators/r/httr2.ts +268 -0
  256. package/src/generators/ruby/httparty.ts +351 -0
  257. package/src/generators/ruby/ruby.ts +564 -0
  258. package/src/generators/rust.ts +243 -0
  259. package/src/generators/swift.ts +235 -0
  260. package/src/generators/wget.ts +533 -0
  261. package/src/index.ts +89 -0
  262. package/src/parse.ts +72 -0
  263. package/src/shell/Parser.ts +8 -0
  264. package/src/shell/Word.ts +531 -0
  265. package/src/shell/tokenizer.ts +736 -0
  266. package/src/shell/webParser.ts +18 -0
  267. package/src/utils.ts +25 -0
  268. package/tools/compare-requests.ts +571 -0
  269. package/tools/extract_curl_args.py +511 -0
  270. package/tools/gen-test.ts +129 -0
package/src/Request.ts ADDED
@@ -0,0 +1,1649 @@
1
+ import { Word, eq, mergeWords, joinWords } from "./shell/Word.js";
2
+
3
+ import { CCError, has, isInt } from "./utils.js";
4
+ import { warnf, warnIfPartsIgnored } from "./Warnings.js";
5
+ import type { Warnings, Support } from "./Warnings.js";
6
+ import type {
7
+ GlobalConfig,
8
+ OperationConfig,
9
+ SrcDataParam,
10
+ } from "./curl/opts.js";
11
+
12
+ import { Headers, parseCookies, parseCookiesStrict } from "./Headers.js";
13
+ import type { Cookies } from "./Headers.js";
14
+
15
+ import { pickAuth, type AuthType } from "./curl/auth.js";
16
+ export { AuthType } from "./curl/auth.js";
17
+
18
+ import { parseurl, type Curl_URL } from "./curl/url.js";
19
+
20
+ import { parseQueryString, percentEncodePlus } from "./Query.js";
21
+ import type { QueryList, QueryDict } from "./Query.js";
22
+
23
+ import { parseForm } from "./curl/form.js";
24
+ import type { FormParam } from "./curl/form.js";
25
+
26
+ export type FileParamType = "data" | "binary" | "urlencode" | "json";
27
+ export type DataType = FileParamType | "raw";
28
+
29
+ export type FileDataParam = {
30
+ filetype: FileParamType;
31
+ // The left side of "=" for --data-urlencode, can't be empty string
32
+ name?: Word;
33
+ filename: Word;
34
+ };
35
+ // "raw"-type SrcDataParams, and `FileParamType`s that read from stdin
36
+ // when we have its contents (because it comes from a pipe) are converted
37
+ // to plain strings
38
+ export type DataParam = Word | FileDataParam;
39
+
40
+ // struct getout
41
+ // https://github.com/curl/curl/blob/curl-7_86_0/src/tool_sdecls.h#L96
42
+ export interface RequestUrl {
43
+ // What it looked like in the input, used for error messages
44
+ originalUrl: Word;
45
+
46
+ url: Word;
47
+ // the query string can contain instructions to
48
+ // read the query string from a file, for example with
49
+ // --url-query @filename
50
+ // In that case we put "@filename" in the query string and "filename" here and
51
+ // warn the user that they'll need to modify the code to read that file.
52
+ queryReadsFile?: string;
53
+
54
+ urlObj: Curl_URL;
55
+
56
+ // If the ?query can't be losslessly parsed, then
57
+ // .urlWithoutQueryList === .url
58
+ // .queryList === undefined
59
+ urlWithoutQueryList: Word;
60
+ queryList?: QueryList;
61
+ // When all (if any) repeated keys in queryList happen one after the other
62
+ // ?a=1&a=1&b=2 (okay)
63
+ // ?a=1&b=2&a=1 (doesn't work, queryList is defined but queryDict isn't)
64
+ queryDict?: QueryDict;
65
+
66
+ urlWithoutQueryArray: Word;
67
+ urlWithOriginalQuery: Word;
68
+ // This includes the query in the URL and the query that comes from `--get --data` or `--url-query`
69
+ queryArray?: DataParam[];
70
+ // This is only the query in the URL
71
+ urlQueryArray?: DataParam[];
72
+
73
+ uploadFile?: Word;
74
+ output?: Word;
75
+
76
+ method: Word;
77
+ auth?: [Word, Word];
78
+ // TODO: should authType be per-url as well?
79
+ // authType?: string;
80
+ }
81
+
82
+ export type ProxyType =
83
+ | "http1"
84
+ | "http2"
85
+ | "socks4"
86
+ | "socks4a"
87
+ | "socks5"
88
+ | "socks5-hostname";
89
+
90
+ export interface Request {
91
+ // Will have at least one element (otherwise an error is raised)
92
+ urls: RequestUrl[];
93
+ globoff?: boolean;
94
+ disallowUsernameInUrl?: boolean;
95
+ pathAsIs?: boolean;
96
+
97
+ // Just the part that comes from `--get --data` or `--url-query` (not the query in the URL)
98
+ // unless there's only one URL, then it will include both.
99
+ queryArray?: DataParam[];
100
+
101
+ authType: AuthType;
102
+ proxyAuthType: AuthType;
103
+ awsSigV4?: Word;
104
+ oauth2Bearer?: Word;
105
+ delegation?: Word;
106
+ krb?: Word;
107
+ saslAuthzid?: Word;
108
+ saslIr?: boolean;
109
+ serviceName?: Word;
110
+
111
+ // A null header means the command explicitly disabled sending this header
112
+ headers: Headers;
113
+ proxyHeaders: Headers;
114
+ refererAuto?: boolean;
115
+
116
+ // .cookies is a parsed version of the Cookie header, if it can be parsed.
117
+ // Generators that use .cookies need to delete the header from .headers (usually).
118
+ cookies?: Cookies;
119
+ cookieFiles?: Word[];
120
+ cookieJar?: Word;
121
+ junkSessionCookies?: boolean;
122
+
123
+ compressed?: boolean;
124
+ transferEncoding?: boolean;
125
+
126
+ include?: boolean;
127
+
128
+ multipartUploads?: FormParam[];
129
+ // When multipartUploads comes from parsing a string in --data
130
+ // this can be set to true to say that sending the original data
131
+ // as a string would be more correct.
132
+ multipartUploadsDoesntRoundtrip?: boolean;
133
+ formEscape?: boolean;
134
+
135
+ dataArray?: DataParam[];
136
+ data?: Word;
137
+ dataReadsFile?: string;
138
+ isDataBinary?: boolean;
139
+ isDataRaw?: boolean;
140
+
141
+ ipv4?: boolean;
142
+ ipv6?: boolean;
143
+
144
+ proto?: Word;
145
+ protoRedir?: Word;
146
+ protoDefault?: Word;
147
+
148
+ tcpFastopen?: boolean;
149
+
150
+ localPort?: [Word, Word | null];
151
+
152
+ ignoreContentLength?: boolean;
153
+
154
+ interface?: Word;
155
+
156
+ ciphers?: Word;
157
+ curves?: Word;
158
+ insecure?: boolean;
159
+ certStatus?: boolean;
160
+ cert?: [Word, Word | null];
161
+ certType?: Word;
162
+ key?: Word;
163
+ keyType?: Word;
164
+ pass?: Word;
165
+ cacert?: Word;
166
+ caNative?: boolean;
167
+ sslAllowBeast?: boolean;
168
+ capath?: Word;
169
+ crlfile?: Word;
170
+ pinnedpubkey?: Word;
171
+ randomFile?: Word;
172
+ egdFile?: Word;
173
+ hsts?: Word[]; // a filename
174
+ alpn?: boolean;
175
+
176
+ tlsVersion?: "1" | "1.0" | "1.1" | "1.2" | "1.3";
177
+ tlsMax?: Word;
178
+ tls13Ciphers?: Word;
179
+ tlsauthtype?: Word;
180
+ tlspassword?: Word;
181
+ tlsuser?: Word;
182
+ sslAutoClientCert?: boolean;
183
+ sslNoRevoke?: boolean;
184
+ sslReqd?: boolean;
185
+ sslRevokeBestEffort?: boolean;
186
+ ssl?: boolean;
187
+ sslv2?: boolean;
188
+ sslv3?: boolean;
189
+
190
+ dohUrl?: Word;
191
+ dohInsecure?: boolean;
192
+ dohCertStatus?: boolean;
193
+
194
+ proxy?: Word;
195
+ proxyType?: ProxyType;
196
+ proxyAuth?: Word;
197
+ proxytunnel?: boolean;
198
+ noproxy?: Word; // a list of hosts or "*"
199
+ preproxy?: Word;
200
+ proxyAnyauth?: boolean;
201
+ proxyBasic?: boolean;
202
+ proxyCaNative?: boolean;
203
+ proxyCacert?: Word; // <file>
204
+ proxyCapath?: Word; // <dir>
205
+ proxyCertType?: Word; // <type>
206
+ proxyCert?: Word; // <cert[:passwd]>
207
+ proxyCiphers?: Word; // <list>
208
+ proxyCrlfile?: Word; // <file>
209
+ proxyDigest?: boolean;
210
+ proxyHttp2?: boolean;
211
+ proxyInsecure?: boolean;
212
+ proxyKeyType?: Word; // <type>
213
+ proxyKey?: Word; // <key>
214
+ proxyNegotiate?: boolean;
215
+ proxyNtlm?: boolean;
216
+ proxyPass?: Word; // <phrase>
217
+ proxyPinnedpubkey?: Word; // <hashes>
218
+ proxyServiceName?: Word; // <name>
219
+ proxySslAllowBeast?: boolean;
220
+ proxySslAutoClientCert?: boolean;
221
+ proxyTls13Ciphers?: Word; // <ciphersuite list>
222
+ proxyTlsauthtype?: Word; // <type>
223
+ proxyTlspassword?: Word; // <string>
224
+ proxyTlsuser?: Word; // <name>
225
+ proxyTlsv1?: boolean;
226
+ proxyUser?: Word; // <user:password>
227
+ proxy1?: boolean; // <host[:port]>
228
+
229
+ socks4?: Word;
230
+ socks4a?: Word;
231
+ socks5?: Word;
232
+ socks5Basic?: boolean;
233
+ socks5GssapiNec?: boolean;
234
+ socks5GssapiService?: Word;
235
+ socks5Gssapi?: boolean;
236
+ socks5Hostname?: Word;
237
+
238
+ haproxyClientIp?: Word;
239
+ haproxyProtocol?: boolean;
240
+
241
+ timeout?: Word; // a decimal, seconds
242
+ connectTimeout?: Word; // a decimal, seconds
243
+ expect100Timeout?: Word; // a decimal, seconds
244
+ happyEyeballsTimeoutMs?: Word; // an integer, milliseconds
245
+ speedLimit?: Word; // an integer
246
+ speedTime?: Word; // an integer
247
+ limitRate?: Word; // an integer with an optional unit
248
+ maxFilesize?: Word; // an intger with an optional unit
249
+
250
+ continueAt?: Word; // an integer or "-"
251
+
252
+ crlf?: boolean;
253
+ useAscii?: boolean;
254
+
255
+ remoteTime?: boolean;
256
+
257
+ clobber?: boolean;
258
+
259
+ ftpSkipPasvIp?: boolean;
260
+
261
+ fail?: boolean;
262
+ retry?: Word; // an integer, how many times to retry
263
+ retryMaxTime?: Word; // an integer, seconds
264
+
265
+ keepAlive?: boolean;
266
+ keepAliveTime?: Word; // an integer, seconds
267
+
268
+ altSvc?: Word;
269
+
270
+ followRedirects?: boolean;
271
+ followRedirectsTrusted?: boolean;
272
+ maxRedirects?: Word; // an integer
273
+ post301?: boolean;
274
+ post302?: boolean;
275
+ post303?: boolean;
276
+
277
+ httpVersion?: "1.0" | "1.1" | "2" | "2-prior-knowledge" | "3" | "3-only";
278
+ http0_9?: boolean;
279
+ http2?: boolean;
280
+ http3?: boolean;
281
+
282
+ stdin?: Word;
283
+ stdinFile?: Word;
284
+
285
+ resolve?: Word[]; // a list of host:port:address
286
+ connectTo?: Word[]; // a list of host:port:connect-to-host:connect-to-port
287
+
288
+ unixSocket?: Word;
289
+ abstractUnixSocket?: Word;
290
+ netrc?: "optional" | "required" | "ignored"; // undefined means implicitly "ignored"
291
+ netrcFile?: Word; // if undefined defaults to ~/.netrc
292
+
293
+ // Global options
294
+ verbose?: boolean;
295
+ silent?: boolean;
296
+ }
297
+
298
+ function buildURL(
299
+ global_: GlobalConfig,
300
+ config: OperationConfig,
301
+ url: Word,
302
+ uploadFile?: Word,
303
+ outputFile?: Word,
304
+ stdin?: Word,
305
+ stdinFile?: Word,
306
+ ): RequestUrl {
307
+ const originalUrl = url;
308
+ const u = parseurl(global_, config, url);
309
+
310
+ // https://github.com/curl/curl/blob/curl-7_85_0/src/tool_operate.c#L1124
311
+ // https://github.com/curl/curl/blob/curl-7_85_0/src/tool_operhlp.c#L76
312
+ if (uploadFile) {
313
+ // TODO: it's more complicated
314
+ if (u.path.isEmpty()) {
315
+ u.path = uploadFile.prepend("/");
316
+ } else if (u.path.endsWith("/")) {
317
+ u.path = u.path.add(uploadFile);
318
+ }
319
+
320
+ if (config.get) {
321
+ warnf(global_, [
322
+ "data-ignored",
323
+ "curl doesn't let you pass --get and --upload-file together",
324
+ ]);
325
+ }
326
+ }
327
+
328
+ const urlWithOriginalQuery = mergeWords(
329
+ u.scheme,
330
+ "://",
331
+ u.host,
332
+ u.path,
333
+ u.query,
334
+ u.fragment,
335
+ );
336
+
337
+ // curl example.com example.com?foo=bar --url-query isshared=t
338
+ // will make requests for
339
+ // example.com/?isshared=t
340
+ // example.com/?foo=bar&isshared=t
341
+ //
342
+ // so the query could come from
343
+ // 1. `--url` (i.e. the requested URL)
344
+ // 2. `--url-query` or `--get --data` (the latter takes precedence)
345
+ //
346
+ // If it comes from the latter, we might need to generate code to read
347
+ // from one or more files.
348
+ // When there's multiple urls, the latter applies to all of them
349
+ // but the query from --url only applies to that URL.
350
+ //
351
+ // There's 3 cases for the query:
352
+ // 1. it's well-formed and can be expressed as a list of tuples (or a dict)
353
+ // `?one=1&one=1&two=2`
354
+ // 2. it can't, for example because one of the pieces doesn't have a '='
355
+ // `?one`
356
+ // 3. we need to generate code that reads from a file
357
+ //
358
+ // If there's only one URL we merge the query from the URL with the shared part.
359
+ //
360
+ // If there's multiple URLs and a shared part that reads from a file (case 3),
361
+ // we only write the file reading code once, pass it as the params= argument
362
+ // and the part from the URL has to be passed as a string in the URL
363
+ // and requests will combine the query in the URL with the query in params=.
364
+ //
365
+ // Otherwise, we print each query for each URL individually, either as a
366
+ // list of tuples if we can or in the URL if we can't.
367
+ //
368
+ // When files are passed in through --data-urlencode or --url-query
369
+ // we can usually treat them as case 1 as well (in Python), but that would
370
+ // generate code slightly different from curl because curl reads the file once
371
+ // upfront, whereas we would read the file multiple times and it might contain
372
+ // different data each time (for example if it's /dev/urandom).
373
+ let urlQueryArray: DataParam[] | null = null;
374
+ let queryArray: DataParam[] | null = null;
375
+ let queryStrReadsFile: string | null = null;
376
+ if (u.query.toBool() || (config["url-query"] && config["url-query"].length)) {
377
+ let queryStr: Word | null = null;
378
+
379
+ let queryParts: SrcDataParam[] = [];
380
+ if (u.query.toBool()) {
381
+ // remove the leading '?'
382
+ queryParts.push(["raw", u.query.slice(1)]);
383
+ [queryArray, queryStr, queryStrReadsFile] = buildData(
384
+ queryParts,
385
+ stdin,
386
+ stdinFile,
387
+ );
388
+ urlQueryArray = queryArray;
389
+ }
390
+ if (config["url-query"]) {
391
+ queryParts = queryParts.concat(config["url-query"]);
392
+ [queryArray, queryStr, queryStrReadsFile] = buildData(
393
+ queryParts,
394
+ stdin,
395
+ stdinFile,
396
+ );
397
+ }
398
+
399
+ // TODO: check the curl source code
400
+ // TODO: curl localhost:8888/?
401
+ // will request /?
402
+ // but
403
+ // curl localhost:8888/? --url-query ''
404
+ // (or --get --data '') will request /
405
+ u.query = new Word();
406
+ if (queryStr && queryStr.toBool()) {
407
+ u.query = queryStr.prepend("?");
408
+ }
409
+ }
410
+ const urlWithoutQueryArray = mergeWords(
411
+ u.scheme,
412
+ "://",
413
+ u.host,
414
+ u.path,
415
+ u.fragment,
416
+ );
417
+ url = mergeWords(u.scheme, "://", u.host, u.path, u.query, u.fragment);
418
+ let urlWithoutQueryList = url;
419
+ // TODO: parseQueryString() doesn't accept leading '?'
420
+ let [queryList, queryDict] = parseQueryString(
421
+ u.query.toBool() ? u.query.slice(1) : new Word(),
422
+ );
423
+ if (queryList && queryList.length) {
424
+ // TODO: remove the fragment too?
425
+ urlWithoutQueryList = mergeWords(
426
+ u.scheme,
427
+ "://",
428
+ u.host,
429
+ u.path,
430
+ u.fragment,
431
+ );
432
+ } else {
433
+ queryList = null;
434
+ queryDict = null;
435
+ }
436
+
437
+ // TODO: --path-as-is
438
+ // TODO: --request-target
439
+
440
+ // curl expects you to uppercase methods always. If you do -X PoSt, that's what it
441
+ // will send, but most APIs will helpfully uppercase what you pass in as the method.
442
+ //
443
+ // There are many places where curl determines the method, this is the last one:
444
+ // https://github.com/curl/curl/blob/curl-7_85_0/lib/http.c#L2032
445
+ let method = new Word("GET");
446
+ if (
447
+ config.request &&
448
+ // Safari adds `-X null` if it can't determine the request type
449
+ // https://github.com/WebKit/WebKit/blob/f58ef38d48f42f5d7723691cb090823908ff5f9f/Source/WebInspectorUI/UserInterface/Models/Resource.js#L1250
450
+ !eq(config.request, "null")
451
+ ) {
452
+ method = config.request;
453
+ } else if (config.head) {
454
+ method = new Word("HEAD");
455
+ } else if (uploadFile && uploadFile.toBool()) {
456
+ // --upload-file '' doesn't do anything.
457
+ method = new Word("PUT");
458
+ } else if (!config.get && (has(config, "data") || has(config, "form"))) {
459
+ method = new Word("POST");
460
+ }
461
+
462
+ const requestUrl: RequestUrl = {
463
+ originalUrl,
464
+ urlWithoutQueryList,
465
+ url,
466
+ urlObj: u,
467
+ urlWithOriginalQuery,
468
+ urlWithoutQueryArray,
469
+ method,
470
+ };
471
+ if (queryStrReadsFile) {
472
+ requestUrl.queryReadsFile = queryStrReadsFile;
473
+ }
474
+ if (queryList) {
475
+ requestUrl.queryList = queryList;
476
+ if (queryDict) {
477
+ requestUrl.queryDict = queryDict;
478
+ }
479
+ }
480
+ if (queryArray) {
481
+ requestUrl.queryArray = queryArray;
482
+ }
483
+ if (urlQueryArray) {
484
+ requestUrl.urlQueryArray = urlQueryArray;
485
+ }
486
+ if (uploadFile) {
487
+ if (eq(uploadFile, "-") || eq(uploadFile, ".")) {
488
+ if (stdinFile) {
489
+ requestUrl.uploadFile = stdinFile;
490
+ } else if (stdin) {
491
+ warnf(global_, [
492
+ "upload-file-with-stdin-content",
493
+ "--upload-file with stdin content is not supported",
494
+ ]);
495
+ requestUrl.uploadFile = uploadFile;
496
+
497
+ // TODO: this is complicated,
498
+ // --upload-file only applies per-URL so .data needs to become per-URL...
499
+ // if you pass --data and --upload-file or --get and --upload-file, curl will error
500
+ // if (config.url && config.url.length === 1) {
501
+ // config.data = [["raw", stdin]];
502
+ // } else {
503
+ // warnf(global_, [
504
+ // "upload-file-with-stdin-content-and-multiple-urls",
505
+ // "--upload-file with stdin content and multiple URLs is not supported",
506
+ // ]);
507
+ // }
508
+ } else {
509
+ requestUrl.uploadFile = uploadFile;
510
+ }
511
+ } else {
512
+ requestUrl.uploadFile = uploadFile;
513
+ }
514
+ }
515
+ if (outputFile) {
516
+ // TODO: get stdout redirects of command
517
+ requestUrl.output = outputFile;
518
+ }
519
+
520
+ // --user takes precedence over the URL
521
+ const auth = config.user || u.auth;
522
+ if (auth) {
523
+ const [user, pass] = auth.split(":", 2);
524
+ requestUrl.auth = [user, pass || new Word()];
525
+ }
526
+
527
+ return requestUrl;
528
+ }
529
+
530
+ function buildData(
531
+ configData: SrcDataParam[],
532
+ stdin?: Word,
533
+ stdinFile?: Word,
534
+ ): [DataParam[], Word, string | null] {
535
+ const data: DataParam[] = [];
536
+ let dataStrState = new Word();
537
+ for (const [i, x] of configData.entries()) {
538
+ const type = x[0];
539
+ let value = x[1];
540
+ let name: Word | null = null;
541
+
542
+ if (i > 0 && type !== "json") {
543
+ dataStrState = dataStrState.append("&");
544
+ }
545
+
546
+ if (type === "urlencode") {
547
+ // curl checks for = before @
548
+ const splitOn = value.includes("=") || !value.includes("@") ? "=" : "@";
549
+ // If there's no = or @ then the entire content is treated as a value and encoded
550
+ if (value.includes("@") || value.includes("=")) {
551
+ [name, value] = value.split(splitOn, 2);
552
+ }
553
+
554
+ if (splitOn === "=") {
555
+ if (name && name.toBool()) {
556
+ dataStrState = dataStrState.add(name).append("=");
557
+ }
558
+ // curl's --data-urlencode percent-encodes spaces as "+"
559
+ // https://github.com/curl/curl/blob/curl-7_86_0/src/tool_getparam.c#L630
560
+ dataStrState = dataStrState.add(percentEncodePlus(value));
561
+ continue;
562
+ }
563
+
564
+ name = name && name.toBool() ? name : null;
565
+ value = value.prepend("@");
566
+ }
567
+
568
+ let filename: Word | null = null;
569
+
570
+ if (type !== "raw" && value.startsWith("@")) {
571
+ filename = value.slice(1);
572
+ if (eq(filename, "-")) {
573
+ if (stdin !== undefined) {
574
+ switch (type) {
575
+ case "binary":
576
+ case "json":
577
+ value = stdin;
578
+ break;
579
+ case "urlencode":
580
+ value = mergeWords(
581
+ name && name.length ? name.append("=") : new Word(),
582
+ percentEncodePlus(stdin),
583
+ );
584
+ break;
585
+ default:
586
+ value = stdin.replace(/[\n\r]/g, "");
587
+ }
588
+ filename = null;
589
+ } else if (stdinFile !== undefined) {
590
+ filename = stdinFile;
591
+ } else {
592
+ // TODO: if stdin is read twice, it will be empty the second time
593
+ // TODO: `STDIN_SENTINEL` so that we can tell the difference between
594
+ // a stdinFile called "-" and stdin for the error message
595
+ }
596
+ }
597
+ }
598
+
599
+ if (filename !== null) {
600
+ if (dataStrState.toBool()) {
601
+ data.push(dataStrState);
602
+ dataStrState = new Word();
603
+ }
604
+ const dataParam: DataParam = {
605
+ // If `filename` isn't null, then `type` can't be "raw"
606
+ filetype: type as FileParamType,
607
+ filename,
608
+ };
609
+ if (name) {
610
+ dataParam.name = name;
611
+ }
612
+ data.push(dataParam);
613
+ } else {
614
+ dataStrState = dataStrState.add(value);
615
+ }
616
+ }
617
+ if (dataStrState.toBool()) {
618
+ data.push(dataStrState);
619
+ }
620
+
621
+ let dataStrReadsFile: string | null = null;
622
+ const dataStr = mergeWords(
623
+ ...data.map((d) => {
624
+ if (!(d instanceof Word)) {
625
+ dataStrReadsFile ||= d.filename.toString(); // report first file
626
+ if (d.name) {
627
+ return mergeWords(d.name, "=@", d.filename);
628
+ }
629
+ return d.filename.prepend("@");
630
+ }
631
+ return d;
632
+ }),
633
+ );
634
+
635
+ return [data, dataStr, dataStrReadsFile];
636
+ }
637
+
638
+ // Parses a Content-Type header into a type and a list of parameters
639
+ function parseContentType(
640
+ string: string,
641
+ ): [string, Array<[string, string]>] | null {
642
+ if (!string.includes(";")) {
643
+ return [string, []];
644
+ }
645
+ const semi = string.indexOf(";");
646
+ const type = string.slice(0, semi);
647
+ const rest = string.slice(semi);
648
+
649
+ // See https://www.w3.org/Protocols/rfc1341/4_Content-Type.html
650
+ // TODO: could be better, like reading to the next semicolon
651
+ const params = rest.match(
652
+ /;\s*([^;=]+)=(?:("[^"]*")|([^()<>@,;:\\"/[\]?.=]*))/g,
653
+ );
654
+ if (rest.trim() && !params) {
655
+ return null;
656
+ }
657
+ const parsedParams: Array<[string, string]> = [];
658
+ for (const param of params || []) {
659
+ const parsedParam = param.match(
660
+ /;\s*([^;=]+)=(?:("[^"]*")|([^()<>@,;:\\"/[\]?.=]*))/,
661
+ );
662
+ if (!parsedParam) {
663
+ return null;
664
+ }
665
+ const name = parsedParam[1];
666
+ const value = parsedParam[3] || parsedParam[2].slice(1, -1);
667
+ parsedParams.push([name, value]);
668
+ }
669
+ return [type, parsedParams];
670
+ }
671
+
672
+ // Parses out the boundary= value from a Content-Type header
673
+ function parseBoundary(string: string): string | null {
674
+ const header = parseContentType(string);
675
+ if (!header) {
676
+ return null;
677
+ }
678
+ for (const [name, value] of header[1]) {
679
+ if (name === "boundary") {
680
+ return value;
681
+ }
682
+ }
683
+ return null;
684
+ }
685
+
686
+ function parseRawForm(
687
+ data: string,
688
+ boundary: string,
689
+ ): [FormParam[], boolean] | null {
690
+ const endBoundary = "\r\n--" + boundary + "--\r\n";
691
+ if (!data.endsWith(endBoundary)) {
692
+ return null;
693
+ }
694
+ data = data.slice(0, -endBoundary.length);
695
+
696
+ // TODO: if empty form should it be completely empty?
697
+ boundary = "--" + boundary + "\r\n";
698
+ if (data && !data.startsWith(boundary)) {
699
+ return null;
700
+ }
701
+ data = data.slice(boundary.length);
702
+ const parts = data.split("\r\n" + boundary);
703
+ const form: FormParam[] = [];
704
+ let roundtrips = true;
705
+ for (const part of parts) {
706
+ const lines = part.split("\r\n");
707
+ if (lines.length < 2) {
708
+ return null;
709
+ }
710
+
711
+ const formParam: FormParam = {
712
+ name: new Word(),
713
+ content: new Word(),
714
+ };
715
+ let seenContentDisposition = false;
716
+ const headers: Word[] = [];
717
+ let i = 0;
718
+ for (; i < lines.length; i++) {
719
+ if (lines[i].length === 0) {
720
+ break;
721
+ }
722
+ const [name, value] = lines[i].split(": ", 2);
723
+ if (name === undefined || value === undefined) {
724
+ return null;
725
+ }
726
+ if (name.toLowerCase() === "content-disposition") {
727
+ if (seenContentDisposition) {
728
+ // should only have one
729
+ return null;
730
+ }
731
+
732
+ const contentDisposition = parseContentType(value);
733
+ if (!contentDisposition) {
734
+ return null;
735
+ }
736
+ const [type, params] = contentDisposition;
737
+ if (type !== "form-data") {
738
+ return null;
739
+ }
740
+ let extra = 0;
741
+ for (const [paramName, paramValue] of params) {
742
+ switch (paramName) {
743
+ case "name":
744
+ formParam.name = new Word(paramValue);
745
+ break;
746
+ case "filename":
747
+ formParam.filename = new Word(paramValue);
748
+ break;
749
+ default:
750
+ extra++;
751
+ break;
752
+ }
753
+ }
754
+ if (extra) {
755
+ roundtrips = false;
756
+ // TODO: warn?
757
+ }
758
+ seenContentDisposition = true;
759
+ } else if (name.toLowerCase() === "content-type") {
760
+ formParam.contentType = new Word(value);
761
+ } else {
762
+ headers.push(new Word(lines[i]));
763
+ }
764
+ }
765
+ if (headers.length) {
766
+ formParam.headers = headers;
767
+ }
768
+
769
+ if (!seenContentDisposition) {
770
+ return null;
771
+ }
772
+ if (i === lines.length) {
773
+ return null;
774
+ }
775
+ if (formParam.name.isEmpty()) {
776
+ return null;
777
+ }
778
+ formParam.content = new Word(lines.slice(i + 1).join("\n"));
779
+ form.push(formParam);
780
+ }
781
+ return [form, roundtrips];
782
+ }
783
+
784
+ function buildRequest(
785
+ global_: GlobalConfig,
786
+ config: OperationConfig,
787
+ stdin?: Word,
788
+ stdinFile?: Word,
789
+ ): Request {
790
+ if (!config.url || !config.url.length) {
791
+ // TODO: better error message (could be parsing fail)
792
+ throw new CCError("no URL specified!");
793
+ }
794
+
795
+ const headers = new Headers(config.header, global_.warnings);
796
+ const proxyHeaders = new Headers(
797
+ config["proxy-header"],
798
+ global_.warnings,
799
+ "--proxy-header",
800
+ );
801
+
802
+ let cookies;
803
+ const cookieFiles: Word[] = [];
804
+ const cookieHeader = headers.get("cookie");
805
+ if (cookieHeader) {
806
+ const parsedCookies = parseCookiesStrict(cookieHeader);
807
+ if (parsedCookies) {
808
+ cookies = parsedCookies;
809
+ }
810
+ } else if (cookieHeader === undefined && config.cookie) {
811
+ // If there is a Cookie header, --cookies is ignored
812
+ const cookieStrings: Word[] = [];
813
+ for (const c of config.cookie) {
814
+ // a --cookie without a = character reads from it as a filename
815
+ if (c.includes("=")) {
816
+ cookieStrings.push(c);
817
+ } else {
818
+ cookieFiles.push(c);
819
+ }
820
+ }
821
+ if (cookieStrings.length) {
822
+ const cookieString = joinWords(config.cookie, "; ");
823
+ headers.setIfMissing("Cookie", cookieString);
824
+ const parsedCookies = parseCookies(cookieString);
825
+ if (parsedCookies) {
826
+ cookies = parsedCookies;
827
+ }
828
+ }
829
+ }
830
+
831
+ let refererAuto = false;
832
+ if (config["user-agent"]) {
833
+ headers.setIfMissing("User-Agent", config["user-agent"]);
834
+ }
835
+ if (config.referer) {
836
+ if (config.referer.includes(";auto")) {
837
+ refererAuto = true;
838
+ }
839
+ // referer can be ";auto" or followed by ";auto", we ignore that.
840
+ const referer = config.referer.replace(/;auto$/, "");
841
+ if (referer.length) {
842
+ headers.setIfMissing("Referer", referer);
843
+ }
844
+ }
845
+ if (config.range) {
846
+ let range = config.range.prepend("bytes=");
847
+ if (!range.includes("-")) {
848
+ range = range.append("-");
849
+ }
850
+ headers.setIfMissing("Range", range);
851
+ }
852
+ if (config["time-cond"]) {
853
+ let timecond = config["time-cond"];
854
+ let header = "If-Modified-Since";
855
+ switch (timecond.charAt(0)) {
856
+ case "+":
857
+ timecond = timecond.slice(1);
858
+ break;
859
+ case "-":
860
+ timecond = timecond.slice(1);
861
+ header = "If-Unmodified-Since";
862
+ break;
863
+ case "=":
864
+ timecond = timecond.slice(1);
865
+ header = "Last-Modified";
866
+ break;
867
+ }
868
+ // TODO: parse date
869
+ headers.setIfMissing(header, timecond);
870
+ }
871
+
872
+ let data;
873
+ let dataStr;
874
+ let dataStrReadsFile;
875
+ let queryArray;
876
+ if (config.data && config.data.length) {
877
+ if (config.get) {
878
+ // https://github.com/curl/curl/blob/curl-7_85_0/src/tool_operate.c#L721
879
+ // --get --data will overwrite --url-query, but if there's no --data, for example,
880
+ // curl --url-query bar --get example.com
881
+ // it won't
882
+ // https://daniel.haxx.se/blog/2022/11/10/append-data-to-the-url-query/
883
+ config["url-query"] = config.data;
884
+ delete config.data;
885
+ } else {
886
+ [data, dataStr, dataStrReadsFile] = buildData(
887
+ config.data,
888
+ stdin,
889
+ stdinFile,
890
+ );
891
+ }
892
+ }
893
+ if (config["url-query"]) {
894
+ [queryArray] = buildData(config["url-query"], stdin, stdinFile);
895
+ }
896
+
897
+ const urls: RequestUrl[] = [];
898
+ const uploadFiles = config["upload-file"] || [];
899
+ const outputFiles = config.output || [];
900
+ for (const [i, url] of config.url.entries()) {
901
+ urls.push(
902
+ buildURL(
903
+ global_,
904
+ config,
905
+ url,
906
+ uploadFiles[i],
907
+ outputFiles[i],
908
+ stdin,
909
+ stdinFile,
910
+ ),
911
+ );
912
+ }
913
+ // --get moves --data into the URL's query string
914
+ if (config.get && config.data) {
915
+ delete config.data;
916
+ }
917
+
918
+ if ((config["upload-file"] || []).length > config.url.length) {
919
+ warnf(global_, [
920
+ "too-many-upload-files",
921
+ "Got more --upload-file/-T options than URLs: " +
922
+ config["upload-file"]
923
+ ?.map((f) => JSON.stringify(f.toString()))
924
+ .join(", "),
925
+ ]);
926
+ }
927
+ if ((config.output || []).length > config.url.length) {
928
+ warnf(global_, [
929
+ "too-many-output-files",
930
+ "Got more --output/-o options than URLs: " +
931
+ config.output?.map((f) => JSON.stringify(f.toString())).join(", "),
932
+ ]);
933
+ }
934
+
935
+ const request: Request = {
936
+ urls,
937
+ authType: pickAuth(config.authtype),
938
+ proxyAuthType: pickAuth(config.proxyauthtype),
939
+ headers,
940
+ proxyHeaders,
941
+ };
942
+ // TODO: warn about unused stdin?
943
+ if (stdin) {
944
+ request.stdin = stdin;
945
+ }
946
+ if (stdinFile) {
947
+ request.stdinFile = stdinFile;
948
+ }
949
+
950
+ if (Object.prototype.hasOwnProperty.call(config, "globoff")) {
951
+ request.globoff = config.globoff;
952
+ }
953
+ if (
954
+ Object.prototype.hasOwnProperty.call(config, "disallow-username-in-url")
955
+ ) {
956
+ request.disallowUsernameInUrl = config["disallow-username-in-url"];
957
+ }
958
+ if (Object.prototype.hasOwnProperty.call(config, "path-as-is")) {
959
+ request.pathAsIs = config["path-as-is"];
960
+ }
961
+
962
+ if (refererAuto) {
963
+ request.refererAuto = true;
964
+ }
965
+
966
+ if (cookies) {
967
+ // generators that use .cookies need to do
968
+ // deleteHeader(request, 'cookie')
969
+ request.cookies = cookies;
970
+ }
971
+ if (cookieFiles.length) {
972
+ request.cookieFiles = cookieFiles;
973
+ }
974
+ if (config["cookie-jar"]) {
975
+ request.cookieJar = config["cookie-jar"];
976
+ }
977
+ if (Object.prototype.hasOwnProperty.call(config, "junk-session-cookies")) {
978
+ request.junkSessionCookies = config["junk-session-cookies"];
979
+ }
980
+
981
+ if (Object.prototype.hasOwnProperty.call(config, "compressed")) {
982
+ request.compressed = config.compressed;
983
+ }
984
+ if (Object.prototype.hasOwnProperty.call(config, "tr-encoding")) {
985
+ request.transferEncoding = config["tr-encoding"];
986
+ }
987
+
988
+ if (config.include) {
989
+ request.include = true;
990
+ }
991
+
992
+ if (config.json) {
993
+ headers.setIfMissing("Content-Type", "application/json");
994
+ headers.setIfMissing("Accept", "application/json");
995
+ } else if (config.data) {
996
+ headers.setIfMissing("Content-Type", "application/x-www-form-urlencoded");
997
+ } else if (config.form) {
998
+ // TODO: warn when details (;filename=, etc.) are not supported
999
+ // by each converter.
1000
+ request.multipartUploads = parseForm(config.form, global_.warnings);
1001
+ //headers.setIfMissing("Content-Type", "multipart/form-data");
1002
+ }
1003
+ const contentType = headers.getContentType();
1004
+ const exactContentType = headers.get("Content-Type");
1005
+ if (
1006
+ config.data &&
1007
+ !dataStrReadsFile &&
1008
+ dataStr &&
1009
+ dataStr.isString() &&
1010
+ !config.form &&
1011
+ !request.multipartUploads &&
1012
+ contentType === "multipart/form-data" &&
1013
+ exactContentType &&
1014
+ exactContentType.isString()
1015
+ ) {
1016
+ const boundary = parseBoundary(exactContentType.toString());
1017
+ if (boundary) {
1018
+ const form = parseRawForm(dataStr.toString(), boundary);
1019
+ if (form) {
1020
+ const [parsedForm, roundtrip] = form;
1021
+ request.multipartUploads = parsedForm;
1022
+ if (!roundtrip) {
1023
+ request.multipartUploadsDoesntRoundtrip = true;
1024
+ }
1025
+ }
1026
+ }
1027
+ }
1028
+
1029
+ if (Object.prototype.hasOwnProperty.call(config, "form-escape")) {
1030
+ request.formEscape = config["form-escape"];
1031
+ }
1032
+
1033
+ if (config["aws-sigv4"]) {
1034
+ // https://github.com/curl/curl/blob/curl-7_86_0/lib/setopt.c#L678-L679
1035
+ request.authType = "aws-sigv4";
1036
+ request.awsSigV4 = config["aws-sigv4"];
1037
+ }
1038
+ if (request.authType === "bearer" && config["oauth2-bearer"]) {
1039
+ const bearer = config["oauth2-bearer"].prepend("Bearer ");
1040
+ headers.setIfMissing("Authorization", bearer);
1041
+ request.oauth2Bearer = config["oauth2-bearer"];
1042
+ }
1043
+ if (config.delegation) {
1044
+ request.delegation = config.delegation;
1045
+ }
1046
+ if (config.krb) {
1047
+ request.krb = config.krb;
1048
+ }
1049
+ if (config["sasl-authzid"]) {
1050
+ request.saslAuthzid = config["sasl-authzid"];
1051
+ }
1052
+ if (Object.prototype.hasOwnProperty.call(config, "sasl-ir")) {
1053
+ request.saslIr = config["sasl-ir"];
1054
+ }
1055
+ if (config.negotiate) {
1056
+ request.authType = "negotiate";
1057
+ }
1058
+ if (config["service-name"]) {
1059
+ request.serviceName = config["service-name"];
1060
+ }
1061
+
1062
+ // TODO: ideally we should generate code that explicitly unsets the header too
1063
+ // no HTTP libraries allow that.
1064
+ headers.clearNulls();
1065
+
1066
+ if (config.data && config.data.length) {
1067
+ request.data = dataStr;
1068
+ if (dataStrReadsFile) {
1069
+ request.dataReadsFile = dataStrReadsFile;
1070
+ }
1071
+ request.dataArray = data;
1072
+ // TODO: remove these
1073
+ request.isDataRaw = false;
1074
+ request.isDataBinary = (data || []).some(
1075
+ (d) => !(d instanceof Word) && d.filetype === "binary",
1076
+ );
1077
+ }
1078
+ if (queryArray) {
1079
+ // If we have to generate code that reads from a file, we
1080
+ // need to do it once for all URLs.
1081
+ request.queryArray = queryArray;
1082
+ }
1083
+
1084
+ if (Object.prototype.hasOwnProperty.call(config, "ipv4")) {
1085
+ request["ipv4"] = config["ipv4"];
1086
+ }
1087
+ if (Object.prototype.hasOwnProperty.call(config, "ipv6")) {
1088
+ request["ipv6"] = config["ipv6"];
1089
+ }
1090
+
1091
+ if (config.proto) {
1092
+ // TODO: parse
1093
+ request.proto = config.proto;
1094
+ }
1095
+ if (config["proto-redir"]) {
1096
+ // TODO: parse
1097
+ request.protoRedir = config["proto-redir"];
1098
+ }
1099
+ if (config["proto-default"]) {
1100
+ request.protoDefault = config["proto-default"];
1101
+ }
1102
+
1103
+ if (config["tcp-fastopen"]) {
1104
+ request.tcpFastopen = config["tcp-fastopen"];
1105
+ }
1106
+
1107
+ if (config["local-port"]) {
1108
+ // TODO: check the range
1109
+ const [start, end] = config["local-port"].split("-", 1);
1110
+ if (end && end.toBool()) {
1111
+ request.localPort = [start, end];
1112
+ } else {
1113
+ request.localPort = [config["local-port"], null];
1114
+ }
1115
+ }
1116
+
1117
+ if (Object.prototype.hasOwnProperty.call(config, "ignore-content-length")) {
1118
+ request.ignoreContentLength = config["ignore-content-length"];
1119
+ }
1120
+
1121
+ if (config.interface) {
1122
+ request.interface = config.interface;
1123
+ }
1124
+
1125
+ if (config.ciphers) {
1126
+ request.ciphers = config.ciphers;
1127
+ }
1128
+ if (config.curves) {
1129
+ request.curves = config.curves;
1130
+ }
1131
+ if (config.insecure) {
1132
+ request.insecure = true;
1133
+ }
1134
+ if (Object.prototype.hasOwnProperty.call(config, "cert-status")) {
1135
+ request.certStatus = config["cert-status"];
1136
+ }
1137
+ // TODO: if the URL doesn't start with https://, curl doesn't verify
1138
+ // certificates, etc.
1139
+ if (config.cert) {
1140
+ if (config.cert.startsWith("pkcs11:") || !config.cert.match(/[:\\]/)) {
1141
+ request.cert = [config.cert, null];
1142
+ } else {
1143
+ // TODO: curl does more complex processing
1144
+ // find un-backslash-escaped colon, backslash might also be escaped with a backslash
1145
+ let colon = -1;
1146
+
1147
+ try {
1148
+ // Safari versions older than 16.4 don't support negative lookbehind
1149
+ colon = config.cert.search(/(?<!\\)(?:\\\\)*:/);
1150
+ } catch {
1151
+ colon = config.cert.search(/:/);
1152
+ }
1153
+
1154
+ if (colon === -1) {
1155
+ request.cert = [config.cert, null];
1156
+ } else {
1157
+ const cert = config.cert.slice(0, colon);
1158
+ const password = config.cert.slice(colon + 1);
1159
+ if (password.toBool()) {
1160
+ request.cert = [cert, password];
1161
+ } else {
1162
+ request.cert = [cert, null];
1163
+ }
1164
+ }
1165
+ }
1166
+ }
1167
+ if (config["cert-type"]) {
1168
+ const certType = config["cert-type"];
1169
+ request.certType = certType;
1170
+
1171
+ if (
1172
+ certType.isString() &&
1173
+ !["PEM", "DER", "ENG", "P12"].includes(certType.toString().toUpperCase())
1174
+ ) {
1175
+ warnf(global_, [
1176
+ "cert-type-unknown",
1177
+ "not supported file type " +
1178
+ JSON.stringify(certType.toString()) +
1179
+ " for certificate",
1180
+ ]);
1181
+ }
1182
+ }
1183
+ if (config.key) {
1184
+ request.key = config.key;
1185
+ }
1186
+ if (config["key-type"]) {
1187
+ request.keyType = config["key-type"];
1188
+ }
1189
+ if (config.pass) {
1190
+ request.pass = config.pass;
1191
+ }
1192
+ if (config.cacert) {
1193
+ request.cacert = config.cacert;
1194
+ }
1195
+ if (Object.prototype.hasOwnProperty.call(config, "ca-native")) {
1196
+ request.caNative = config["ca-native"];
1197
+ }
1198
+ if (Object.prototype.hasOwnProperty.call(config, "ssl-allow-beast")) {
1199
+ request.sslAllowBeast = config["ssl-allow-beast"];
1200
+ }
1201
+ if (config.capath) {
1202
+ request.capath = config.capath;
1203
+ }
1204
+ if (config.crlfile) {
1205
+ request.crlfile = config.crlfile;
1206
+ }
1207
+ if (config.pinnedpubkey) {
1208
+ request.pinnedpubkey = config.pinnedpubkey;
1209
+ }
1210
+ if (config["random-file"]) {
1211
+ request.randomFile = config["random-file"];
1212
+ }
1213
+ if (config["egd-file"]) {
1214
+ request.egdFile = config["egd-file"];
1215
+ }
1216
+ if (config.hsts) {
1217
+ request.hsts = config.hsts;
1218
+ }
1219
+ if (Object.prototype.hasOwnProperty.call(config, "alpn")) {
1220
+ request.alpn = config.alpn;
1221
+ }
1222
+
1223
+ if (config.tlsVersion) {
1224
+ request.tlsVersion = config.tlsVersion;
1225
+ }
1226
+ if (config["tls-max"]) {
1227
+ request.tlsMax = config["tls-max"];
1228
+ }
1229
+ if (config["tls13-ciphers"]) {
1230
+ request.tls13Ciphers = config["tls13-ciphers"];
1231
+ }
1232
+ if (config["tlsauthtype"]) {
1233
+ request.tlsauthtype = config["tlsauthtype"];
1234
+ }
1235
+ if (config["tlspassword"]) {
1236
+ request.tlspassword = config["tlspassword"];
1237
+ }
1238
+ if (config["tlsuser"]) {
1239
+ request.tlsuser = config["tlsuser"];
1240
+ }
1241
+ if (Object.prototype.hasOwnProperty.call(config, "ssl-allow-beast")) {
1242
+ request.sslAllowBeast = config["ssl-allow-beast"];
1243
+ }
1244
+ if (Object.prototype.hasOwnProperty.call(config, "ssl-auto-client-cert")) {
1245
+ request.sslAutoClientCert = config["ssl-auto-client-cert"];
1246
+ }
1247
+ if (Object.prototype.hasOwnProperty.call(config, "ssl-no-revoke")) {
1248
+ request.sslNoRevoke = config["ssl-no-revoke"];
1249
+ }
1250
+ if (Object.prototype.hasOwnProperty.call(config, "ssl-reqd")) {
1251
+ request.sslReqd = config["ssl-reqd"];
1252
+ }
1253
+ if (Object.prototype.hasOwnProperty.call(config, "ssl-revoke-best-effort")) {
1254
+ request.sslRevokeBestEffort = config["ssl-revoke-best-effort"];
1255
+ }
1256
+ if (Object.prototype.hasOwnProperty.call(config, "ssl")) {
1257
+ request.ssl = config["ssl"];
1258
+ }
1259
+ if (Object.prototype.hasOwnProperty.call(config, "sslv2")) {
1260
+ request.sslv2 = config["sslv2"];
1261
+ }
1262
+ if (Object.prototype.hasOwnProperty.call(config, "sslv3")) {
1263
+ request.sslv3 = config["sslv3"];
1264
+ }
1265
+
1266
+ if (config["doh-url"]) {
1267
+ request.dohUrl = config["doh-url"];
1268
+ }
1269
+ if (Object.prototype.hasOwnProperty.call(config, "doh-insecure")) {
1270
+ request.dohInsecure = config["doh-insecure"];
1271
+ }
1272
+ if (Object.prototype.hasOwnProperty.call(config, "doh-cert-status")) {
1273
+ request.dohCertStatus = config["doh-cert-status"];
1274
+ }
1275
+
1276
+ if (config.proxy) {
1277
+ // https://github.com/curl/curl/blob/e498a9b1fe5964a18eb2a3a99dc52160d2768261/lib/url.c#L2388-L2390
1278
+ request.proxy = config.proxy;
1279
+ if (request.proxyType && request.proxyType !== "http2") {
1280
+ delete request.proxyType;
1281
+ }
1282
+ if (config["proxy-user"]) {
1283
+ request.proxyAuth = config["proxy-user"];
1284
+ }
1285
+ }
1286
+ if (Object.prototype.hasOwnProperty.call(config, "proxytunnel")) {
1287
+ request.proxytunnel = config.proxytunnel;
1288
+ }
1289
+ if (config.noproxy) {
1290
+ request.noproxy = config.noproxy;
1291
+ }
1292
+ if (config.preproxy) {
1293
+ request.preproxy = config.preproxy;
1294
+ }
1295
+ if (Object.prototype.hasOwnProperty.call(config, "proxy-anyauth")) {
1296
+ request.proxyAnyauth = config["proxy-anyauth"];
1297
+ }
1298
+ if (Object.prototype.hasOwnProperty.call(config, "proxy-basic")) {
1299
+ request.proxyBasic = config["proxy-basic"];
1300
+ }
1301
+ if (Object.prototype.hasOwnProperty.call(config, "proxy-digest")) {
1302
+ request.proxyDigest = config["proxy-digest"];
1303
+ }
1304
+ if (Object.prototype.hasOwnProperty.call(config, "proxy-negotiate")) {
1305
+ request.proxyNegotiate = config["proxy-negotiate"];
1306
+ }
1307
+ if (Object.prototype.hasOwnProperty.call(config, "proxy-ntlm")) {
1308
+ request.proxyNtlm = config["proxy-ntlm"];
1309
+ }
1310
+ if (Object.prototype.hasOwnProperty.call(config, "proxy-ca-native")) {
1311
+ request.proxyCaNative = config["proxy-ca-native"];
1312
+ }
1313
+ if (config["proxy-cacert"]) {
1314
+ request.proxyCacert = config["proxy-cacert"];
1315
+ }
1316
+ if (config["proxy-capath"]) {
1317
+ request.proxyCapath = config["proxy-capath"];
1318
+ }
1319
+ if (config["proxy-cert-type"]) {
1320
+ request.proxyCertType = config["proxy-cert-type"];
1321
+ }
1322
+ if (config["proxy-cert"]) {
1323
+ request.proxyCert = config["proxy-cert"];
1324
+ }
1325
+ if (config["proxy-ciphers"]) {
1326
+ request.proxyCiphers = config["proxy-ciphers"];
1327
+ }
1328
+ if (config["proxy-crlfile"]) {
1329
+ request.proxyCrlfile = config["proxy-crlfile"];
1330
+ }
1331
+ if (config["proxy-http2"]) {
1332
+ request.proxyType = "http2";
1333
+ }
1334
+ if (config["proxy1.0"]) {
1335
+ request.proxy = config["proxy1.0"];
1336
+ request.proxyType = "http1";
1337
+ }
1338
+ if (Object.prototype.hasOwnProperty.call(config, "proxy-insecure")) {
1339
+ request.proxyInsecure = config["proxy-insecure"];
1340
+ }
1341
+ if (config["proxy-key"]) {
1342
+ request.proxyKey = config["proxy-key"];
1343
+ }
1344
+ if (config["proxy-key-type"]) {
1345
+ request.proxyKeyType = config["proxy-key-type"];
1346
+ }
1347
+ if (config["proxy-pass"]) {
1348
+ request.proxyPass = config["proxy-pass"];
1349
+ }
1350
+ if (config["proxy-pinnedpubkey"]) {
1351
+ request.proxyPinnedpubkey = config["proxy-pinnedpubkey"];
1352
+ }
1353
+ if (config["proxy-pinnedpubkey"]) {
1354
+ request.proxyPinnedpubkey = config["proxy-pinnedpubkey"];
1355
+ }
1356
+ if (config["proxy-service-name"]) {
1357
+ request.proxyServiceName = config["proxy-service-name"];
1358
+ }
1359
+ if (Object.prototype.hasOwnProperty.call(config, "proxy-ssl-allow-beast")) {
1360
+ request.proxySslAllowBeast = config["proxy-ssl-allow-beast"];
1361
+ }
1362
+ if (
1363
+ Object.prototype.hasOwnProperty.call(config, "proxy-ssl-auto-client-cert")
1364
+ ) {
1365
+ request.proxySslAutoClientCert = config["proxy-ssl-auto-client-cert"];
1366
+ }
1367
+ if (config["proxy-tls13-ciphers"]) {
1368
+ request.proxyTls13Ciphers = config["proxy-tls13-ciphers"];
1369
+ }
1370
+ if (config["proxy-tlsauthtype"]) {
1371
+ request.proxyTlsauthtype = config["proxy-tlsauthtype"];
1372
+ if (
1373
+ request.proxyTlsauthtype.isString() &&
1374
+ !eq(request.proxyTlsauthtype, "SRP")
1375
+ ) {
1376
+ warnf(global_, [
1377
+ "proxy-tlsauthtype",
1378
+ "proxy-tlsauthtype is not supported: " + request.proxyTlsauthtype,
1379
+ ]);
1380
+ }
1381
+ }
1382
+ if (config["proxy-tlspassword"]) {
1383
+ request.proxyTlspassword = config["proxy-tlspassword"];
1384
+ }
1385
+ if (config["proxy-tlsuser"]) {
1386
+ request.proxyTlsuser = config["proxy-tlsuser"];
1387
+ }
1388
+ if (Object.prototype.hasOwnProperty.call(config, "proxy-tlsv1")) {
1389
+ request.proxyTlsv1 = config["proxy-tlsv1"];
1390
+ }
1391
+ if (config["proxy-user"]) {
1392
+ request.proxyUser = config["proxy-user"];
1393
+ }
1394
+ if (Object.prototype.hasOwnProperty.call(config, "proxytunnel")) {
1395
+ request.proxytunnel = config["proxytunnel"];
1396
+ }
1397
+
1398
+ if (config["socks4"]) {
1399
+ request.proxy = config["socks4"];
1400
+ request.proxyType = "socks4";
1401
+ }
1402
+ if (config["socks4a"]) {
1403
+ request.proxy = config["socks4a"];
1404
+ request.proxyType = "socks4a";
1405
+ }
1406
+ if (config["socks5"]) {
1407
+ request.proxy = config["socks5"];
1408
+ request.proxyType = "socks5";
1409
+ }
1410
+ if (config["socks5-hostname"]) {
1411
+ request.proxy = config["socks5-hostname"];
1412
+ request.proxyType = "socks5-hostname";
1413
+ }
1414
+ if (Object.prototype.hasOwnProperty.call(config, "socks5-basic")) {
1415
+ request.socks5Basic = config["socks5-basic"];
1416
+ }
1417
+ if (Object.prototype.hasOwnProperty.call(config, "socks5-gssapi-nec")) {
1418
+ request.socks5GssapiNec = config["socks5-gssapi-nec"];
1419
+ }
1420
+ if (config["socks5-gssapi-service"]) {
1421
+ request.socks5GssapiService = config["socks5-gssapi-service"];
1422
+ }
1423
+ if (Object.prototype.hasOwnProperty.call(config, "socks5-gssapi")) {
1424
+ request.socks5Gssapi = config["socks5-gssapi"];
1425
+ }
1426
+
1427
+ if (config["haproxy-clientip"]) {
1428
+ request.haproxyClientIp = config["haproxy-clientip"];
1429
+ }
1430
+ if (Object.prototype.hasOwnProperty.call(config, "haproxy-protocol")) {
1431
+ request.haproxyProtocol = config["haproxy-protocol"];
1432
+ }
1433
+
1434
+ if (config["max-time"]) {
1435
+ request.timeout = config["max-time"];
1436
+ if (
1437
+ config["max-time"].isString() &&
1438
+ // TODO: parseFloat() like curl
1439
+ isNaN(parseFloat(config["max-time"].toString()))
1440
+ ) {
1441
+ warnf(global_, [
1442
+ "max-time-not-number",
1443
+ "option --max-time: expected a proper numerical parameter: " +
1444
+ JSON.stringify(config["max-time"].toString()),
1445
+ ]);
1446
+ }
1447
+ }
1448
+ if (config["connect-timeout"]) {
1449
+ request.connectTimeout = config["connect-timeout"];
1450
+ if (
1451
+ config["connect-timeout"].isString() &&
1452
+ isNaN(parseFloat(config["connect-timeout"].toString()))
1453
+ ) {
1454
+ warnf(global_, [
1455
+ "connect-timeout-not-number",
1456
+ "option --connect-timeout: expected a proper numerical parameter: " +
1457
+ JSON.stringify(config["connect-timeout"].toString()),
1458
+ ]);
1459
+ }
1460
+ }
1461
+ if (config["expect100-timeout"]) {
1462
+ request.expect100Timeout = config["expect100-timeout"];
1463
+ if (
1464
+ config["expect100-timeout"].isString() &&
1465
+ isNaN(parseFloat(config["expect100-timeout"].toString()))
1466
+ ) {
1467
+ warnf(global_, [
1468
+ "expect100-timeout-not-number",
1469
+ "option --expect100-timeout: expected a proper numerical parameter: " +
1470
+ JSON.stringify(config["expect100-timeout"].toString()),
1471
+ ]);
1472
+ }
1473
+ }
1474
+ if (config["happy-eyeballs-timeout-ms"]) {
1475
+ request.happyEyeballsTimeoutMs = config["happy-eyeballs-timeout-ms"];
1476
+ }
1477
+ if (config["speed-limit"]) {
1478
+ request.speedLimit = config["speed-limit"];
1479
+ }
1480
+ if (config["speed-time"]) {
1481
+ request.speedTime = config["speed-time"];
1482
+ }
1483
+ if (config["limit-rate"]) {
1484
+ request.limitRate = config["limit-rate"];
1485
+ }
1486
+ if (config["max-filesize"]) {
1487
+ request.maxFilesize = config["max-filesize"];
1488
+ }
1489
+
1490
+ if (Object.prototype.hasOwnProperty.call(config, "keepalive")) {
1491
+ request.keepAlive = config.keepalive;
1492
+ }
1493
+ if (config["keepalive-time"]) {
1494
+ request.keepAliveTime = config["keepalive-time"];
1495
+ }
1496
+
1497
+ if (config["alt-svc"]) {
1498
+ request.altSvc = config["alt-svc"];
1499
+ }
1500
+
1501
+ if (Object.prototype.hasOwnProperty.call(config, "location")) {
1502
+ request.followRedirects = config.location;
1503
+ }
1504
+ if (config["location-trusted"]) {
1505
+ request.followRedirectsTrusted = config["location-trusted"];
1506
+ }
1507
+ if (config["max-redirs"]) {
1508
+ request.maxRedirects = config["max-redirs"].trim();
1509
+ if (
1510
+ config["max-redirs"].isString() &&
1511
+ !isInt(config["max-redirs"].toString())
1512
+ ) {
1513
+ warnf(global_, [
1514
+ "max-redirs-not-int",
1515
+ "option --max-redirs: expected a proper numerical parameter: " +
1516
+ JSON.stringify(config["max-redirs"].toString()),
1517
+ ]);
1518
+ }
1519
+ }
1520
+ if (Object.prototype.hasOwnProperty.call(config, "post301")) {
1521
+ request.post301 = config.post301;
1522
+ }
1523
+ if (Object.prototype.hasOwnProperty.call(config, "post302")) {
1524
+ request.post302 = config.post302;
1525
+ }
1526
+ if (Object.prototype.hasOwnProperty.call(config, "post303")) {
1527
+ request.post303 = config.post303;
1528
+ }
1529
+
1530
+ if (config.fail) {
1531
+ request.fail = config.fail;
1532
+ }
1533
+
1534
+ if (config.retry) {
1535
+ request.retry = config.retry;
1536
+ }
1537
+ if (config["retry-max-time"]) {
1538
+ request.retryMaxTime = config["retry-max-time"];
1539
+ }
1540
+
1541
+ if (Object.prototype.hasOwnProperty.call(config, "ftp-skip-pasv-ip")) {
1542
+ request.ftpSkipPasvIp = config["ftp-skip-pasv-ip"];
1543
+ }
1544
+
1545
+ if (config.httpVersion) {
1546
+ if (
1547
+ config.httpVersion === "2" ||
1548
+ config.httpVersion === "2-prior-knowledge"
1549
+ ) {
1550
+ request.http2 = true;
1551
+ }
1552
+ if (config.httpVersion === "3" || config.httpVersion === "3-only") {
1553
+ request.http3 = true;
1554
+ }
1555
+ request.httpVersion = config.httpVersion;
1556
+ }
1557
+ if (Object.prototype.hasOwnProperty.call(config, "http0.9")) {
1558
+ request.http0_9 = config["http0.9"];
1559
+ }
1560
+
1561
+ if (config.resolve && config.resolve.length) {
1562
+ request.resolve = config.resolve;
1563
+ }
1564
+ if (config["connect-to"] && config["connect-to"].length) {
1565
+ request.connectTo = config["connect-to"];
1566
+ }
1567
+
1568
+ if (config["unix-socket"]) {
1569
+ request.unixSocket = config["unix-socket"];
1570
+ }
1571
+ if (config["abstract-unix-socket"]) {
1572
+ request.abstractUnixSocket = config["abstract-unix-socket"];
1573
+ }
1574
+
1575
+ if (config["netrc-optional"]) {
1576
+ request.netrc = "optional";
1577
+ } else if (config.netrc || config["netrc-file"]) {
1578
+ request.netrc = "required";
1579
+ } else if (config.netrc === false) {
1580
+ // TODO || config["netrc-optional"] === false ?
1581
+ request.netrc = "ignored";
1582
+ }
1583
+ if (config["netrc-file"]) {
1584
+ request.netrcFile = config["netrc-file"];
1585
+ }
1586
+
1587
+ if (config["use-ascii"]) {
1588
+ request.useAscii = config["use-ascii"];
1589
+ }
1590
+
1591
+ if (config["continue-at"]) {
1592
+ request.continueAt = config["continue-at"];
1593
+ }
1594
+
1595
+ if (Object.prototype.hasOwnProperty.call(config, "crlf")) {
1596
+ request.crlf = config.crlf;
1597
+ }
1598
+
1599
+ if (Object.prototype.hasOwnProperty.call(config, "clobber")) {
1600
+ request.clobber = config.clobber;
1601
+ }
1602
+ if (Object.prototype.hasOwnProperty.call(config, "remote-time")) {
1603
+ request.remoteTime = config["remote-time"];
1604
+ }
1605
+
1606
+ // Global options
1607
+ if (Object.prototype.hasOwnProperty.call(global_, "verbose")) {
1608
+ request.verbose = global_.verbose;
1609
+ }
1610
+ if (Object.prototype.hasOwnProperty.call(global_, "silent")) {
1611
+ request.silent = global_.silent;
1612
+ }
1613
+
1614
+ return request;
1615
+ }
1616
+
1617
+ export function buildRequests(
1618
+ global_: GlobalConfig,
1619
+ stdin?: Word,
1620
+ stdinFile?: Word,
1621
+ ): Request[] {
1622
+ if (!global_.configs.length) {
1623
+ // shouldn't happen
1624
+ warnf(global_, ["no-configs", "got empty config object"]);
1625
+ }
1626
+ return global_.configs.map((config) =>
1627
+ buildRequest(global_, config, stdin, stdinFile),
1628
+ );
1629
+ }
1630
+
1631
+ export function getFirst(
1632
+ requests: Request[],
1633
+ warnings: Warnings,
1634
+ support?: Support,
1635
+ ): Request {
1636
+ if (requests.length > 1) {
1637
+ warnings.push([
1638
+ "next",
1639
+ // TODO: better message, we might have two requests because of
1640
+ // --next or because of multiple curl commands or both
1641
+ "got " +
1642
+ requests.length +
1643
+ " curl requests, only converting the first one",
1644
+ ]);
1645
+ }
1646
+ const request = requests[0];
1647
+ warnIfPartsIgnored(request, warnings, support);
1648
+ return request;
1649
+ }