curb 1.3.1 → 1.3.3

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.
data/ext/curb_easy.c CHANGED
@@ -107,6 +107,11 @@ static VALUE call_stream_to_s(VALUE stream) {
107
107
  return rb_funcall(stream, rb_intern("to_s"), 0);
108
108
  }
109
109
 
110
+ static VALUE call_string_value(VALUE str) {
111
+ StringValue(str);
112
+ return str;
113
+ }
114
+
110
115
  struct stream_seek_call_args {
111
116
  VALUE stream;
112
117
  curl_off_t offset;
@@ -141,6 +146,30 @@ static VALUE call_with_easy_callback_active(VALUE argp) {
141
146
  return with_easy_callback_active(args->rbce, args->func, args->arg);
142
147
  }
143
148
 
149
+ static VALUE rescue_easy_callback(ruby_curl_easy *rbce, VALUE (*func)(VALUE), VALUE arg) {
150
+ struct easy_callback_dispatch_args dispatch_args;
151
+ dispatch_args.rbce = rbce;
152
+ dispatch_args.func = func;
153
+ dispatch_args.arg = arg;
154
+ return rb_rescue(call_with_easy_callback_active, (VALUE)&dispatch_args, callback_exception_store_on_easy, (VALUE)rbce);
155
+ }
156
+
157
+ static size_t curl_read_abort_result(void) {
158
+ #ifdef CURL_READFUNC_ABORT
159
+ return CURL_READFUNC_ABORT;
160
+ #else
161
+ return 0;
162
+ #endif
163
+ }
164
+
165
+ static int curl_seek_fail_result(void) {
166
+ #ifdef CURL_SEEKFUNC_FAIL
167
+ return CURL_SEEKFUNC_FAIL;
168
+ #else
169
+ return 1;
170
+ #endif
171
+ }
172
+
144
173
  /* Default body handler appends to easy.body_data buffer */
145
174
  static size_t default_body_handler(char *stream,
146
175
  size_t size,
@@ -178,18 +207,36 @@ static size_t read_data_handler(void *ptr,
178
207
  ruby_curl_easy *rbce) {
179
208
  VALUE upload = rb_easy_get("upload");
180
209
  size_t read_bytes = (size*nmemb);
181
- VALUE stream = ruby_curl_upload_stream_get(upload);
210
+ VALUE stream;
211
+
212
+ if (NIL_P(upload)) {
213
+ return curl_read_abort_result();
214
+ }
215
+
216
+ stream = ruby_curl_upload_stream_get(upload);
182
217
 
183
218
  if (rb_respond_to(stream, rb_intern("read"))) {//if (rb_respond_to(stream, rb_intern("to_s"))) {
184
219
  /* copy read_bytes from stream into ptr */
185
220
  struct stream_read_call_args args;
186
221
  args.stream = stream;
187
222
  args.read_bytes = read_bytes;
188
- VALUE str = with_easy_callback_active(rbce, call_stream_read, (VALUE)&args);
223
+ VALUE str = rescue_easy_callback(rbce, call_stream_read, (VALUE)&args);
189
224
  if( str != Qnil ) {
190
- StringValue(str);
191
- memcpy(ptr, RSTRING_PTR(str), RSTRING_LEN(str));
192
- return RSTRING_LEN(str);
225
+ size_t str_len;
226
+
227
+ str = rescue_easy_callback(rbce, call_string_value, str);
228
+ if (str == Qfalse || str == Qnil) {
229
+ return curl_read_abort_result();
230
+ }
231
+
232
+ str_len = (size_t)RSTRING_LEN(str);
233
+ if (str_len > read_bytes) {
234
+ snprintf(rbce->err_buf, CURL_ERROR_SIZE, "read callback returned more data than requested");
235
+ return curl_read_abort_result();
236
+ }
237
+
238
+ memcpy(ptr, RSTRING_PTR(str), str_len);
239
+ return str_len;
193
240
  }
194
241
  else {
195
242
  return 0;
@@ -202,9 +249,17 @@ static size_t read_data_handler(void *ptr,
202
249
  size_t remaining;
203
250
  char *str_ptr;
204
251
  TypedData_Get_Struct(upload, ruby_curl_upload, &ruby_curl_upload_data_type, rbcu);
205
- str = with_easy_callback_active(rbce, call_stream_to_s, stream);
206
- StringValue(str);
252
+ str = rescue_easy_callback(rbce, call_stream_to_s, stream);
253
+ str = rescue_easy_callback(rbce, call_string_value, str);
254
+ if (str == Qfalse || str == Qnil) {
255
+ return curl_read_abort_result();
256
+ }
257
+
207
258
  len = RSTRING_LEN(str);
259
+ if (rbcu->offset >= len) {
260
+ return 0;
261
+ }
262
+
208
263
  remaining = len - rbcu->offset;
209
264
  str_ptr = RSTRING_PTR(str);
210
265
 
@@ -232,14 +287,23 @@ int seek_data_handler(ruby_curl_easy *rbce,
232
287
  int origin) {
233
288
 
234
289
  VALUE upload = rb_easy_get("upload");
235
- VALUE stream = ruby_curl_upload_stream_get(upload);
290
+ VALUE stream;
291
+
292
+ if (NIL_P(upload)) {
293
+ return curl_seek_fail_result();
294
+ }
295
+
296
+ stream = ruby_curl_upload_stream_get(upload);
236
297
 
237
298
  if (rb_respond_to(stream, rb_intern("seek"))) {
238
299
  struct stream_seek_call_args args;
239
300
  args.stream = stream;
240
301
  args.offset = offset;
241
- args.origin = SEEK_SET;
242
- with_easy_callback_active(rbce, call_stream_seek, (VALUE)&args);
302
+ args.origin = origin;
303
+ rescue_easy_callback(rbce, call_stream_seek, (VALUE)&args);
304
+ if (!NIL_P(rbce->callback_error)) {
305
+ return curl_seek_fail_result();
306
+ }
243
307
  } else {
244
308
  ruby_curl_upload *rbcu;
245
309
  TypedData_Get_Struct(upload, ruby_curl_upload, &ruby_curl_upload_data_type, rbcu);
@@ -725,6 +789,24 @@ static struct curl_slist *duplicate_curl_slist(struct curl_slist *list) {
725
789
  return dup;
726
790
  }
727
791
 
792
+ static VALUE duplicate_upload(VALUE upload) {
793
+ ruby_curl_upload *rbcu, *newrbcu;
794
+ VALUE new_upload;
795
+
796
+ if (NIL_P(upload)) {
797
+ return Qnil;
798
+ }
799
+
800
+ TypedData_Get_Struct(upload, ruby_curl_upload, &ruby_curl_upload_data_type, rbcu);
801
+
802
+ new_upload = ruby_curl_upload_new(cCurlUpload);
803
+ TypedData_Get_Struct(new_upload, ruby_curl_upload, &ruby_curl_upload_data_type, newrbcu);
804
+ newrbcu->stream = rbcu->stream;
805
+ newrbcu->offset = rbcu->offset;
806
+
807
+ return new_upload;
808
+ }
809
+
728
810
  /*
729
811
  * call-seq:
730
812
  * easy.clone => <easy clone>
@@ -747,6 +829,10 @@ static VALUE ruby_curl_easy_clone(VALUE self) {
747
829
 
748
830
  /* now deep copy */
749
831
  newrbce->curl = curl_easy_duphandle(rbce->curl);
832
+ if (!newrbce->curl) {
833
+ free(newrbce);
834
+ rb_raise(rb_eNoMemError, "Failed to duplicate Curl::Easy handle");
835
+ }
750
836
  newrbce->curl_headers = (rbce->curl_headers) ? duplicate_curl_slist(rbce->curl_headers) : NULL;
751
837
  newrbce->curl_proxy_headers = (rbce->curl_proxy_headers) ? duplicate_curl_slist(rbce->curl_proxy_headers) : NULL;
752
838
  newrbce->curl_ftp_commands = (rbce->curl_ftp_commands) ? duplicate_curl_slist(rbce->curl_ftp_commands) : NULL;
@@ -763,6 +849,21 @@ static VALUE ruby_curl_easy_clone(VALUE self) {
763
849
  /* Set the error buffer on the new curl handle using the new err_buf */
764
850
  curl_easy_setopt(newrbce->curl, CURLOPT_ERRORBUFFER, newrbce->err_buf);
765
851
 
852
+ if (newrbce->opts != Qnil) {
853
+ VALUE upload = rb_hash_aref(newrbce->opts, rb_easy_hkey("upload"));
854
+ if (!NIL_P(upload)) {
855
+ rb_hash_aset(newrbce->opts, rb_easy_hkey("upload"), duplicate_upload(upload));
856
+ curl_easy_setopt(newrbce->curl, CURLOPT_READFUNCTION, (curl_read_callback)read_data_handler);
857
+ curl_easy_setopt(newrbce->curl, CURLOPT_READDATA, newrbce);
858
+ #ifdef HAVE_CURLOPT_SEEKFUNCTION
859
+ curl_easy_setopt(newrbce->curl, CURLOPT_SEEKFUNCTION, (curl_seek_callback)seek_data_handler);
860
+ #endif
861
+ #ifdef HAVE_CURLOPT_SEEKDATA
862
+ curl_easy_setopt(newrbce->curl, CURLOPT_SEEKDATA, newrbce);
863
+ #endif
864
+ }
865
+ }
866
+
766
867
  VALUE clone = TypedData_Wrap_Struct(cCurlEasy, &ruby_curl_easy_data_type, newrbce);
767
868
  newrbce->self = clone;
768
869
  curl_easy_setopt(newrbce->curl, CURLOPT_PRIVATE, (void*)newrbce);
@@ -801,6 +902,8 @@ static VALUE ruby_curl_easy_close(VALUE self) {
801
902
  ruby_curl_easy_zero(rbce);
802
903
  rbce->self = self;
803
904
 
905
+ curl_easy_setopt(rbce->curl, CURLOPT_ERRORBUFFER, rbce->err_buf);
906
+
804
907
  /* give the new curl handle a reference back to the ruby object */
805
908
  ecode = curl_easy_setopt(rbce->curl, CURLOPT_PRIVATE, (void*)rbce);
806
909
  if (ecode != CURLE_OK) {
@@ -1268,35 +1371,15 @@ static VALUE ruby_curl_easy_put_data_set(VALUE self, VALUE data) {
1268
1371
  ruby_curl_easy *rbce;
1269
1372
  CURL *curl;
1270
1373
  VALUE upload;
1374
+ VALUE upload_stream = data;
1271
1375
  VALUE headers;
1376
+ VALUE infile_size = Qnil;
1272
1377
 
1273
1378
  TypedData_Get_Struct(self, ruby_curl_easy, &ruby_curl_easy_data_type, rbce);
1274
1379
 
1275
- upload = ruby_curl_upload_new(cCurlUpload);
1276
- ruby_curl_upload_stream_set(upload,data);
1277
-
1278
- curl = rbce->curl;
1279
- rb_easy_set("upload", upload); /* keep the upload object alive as long as
1280
- the easy handle is active or until the upload
1281
- is complete or terminated... */
1282
-
1283
- curl_easy_setopt(curl, CURLOPT_NOBODY, 0);
1284
- curl_easy_setopt(curl, CURLOPT_POST, 0);
1285
- curl_easy_setopt(curl, CURLOPT_POSTFIELDS, NULL);
1286
- curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, 0);
1287
- curl_easy_setopt(curl, CURLOPT_UPLOAD, 1);
1288
- curl_easy_setopt(curl, CURLOPT_READFUNCTION, (curl_read_callback)read_data_handler);
1289
- #ifdef HAVE_CURLOPT_SEEKFUNCTION
1290
- curl_easy_setopt(curl, CURLOPT_SEEKFUNCTION, (curl_seek_callback)seek_data_handler);
1291
- #endif
1292
- curl_easy_setopt(curl, CURLOPT_READDATA, rbce);
1293
- #ifdef HAVE_CURLOPT_SEEKDATA
1294
- curl_easy_setopt(curl, CURLOPT_SEEKDATA, rbce);
1295
- #endif
1296
-
1297
1380
  /*
1298
- * we need to set specific headers for the PUT to work... so
1299
- * convert the internal headers structure to a HASH if one is set
1381
+ * Validate and prepare Ruby-visible state before mutating the CURL handle.
1382
+ * Several branches below can raise (header type, stat, size, to_s).
1300
1383
  */
1301
1384
  if (!rb_easy_nil("headers")) {
1302
1385
  if (rb_easy_type_check("headers", T_ARRAY) || rb_easy_type_check("headers", T_STRING)) {
@@ -1304,43 +1387,80 @@ static VALUE ruby_curl_easy_put_data_set(VALUE self, VALUE data) {
1304
1387
  }
1305
1388
  }
1306
1389
 
1307
- // exit fast if the payload is empty
1308
- if (NIL_P(data)) { return data; }
1390
+ if (!NIL_P(data) && !rb_respond_to(data, rb_intern("read"))) {
1391
+ if (rb_respond_to(data, rb_intern("to_s"))) {
1392
+ upload_stream = rb_obj_as_string(data);
1393
+ } else {
1394
+ rb_raise(rb_eRuntimeError, "PUT data must respond to read or to_s");
1395
+ }
1396
+ }
1309
1397
 
1310
1398
  headers = rb_easy_get("headers");
1311
1399
  if( headers == Qnil ) {
1312
1400
  headers = rb_hash_new();
1313
1401
  }
1314
1402
 
1315
- if (rb_respond_to(data, rb_intern("read"))) {
1316
- VALUE stat = rb_funcall(data, rb_intern("stat"), 0);
1317
- if( stat && rb_hash_aref(headers, rb_str_new2("Content-Length")) == Qnil) {
1403
+ if (!NIL_P(data) && rb_respond_to(data, rb_intern("read"))) {
1404
+ VALUE stat = Qnil;
1405
+ if (rb_respond_to(data, rb_intern("stat"))) {
1406
+ stat = rb_funcall(data, rb_intern("stat"), 0);
1407
+ }
1408
+ if(!NIL_P(stat) && stat != Qfalse && rb_hash_aref(headers, rb_str_new2("Content-Length")) == Qnil) {
1318
1409
  VALUE size;
1319
1410
  if( rb_hash_aref(headers, rb_str_new2("Expect")) == Qnil ) {
1320
1411
  rb_hash_aset(headers, rb_str_new2("Expect"), rb_str_new2(""));
1321
1412
  }
1322
1413
  size = rb_funcall(stat, rb_intern("size"), 0);
1323
- curl_easy_setopt(curl, CURLOPT_INFILESIZE, NUM2LONG(size));
1414
+ infile_size = size;
1324
1415
  }
1325
1416
  else if( rb_hash_aref(headers, rb_str_new2("Content-Length")) == Qnil && rb_hash_aref(headers, rb_str_new2("Transfer-Encoding")) == Qnil ) {
1326
1417
  rb_hash_aset(headers, rb_str_new2("Transfer-Encoding"), rb_str_new2("chunked"));
1327
1418
  }
1328
1419
  else if( rb_hash_aref(headers, rb_str_new2("Content-Length")) ) {
1329
1420
  VALUE size = rb_funcall(rb_hash_aref(headers, rb_str_new2("Content-Length")), rb_intern("to_i"), 0);
1330
- curl_easy_setopt(curl, CURLOPT_INFILESIZE, NUM2LONG(size));
1421
+ infile_size = size;
1331
1422
  }
1332
1423
  }
1333
- else if (rb_respond_to(data, rb_intern("to_s"))) {
1334
- curl_easy_setopt(curl, CURLOPT_INFILESIZE, RSTRING_LEN(data));
1424
+ else if (!NIL_P(data) && rb_respond_to(data, rb_intern("to_s"))) {
1425
+ infile_size = LONG2NUM(RSTRING_LEN(upload_stream));
1335
1426
  if( rb_hash_aref(headers, rb_str_new2("Expect")) == Qnil ) {
1336
1427
  rb_hash_aset(headers, rb_str_new2("Expect"), rb_str_new2(""));
1337
1428
  }
1338
1429
  }
1430
+ else if (NIL_P(data)) {
1431
+ /* Preserve legacy nil handling: configure an upload with no payload. */
1432
+ }
1339
1433
  else {
1340
1434
  rb_raise(rb_eRuntimeError, "PUT data must respond to read or to_s");
1341
1435
  }
1342
1436
  rb_easy_set("headers",headers);
1343
1437
 
1438
+ upload = ruby_curl_upload_new(cCurlUpload);
1439
+ ruby_curl_upload_stream_set(upload, upload_stream);
1440
+
1441
+ curl = rbce->curl;
1442
+ rb_easy_set("upload", upload); /* keep the upload object alive as long as
1443
+ the easy handle is active or until the upload
1444
+ is complete or terminated... */
1445
+
1446
+ curl_easy_setopt(curl, CURLOPT_NOBODY, 0);
1447
+ curl_easy_setopt(curl, CURLOPT_POST, 0);
1448
+ curl_easy_setopt(curl, CURLOPT_POSTFIELDS, NULL);
1449
+ curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, 0);
1450
+ curl_easy_setopt(curl, CURLOPT_UPLOAD, 1);
1451
+ curl_easy_setopt(curl, CURLOPT_READFUNCTION, (curl_read_callback)read_data_handler);
1452
+ #ifdef HAVE_CURLOPT_SEEKFUNCTION
1453
+ curl_easy_setopt(curl, CURLOPT_SEEKFUNCTION, (curl_seek_callback)seek_data_handler);
1454
+ #endif
1455
+ curl_easy_setopt(curl, CURLOPT_READDATA, rbce);
1456
+ #ifdef HAVE_CURLOPT_SEEKDATA
1457
+ curl_easy_setopt(curl, CURLOPT_SEEKDATA, rbce);
1458
+ #endif
1459
+
1460
+ if (!NIL_P(infile_size)) {
1461
+ curl_easy_setopt(curl, CURLOPT_INFILESIZE, NUM2LONG(infile_size));
1462
+ }
1463
+
1344
1464
  // if we made it this far, all should be well.
1345
1465
  return data;
1346
1466
  }
@@ -2655,7 +2775,7 @@ static VALUE cb_each_ftp_command(VALUE ftp_command, VALUE wrap, int _c, const VA
2655
2775
  TypedData_Get_Struct(wrap, struct curl_slist *, &curl_slist_ptr_type, list);
2656
2776
 
2657
2777
  ftp_command_string = rb_obj_as_string(ftp_command);
2658
- struct curl_slist *new_list = curl_slist_append(*list, StringValuePtr(ftp_command));
2778
+ struct curl_slist *new_list = curl_slist_append(*list, StringValuePtr(ftp_command_string));
2659
2779
  if (!new_list) {
2660
2780
  rb_raise(rb_eNoMemError, "Failed to append to FTP command list");
2661
2781
  }
@@ -2673,7 +2793,7 @@ static VALUE cb_each_resolve(VALUE resolve, VALUE wrap, int _c, const VALUE *_pt
2673
2793
  TypedData_Get_Struct(wrap, struct curl_slist *, &curl_slist_ptr_type, list);
2674
2794
 
2675
2795
  resolve_string = rb_obj_as_string(resolve);
2676
- struct curl_slist *new_list = curl_slist_append(*list, StringValuePtr(resolve));
2796
+ struct curl_slist *new_list = curl_slist_append(*list, StringValuePtr(resolve_string));
2677
2797
  if (!new_list) {
2678
2798
  rb_raise(rb_eNoMemError, "Failed to append to resolve list");
2679
2799
  }
@@ -3054,6 +3174,13 @@ VALUE ruby_curl_easy_setup(ruby_curl_easy *rbce) {
3054
3174
  if (rb_easy_type_check("resolve", T_ARRAY)) {
3055
3175
  VALUE wrap = TypedData_Wrap_Struct(rb_cObject, &curl_slist_ptr_type, rslv);
3056
3176
  rb_block_call(rb_easy_get("resolve"), rb_intern("each"), 0, NULL, cb_each_resolve, wrap);
3177
+ } else {
3178
+ VALUE resolve_str = rb_obj_as_string(rb_easy_get("resolve"));
3179
+ struct curl_slist *new_list = curl_slist_append(*rslv, StringValuePtr(resolve_str));
3180
+ if (!new_list) {
3181
+ rb_raise(rb_eNoMemError, "Failed to append to resolve list");
3182
+ }
3183
+ *rslv = new_list;
3057
3184
  }
3058
3185
 
3059
3186
  if (*rslv) {
@@ -3095,6 +3222,12 @@ VALUE ruby_curl_easy_cleanup( VALUE self, ruby_curl_easy *rbce ) {
3095
3222
  curl_easy_setopt(curl, CURLOPT_UPLOAD, 0);
3096
3223
  curl_easy_setopt(curl, CURLOPT_READFUNCTION, NULL);
3097
3224
  curl_easy_setopt(curl, CURLOPT_READDATA, NULL);
3225
+ #ifdef HAVE_CURLOPT_SEEKFUNCTION
3226
+ curl_easy_setopt(curl, CURLOPT_SEEKFUNCTION, NULL);
3227
+ #endif
3228
+ #ifdef HAVE_CURLOPT_SEEKDATA
3229
+ curl_easy_setopt(curl, CURLOPT_SEEKDATA, NULL);
3230
+ #endif
3098
3231
  curl_easy_setopt(curl, CURLOPT_INFILESIZE, 0);
3099
3232
  }
3100
3233
 
@@ -3104,6 +3237,44 @@ VALUE ruby_curl_easy_cleanup( VALUE self, ruby_curl_easy *rbce ) {
3104
3237
  return Qnil;
3105
3238
  }
3106
3239
 
3240
+ struct easy_perform_request_restore_args {
3241
+ VALUE self;
3242
+ CURL *curl;
3243
+ ruby_curl_easy *rbce;
3244
+ int clear_customrequest;
3245
+ int clear_nobody;
3246
+ int clear_postfields;
3247
+ };
3248
+
3249
+ static VALUE perform_with_request_restore_body(VALUE argp) {
3250
+ struct easy_perform_request_restore_args *args = (struct easy_perform_request_restore_args *)argp;
3251
+ return rb_funcall(args->self, rb_intern("perform"), 0);
3252
+ }
3253
+
3254
+ static VALUE perform_with_request_restore_ensure(VALUE argp) {
3255
+ struct easy_perform_request_restore_args *args = (struct easy_perform_request_restore_args *)argp;
3256
+
3257
+ if (args->curl) {
3258
+ if (args->clear_nobody) {
3259
+ curl_easy_setopt(args->curl, CURLOPT_NOBODY, 0L);
3260
+ }
3261
+ if (args->clear_customrequest) {
3262
+ curl_easy_setopt(args->curl, CURLOPT_CUSTOMREQUEST, NULL);
3263
+ }
3264
+ if (args->clear_postfields) {
3265
+ curl_easy_setopt(args->curl, CURLOPT_POST, 0L);
3266
+ curl_easy_setopt(args->curl, CURLOPT_POSTFIELDS, NULL);
3267
+ curl_easy_setopt(args->curl, CURLOPT_POSTFIELDSIZE, 0L);
3268
+ curl_easy_setopt(args->curl, CURLOPT_HTTPGET, 1L);
3269
+ if (args->rbce && !NIL_P(args->rbce->opts)) {
3270
+ rb_hash_delete(args->rbce->opts, rb_easy_hkey("postdata_buffer"));
3271
+ }
3272
+ }
3273
+ }
3274
+
3275
+ return Qnil;
3276
+ }
3277
+
3107
3278
  /*
3108
3279
  * Common implementation of easy.http(verb) and easy.http_delete
3109
3280
  */
@@ -3136,13 +3307,9 @@ static VALUE ruby_curl_easy_perform_verb_str(VALUE self, const char *verb) {
3136
3307
  curl_easy_setopt(curl, CURLOPT_POST, 0L);
3137
3308
  }
3138
3309
 
3139
- retval = rb_funcall(self, rb_intern("perform"), 0);
3140
-
3141
- /* Restore state after request. */
3142
- if (is_head) {
3143
- curl_easy_setopt(curl, CURLOPT_NOBODY, 0L);
3144
- }
3145
- curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, NULL);
3310
+ struct easy_perform_request_restore_args restore_args = { self, curl, rbce, 1, is_head, 0 };
3311
+ retval = rb_ensure(perform_with_request_restore_body, (VALUE)&restore_args,
3312
+ perform_with_request_restore_ensure, (VALUE)&restore_args);
3146
3313
 
3147
3314
  return retval;
3148
3315
  }
@@ -3173,19 +3340,68 @@ static VALUE call_easy_perform(VALUE self) {
3173
3340
  }
3174
3341
 
3175
3342
  struct easy_form_perform_args {
3343
+ VALUE self;
3176
3344
  CURL *curl;
3345
+ int argc;
3346
+ VALUE *argv;
3177
3347
  struct curl_httppost *first;
3348
+ struct curl_httppost *last;
3349
+ int clear_customrequest;
3350
+ int form_set_on_curl;
3178
3351
  };
3179
3352
 
3353
+ static void append_multipart_form_argument(VALUE arg,
3354
+ struct curl_httppost **first,
3355
+ struct curl_httppost **last) {
3356
+ if (rb_obj_is_instance_of(arg, cCurlPostField)) {
3357
+ append_to_form(arg, first, last);
3358
+ } else if (rb_type(arg) == T_ARRAY) {
3359
+ long j, argv_len = RARRAY_LEN(arg);
3360
+ for (j = 0; j < argv_len; ++j) {
3361
+ VALUE field = rb_ary_entry(arg, j);
3362
+ if (rb_obj_is_instance_of(field, cCurlPostField)) {
3363
+ append_to_form(field, first, last);
3364
+ } else {
3365
+ rb_raise(eCurlErrInvalidPostField,
3366
+ "You must use PostFields only with multipart form posts");
3367
+ }
3368
+ }
3369
+ } else {
3370
+ rb_raise(eCurlErrInvalidPostField,
3371
+ "You must use PostFields only with multipart form posts");
3372
+ }
3373
+ }
3374
+
3375
+ static VALUE build_and_perform_multipart_form(VALUE argp) {
3376
+ struct easy_form_perform_args *args = (struct easy_form_perform_args *)argp;
3377
+ int i;
3378
+
3379
+ for (i = 0; i < args->argc; i++) {
3380
+ append_multipart_form_argument(args->argv[i], &args->first, &args->last);
3381
+ }
3382
+
3383
+ curl_easy_setopt(args->curl, CURLOPT_POST, 0);
3384
+ curl_easy_setopt(args->curl, CURLOPT_HTTPPOST, args->first);
3385
+ args->form_set_on_curl = 1;
3386
+
3387
+ return call_easy_perform(args->self);
3388
+ }
3389
+
3180
3390
  static VALUE ensure_free_form_post(VALUE argp) {
3181
3391
  struct easy_form_perform_args *args = (struct easy_form_perform_args *)argp;
3182
3392
  if (args->curl) {
3183
- curl_easy_setopt(args->curl, CURLOPT_HTTPPOST, NULL);
3393
+ if (args->form_set_on_curl) {
3394
+ curl_easy_setopt(args->curl, CURLOPT_HTTPPOST, NULL);
3395
+ }
3396
+ if (args->clear_customrequest) {
3397
+ curl_easy_setopt(args->curl, CURLOPT_CUSTOMREQUEST, NULL);
3398
+ }
3184
3399
  }
3185
3400
  if (args->first) {
3186
3401
  curl_formfree(args->first);
3187
3402
  args->first = NULL;
3188
3403
  }
3404
+ args->last = NULL;
3189
3405
  return Qnil;
3190
3406
  }
3191
3407
 
@@ -3216,7 +3432,6 @@ static VALUE ensure_free_form_post(VALUE argp) {
3216
3432
  static VALUE ruby_curl_easy_perform_post(int argc, VALUE *argv, VALUE self) {
3217
3433
  ruby_curl_easy *rbce;
3218
3434
  CURL *curl;
3219
- int i;
3220
3435
  VALUE args_ary;
3221
3436
 
3222
3437
  rb_scan_args(argc, argv, "*", &args_ary);
@@ -3230,33 +3445,8 @@ static VALUE ruby_curl_easy_perform_post(int argc, VALUE *argv, VALUE self) {
3230
3445
 
3231
3446
  if (rbce->multipart_form_post) {
3232
3447
  VALUE ret;
3233
- struct curl_httppost *first = NULL, *last = NULL;
3234
-
3235
- // Make the multipart form
3236
- for (i = 0; i < argc; i++) {
3237
- if (rb_obj_is_instance_of(argv[i], cCurlPostField)) {
3238
- append_to_form(argv[i], &first, &last);
3239
- } else if (rb_type(argv[i]) == T_ARRAY) {
3240
- // see: https://github.com/rvanlieshout/curb/commit/8bcdefddc0162484681ebd1a92d52a642666a445
3241
- long c = 0, argv_len = RARRAY_LEN(argv[i]);
3242
- for (; c < argv_len; ++c) {
3243
- if (rb_obj_is_instance_of(rb_ary_entry(argv[i],c), cCurlPostField)) {
3244
- append_to_form(rb_ary_entry(argv[i],c), &first, &last);
3245
- } else {
3246
- rb_raise(eCurlErrInvalidPostField, "You must use PostFields only with multipart form posts");
3247
- return Qnil;
3248
- }
3249
- }
3250
- } else {
3251
- rb_raise(eCurlErrInvalidPostField, "You must use PostFields only with multipart form posts");
3252
- return Qnil;
3253
- }
3254
- }
3255
-
3256
- curl_easy_setopt(curl, CURLOPT_POST, 0);
3257
- curl_easy_setopt(curl, CURLOPT_HTTPPOST, first);
3258
- struct easy_form_perform_args perform_args = { curl, first };
3259
- ret = rb_ensure(call_easy_perform, self, ensure_free_form_post, (VALUE)&perform_args);
3448
+ struct easy_form_perform_args perform_args = { self, curl, argc, argv, NULL, NULL, 0, 0 };
3449
+ ret = rb_ensure(build_and_perform_multipart_form, (VALUE)&perform_args, ensure_free_form_post, (VALUE)&perform_args);
3260
3450
 
3261
3451
  return ret;
3262
3452
  } else {
@@ -3277,7 +3467,9 @@ static VALUE ruby_curl_easy_perform_post(int argc, VALUE *argv, VALUE self) {
3277
3467
  ruby_curl_easy_post_body_set(self, post_body);
3278
3468
  }
3279
3469
 
3280
- return rb_funcall(self, rb_intern("perform"), 0);
3470
+ struct easy_perform_request_restore_args restore_args = { self, curl, rbce, 1, 0, 0 };
3471
+ return rb_ensure(perform_with_request_restore_body, (VALUE)&restore_args,
3472
+ perform_with_request_restore_ensure, (VALUE)&restore_args);
3281
3473
  }
3282
3474
  }
3283
3475
  }
@@ -3300,7 +3492,6 @@ static VALUE ruby_curl_easy_perform_post(int argc, VALUE *argv, VALUE self) {
3300
3492
  static VALUE ruby_curl_easy_perform_patch(int argc, VALUE *argv, VALUE self) {
3301
3493
  ruby_curl_easy *rbce;
3302
3494
  CURL *curl;
3303
- int i;
3304
3495
  VALUE args_ary;
3305
3496
 
3306
3497
  rb_scan_args(argc, argv, "*", &args_ary);
@@ -3315,35 +3506,8 @@ static VALUE ruby_curl_easy_perform_patch(int argc, VALUE *argv, VALUE self) {
3315
3506
 
3316
3507
  if (rbce->multipart_form_post) {
3317
3508
  VALUE ret;
3318
- struct curl_httppost *first = NULL, *last = NULL;
3319
-
3320
- /* Build the multipart form (same logic as for POST) */
3321
- for (i = 0; i < argc; i++) {
3322
- if (rb_obj_is_instance_of(argv[i], cCurlPostField)) {
3323
- append_to_form(argv[i], &first, &last);
3324
- } else if (rb_type(argv[i]) == T_ARRAY) {
3325
- long j, argv_len = RARRAY_LEN(argv[i]);
3326
- for (j = 0; j < argv_len; ++j) {
3327
- if (rb_obj_is_instance_of(rb_ary_entry(argv[i], j), cCurlPostField)) {
3328
- append_to_form(rb_ary_entry(argv[i], j), &first, &last);
3329
- } else {
3330
- rb_raise(eCurlErrInvalidPostField,
3331
- "You must use PostFields only with multipart form posts");
3332
- return Qnil;
3333
- }
3334
- }
3335
- } else {
3336
- rb_raise(eCurlErrInvalidPostField,
3337
- "You must use PostFields only with multipart form posts");
3338
- return Qnil;
3339
- }
3340
- }
3341
- /* Disable the POST flag */
3342
- curl_easy_setopt(curl, CURLOPT_POST, 0);
3343
- /* Use the built multipart form as the request body */
3344
- curl_easy_setopt(curl, CURLOPT_HTTPPOST, first);
3345
- struct easy_form_perform_args perform_args = { curl, first };
3346
- ret = rb_ensure(call_easy_perform, self, ensure_free_form_post, (VALUE)&perform_args);
3509
+ struct easy_form_perform_args perform_args = { self, curl, argc, argv, NULL, NULL, 1, 0 };
3510
+ ret = rb_ensure(build_and_perform_multipart_form, (VALUE)&perform_args, ensure_free_form_post, (VALUE)&perform_args);
3347
3511
  return ret;
3348
3512
  } else {
3349
3513
  /* Join arguments into a raw PATCH body */
@@ -3359,7 +3523,9 @@ static VALUE ruby_curl_easy_perform_patch(int argc, VALUE *argv, VALUE self) {
3359
3523
  if (rb_easy_nil("postdata_buffer")) {
3360
3524
  ruby_curl_easy_post_body_set(self, patch_body);
3361
3525
  }
3362
- return rb_funcall(self, rb_intern("perform"), 0);
3526
+ struct easy_perform_request_restore_args restore_args = { self, curl, rbce, 1, 0, 1 };
3527
+ return rb_ensure(perform_with_request_restore_body, (VALUE)&restore_args,
3528
+ perform_with_request_restore_ensure, (VALUE)&restore_args);
3363
3529
  }
3364
3530
  }
3365
3531
  }
@@ -3376,7 +3542,6 @@ static VALUE ruby_curl_easy_perform_put(int argc, VALUE *argv, VALUE self) {
3376
3542
  ruby_curl_easy *rbce;
3377
3543
  CURL *curl;
3378
3544
  VALUE args_ary;
3379
- int i;
3380
3545
 
3381
3546
  rb_scan_args(argc, argv, "*", &args_ary);
3382
3547
  TypedData_Get_Struct(self, ruby_curl_easy, &ruby_curl_easy_data_type, rbce);
@@ -3399,33 +3564,8 @@ static VALUE ruby_curl_easy_perform_put(int argc, VALUE *argv, VALUE self) {
3399
3564
  /* Otherwise, if multipart_form_post is true, use multipart logic */
3400
3565
  else if (rbce->multipart_form_post) {
3401
3566
  VALUE ret;
3402
- struct curl_httppost *first = NULL, *last = NULL;
3403
- for (i = 0; i < RARRAY_LEN(args_ary); i++) {
3404
- VALUE field = rb_ary_entry(args_ary, i);
3405
- if (rb_obj_is_instance_of(field, cCurlPostField)) {
3406
- append_to_form(field, &first, &last);
3407
- } else if (rb_type(field) == T_ARRAY) {
3408
- long j;
3409
- for (j = 0; j < RARRAY_LEN(field); j++) {
3410
- VALUE subfield = rb_ary_entry(field, j);
3411
- if (rb_obj_is_instance_of(subfield, cCurlPostField)) {
3412
- append_to_form(subfield, &first, &last);
3413
- } else {
3414
- rb_raise(eCurlErrInvalidPostField,
3415
- "You must use PostFields only with multipart form posts");
3416
- }
3417
- }
3418
- } else {
3419
- rb_raise(eCurlErrInvalidPostField,
3420
- "You must use PostFields only with multipart form posts");
3421
- }
3422
- }
3423
- curl_easy_setopt(curl, CURLOPT_POST, 0);
3424
- curl_easy_setopt(curl, CURLOPT_HTTPPOST, first);
3425
- /* Set the method explicitly to PUT */
3426
- curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "PUT");
3427
- struct easy_form_perform_args perform_args = { curl, first };
3428
- ret = rb_ensure(call_easy_perform, self, ensure_free_form_post, (VALUE)&perform_args);
3567
+ struct easy_form_perform_args perform_args = { self, curl, argc, argv, NULL, NULL, 1, 0 };
3568
+ ret = rb_ensure(build_and_perform_multipart_form, (VALUE)&perform_args, ensure_free_form_post, (VALUE)&perform_args);
3429
3569
  return ret;
3430
3570
  }
3431
3571
  /* Fallback: join all arguments */
@@ -3436,7 +3576,9 @@ static VALUE ruby_curl_easy_perform_put(int argc, VALUE *argv, VALUE self) {
3436
3576
  ruby_curl_easy_put_data_set(self, post_body);
3437
3577
  }
3438
3578
  }
3439
- return rb_funcall(self, rb_intern("perform"), 0);
3579
+ struct easy_perform_request_restore_args restore_args = { self, curl, rbce, 1, 0, 0 };
3580
+ return rb_ensure(perform_with_request_restore_body, (VALUE)&restore_args,
3581
+ perform_with_request_restore_ensure, (VALUE)&restore_args);
3440
3582
  }
3441
3583
 
3442
3584
 
@@ -4503,6 +4645,7 @@ static VALUE ruby_curl_easy_set_opt(VALUE self, VALUE opt, VALUE val) {
4503
4645
  }
4504
4646
  /* Save the list pointer in the ruby_curl_easy structure for cleanup later */
4505
4647
  rbce->curl_resolve = list;
4648
+ rb_hash_aset(rbce->opts, rb_easy_hkey("resolve"), val);
4506
4649
  curl_easy_setopt(rbce->curl, CURLOPT_RESOLVE, list);
4507
4650
  } break;
4508
4651
  #endif