curl-wrap 2.0.1 → 2.1.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/README.md +66 -0
- package/index.d.ts +18 -2
- package/index.js +363 -42
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -45,6 +45,9 @@ curl.headers({
|
|
|
45
45
|
'User-Agent': 'curl-wrap/1.0',
|
|
46
46
|
'Accept': 'application/json'
|
|
47
47
|
});
|
|
48
|
+
|
|
49
|
+
// clear all headers
|
|
50
|
+
curl.clearHeaders();
|
|
48
51
|
```
|
|
49
52
|
|
|
50
53
|
### Setting Cookies
|
|
@@ -98,6 +101,9 @@ curl.impersonate('firefox');
|
|
|
98
101
|
curl.impersonate('safari');
|
|
99
102
|
curl.impersonate('safariMobile');
|
|
100
103
|
curl.impersonate('edge');
|
|
104
|
+
|
|
105
|
+
// Or create a new instance with impersonation
|
|
106
|
+
const chromeCurl = Curl.impersonate('chrome');
|
|
101
107
|
```
|
|
102
108
|
|
|
103
109
|
### Setting Request Method
|
|
@@ -165,6 +171,66 @@ const response = await curl;
|
|
|
165
171
|
console.log(response.stderr);
|
|
166
172
|
```
|
|
167
173
|
|
|
174
|
+
### Buffer Response
|
|
175
|
+
|
|
176
|
+
```js
|
|
177
|
+
curl.asBuffer();
|
|
178
|
+
const response = await curl;
|
|
179
|
+
console.log(response.body); // Buffer object instead of string
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
### Export as Curl Command
|
|
183
|
+
|
|
184
|
+
```js
|
|
185
|
+
const curl = Curl.url('https://api.example.com')
|
|
186
|
+
.post()
|
|
187
|
+
.header('Content-Type', 'application/json')
|
|
188
|
+
.body({key: 'value'});
|
|
189
|
+
|
|
190
|
+
const curlCommand = await curl.exportAsCurl();
|
|
191
|
+
console.log(curlCommand);
|
|
192
|
+
// Output:
|
|
193
|
+
// curl 'https://api.example.com' \
|
|
194
|
+
// --request POST \
|
|
195
|
+
// --header 'content-type: application/json' \
|
|
196
|
+
// --data-raw '{"key":"value"}'
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
### Import from Curl Command
|
|
200
|
+
|
|
201
|
+
```js
|
|
202
|
+
const curlCommand = `curl 'https://api.example.com' \
|
|
203
|
+
--request POST \
|
|
204
|
+
--header 'Content-Type: application/json' \
|
|
205
|
+
--header 'Authorization: Bearer token123' \
|
|
206
|
+
--cookie 'session=abc123; user=john' \
|
|
207
|
+
--data-raw '{"name":"John","age":30}'`;
|
|
208
|
+
|
|
209
|
+
const curl = Curl.fromCurl(curlCommand);
|
|
210
|
+
const response = await curl;
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
### Cloning Requests
|
|
214
|
+
|
|
215
|
+
```js
|
|
216
|
+
const baseCurl = Curl.url('https://api.example.com')
|
|
217
|
+
.header('Authorization', 'Bearer token123')
|
|
218
|
+
.header('Content-Type', 'application/json');
|
|
219
|
+
|
|
220
|
+
// Clone and modify for different endpoints
|
|
221
|
+
const getUserCurl = baseCurl.clone()
|
|
222
|
+
.url('https://api.example.com/user')
|
|
223
|
+
.get();
|
|
224
|
+
const createUserCurl = baseCurl.clone()
|
|
225
|
+
.url('https://api.example.com/user')
|
|
226
|
+
.post()
|
|
227
|
+
.body({name: 'John'});
|
|
228
|
+
|
|
229
|
+
// Both requests share the same headers but have different URLs and methods
|
|
230
|
+
const userResponse = await getUserCurl;
|
|
231
|
+
const createResponse = await createUserCurl;
|
|
232
|
+
```
|
|
233
|
+
|
|
168
234
|
### Method Chaining
|
|
169
235
|
|
|
170
236
|
```js
|
package/index.d.ts
CHANGED
|
@@ -3,6 +3,7 @@ import { CookieJar } from "tough-cookie";
|
|
|
3
3
|
interface CurlOptions {
|
|
4
4
|
method?: string; // HTTP method (e.g., GET, POST)
|
|
5
5
|
headers?: Record<string, string>; // Request headers
|
|
6
|
+
cookies?: Record<string, string>; // Request cookies
|
|
6
7
|
body?: string | object | null; // Request body
|
|
7
8
|
timeout?: number; // Timeout in milliseconds
|
|
8
9
|
[key: string]: any; // Allow additional options
|
|
@@ -10,7 +11,7 @@ interface CurlOptions {
|
|
|
10
11
|
|
|
11
12
|
interface CurlResponse {
|
|
12
13
|
url: string;
|
|
13
|
-
body: string; // Response body as a string
|
|
14
|
+
body: string | Buffer; // Response body as a string or Buffer
|
|
14
15
|
headers: Record<string, string>; // Response headers
|
|
15
16
|
statusCode: number; // HTTP status code
|
|
16
17
|
status: number; // alias for .statusCode
|
|
@@ -50,6 +51,8 @@ export class Curl {
|
|
|
50
51
|
static getGlobalCookieJar(): CookieJar;
|
|
51
52
|
|
|
52
53
|
static hasCurlImpersonate(): boolean;
|
|
54
|
+
static impersonate(browser?: BrowserType): Curl;
|
|
55
|
+
static fromCurl(curlCommand: string): Curl;
|
|
53
56
|
|
|
54
57
|
cliCommand(command: string): Curl;
|
|
55
58
|
cliOptions(options: string | string[]): Curl;
|
|
@@ -61,6 +64,7 @@ export class Curl {
|
|
|
61
64
|
|
|
62
65
|
header(headerName: string | object, headerValue?: string): Curl;
|
|
63
66
|
headers(headers: object): Curl;
|
|
67
|
+
clearHeaders(): Curl;
|
|
64
68
|
|
|
65
69
|
json(body?: object): Curl;
|
|
66
70
|
body(body: any, contentType?: string): Curl;
|
|
@@ -78,10 +82,11 @@ export class Curl {
|
|
|
78
82
|
cookieName: string | boolean | object,
|
|
79
83
|
cookieValue?: string
|
|
80
84
|
): Curl;
|
|
81
|
-
cookies(cookies: object | boolean): Curl;
|
|
85
|
+
cookies(cookies: object | boolean | string): Curl;
|
|
82
86
|
|
|
83
87
|
globalCookies(options?: boolean | object): Curl;
|
|
84
88
|
cookieJar(cookieJar: CookieJar, options?: { readOnly?: boolean }): Curl;
|
|
89
|
+
getCookieJar(): CookieJar | undefined;
|
|
85
90
|
cookieFile(fileName: string, options?: object): Curl;
|
|
86
91
|
|
|
87
92
|
timeout(timeout: number): Curl;
|
|
@@ -113,6 +118,12 @@ export class Curl {
|
|
|
113
118
|
|
|
114
119
|
verbose(isVerbose?: boolean): Curl;
|
|
115
120
|
|
|
121
|
+
asBuffer(returnAsBuffer?: boolean): Curl;
|
|
122
|
+
|
|
123
|
+
clone(): Curl;
|
|
124
|
+
|
|
125
|
+
exportAsCurl(): Promise<string>;
|
|
126
|
+
|
|
116
127
|
fetch(): Promise<CurlResponse>;
|
|
117
128
|
|
|
118
129
|
then(
|
|
@@ -130,4 +141,9 @@ export class Curl {
|
|
|
130
141
|
private options: any;
|
|
131
142
|
private _fields: Record<string, any>;
|
|
132
143
|
private _query: Record<string, any>;
|
|
144
|
+
private _cookies: Record<string, any>;
|
|
145
|
+
private _cookieJar?: CookieJar;
|
|
146
|
+
private _readCookieJar?: CookieJar;
|
|
147
|
+
private _cookieFileFn?: () => Promise<void>;
|
|
148
|
+
private _cookieFileFnRes?: () => Promise<void>;
|
|
133
149
|
}
|
package/index.js
CHANGED
|
@@ -42,10 +42,59 @@ function makeProxyUrl(proxy, options) {
|
|
|
42
42
|
return uri.toString();
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
+
/**
|
|
46
|
+
* Parse curl command string into arguments array
|
|
47
|
+
*
|
|
48
|
+
* @param {string} command
|
|
49
|
+
* @return {string[]}
|
|
50
|
+
*/
|
|
51
|
+
function parseCommand(command) {
|
|
52
|
+
// Clean up the command - remove line breaks and extra spaces
|
|
53
|
+
command = command.replace(/\\\s*\n/g, ' ').replace(/\s+/g, ' ').trim();
|
|
54
|
+
|
|
55
|
+
const args = [];
|
|
56
|
+
let current = '';
|
|
57
|
+
let inQuotes = false;
|
|
58
|
+
let quoteChar = '';
|
|
59
|
+
|
|
60
|
+
for (let i = 0; i < command.length; i++) {
|
|
61
|
+
const char = command[i];
|
|
62
|
+
const nextChar = command[i + 1];
|
|
63
|
+
|
|
64
|
+
if ((char === '"' || char === "'") && !inQuotes) {
|
|
65
|
+
inQuotes = true;
|
|
66
|
+
quoteChar = char;
|
|
67
|
+
}
|
|
68
|
+
else if (char === quoteChar && inQuotes) {
|
|
69
|
+
inQuotes = false;
|
|
70
|
+
quoteChar = '';
|
|
71
|
+
}
|
|
72
|
+
else if (char === ' ' && !inQuotes) {
|
|
73
|
+
if (current.trim()) {
|
|
74
|
+
args.push(current.trim());
|
|
75
|
+
current = '';
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
else if (char === '\\' && nextChar === quoteChar && inQuotes) {
|
|
79
|
+
current += nextChar;
|
|
80
|
+
i++; // Skip next character
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
current += char;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (current.trim()) {
|
|
88
|
+
args.push(current.trim());
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return args;
|
|
92
|
+
}
|
|
93
|
+
|
|
45
94
|
class CurlResponse {
|
|
46
|
-
constructor() {
|
|
95
|
+
constructor({body = ''} = {}) {
|
|
47
96
|
this.url = '';
|
|
48
|
-
this.body =
|
|
97
|
+
this.body = body;
|
|
49
98
|
this.headers = {};
|
|
50
99
|
this.statusCode = 0;
|
|
51
100
|
this.ip = '';
|
|
@@ -81,6 +130,31 @@ class CurlResponse {
|
|
|
81
130
|
}
|
|
82
131
|
}
|
|
83
132
|
|
|
133
|
+
/**
|
|
134
|
+
* Parse cookies string into object
|
|
135
|
+
*
|
|
136
|
+
* @param {string} cookieStr
|
|
137
|
+
* @return {object}
|
|
138
|
+
*/
|
|
139
|
+
function parseCookies(cookieStr) {
|
|
140
|
+
const cookies = {};
|
|
141
|
+
if (!cookieStr) return cookies;
|
|
142
|
+
const parts = cookieStr.split(';');
|
|
143
|
+
|
|
144
|
+
for (const part of parts) {
|
|
145
|
+
const equalIndex = part.indexOf('=');
|
|
146
|
+
if (equalIndex !== -1) {
|
|
147
|
+
const key = part.slice(0, equalIndex).trim();
|
|
148
|
+
const value = part.slice(equalIndex + 1).trim();
|
|
149
|
+
if (key && value) {
|
|
150
|
+
cookies[key] = value;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
return cookies;
|
|
156
|
+
}
|
|
157
|
+
|
|
84
158
|
class Curl {
|
|
85
159
|
/**
|
|
86
160
|
* Creates a new Curl object.
|
|
@@ -291,10 +365,14 @@ class Curl {
|
|
|
291
365
|
return this;
|
|
292
366
|
}
|
|
293
367
|
|
|
368
|
+
/**
|
|
369
|
+
* @typedef {"chrome"|"chromeMobile"|"edge"|"safari"|"safariMobile"|"firefox"} BrowserType
|
|
370
|
+
*/
|
|
371
|
+
|
|
294
372
|
/**
|
|
295
373
|
* impersonate a browser
|
|
296
374
|
*
|
|
297
|
-
* @param {
|
|
375
|
+
* @param {BrowserType} browser browser to impersonate
|
|
298
376
|
* @return {Curl} self
|
|
299
377
|
*/
|
|
300
378
|
impersonate(browser = 'chrome') {
|
|
@@ -566,6 +644,19 @@ class Curl {
|
|
|
566
644
|
return this;
|
|
567
645
|
}
|
|
568
646
|
|
|
647
|
+
/**
|
|
648
|
+
* @static
|
|
649
|
+
* impersonate a browser
|
|
650
|
+
*
|
|
651
|
+
* @param {BrowserType} browser browser to impersonate
|
|
652
|
+
* @return {Curl} self
|
|
653
|
+
*/
|
|
654
|
+
static impersonate(browser = 'chrome') {
|
|
655
|
+
const curl = new this();
|
|
656
|
+
curl.impersonate(browser);
|
|
657
|
+
return curl;
|
|
658
|
+
}
|
|
659
|
+
|
|
569
660
|
/**
|
|
570
661
|
* Set or unset the followRedirect option for the connection.
|
|
571
662
|
*
|
|
@@ -620,6 +711,16 @@ class Curl {
|
|
|
620
711
|
return this;
|
|
621
712
|
}
|
|
622
713
|
|
|
714
|
+
/**
|
|
715
|
+
* Clear all headers
|
|
716
|
+
*
|
|
717
|
+
* @return {Curl} self
|
|
718
|
+
*/
|
|
719
|
+
clearHeaders() {
|
|
720
|
+
this.options.headers = {};
|
|
721
|
+
return this;
|
|
722
|
+
}
|
|
723
|
+
|
|
623
724
|
/**
|
|
624
725
|
* Set the content type as json
|
|
625
726
|
* Optionally also set the body of the request.
|
|
@@ -739,18 +840,15 @@ class Curl {
|
|
|
739
840
|
*/
|
|
740
841
|
cookie(cookieName, cookieValue) {
|
|
741
842
|
if (cookieValue === undefined) {
|
|
742
|
-
this.
|
|
843
|
+
this.cookies(cookieName);
|
|
743
844
|
}
|
|
744
845
|
else if (typeof cookieName === 'string') {
|
|
745
|
-
if (
|
|
746
|
-
delete this._cookies[cookieName];
|
|
747
|
-
}
|
|
748
|
-
else {
|
|
846
|
+
if (typeof cookieValue === 'string') {
|
|
749
847
|
this._cookies[cookieName] = cookieValue;
|
|
750
848
|
}
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
849
|
+
else if (cookieValue === null || cookieValue === false) {
|
|
850
|
+
delete this._cookies[cookieName];
|
|
851
|
+
}
|
|
754
852
|
}
|
|
755
853
|
|
|
756
854
|
return this;
|
|
@@ -760,7 +858,7 @@ class Curl {
|
|
|
760
858
|
* Sets multiple cookies.
|
|
761
859
|
* Can be used to enable global cookies, if cookies is set to true.
|
|
762
860
|
*
|
|
763
|
-
* @param {object|boolean} cookies object representing the cookies
|
|
861
|
+
* @param {object|boolean|string} cookies object representing the cookies
|
|
764
862
|
* and their values as key:value pairs.
|
|
765
863
|
* @return {Curl} self
|
|
766
864
|
*/
|
|
@@ -771,6 +869,10 @@ class Curl {
|
|
|
771
869
|
else if (cookies && typeof cookies === 'object') {
|
|
772
870
|
Object.assign(this._cookies, cookies);
|
|
773
871
|
}
|
|
872
|
+
else if (typeof cookies === 'string') {
|
|
873
|
+
const cookies = parseCookies(cookieName);
|
|
874
|
+
Object.assign(this._cookies, cookies);
|
|
875
|
+
}
|
|
774
876
|
|
|
775
877
|
return this;
|
|
776
878
|
}
|
|
@@ -783,8 +885,8 @@ class Curl {
|
|
|
783
885
|
*/
|
|
784
886
|
globalCookies(options = true) {
|
|
785
887
|
if (options === false || options === null) {
|
|
786
|
-
delete this.
|
|
787
|
-
delete this.
|
|
888
|
+
delete this._cookieJar;
|
|
889
|
+
delete this._readCookieJar;
|
|
788
890
|
return this;
|
|
789
891
|
}
|
|
790
892
|
|
|
@@ -804,15 +906,24 @@ class Curl {
|
|
|
804
906
|
delete this._cookieFileFnRes;
|
|
805
907
|
|
|
806
908
|
if (options.readOnly) {
|
|
807
|
-
this.
|
|
909
|
+
this._readCookieJar = cookieJar;
|
|
808
910
|
}
|
|
809
911
|
else {
|
|
810
|
-
this.
|
|
912
|
+
this._cookieJar = cookieJar;
|
|
811
913
|
}
|
|
812
914
|
|
|
813
915
|
return this;
|
|
814
916
|
}
|
|
815
917
|
|
|
918
|
+
/**
|
|
919
|
+
* Get the current cookie jar
|
|
920
|
+
*
|
|
921
|
+
* @return {CookieJar|undefined} the current cookie jar
|
|
922
|
+
*/
|
|
923
|
+
getCookieJar() {
|
|
924
|
+
return this._cookieJar || this._readCookieJar;
|
|
925
|
+
}
|
|
926
|
+
|
|
816
927
|
/**
|
|
817
928
|
* Set the value of cookie jar based on a file (cookie store).
|
|
818
929
|
*
|
|
@@ -822,10 +933,10 @@ class Curl {
|
|
|
822
933
|
cookieFile(fileName, options = {}) {
|
|
823
934
|
const setCookieJar = (cookieJar) => {
|
|
824
935
|
if (options.readOnly) {
|
|
825
|
-
this.
|
|
936
|
+
this._readCookieJar = cookieJar;
|
|
826
937
|
}
|
|
827
938
|
else {
|
|
828
|
-
this.
|
|
939
|
+
this._cookieJar = cookieJar;
|
|
829
940
|
}
|
|
830
941
|
};
|
|
831
942
|
|
|
@@ -843,7 +954,7 @@ class Curl {
|
|
|
843
954
|
|
|
844
955
|
if (!options.readOnly) {
|
|
845
956
|
this._cookieFileFnRes = async () => {
|
|
846
|
-
const cookieJar = this.
|
|
957
|
+
const cookieJar = this._cookieJar;
|
|
847
958
|
if (!cookieJar) return;
|
|
848
959
|
try {
|
|
849
960
|
const contents = JSON.stringify(cookieJar.toJSON());
|
|
@@ -1088,6 +1199,205 @@ class Curl {
|
|
|
1088
1199
|
return this;
|
|
1089
1200
|
}
|
|
1090
1201
|
|
|
1202
|
+
/**
|
|
1203
|
+
* Set if the body is to be returned as a buffer
|
|
1204
|
+
*
|
|
1205
|
+
* @param {boolean} [returnAsBuffer=true]
|
|
1206
|
+
* @return {Curl} self
|
|
1207
|
+
*/
|
|
1208
|
+
asBuffer(returnAsBuffer = true) {
|
|
1209
|
+
this.options.asBuffer = returnAsBuffer;
|
|
1210
|
+
return this;
|
|
1211
|
+
}
|
|
1212
|
+
|
|
1213
|
+
/**
|
|
1214
|
+
* Create a clone of this Curl instance with the same options
|
|
1215
|
+
*
|
|
1216
|
+
* @return {Curl} new Curl instance with cloned options
|
|
1217
|
+
*/
|
|
1218
|
+
clone() {
|
|
1219
|
+
const cloned = new this.constructor();
|
|
1220
|
+
|
|
1221
|
+
// Deep clone the options object (only serializable properties)
|
|
1222
|
+
cloned.options = JSON.parse(JSON.stringify(this.options));
|
|
1223
|
+
|
|
1224
|
+
// Copy non-serializable properties
|
|
1225
|
+
if (this._cookieJar) {
|
|
1226
|
+
cloned._cookieJar = this._cookieJar;
|
|
1227
|
+
}
|
|
1228
|
+
if (this._readCookieJar) {
|
|
1229
|
+
cloned._readCookieJar = this._readCookieJar;
|
|
1230
|
+
}
|
|
1231
|
+
if (this._cookieFileFn) {
|
|
1232
|
+
cloned._cookieFileFn = this._cookieFileFn;
|
|
1233
|
+
}
|
|
1234
|
+
if (this._cookieFileFnRes) {
|
|
1235
|
+
cloned._cookieFileFnRes = this._cookieFileFnRes;
|
|
1236
|
+
}
|
|
1237
|
+
|
|
1238
|
+
return cloned;
|
|
1239
|
+
}
|
|
1240
|
+
|
|
1241
|
+
/**
|
|
1242
|
+
* Export the current request as a curl command string
|
|
1243
|
+
*
|
|
1244
|
+
* @return {Promise<string>} curl command as a string
|
|
1245
|
+
*/
|
|
1246
|
+
async exportAsCurl() {
|
|
1247
|
+
const cmd = this.options.cliCommand || 'curl';
|
|
1248
|
+
const args = await this.getCurlArgs();
|
|
1249
|
+
// Filter out internal options that shouldn't appear in exported commands
|
|
1250
|
+
const skipOptions = ['--silent', '--no-keepalive', '--keepalive', '--write-out', '--insecure', '--location'];
|
|
1251
|
+
const escape = arg => `'${arg.replace(/'/g, "'\"'\"'")}'`;
|
|
1252
|
+
|
|
1253
|
+
const url = args[0];
|
|
1254
|
+
const lines = [`${cmd} ${escape(url)}`];
|
|
1255
|
+
for (let i = 1; i < args.length; i++) {
|
|
1256
|
+
const arg = String(args[i]);
|
|
1257
|
+
const nextArg = String(args[i + 1] ?? '');
|
|
1258
|
+
const isNextArgValue = !nextArg.startsWith('--');
|
|
1259
|
+
if (isNextArgValue) {
|
|
1260
|
+
i++;
|
|
1261
|
+
}
|
|
1262
|
+
// Skip internal options and their values
|
|
1263
|
+
if (skipOptions.includes(arg)) continue;
|
|
1264
|
+
if (isNextArgValue) {
|
|
1265
|
+
const escapedNextArg = /[\s"'`&:;,><|(){}~^%#@!/=+\$\?\*\[\]]/.test(nextArg) ? escape(nextArg) : nextArg;
|
|
1266
|
+
lines.push(`${arg} ${escapedNextArg}`);
|
|
1267
|
+
}
|
|
1268
|
+
else {
|
|
1269
|
+
lines.push(arg);
|
|
1270
|
+
}
|
|
1271
|
+
}
|
|
1272
|
+
|
|
1273
|
+
return lines.join(' \\\n ');
|
|
1274
|
+
}
|
|
1275
|
+
|
|
1276
|
+
/**
|
|
1277
|
+
* @static
|
|
1278
|
+
* Create a new Curl instance from a curl command string
|
|
1279
|
+
*
|
|
1280
|
+
* @param {string} curlCommand curl command string to parse
|
|
1281
|
+
* @return {Curl} new Curl instance
|
|
1282
|
+
*/
|
|
1283
|
+
static fromCurl(curlCommand) {
|
|
1284
|
+
const curl = new this();
|
|
1285
|
+
curl.options.headers = {};
|
|
1286
|
+
|
|
1287
|
+
// Parse the command into arguments
|
|
1288
|
+
const args = parseCommand(curlCommand);
|
|
1289
|
+
|
|
1290
|
+
let method;
|
|
1291
|
+
let body;
|
|
1292
|
+
|
|
1293
|
+
// Process arguments
|
|
1294
|
+
for (let i = 0; i < args.length; i++) {
|
|
1295
|
+
const arg = args[i];
|
|
1296
|
+
const nextArg = args[i + 1];
|
|
1297
|
+
const isNextArgValue = nextArg && !nextArg.startsWith('-');
|
|
1298
|
+
|
|
1299
|
+
if (i === 0 && !arg.startsWith('curl')) {
|
|
1300
|
+
// First argument should be the URL if no 'curl' command prefix
|
|
1301
|
+
curl.url(arg);
|
|
1302
|
+
}
|
|
1303
|
+
else if (i === 1 && args[0].startsWith('curl')) {
|
|
1304
|
+
// Second argument is URL when first is 'curl'
|
|
1305
|
+
curl.url(arg);
|
|
1306
|
+
}
|
|
1307
|
+
else if (arg === '--request' || arg === '-X') {
|
|
1308
|
+
if (!isNextArgValue) continue;
|
|
1309
|
+
method = nextArg;
|
|
1310
|
+
i++;
|
|
1311
|
+
}
|
|
1312
|
+
else if (arg === '--header' || arg === '-H') {
|
|
1313
|
+
if (!isNextArgValue) continue;
|
|
1314
|
+
const colonIndex = nextArg.indexOf(':');
|
|
1315
|
+
if (colonIndex !== -1) {
|
|
1316
|
+
const headerName = nextArg.slice(0, colonIndex).trim();
|
|
1317
|
+
const headerValue = nextArg.slice(colonIndex + 1).trim();
|
|
1318
|
+
curl.header(headerName, headerValue);
|
|
1319
|
+
}
|
|
1320
|
+
i++;
|
|
1321
|
+
}
|
|
1322
|
+
else if (arg === '--data' || arg === '--data-raw' || arg === '-d') {
|
|
1323
|
+
if (!isNextArgValue) continue;
|
|
1324
|
+
body = nextArg;
|
|
1325
|
+
i++;
|
|
1326
|
+
}
|
|
1327
|
+
else if (arg === '--cookie' || arg === '-b') {
|
|
1328
|
+
if (!isNextArgValue) continue;
|
|
1329
|
+
curl.header('cookie', nextArg);
|
|
1330
|
+
i++;
|
|
1331
|
+
}
|
|
1332
|
+
else if (arg === '--user-agent' || arg === '-A') {
|
|
1333
|
+
if (!isNextArgValue) continue;
|
|
1334
|
+
curl.userAgent(nextArg);
|
|
1335
|
+
i++;
|
|
1336
|
+
}
|
|
1337
|
+
else if (arg === '--referer' || arg === '-e') {
|
|
1338
|
+
if (!isNextArgValue) continue;
|
|
1339
|
+
curl.referer(nextArg);
|
|
1340
|
+
i++;
|
|
1341
|
+
}
|
|
1342
|
+
else if (arg === '--max-time' || arg === '-m') {
|
|
1343
|
+
if (!isNextArgValue) continue;
|
|
1344
|
+
const timeout = Number(nextArg);
|
|
1345
|
+
if (timeout > 0) curl.timeout(timeout);
|
|
1346
|
+
i++;
|
|
1347
|
+
}
|
|
1348
|
+
else if (arg === '--proxy' || arg === '-x') {
|
|
1349
|
+
if (!isNextArgValue) continue;
|
|
1350
|
+
curl.proxy(nextArg);
|
|
1351
|
+
i++;
|
|
1352
|
+
}
|
|
1353
|
+
else if (arg === '--compressed') {
|
|
1354
|
+
curl.compress(true);
|
|
1355
|
+
}
|
|
1356
|
+
else if (arg === '--location' || arg === '-L') {
|
|
1357
|
+
curl.followRedirect(true);
|
|
1358
|
+
}
|
|
1359
|
+
else if (arg === '--max-redirs') {
|
|
1360
|
+
if (!isNextArgValue) continue;
|
|
1361
|
+
const maxRedir = Math.floor(Number(nextArg));
|
|
1362
|
+
if (maxRedir > 0) curl.maxRedirects(maxRedir);
|
|
1363
|
+
i++;
|
|
1364
|
+
}
|
|
1365
|
+
else if (arg === '--verbose' || arg === '-v') {
|
|
1366
|
+
curl.verbose(true);
|
|
1367
|
+
}
|
|
1368
|
+
else if (arg.startsWith('--') || arg.startsWith('-')) {
|
|
1369
|
+
// Ignore internal options that shouldn't be imported
|
|
1370
|
+
const ignoreOptions = ['--silent', '--no-keepalive', '--keepalive', '--write-out', '--insecure'];
|
|
1371
|
+
if (ignoreOptions.includes(arg)) {
|
|
1372
|
+
// If the ignored option has a value, ignore it too
|
|
1373
|
+
if (isNextArgValue) {
|
|
1374
|
+
i++;
|
|
1375
|
+
}
|
|
1376
|
+
}
|
|
1377
|
+
else {
|
|
1378
|
+
// Handle unknown curl options by adding them as cliOptions
|
|
1379
|
+
if (isNextArgValue) {
|
|
1380
|
+
curl.cliOptions([arg, nextArg]);
|
|
1381
|
+
i++; // Skip next argument
|
|
1382
|
+
}
|
|
1383
|
+
else {
|
|
1384
|
+
curl.cliOptions(arg);
|
|
1385
|
+
}
|
|
1386
|
+
}
|
|
1387
|
+
}
|
|
1388
|
+
}
|
|
1389
|
+
|
|
1390
|
+
if (body) {
|
|
1391
|
+
curl.body(body);
|
|
1392
|
+
method = method || 'POST';
|
|
1393
|
+
}
|
|
1394
|
+
if (method) {
|
|
1395
|
+
curl.method(method);
|
|
1396
|
+
}
|
|
1397
|
+
|
|
1398
|
+
return curl;
|
|
1399
|
+
}
|
|
1400
|
+
|
|
1091
1401
|
/**
|
|
1092
1402
|
* Add the options 'fields' to the options body, form or qs
|
|
1093
1403
|
* on the basis of the request method.
|
|
@@ -1167,7 +1477,7 @@ class Curl {
|
|
|
1167
1477
|
await this._cookieFileFn();
|
|
1168
1478
|
}
|
|
1169
1479
|
|
|
1170
|
-
const cookieJar =
|
|
1480
|
+
const cookieJar = this._readCookieJar || this._cookieJar;
|
|
1171
1481
|
if (cookieJar) {
|
|
1172
1482
|
const jarCookies = await cookieJar.getCookies(options.url);
|
|
1173
1483
|
if (jarCookies) {
|
|
@@ -1203,19 +1513,12 @@ class Curl {
|
|
|
1203
1513
|
'%{stderr}===<json>==={"json":%{json},"headers":%{header_json}}===</json>===',
|
|
1204
1514
|
];
|
|
1205
1515
|
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
const cookieHeader = await this.getCookieHeader();
|
|
1211
|
-
if (cookieHeader) {
|
|
1212
|
-
this.header('cookie', cookieHeader);
|
|
1516
|
+
const cliOptions = options.cliOptions;
|
|
1517
|
+
if (cliOptions) {
|
|
1518
|
+
args.push(...cliOptions);
|
|
1213
1519
|
}
|
|
1214
1520
|
|
|
1215
|
-
|
|
1216
|
-
args.push('--header', `${key}: ${value}`);
|
|
1217
|
-
}
|
|
1218
|
-
if (options.compress) {
|
|
1521
|
+
if (options.compress && !cliOptions?.includes('--compressed')) {
|
|
1219
1522
|
args.push('--compressed');
|
|
1220
1523
|
}
|
|
1221
1524
|
if (options.proxy && options.useProxy !== false) {
|
|
@@ -1237,9 +1540,17 @@ class Curl {
|
|
|
1237
1540
|
args.push('--verbose');
|
|
1238
1541
|
}
|
|
1239
1542
|
|
|
1240
|
-
const
|
|
1241
|
-
if (
|
|
1242
|
-
|
|
1543
|
+
const cookieHeader = await this.getCookieHeader();
|
|
1544
|
+
if (cookieHeader) {
|
|
1545
|
+
this.header('cookie', cookieHeader);
|
|
1546
|
+
}
|
|
1547
|
+
|
|
1548
|
+
for (const [key, value] of Object.entries(options.headers)) {
|
|
1549
|
+
args.push('--header', `${key}: ${value}`);
|
|
1550
|
+
}
|
|
1551
|
+
|
|
1552
|
+
if (body) {
|
|
1553
|
+
args.push('--data-raw', body);
|
|
1243
1554
|
}
|
|
1244
1555
|
|
|
1245
1556
|
return args;
|
|
@@ -1247,18 +1558,28 @@ class Curl {
|
|
|
1247
1558
|
|
|
1248
1559
|
async fetch() {
|
|
1249
1560
|
const startTime = Date.now();
|
|
1250
|
-
const
|
|
1561
|
+
const options = this.options;
|
|
1562
|
+
const cmd = options.cliCommand || 'curl';
|
|
1251
1563
|
const args = await this.getCurlArgs();
|
|
1252
1564
|
const curl = spawn(cmd, args);
|
|
1253
|
-
const cookieJar = this.
|
|
1565
|
+
const cookieJar = this._cookieJar;
|
|
1254
1566
|
|
|
1255
|
-
let stdout = '';
|
|
1567
|
+
let stdout = options.asBuffer ? Buffer.from([]) : '';
|
|
1256
1568
|
let stderr = '';
|
|
1257
|
-
const response = new CurlResponse(
|
|
1569
|
+
const response = new CurlResponse({
|
|
1570
|
+
body: options.asBuffer ? Buffer.from([]) : '',
|
|
1571
|
+
});
|
|
1258
1572
|
return new Promise((resolve, reject) => {
|
|
1259
|
-
|
|
1260
|
-
stdout
|
|
1261
|
-
|
|
1573
|
+
if (options.asBuffer) {
|
|
1574
|
+
curl.stdout.on('data', (data) => {
|
|
1575
|
+
stdout = Buffer.concat([stdout, data]);
|
|
1576
|
+
});
|
|
1577
|
+
}
|
|
1578
|
+
else {
|
|
1579
|
+
curl.stdout.on('data', (data) => {
|
|
1580
|
+
stdout += data;
|
|
1581
|
+
});
|
|
1582
|
+
}
|
|
1262
1583
|
|
|
1263
1584
|
curl.stderr.on('data', (data) => {
|
|
1264
1585
|
stderr += data;
|
|
@@ -1272,7 +1593,7 @@ class Curl {
|
|
|
1272
1593
|
curl.on('close', async (code) => {
|
|
1273
1594
|
response.timeTaken = Date.now() - startTime;
|
|
1274
1595
|
response.exitCode = code;
|
|
1275
|
-
response.url =
|
|
1596
|
+
response.url = options.url;
|
|
1276
1597
|
response.body = stdout;
|
|
1277
1598
|
stderr = stderr.replace(/===<json>===(.*)===<\/json>===/s, (match, p1) => {
|
|
1278
1599
|
try {
|