curl-wrap 2.0.1 → 2.1.1
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 +364 -40
- 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,19 +840,19 @@ 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 (
|
|
846
|
+
if (typeof cookieValue === 'string') {
|
|
847
|
+
this._cookies[cookieName] = cookieValue;
|
|
848
|
+
}
|
|
849
|
+
else if (cookieValue === null || cookieValue === false) {
|
|
746
850
|
delete this._cookies[cookieName];
|
|
747
851
|
}
|
|
748
852
|
else {
|
|
749
|
-
this._cookies[cookieName] = cookieValue;
|
|
853
|
+
this._cookies[cookieName] = JSON.stringify(cookieValue);
|
|
750
854
|
}
|
|
751
855
|
}
|
|
752
|
-
else if (cookieName && typeof cookieName === 'object') {
|
|
753
|
-
Object.assign(this._cookies, cookieName);
|
|
754
|
-
}
|
|
755
856
|
|
|
756
857
|
return this;
|
|
757
858
|
}
|
|
@@ -760,7 +861,7 @@ class Curl {
|
|
|
760
861
|
* Sets multiple cookies.
|
|
761
862
|
* Can be used to enable global cookies, if cookies is set to true.
|
|
762
863
|
*
|
|
763
|
-
* @param {object|boolean} cookies object representing the cookies
|
|
864
|
+
* @param {object|boolean|string} cookies object representing the cookies
|
|
764
865
|
* and their values as key:value pairs.
|
|
765
866
|
* @return {Curl} self
|
|
766
867
|
*/
|
|
@@ -771,6 +872,10 @@ class Curl {
|
|
|
771
872
|
else if (cookies && typeof cookies === 'object') {
|
|
772
873
|
Object.assign(this._cookies, cookies);
|
|
773
874
|
}
|
|
875
|
+
else if (typeof cookies === 'string') {
|
|
876
|
+
const cookies = parseCookies(cookieName);
|
|
877
|
+
Object.assign(this._cookies, cookies);
|
|
878
|
+
}
|
|
774
879
|
|
|
775
880
|
return this;
|
|
776
881
|
}
|
|
@@ -783,8 +888,8 @@ class Curl {
|
|
|
783
888
|
*/
|
|
784
889
|
globalCookies(options = true) {
|
|
785
890
|
if (options === false || options === null) {
|
|
786
|
-
delete this.
|
|
787
|
-
delete this.
|
|
891
|
+
delete this._cookieJar;
|
|
892
|
+
delete this._readCookieJar;
|
|
788
893
|
return this;
|
|
789
894
|
}
|
|
790
895
|
|
|
@@ -804,15 +909,24 @@ class Curl {
|
|
|
804
909
|
delete this._cookieFileFnRes;
|
|
805
910
|
|
|
806
911
|
if (options.readOnly) {
|
|
807
|
-
this.
|
|
912
|
+
this._readCookieJar = cookieJar;
|
|
808
913
|
}
|
|
809
914
|
else {
|
|
810
|
-
this.
|
|
915
|
+
this._cookieJar = cookieJar;
|
|
811
916
|
}
|
|
812
917
|
|
|
813
918
|
return this;
|
|
814
919
|
}
|
|
815
920
|
|
|
921
|
+
/**
|
|
922
|
+
* Get the current cookie jar
|
|
923
|
+
*
|
|
924
|
+
* @return {CookieJar|undefined} the current cookie jar
|
|
925
|
+
*/
|
|
926
|
+
getCookieJar() {
|
|
927
|
+
return this._cookieJar || this._readCookieJar;
|
|
928
|
+
}
|
|
929
|
+
|
|
816
930
|
/**
|
|
817
931
|
* Set the value of cookie jar based on a file (cookie store).
|
|
818
932
|
*
|
|
@@ -822,10 +936,10 @@ class Curl {
|
|
|
822
936
|
cookieFile(fileName, options = {}) {
|
|
823
937
|
const setCookieJar = (cookieJar) => {
|
|
824
938
|
if (options.readOnly) {
|
|
825
|
-
this.
|
|
939
|
+
this._readCookieJar = cookieJar;
|
|
826
940
|
}
|
|
827
941
|
else {
|
|
828
|
-
this.
|
|
942
|
+
this._cookieJar = cookieJar;
|
|
829
943
|
}
|
|
830
944
|
};
|
|
831
945
|
|
|
@@ -843,7 +957,7 @@ class Curl {
|
|
|
843
957
|
|
|
844
958
|
if (!options.readOnly) {
|
|
845
959
|
this._cookieFileFnRes = async () => {
|
|
846
|
-
const cookieJar = this.
|
|
960
|
+
const cookieJar = this._cookieJar;
|
|
847
961
|
if (!cookieJar) return;
|
|
848
962
|
try {
|
|
849
963
|
const contents = JSON.stringify(cookieJar.toJSON());
|
|
@@ -1088,6 +1202,205 @@ class Curl {
|
|
|
1088
1202
|
return this;
|
|
1089
1203
|
}
|
|
1090
1204
|
|
|
1205
|
+
/**
|
|
1206
|
+
* Set if the body is to be returned as a buffer
|
|
1207
|
+
*
|
|
1208
|
+
* @param {boolean} [returnAsBuffer=true]
|
|
1209
|
+
* @return {Curl} self
|
|
1210
|
+
*/
|
|
1211
|
+
asBuffer(returnAsBuffer = true) {
|
|
1212
|
+
this.options.asBuffer = returnAsBuffer;
|
|
1213
|
+
return this;
|
|
1214
|
+
}
|
|
1215
|
+
|
|
1216
|
+
/**
|
|
1217
|
+
* Create a clone of this Curl instance with the same options
|
|
1218
|
+
*
|
|
1219
|
+
* @return {Curl} new Curl instance with cloned options
|
|
1220
|
+
*/
|
|
1221
|
+
clone() {
|
|
1222
|
+
const cloned = new this.constructor();
|
|
1223
|
+
|
|
1224
|
+
// Deep clone the options object (only serializable properties)
|
|
1225
|
+
cloned.options = JSON.parse(JSON.stringify(this.options));
|
|
1226
|
+
|
|
1227
|
+
// Copy non-serializable properties
|
|
1228
|
+
if (this._cookieJar) {
|
|
1229
|
+
cloned._cookieJar = this._cookieJar;
|
|
1230
|
+
}
|
|
1231
|
+
if (this._readCookieJar) {
|
|
1232
|
+
cloned._readCookieJar = this._readCookieJar;
|
|
1233
|
+
}
|
|
1234
|
+
if (this._cookieFileFn) {
|
|
1235
|
+
cloned._cookieFileFn = this._cookieFileFn;
|
|
1236
|
+
}
|
|
1237
|
+
if (this._cookieFileFnRes) {
|
|
1238
|
+
cloned._cookieFileFnRes = this._cookieFileFnRes;
|
|
1239
|
+
}
|
|
1240
|
+
|
|
1241
|
+
return cloned;
|
|
1242
|
+
}
|
|
1243
|
+
|
|
1244
|
+
/**
|
|
1245
|
+
* Export the current request as a curl command string
|
|
1246
|
+
*
|
|
1247
|
+
* @return {Promise<string>} curl command as a string
|
|
1248
|
+
*/
|
|
1249
|
+
async exportAsCurl() {
|
|
1250
|
+
const cmd = this.options.cliCommand || 'curl';
|
|
1251
|
+
const args = await this.getCurlArgs();
|
|
1252
|
+
// Filter out internal options that shouldn't appear in exported commands
|
|
1253
|
+
const skipOptions = ['--silent', '--no-keepalive', '--keepalive', '--write-out', '--insecure', '--location'];
|
|
1254
|
+
const escape = arg => `'${arg.replace(/'/g, "'\"'\"'")}'`;
|
|
1255
|
+
|
|
1256
|
+
const url = args[0];
|
|
1257
|
+
const lines = [`${cmd} ${escape(url)}`];
|
|
1258
|
+
for (let i = 1; i < args.length; i++) {
|
|
1259
|
+
const arg = String(args[i]);
|
|
1260
|
+
const nextArg = String(args[i + 1] ?? '');
|
|
1261
|
+
const isNextArgValue = !nextArg.startsWith('--');
|
|
1262
|
+
if (isNextArgValue) {
|
|
1263
|
+
i++;
|
|
1264
|
+
}
|
|
1265
|
+
// Skip internal options and their values
|
|
1266
|
+
if (skipOptions.includes(arg)) continue;
|
|
1267
|
+
if (isNextArgValue) {
|
|
1268
|
+
const escapedNextArg = /[\s"'`&:;,><|(){}~^%#@!/=+\$\?\*\[\]]/.test(nextArg) ? escape(nextArg) : nextArg;
|
|
1269
|
+
lines.push(`${arg} ${escapedNextArg}`);
|
|
1270
|
+
}
|
|
1271
|
+
else {
|
|
1272
|
+
lines.push(arg);
|
|
1273
|
+
}
|
|
1274
|
+
}
|
|
1275
|
+
|
|
1276
|
+
return lines.join(' \\\n ');
|
|
1277
|
+
}
|
|
1278
|
+
|
|
1279
|
+
/**
|
|
1280
|
+
* @static
|
|
1281
|
+
* Create a new Curl instance from a curl command string
|
|
1282
|
+
*
|
|
1283
|
+
* @param {string} curlCommand curl command string to parse
|
|
1284
|
+
* @return {Curl} new Curl instance
|
|
1285
|
+
*/
|
|
1286
|
+
static fromCurl(curlCommand) {
|
|
1287
|
+
const curl = new this();
|
|
1288
|
+
curl.options.headers = {};
|
|
1289
|
+
|
|
1290
|
+
// Parse the command into arguments
|
|
1291
|
+
const args = parseCommand(curlCommand);
|
|
1292
|
+
|
|
1293
|
+
let method;
|
|
1294
|
+
let body;
|
|
1295
|
+
|
|
1296
|
+
// Process arguments
|
|
1297
|
+
for (let i = 0; i < args.length; i++) {
|
|
1298
|
+
const arg = args[i];
|
|
1299
|
+
const nextArg = args[i + 1];
|
|
1300
|
+
const isNextArgValue = nextArg && !nextArg.startsWith('-');
|
|
1301
|
+
|
|
1302
|
+
if (i === 0 && !arg.startsWith('curl')) {
|
|
1303
|
+
// First argument should be the URL if no 'curl' command prefix
|
|
1304
|
+
curl.url(arg);
|
|
1305
|
+
}
|
|
1306
|
+
else if (i === 1 && args[0].startsWith('curl')) {
|
|
1307
|
+
// Second argument is URL when first is 'curl'
|
|
1308
|
+
curl.url(arg);
|
|
1309
|
+
}
|
|
1310
|
+
else if (arg === '--request' || arg === '-X') {
|
|
1311
|
+
if (!isNextArgValue) continue;
|
|
1312
|
+
method = nextArg;
|
|
1313
|
+
i++;
|
|
1314
|
+
}
|
|
1315
|
+
else if (arg === '--header' || arg === '-H') {
|
|
1316
|
+
if (!isNextArgValue) continue;
|
|
1317
|
+
const colonIndex = nextArg.indexOf(':');
|
|
1318
|
+
if (colonIndex !== -1) {
|
|
1319
|
+
const headerName = nextArg.slice(0, colonIndex).trim();
|
|
1320
|
+
const headerValue = nextArg.slice(colonIndex + 1).trim();
|
|
1321
|
+
curl.header(headerName, headerValue);
|
|
1322
|
+
}
|
|
1323
|
+
i++;
|
|
1324
|
+
}
|
|
1325
|
+
else if (arg === '--data' || arg === '--data-raw' || arg === '-d') {
|
|
1326
|
+
if (!isNextArgValue) continue;
|
|
1327
|
+
body = nextArg;
|
|
1328
|
+
i++;
|
|
1329
|
+
}
|
|
1330
|
+
else if (arg === '--cookie' || arg === '-b') {
|
|
1331
|
+
if (!isNextArgValue) continue;
|
|
1332
|
+
curl.header('cookie', nextArg);
|
|
1333
|
+
i++;
|
|
1334
|
+
}
|
|
1335
|
+
else if (arg === '--user-agent' || arg === '-A') {
|
|
1336
|
+
if (!isNextArgValue) continue;
|
|
1337
|
+
curl.userAgent(nextArg);
|
|
1338
|
+
i++;
|
|
1339
|
+
}
|
|
1340
|
+
else if (arg === '--referer' || arg === '-e') {
|
|
1341
|
+
if (!isNextArgValue) continue;
|
|
1342
|
+
curl.referer(nextArg);
|
|
1343
|
+
i++;
|
|
1344
|
+
}
|
|
1345
|
+
else if (arg === '--max-time' || arg === '-m') {
|
|
1346
|
+
if (!isNextArgValue) continue;
|
|
1347
|
+
const timeout = Number(nextArg);
|
|
1348
|
+
if (timeout > 0) curl.timeout(timeout);
|
|
1349
|
+
i++;
|
|
1350
|
+
}
|
|
1351
|
+
else if (arg === '--proxy' || arg === '-x') {
|
|
1352
|
+
if (!isNextArgValue) continue;
|
|
1353
|
+
curl.proxy(nextArg);
|
|
1354
|
+
i++;
|
|
1355
|
+
}
|
|
1356
|
+
else if (arg === '--compressed') {
|
|
1357
|
+
curl.compress(true);
|
|
1358
|
+
}
|
|
1359
|
+
else if (arg === '--location' || arg === '-L') {
|
|
1360
|
+
curl.followRedirect(true);
|
|
1361
|
+
}
|
|
1362
|
+
else if (arg === '--max-redirs') {
|
|
1363
|
+
if (!isNextArgValue) continue;
|
|
1364
|
+
const maxRedir = Math.floor(Number(nextArg));
|
|
1365
|
+
if (maxRedir > 0) curl.maxRedirects(maxRedir);
|
|
1366
|
+
i++;
|
|
1367
|
+
}
|
|
1368
|
+
else if (arg === '--verbose' || arg === '-v') {
|
|
1369
|
+
curl.verbose(true);
|
|
1370
|
+
}
|
|
1371
|
+
else if (arg.startsWith('--') || arg.startsWith('-')) {
|
|
1372
|
+
// Ignore internal options that shouldn't be imported
|
|
1373
|
+
const ignoreOptions = ['--silent', '--no-keepalive', '--keepalive', '--write-out', '--insecure'];
|
|
1374
|
+
if (ignoreOptions.includes(arg)) {
|
|
1375
|
+
// If the ignored option has a value, ignore it too
|
|
1376
|
+
if (isNextArgValue) {
|
|
1377
|
+
i++;
|
|
1378
|
+
}
|
|
1379
|
+
}
|
|
1380
|
+
else {
|
|
1381
|
+
// Handle unknown curl options by adding them as cliOptions
|
|
1382
|
+
if (isNextArgValue) {
|
|
1383
|
+
curl.cliOptions([arg, nextArg]);
|
|
1384
|
+
i++; // Skip next argument
|
|
1385
|
+
}
|
|
1386
|
+
else {
|
|
1387
|
+
curl.cliOptions(arg);
|
|
1388
|
+
}
|
|
1389
|
+
}
|
|
1390
|
+
}
|
|
1391
|
+
}
|
|
1392
|
+
|
|
1393
|
+
if (body) {
|
|
1394
|
+
curl.body(body);
|
|
1395
|
+
method = method || 'POST';
|
|
1396
|
+
}
|
|
1397
|
+
if (method) {
|
|
1398
|
+
curl.method(method);
|
|
1399
|
+
}
|
|
1400
|
+
|
|
1401
|
+
return curl;
|
|
1402
|
+
}
|
|
1403
|
+
|
|
1091
1404
|
/**
|
|
1092
1405
|
* Add the options 'fields' to the options body, form or qs
|
|
1093
1406
|
* on the basis of the request method.
|
|
@@ -1167,7 +1480,7 @@ class Curl {
|
|
|
1167
1480
|
await this._cookieFileFn();
|
|
1168
1481
|
}
|
|
1169
1482
|
|
|
1170
|
-
const cookieJar =
|
|
1483
|
+
const cookieJar = this._readCookieJar || this._cookieJar;
|
|
1171
1484
|
if (cookieJar) {
|
|
1172
1485
|
const jarCookies = await cookieJar.getCookies(options.url);
|
|
1173
1486
|
if (jarCookies) {
|
|
@@ -1203,19 +1516,12 @@ class Curl {
|
|
|
1203
1516
|
'%{stderr}===<json>==={"json":%{json},"headers":%{header_json}}===</json>===',
|
|
1204
1517
|
];
|
|
1205
1518
|
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
const cookieHeader = await this.getCookieHeader();
|
|
1211
|
-
if (cookieHeader) {
|
|
1212
|
-
this.header('cookie', cookieHeader);
|
|
1519
|
+
const cliOptions = options.cliOptions;
|
|
1520
|
+
if (cliOptions) {
|
|
1521
|
+
args.push(...cliOptions);
|
|
1213
1522
|
}
|
|
1214
1523
|
|
|
1215
|
-
|
|
1216
|
-
args.push('--header', `${key}: ${value}`);
|
|
1217
|
-
}
|
|
1218
|
-
if (options.compress) {
|
|
1524
|
+
if (options.compress && !cliOptions?.includes('--compressed')) {
|
|
1219
1525
|
args.push('--compressed');
|
|
1220
1526
|
}
|
|
1221
1527
|
if (options.proxy && options.useProxy !== false) {
|
|
@@ -1237,9 +1543,17 @@ class Curl {
|
|
|
1237
1543
|
args.push('--verbose');
|
|
1238
1544
|
}
|
|
1239
1545
|
|
|
1240
|
-
const
|
|
1241
|
-
if (
|
|
1242
|
-
|
|
1546
|
+
const cookieHeader = await this.getCookieHeader();
|
|
1547
|
+
if (cookieHeader) {
|
|
1548
|
+
this.header('cookie', cookieHeader);
|
|
1549
|
+
}
|
|
1550
|
+
|
|
1551
|
+
for (const [key, value] of Object.entries(options.headers)) {
|
|
1552
|
+
args.push('--header', `${key}: ${value}`);
|
|
1553
|
+
}
|
|
1554
|
+
|
|
1555
|
+
if (body) {
|
|
1556
|
+
args.push('--data-raw', body);
|
|
1243
1557
|
}
|
|
1244
1558
|
|
|
1245
1559
|
return args;
|
|
@@ -1247,18 +1561,28 @@ class Curl {
|
|
|
1247
1561
|
|
|
1248
1562
|
async fetch() {
|
|
1249
1563
|
const startTime = Date.now();
|
|
1250
|
-
const
|
|
1564
|
+
const options = this.options;
|
|
1565
|
+
const cmd = options.cliCommand || 'curl';
|
|
1251
1566
|
const args = await this.getCurlArgs();
|
|
1252
1567
|
const curl = spawn(cmd, args);
|
|
1253
|
-
const cookieJar = this.
|
|
1568
|
+
const cookieJar = this._cookieJar;
|
|
1254
1569
|
|
|
1255
|
-
let stdout = '';
|
|
1570
|
+
let stdout = options.asBuffer ? Buffer.from([]) : '';
|
|
1256
1571
|
let stderr = '';
|
|
1257
|
-
const response = new CurlResponse(
|
|
1572
|
+
const response = new CurlResponse({
|
|
1573
|
+
body: options.asBuffer ? Buffer.from([]) : '',
|
|
1574
|
+
});
|
|
1258
1575
|
return new Promise((resolve, reject) => {
|
|
1259
|
-
|
|
1260
|
-
stdout
|
|
1261
|
-
|
|
1576
|
+
if (options.asBuffer) {
|
|
1577
|
+
curl.stdout.on('data', (data) => {
|
|
1578
|
+
stdout = Buffer.concat([stdout, data]);
|
|
1579
|
+
});
|
|
1580
|
+
}
|
|
1581
|
+
else {
|
|
1582
|
+
curl.stdout.on('data', (data) => {
|
|
1583
|
+
stdout += data;
|
|
1584
|
+
});
|
|
1585
|
+
}
|
|
1262
1586
|
|
|
1263
1587
|
curl.stderr.on('data', (data) => {
|
|
1264
1588
|
stderr += data;
|
|
@@ -1272,7 +1596,7 @@ class Curl {
|
|
|
1272
1596
|
curl.on('close', async (code) => {
|
|
1273
1597
|
response.timeTaken = Date.now() - startTime;
|
|
1274
1598
|
response.exitCode = code;
|
|
1275
|
-
response.url =
|
|
1599
|
+
response.url = options.url;
|
|
1276
1600
|
response.body = stdout;
|
|
1277
1601
|
stderr = stderr.replace(/===<json>===(.*)===<\/json>===/s, (match, p1) => {
|
|
1278
1602
|
try {
|