curb 1.4.0 → 1.4.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.
- checksums.yaml +4 -4
- data/ChangeLog.md +432 -0
- data/README.md +25 -5
- data/ext/curb.h +3 -3
- data/ext/curb_easy.c +274 -161
- data/ext/curb_easy.h +5 -0
- data/ext/curb_macros.h +16 -0
- data/ext/curb_multi.c +51 -8
- data/ext/curb_multi.h +4 -0
- data/ext/curb_postfield.c +103 -114
- data/ext/curb_postfield.h +10 -1
- data/lib/curl/easy.rb +10 -3
- data/lib/curl/multi.rb +1 -1
- data/lib/curl.rb +9 -7
- data/tests/tc_curl_easy.rb +116 -0
- data/tests/tc_curl_easy_cookielist.rb +10 -8
- data/tests/tc_curl_maxfilesize.rb +109 -5
- data/tests/tc_curl_native_coverage.rb +57 -0
- data/tests/tc_curl_network_policy.rb +108 -1
- data/tests/tc_curl_postfield.rb +137 -0
- data/tests/tc_curl_protocols.rb +19 -7
- data/tests/tc_curl_transfer_guard.rb +112 -0
- data/tests/tc_ractor.rb +48 -1
- metadata +5 -2
data/ext/curb_easy.h
CHANGED
|
@@ -93,6 +93,8 @@ typedef struct {
|
|
|
93
93
|
char unrestricted_auth;
|
|
94
94
|
char verbose;
|
|
95
95
|
char multipart_form_post;
|
|
96
|
+
char multipart_building; /* Disallow reentry while Ruby builds the form. */
|
|
97
|
+
char multipart_form_active; /* Temporary CURLOPT_HTTPPOST owned by perform. */
|
|
96
98
|
char enable_cookies;
|
|
97
99
|
char cookielist_engine_enabled; /* track if CURLOPT_COOKIELIST was used with a non-command to enable engine */
|
|
98
100
|
char ignore_content_length;
|
|
@@ -124,6 +126,9 @@ typedef struct {
|
|
|
124
126
|
extern VALUE cCurlEasy;
|
|
125
127
|
extern const rb_data_type_t ruby_curl_easy_data_type;
|
|
126
128
|
|
|
129
|
+
ruby_curl_easy *ruby_curl_easy_get_handle(VALUE self);
|
|
130
|
+
void ruby_curl_easy_check_access(ruby_curl_easy *rbce);
|
|
131
|
+
|
|
127
132
|
VALUE ruby_curl_easy_setup(ruby_curl_easy *rbce);
|
|
128
133
|
VALUE ruby_curl_easy_cleanup(VALUE self, ruby_curl_easy *rbce);
|
|
129
134
|
VALUE rb_curl_easy_take_callback_error(ruby_curl_easy *rbce);
|
data/ext/curb_macros.h
CHANGED
|
@@ -20,12 +20,17 @@
|
|
|
20
20
|
#define rb_easy_get_str(key) \
|
|
21
21
|
RSTRING_PTR((rb_easy_type_check(key,T_STRING) ? rb_easy_get(key) : rb_str_to_str(rb_easy_get(key))))
|
|
22
22
|
|
|
23
|
+
/* Easy attribute macros also participate in the native access guard. */
|
|
24
|
+
#define CURB_CHECK_ACCESS_ruby_curl_easy(ptr) ruby_curl_easy_check_access(ptr)
|
|
25
|
+
#define CURB_CHECK_ACCESS_ruby_curl_postfield(ptr) ((void)0)
|
|
26
|
+
|
|
23
27
|
/* getter/setter macros for various things */
|
|
24
28
|
/* setter for anything that stores a ruby VALUE in the struct */
|
|
25
29
|
#define CURB_OBJECT_SETTER(type, attr) \
|
|
26
30
|
type *ptr; \
|
|
27
31
|
\
|
|
28
32
|
TypedData_Get_Struct(self, type, &type##_data_type, ptr); \
|
|
33
|
+
CURB_CHECK_ACCESS_##type(ptr); \
|
|
29
34
|
ptr->attr = attr; \
|
|
30
35
|
\
|
|
31
36
|
return attr;
|
|
@@ -35,6 +40,7 @@
|
|
|
35
40
|
type *ptr; \
|
|
36
41
|
\
|
|
37
42
|
TypedData_Get_Struct(self, type, &type##_data_type, ptr); \
|
|
43
|
+
CURB_CHECK_ACCESS_##type(ptr); \
|
|
38
44
|
return ptr->attr;
|
|
39
45
|
|
|
40
46
|
/* setter for anything that stores a ruby VALUE in the struct opts hash */
|
|
@@ -42,6 +48,7 @@
|
|
|
42
48
|
type *ptr; \
|
|
43
49
|
\
|
|
44
50
|
TypedData_Get_Struct(self, type, &type##_data_type, ptr); \
|
|
51
|
+
CURB_CHECK_ACCESS_##type(ptr); \
|
|
45
52
|
rb_hash_aset(ptr->opts, rb_easy_hkey(#attr), attr); \
|
|
46
53
|
\
|
|
47
54
|
return attr;
|
|
@@ -51,12 +58,14 @@
|
|
|
51
58
|
type *ptr; \
|
|
52
59
|
\
|
|
53
60
|
TypedData_Get_Struct(self, type, &type##_data_type, ptr); \
|
|
61
|
+
CURB_CHECK_ACCESS_##type(ptr); \
|
|
54
62
|
return rb_hash_aref(ptr->opts, rb_easy_hkey(#attr));
|
|
55
63
|
|
|
56
64
|
/* setter for bool flags */
|
|
57
65
|
#define CURB_BOOLEAN_SETTER(type, attr) \
|
|
58
66
|
type *ptr; \
|
|
59
67
|
TypedData_Get_Struct(self, type, &type##_data_type, ptr); \
|
|
68
|
+
CURB_CHECK_ACCESS_##type(ptr); \
|
|
60
69
|
\
|
|
61
70
|
if (attr == Qnil || attr == Qfalse) { \
|
|
62
71
|
ptr->attr = 0; \
|
|
@@ -70,6 +79,7 @@
|
|
|
70
79
|
#define CURB_BOOLEAN_GETTER(type, attr) \
|
|
71
80
|
type *ptr; \
|
|
72
81
|
TypedData_Get_Struct(self, type, &type##_data_type, ptr); \
|
|
82
|
+
CURB_CHECK_ACCESS_##type(ptr); \
|
|
73
83
|
\
|
|
74
84
|
return((ptr->attr) ? Qtrue : Qfalse);
|
|
75
85
|
|
|
@@ -79,6 +89,7 @@
|
|
|
79
89
|
VALUE oldproc; \
|
|
80
90
|
\
|
|
81
91
|
TypedData_Get_Struct(self, type, &type##_data_type, ptr); \
|
|
92
|
+
CURB_CHECK_ACCESS_##type(ptr); \
|
|
82
93
|
\
|
|
83
94
|
oldproc = ptr->handler; \
|
|
84
95
|
rb_scan_args(argc, argv, "0&", &ptr->handler); \
|
|
@@ -91,6 +102,7 @@
|
|
|
91
102
|
VALUE oldproc, newproc; \
|
|
92
103
|
\
|
|
93
104
|
TypedData_Get_Struct(self, type, &type##_data_type, ptr); \
|
|
105
|
+
CURB_CHECK_ACCESS_##type(ptr); \
|
|
94
106
|
\
|
|
95
107
|
oldproc = rb_hash_aref(ptr->opts, rb_easy_hkey(#handler)); \
|
|
96
108
|
rb_scan_args(argc, argv, "0&", &newproc); \
|
|
@@ -104,6 +116,7 @@
|
|
|
104
116
|
type *ptr; \
|
|
105
117
|
\
|
|
106
118
|
TypedData_Get_Struct(self, type, &type##_data_type, ptr); \
|
|
119
|
+
CURB_CHECK_ACCESS_##type(ptr); \
|
|
107
120
|
if (attr == Qnil) { \
|
|
108
121
|
ptr->attr = nilval; \
|
|
109
122
|
} else { \
|
|
@@ -117,6 +130,7 @@
|
|
|
117
130
|
type *ptr; \
|
|
118
131
|
\
|
|
119
132
|
TypedData_Get_Struct(self, type, &type##_data_type, ptr); \
|
|
133
|
+
CURB_CHECK_ACCESS_##type(ptr); \
|
|
120
134
|
if (ptr->attr == nilval) { \
|
|
121
135
|
return Qnil; \
|
|
122
136
|
} else { \
|
|
@@ -128,6 +142,7 @@
|
|
|
128
142
|
type *ptr; \
|
|
129
143
|
\
|
|
130
144
|
TypedData_Get_Struct(self, type, &type##_data_type, ptr); \
|
|
145
|
+
CURB_CHECK_ACCESS_##type(ptr); \
|
|
131
146
|
if (attr == Qnil) { \
|
|
132
147
|
ptr->attr = 0; \
|
|
133
148
|
} else { \
|
|
@@ -147,6 +162,7 @@
|
|
|
147
162
|
type *ptr; \
|
|
148
163
|
\
|
|
149
164
|
TypedData_Get_Struct(self, type, &type##_data_type, ptr); \
|
|
165
|
+
CURB_CHECK_ACCESS_##type(ptr); \
|
|
150
166
|
if (ptr->attr == 0) { \
|
|
151
167
|
return Qnil; \
|
|
152
168
|
} else { \
|
data/ext/curb_multi.c
CHANGED
|
@@ -250,7 +250,7 @@ static void rb_curl_multi_detach_all(ruby_curl_multi *rbcm);
|
|
|
250
250
|
static void curl_multi_mark(void *ptr);
|
|
251
251
|
static void ruby_curl_multi_ensure_handle(ruby_curl_multi *rbcm);
|
|
252
252
|
static int rb_curl_multi_has_easy(ruby_curl_multi *rbcm, ruby_curl_easy *rbce);
|
|
253
|
-
|
|
253
|
+
void rb_curl_multi_remove_request_reference(VALUE self, VALUE easy);
|
|
254
254
|
static VALUE ruby_curl_multi_mark_closed(VALUE self);
|
|
255
255
|
static VALUE ruby_curl_multi_alloc(VALUE klass);
|
|
256
256
|
static VALUE ruby_curl_multi_initialize(VALUE self);
|
|
@@ -494,7 +494,29 @@ static int rb_curl_multi_has_easy(ruby_curl_multi *rbcm, ruby_curl_easy *rbce) {
|
|
|
494
494
|
return st_lookup(rbcm->attached, (st_data_t)rbce, &value);
|
|
495
495
|
}
|
|
496
496
|
|
|
497
|
-
|
|
497
|
+
/* Attachment, rather than easy.multi alone, distinguishes a live transfer
|
|
498
|
+
* from a completed easy whose status callback still sees its former multi. */
|
|
499
|
+
int rb_curl_multi_easy_performing(void *rbce_ptr) {
|
|
500
|
+
ruby_curl_easy *rbce = (ruby_curl_easy *)rbce_ptr;
|
|
501
|
+
ruby_curl_multi *rbcm = ruby_curl_multi_pointer_if_compatible(rbce->multi);
|
|
502
|
+
return rbcm && rbcm->perform_active && rb_curl_multi_has_easy(rbcm, rbce);
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
static void ruby_curl_multi_check_thread(ruby_curl_multi *rbcm) {
|
|
506
|
+
if (rbcm->perform_active && rbcm->perform_thread != rb_thread_current()) {
|
|
507
|
+
rb_raise(rb_eRuntimeError, "Cannot access a Curl::Multi handle performing on another thread");
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
void rb_curl_multi_check_easy_thread(void *rbce_ptr) {
|
|
512
|
+
ruby_curl_easy *rbce = (ruby_curl_easy *)rbce_ptr;
|
|
513
|
+
ruby_curl_multi *rbcm = ruby_curl_multi_pointer_if_compatible(rbce->multi);
|
|
514
|
+
if (rbcm && rb_curl_multi_has_easy(rbcm, rbce)) {
|
|
515
|
+
ruby_curl_multi_check_thread(rbcm);
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
void rb_curl_multi_remove_request_reference(VALUE self, VALUE easy) {
|
|
498
520
|
VALUE requests;
|
|
499
521
|
VALUE object_id;
|
|
500
522
|
VALUE safety_signatures;
|
|
@@ -569,6 +591,7 @@ static void ruby_curl_multi_init(ruby_curl_multi *rbcm) {
|
|
|
569
591
|
rbcm->running = 0;
|
|
570
592
|
rbcm->closed = 0;
|
|
571
593
|
rbcm->perform_active = 0;
|
|
594
|
+
rbcm->perform_thread = Qnil;
|
|
572
595
|
rbcm->callback_active = 0;
|
|
573
596
|
rbcm->allow_close_during_perform = 0;
|
|
574
597
|
|
|
@@ -586,6 +609,7 @@ static void ruby_curl_multi_init(ruby_curl_multi *rbcm) {
|
|
|
586
609
|
}
|
|
587
610
|
|
|
588
611
|
static void ruby_curl_multi_ensure_handle(ruby_curl_multi *rbcm) {
|
|
612
|
+
ruby_curl_multi_check_thread(rbcm);
|
|
589
613
|
if (!rbcm->handle) {
|
|
590
614
|
if (rbcm->closed) {
|
|
591
615
|
raise_curl_multi_error_exception(CURLM_BAD_HANDLE);
|
|
@@ -615,6 +639,10 @@ static VALUE ruby_curl_multi_initialize(VALUE self) {
|
|
|
615
639
|
|
|
616
640
|
TypedData_Get_Struct(self, ruby_curl_multi, &ruby_curl_multi_data_type, rbcm);
|
|
617
641
|
|
|
642
|
+
if (rbcm->perform_active) {
|
|
643
|
+
rb_raise(rb_eRuntimeError, "Cannot initialize an active Curl::Multi handle during perform");
|
|
644
|
+
}
|
|
645
|
+
|
|
618
646
|
ruby_curl_multi_init(rbcm);
|
|
619
647
|
|
|
620
648
|
/*
|
|
@@ -781,7 +809,7 @@ VALUE ruby_curl_multi_add(VALUE self, VALUE easy) {
|
|
|
781
809
|
ruby_curl_multi *existing_rbcm;
|
|
782
810
|
|
|
783
811
|
TypedData_Get_Struct(self, ruby_curl_multi, &ruby_curl_multi_data_type, rbcm);
|
|
784
|
-
|
|
812
|
+
rbce = ruby_curl_easy_get_handle(easy);
|
|
785
813
|
ruby_curl_multi_ensure_handle(rbcm);
|
|
786
814
|
|
|
787
815
|
if (rb_curl_multi_has_easy(rbcm, rbce)) {
|
|
@@ -846,6 +874,7 @@ VALUE ruby_curl_multi_remove(VALUE self, VALUE rb_easy_handle) {
|
|
|
846
874
|
|
|
847
875
|
TypedData_Get_Struct(self, ruby_curl_multi, &ruby_curl_multi_data_type, rbcm);
|
|
848
876
|
|
|
877
|
+
ruby_curl_multi_check_thread(rbcm);
|
|
849
878
|
rb_curl_multi_remove(rbcm, rb_easy_handle);
|
|
850
879
|
|
|
851
880
|
return self;
|
|
@@ -855,7 +884,7 @@ static void rb_curl_multi_remove(ruby_curl_multi *rbcm, VALUE easy) {
|
|
|
855
884
|
CURLMcode result;
|
|
856
885
|
ruby_curl_easy *rbce;
|
|
857
886
|
|
|
858
|
-
|
|
887
|
+
rbce = ruby_curl_easy_get_handle(easy);
|
|
859
888
|
result = curl_multi_remove_handle(rbcm->handle, rbce->curl);
|
|
860
889
|
if (result != 0) {
|
|
861
890
|
raise_curl_multi_error_exception(result);
|
|
@@ -1022,7 +1051,8 @@ static VALUE rb_curl_multi_run_completion_callbacks(VALUE argp) {
|
|
|
1022
1051
|
VALUE easy_callback_error = Qnil;
|
|
1023
1052
|
VALUE callargs;
|
|
1024
1053
|
VALUE did_raise = rb_hash_new();
|
|
1025
|
-
long redirect_count;
|
|
1054
|
+
long redirect_count = 0;
|
|
1055
|
+
CURLcode info_result;
|
|
1026
1056
|
|
|
1027
1057
|
args->rbcm->callback_active = 1;
|
|
1028
1058
|
|
|
@@ -1040,12 +1070,18 @@ static VALUE rb_curl_multi_run_completion_callbacks(VALUE argp) {
|
|
|
1040
1070
|
}
|
|
1041
1071
|
|
|
1042
1072
|
#ifdef HAVE_CURLINFO_RESPONSE_CODE
|
|
1043
|
-
curl_easy_getinfo(args->rbce->curl, CURLINFO_RESPONSE_CODE, &response_code);
|
|
1073
|
+
info_result = curl_easy_getinfo(args->rbce->curl, CURLINFO_RESPONSE_CODE, &response_code);
|
|
1044
1074
|
#else /* use fdsets path for waiting */
|
|
1045
1075
|
// old libcurl
|
|
1046
|
-
curl_easy_getinfo(args->rbce->curl, CURLINFO_HTTP_CODE, &response_code);
|
|
1076
|
+
info_result = curl_easy_getinfo(args->rbce->curl, CURLINFO_HTTP_CODE, &response_code);
|
|
1047
1077
|
#endif
|
|
1048
|
-
|
|
1078
|
+
if (info_result != CURLE_OK) {
|
|
1079
|
+
raise_curl_easy_error_exception(info_result);
|
|
1080
|
+
}
|
|
1081
|
+
info_result = curl_easy_getinfo(args->rbce->curl, CURLINFO_REDIRECT_COUNT, &redirect_count);
|
|
1082
|
+
if (info_result != CURLE_OK) {
|
|
1083
|
+
raise_curl_easy_error_exception(info_result);
|
|
1084
|
+
}
|
|
1049
1085
|
|
|
1050
1086
|
if (args->result != 0) {
|
|
1051
1087
|
if (!rb_easy_nil("failure_proc")) {
|
|
@@ -2507,6 +2543,7 @@ static VALUE ruby_curl_multi_perform_guard_ensure(VALUE self) {
|
|
|
2507
2543
|
ruby_curl_multi *rbcm;
|
|
2508
2544
|
TypedData_Get_Struct(self, ruby_curl_multi, &ruby_curl_multi_data_type, rbcm);
|
|
2509
2545
|
rbcm->perform_active = 0;
|
|
2546
|
+
rbcm->perform_thread = Qnil;
|
|
2510
2547
|
rbcm->callback_active = 0;
|
|
2511
2548
|
rbcm->allow_close_during_perform = 0;
|
|
2512
2549
|
return Qnil;
|
|
@@ -2521,7 +2558,11 @@ static VALUE ruby_curl_multi_with_perform_guard(int argc, VALUE *argv, VALUE sel
|
|
|
2521
2558
|
rb_raise(rb_eRuntimeError, "Cannot recursively perform an active Curl::Multi handle");
|
|
2522
2559
|
}
|
|
2523
2560
|
|
|
2561
|
+
/* Reopening an autoclosed multi initializes its guard fields. Do this before
|
|
2562
|
+
* claiming ownership so repeated performs retain the full-lifetime guard. */
|
|
2563
|
+
ruby_curl_multi_ensure_handle(rbcm);
|
|
2524
2564
|
rbcm->perform_active = 1;
|
|
2565
|
+
rbcm->perform_thread = rb_thread_current();
|
|
2525
2566
|
args.argc = argc;
|
|
2526
2567
|
args.argv = argv;
|
|
2527
2568
|
args.self = self;
|
|
@@ -2578,6 +2619,7 @@ VALUE ruby_curl_multi_close(VALUE self) {
|
|
|
2578
2619
|
ruby_curl_multi *rbcm;
|
|
2579
2620
|
TypedData_Get_Struct(self, ruby_curl_multi, &ruby_curl_multi_data_type, rbcm);
|
|
2580
2621
|
|
|
2622
|
+
ruby_curl_multi_check_thread(rbcm);
|
|
2581
2623
|
if ((rbcm->perform_active || rbcm->callback_active) && !rbcm->allow_close_during_perform) {
|
|
2582
2624
|
rb_raise(rb_eRuntimeError, "Cannot close an active Curl::Multi handle during perform");
|
|
2583
2625
|
}
|
|
@@ -2615,6 +2657,7 @@ static int mark_attached_i(st_data_t key, st_data_t val, st_data_t arg) {
|
|
|
2615
2657
|
static void curl_multi_mark(void *ptr) {
|
|
2616
2658
|
ruby_curl_multi *rbcm = (ruby_curl_multi *)ptr;
|
|
2617
2659
|
if (!rbcm) return;
|
|
2660
|
+
rb_gc_mark(rbcm->perform_thread);
|
|
2618
2661
|
if (rbcm->attached) {
|
|
2619
2662
|
st_foreach(rbcm->attached, mark_attached_i, (st_data_t)0);
|
|
2620
2663
|
}
|
data/ext/curb_multi.h
CHANGED
|
@@ -17,6 +17,7 @@ typedef struct {
|
|
|
17
17
|
int running;
|
|
18
18
|
char closed;
|
|
19
19
|
char perform_active;
|
|
20
|
+
VALUE perform_thread; /* Owner while a drive loop may release the GVL. */
|
|
20
21
|
char callback_active;
|
|
21
22
|
char allow_close_during_perform;
|
|
22
23
|
CURLM *handle;
|
|
@@ -31,6 +32,9 @@ extern VALUE cCurlMulti;
|
|
|
31
32
|
extern const rb_data_type_t ruby_curl_multi_data_type;
|
|
32
33
|
|
|
33
34
|
void init_curb_multi();
|
|
35
|
+
int rb_curl_multi_easy_performing(void *rbce_ptr);
|
|
36
|
+
void rb_curl_multi_check_easy_thread(void *rbce_ptr);
|
|
37
|
+
void rb_curl_multi_remove_request_reference(VALUE self, VALUE easy);
|
|
34
38
|
void rb_curl_multi_forget_easy(ruby_curl_multi *rbcm, void *rbce_ptr);
|
|
35
39
|
CURLMcode rb_curl_multi_detach_easy(ruby_curl_multi *rbcm, void *rbce_ptr);
|
|
36
40
|
|
data/ext/curb_postfield.c
CHANGED
|
@@ -20,128 +20,117 @@ VALUE cCurlPostField;
|
|
|
20
20
|
|
|
21
21
|
/* ================= APPEND FORM FUNC ================ */
|
|
22
22
|
|
|
23
|
-
/*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
23
|
+
/* Snapshot before invoking further Ruby coercions. Native storage is private
|
|
24
|
+
* to this request and cannot move under GC or require VM-global GC roots. */
|
|
25
|
+
static ruby_curl_form_string *form_string_snapshot(VALUE value, ruby_curl_form_string **strings) {
|
|
26
|
+
ruby_curl_form_string *entry;
|
|
27
|
+
long length;
|
|
28
|
+
StringValue(value);
|
|
29
|
+
length = RSTRING_LEN(value);
|
|
30
|
+
/* One allocation for the list entry, payload and terminating NUL. */
|
|
31
|
+
entry = ruby_xmalloc(sizeof(*entry) + (size_t)length + 1);
|
|
32
|
+
entry->data = (char *)(entry + 1);
|
|
33
|
+
entry->length = length;
|
|
34
|
+
memcpy(entry->data, RSTRING_PTR(value), (size_t)length);
|
|
35
|
+
entry->data[length] = '\0';
|
|
36
|
+
entry->next = *strings;
|
|
37
|
+
*strings = entry;
|
|
38
|
+
RB_GC_GUARD(value);
|
|
39
|
+
return entry;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
static void free_form_strings_until(ruby_curl_form_string **strings,
|
|
43
|
+
ruby_curl_form_string *stop) {
|
|
44
|
+
while (*strings != stop) {
|
|
45
|
+
ruby_curl_form_string *next = (*strings)->next;
|
|
46
|
+
xfree(*strings);
|
|
47
|
+
*strings = next;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
void free_form_strings(ruby_curl_form_string *strings) {
|
|
52
|
+
free_form_strings_until(&strings, NULL);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
void append_to_form(VALUE self,
|
|
56
|
+
struct curl_httppost **first,
|
|
57
|
+
struct curl_httppost **last,
|
|
58
|
+
ruby_curl_form_string **strings) {
|
|
32
59
|
ruby_curl_postfield *rbcpf;
|
|
33
60
|
CURLFORMcode result = -1;
|
|
34
|
-
|
|
61
|
+
ruby_curl_form_string *name, *content = NULL, *remote = NULL, *local = NULL, *type = NULL;
|
|
62
|
+
ruby_curl_form_string *previous_strings = *strings;
|
|
63
|
+
int is_file;
|
|
64
|
+
struct curl_forms form[8];
|
|
65
|
+
int n = 0;
|
|
66
|
+
|
|
35
67
|
TypedData_Get_Struct(self, ruby_curl_postfield, &ruby_curl_postfield_data_type, rbcpf);
|
|
36
|
-
|
|
37
|
-
if (rbcpf->name == Qnil) {
|
|
68
|
+
if (NIL_P(rbcpf->name)) {
|
|
38
69
|
rb_raise(eCurlErrInvalidPostField, "Cannot post unnamed field");
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
} else {
|
|
56
|
-
result = curl_formadd(first, last, CURLFORM_PTRNAME, StringValuePtr(rbcpf->name),
|
|
57
|
-
CURLFORM_BUFFER, StringValuePtr(rbcpf->remote_file),
|
|
58
|
-
CURLFORM_BUFFERPTR, StringValuePtr(rbcpf->buffer_str),
|
|
59
|
-
CURLFORM_BUFFERLENGTH, RSTRING_LEN(rbcpf->buffer_str),
|
|
60
|
-
CURLFORM_CONTENTTYPE, StringValuePtr(rbcpf->content_type),
|
|
61
|
-
CURLFORM_END);
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
} else if (rbcpf->content != Qnil) {
|
|
65
|
-
// with content
|
|
66
|
-
if (rbcpf->remote_file == Qnil) {
|
|
67
|
-
rb_raise(eCurlErrInvalidPostField, "Cannot post file upload field with no filename");
|
|
68
|
-
} else {
|
|
69
|
-
if (rbcpf->content_type == Qnil) {
|
|
70
|
-
result = curl_formadd(first, last, CURLFORM_PTRNAME, StringValuePtr(rbcpf->name),
|
|
71
|
-
CURLFORM_BUFFER, StringValuePtr(rbcpf->remote_file),
|
|
72
|
-
CURLFORM_BUFFERPTR, StringValuePtr(rbcpf->content),
|
|
73
|
-
CURLFORM_BUFFERLENGTH, RSTRING_LEN(rbcpf->content),
|
|
74
|
-
CURLFORM_END);
|
|
75
|
-
} else {
|
|
76
|
-
result = curl_formadd(first, last, CURLFORM_PTRNAME, StringValuePtr(rbcpf->name),
|
|
77
|
-
CURLFORM_BUFFER, StringValuePtr(rbcpf->remote_file),
|
|
78
|
-
CURLFORM_BUFFERPTR, StringValuePtr(rbcpf->content),
|
|
79
|
-
CURLFORM_BUFFERLENGTH, RSTRING_LEN(rbcpf->content),
|
|
80
|
-
CURLFORM_CONTENTTYPE, StringValuePtr(rbcpf->content_type),
|
|
81
|
-
CURLFORM_END);
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
} else if (rbcpf->local_file != Qnil) {
|
|
85
|
-
// with local filename
|
|
86
|
-
if (rbcpf->local_file == Qnil) {
|
|
87
|
-
rb_raise(eCurlErrInvalidPostField, "Cannot post file upload field no filename");
|
|
88
|
-
} else {
|
|
89
|
-
if (rbcpf->remote_file == Qnil) {
|
|
90
|
-
rbcpf->remote_file = rbcpf->local_file;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
if (rbcpf->content_type == Qnil) {
|
|
94
|
-
result = curl_formadd(first, last, CURLFORM_PTRNAME, StringValuePtr(rbcpf->name),
|
|
95
|
-
CURLFORM_FILE, StringValuePtr(rbcpf->local_file),
|
|
96
|
-
CURLFORM_FILENAME, StringValuePtr(rbcpf->remote_file),
|
|
97
|
-
CURLFORM_END);
|
|
98
|
-
} else {
|
|
99
|
-
result = curl_formadd(first, last, CURLFORM_PTRNAME, StringValuePtr(rbcpf->name),
|
|
100
|
-
CURLFORM_FILE, StringValuePtr(rbcpf->local_file),
|
|
101
|
-
CURLFORM_FILENAME, StringValuePtr(rbcpf->remote_file),
|
|
102
|
-
CURLFORM_CONTENTTYPE, StringValuePtr(rbcpf->content_type),
|
|
103
|
-
CURLFORM_END);
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
} else {
|
|
107
|
-
rb_raise(eCurlErrInvalidPostField, "Cannot post file upload field with no data");
|
|
70
|
+
}
|
|
71
|
+
is_file = !NIL_P(rbcpf->local_file) || !NIL_P(rbcpf->remote_file);
|
|
72
|
+
if (!NIL_P(rbcpf->content_proc)) {
|
|
73
|
+
rbcpf->buffer_str = rb_funcall(rbcpf->content_proc, idCall, 1, self);
|
|
74
|
+
StringValue(rbcpf->buffer_str);
|
|
75
|
+
content = form_string_snapshot(rbcpf->buffer_str, strings);
|
|
76
|
+
} else if (!NIL_P(rbcpf->content)) {
|
|
77
|
+
StringValue(rbcpf->content);
|
|
78
|
+
content = form_string_snapshot(rbcpf->content, strings);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
name = form_string_snapshot(rbcpf->name, strings);
|
|
82
|
+
if (is_file) {
|
|
83
|
+
if (content != NULL) {
|
|
84
|
+
if (NIL_P(rbcpf->remote_file)) {
|
|
85
|
+
rb_raise(eCurlErrInvalidPostField, "Cannot post file upload field with no filename");
|
|
108
86
|
}
|
|
87
|
+
} else if (!NIL_P(rbcpf->local_file)) {
|
|
88
|
+
local = form_string_snapshot(rbcpf->local_file, strings);
|
|
89
|
+
if (NIL_P(rbcpf->remote_file)) rbcpf->remote_file = rbcpf->local_file;
|
|
109
90
|
} else {
|
|
110
|
-
|
|
111
|
-
if (rbcpf->content_proc != Qnil) {
|
|
112
|
-
rbcpf->buffer_str = rb_funcall(rbcpf->content_proc, idCall, 1, self);
|
|
113
|
-
|
|
114
|
-
if (rbcpf->content_type == Qnil) {
|
|
115
|
-
result = curl_formadd(first, last, CURLFORM_PTRNAME, StringValuePtr(rbcpf->name),
|
|
116
|
-
CURLFORM_PTRCONTENTS, StringValuePtr(rbcpf->buffer_str),
|
|
117
|
-
CURLFORM_CONTENTSLENGTH, RSTRING_LEN(rbcpf->buffer_str),
|
|
118
|
-
CURLFORM_END);
|
|
119
|
-
} else {
|
|
120
|
-
result = curl_formadd(first, last, CURLFORM_PTRNAME, StringValuePtr(rbcpf->name),
|
|
121
|
-
CURLFORM_PTRCONTENTS, StringValuePtr(rbcpf->buffer_str),
|
|
122
|
-
CURLFORM_CONTENTSLENGTH, RSTRING_LEN(rbcpf->buffer_str),
|
|
123
|
-
CURLFORM_CONTENTTYPE, StringValuePtr(rbcpf->content_type),
|
|
124
|
-
CURLFORM_END);
|
|
125
|
-
}
|
|
126
|
-
} else if (rbcpf->content != Qnil) {
|
|
127
|
-
if (rbcpf->content_type == Qnil) {
|
|
128
|
-
result = curl_formadd(first, last, CURLFORM_PTRNAME, StringValuePtr(rbcpf->name),
|
|
129
|
-
CURLFORM_PTRCONTENTS, StringValuePtr(rbcpf->content),
|
|
130
|
-
CURLFORM_CONTENTSLENGTH, RSTRING_LEN(rbcpf->content),
|
|
131
|
-
CURLFORM_END);
|
|
132
|
-
} else {
|
|
133
|
-
result = curl_formadd(first, last, CURLFORM_PTRNAME, StringValuePtr(rbcpf->name),
|
|
134
|
-
CURLFORM_PTRCONTENTS, StringValuePtr(rbcpf->content),
|
|
135
|
-
CURLFORM_CONTENTSLENGTH, RSTRING_LEN(rbcpf->content),
|
|
136
|
-
CURLFORM_CONTENTTYPE, StringValuePtr(rbcpf->content_type),
|
|
137
|
-
CURLFORM_END);
|
|
138
|
-
}
|
|
139
|
-
} else {
|
|
140
|
-
rb_raise(eCurlErrInvalidPostField, "Cannot post content field with no data");
|
|
141
|
-
}
|
|
91
|
+
rb_raise(eCurlErrInvalidPostField, "Cannot post file upload field with no data");
|
|
142
92
|
}
|
|
93
|
+
remote = form_string_snapshot(rbcpf->remote_file, strings);
|
|
94
|
+
} else if (content == NULL) {
|
|
95
|
+
rb_raise(eCurlErrInvalidPostField, "Cannot post content field with no data");
|
|
143
96
|
}
|
|
144
|
-
|
|
97
|
+
if (!NIL_P(rbcpf->content_type)) {
|
|
98
|
+
type = form_string_snapshot(rbcpf->content_type, strings);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/* libcurl copies metadata but borrows the private payload for the request.
|
|
102
|
+
* This avoids a second form-owned copy of ordinary field contents. */
|
|
103
|
+
form[n].option = CURLFORM_COPYNAME;
|
|
104
|
+
form[n++].value = name->data;
|
|
105
|
+
if (is_file) {
|
|
106
|
+
form[n].option = content == NULL ? CURLFORM_FILE : CURLFORM_BUFFER;
|
|
107
|
+
form[n++].value = (content == NULL ? local : remote)->data;
|
|
108
|
+
if (content == NULL) {
|
|
109
|
+
form[n].option = CURLFORM_FILENAME;
|
|
110
|
+
form[n++].value = remote->data;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
if (type != NULL) {
|
|
114
|
+
form[n].option = CURLFORM_CONTENTTYPE;
|
|
115
|
+
form[n++].value = type->data;
|
|
116
|
+
}
|
|
117
|
+
form[n].option = CURLFORM_END;
|
|
118
|
+
form[n].value = NULL;
|
|
119
|
+
|
|
120
|
+
if (content == NULL) {
|
|
121
|
+
result = curl_formadd(first, last, CURLFORM_ARRAY, form, CURLFORM_END);
|
|
122
|
+
} else {
|
|
123
|
+
result = curl_formadd(first, last, CURLFORM_ARRAY, form,
|
|
124
|
+
is_file ? CURLFORM_BUFFERPTR : CURLFORM_PTRCONTENTS,
|
|
125
|
+
content->data,
|
|
126
|
+
is_file ? CURLFORM_BUFFERLENGTH : CURLFORM_CONTENTSLENGTH,
|
|
127
|
+
content->length, CURLFORM_END);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/* Metadata was copied by curl_formadd. Retain only the payload, if any,
|
|
131
|
+
* which was the first snapshot allocated for this field. */
|
|
132
|
+
free_form_strings_until(strings, content ? content : previous_strings);
|
|
133
|
+
|
|
145
134
|
if (result != 0) {
|
|
146
135
|
const char *reason = NULL;
|
|
147
136
|
|
data/ext/curb_postfield.h
CHANGED
|
@@ -32,9 +32,18 @@ typedef struct {
|
|
|
32
32
|
extern VALUE cCurlPostField;
|
|
33
33
|
extern const rb_data_type_t ruby_curl_postfield_data_type;
|
|
34
34
|
|
|
35
|
+
typedef struct ruby_curl_form_string {
|
|
36
|
+
struct ruby_curl_form_string *next;
|
|
37
|
+
long length;
|
|
38
|
+
char *data;
|
|
39
|
+
} ruby_curl_form_string;
|
|
40
|
+
|
|
41
|
+
void free_form_strings(ruby_curl_form_string *strings);
|
|
42
|
+
|
|
35
43
|
void append_to_form(VALUE self,
|
|
36
44
|
struct curl_httppost **first,
|
|
37
|
-
struct curl_httppost **last
|
|
45
|
+
struct curl_httppost **last,
|
|
46
|
+
ruby_curl_form_string **strings);
|
|
38
47
|
|
|
39
48
|
void init_curb_postfield();
|
|
40
49
|
|
data/lib/curl/easy.rb
CHANGED
|
@@ -184,10 +184,16 @@ module Curl
|
|
|
184
184
|
set_protocol_allowlist('CURLOPT_REDIR_PROTOCOLS_STR', 'CURLOPT_REDIR_PROTOCOLS', protocols)
|
|
185
185
|
end
|
|
186
186
|
|
|
187
|
-
def safe_http!
|
|
187
|
+
def safe_http!(options = {})
|
|
188
|
+
options = Curl.__send__(:safe_http_options, options)
|
|
189
|
+
config = Curl.__send__(:ractor_local_state)[:safety_config] || Curl::SafetyConfig.new
|
|
190
|
+
limit = Curl::SafetyConfig.new
|
|
191
|
+
limit.max_body_bytes = options.fetch(:max_body_bytes, config.max_body_bytes)
|
|
188
192
|
__curb_set_safety_override!(
|
|
189
193
|
:protocols => [:http, :https],
|
|
190
|
-
:redirect_protocols => [:http, :https]
|
|
194
|
+
:redirect_protocols => [:http, :https],
|
|
195
|
+
:network_policy => :public,
|
|
196
|
+
:max_body_bytes => limit.max_body_bytes
|
|
191
197
|
)
|
|
192
198
|
end
|
|
193
199
|
|
|
@@ -197,7 +203,8 @@ module Curl
|
|
|
197
203
|
@__curb_safety_override = (defined?(@__curb_safety_override) && @__curb_safety_override) ? @__curb_safety_override.dup : {}
|
|
198
204
|
@__curb_safety_override[:protocols] = Array(options[:protocols]).map { |protocol| protocol.to_s.downcase.to_sym } if options.key?(:protocols)
|
|
199
205
|
@__curb_safety_override[:redirect_protocols] = Array(options[:redirect_protocols]).map { |protocol| protocol.to_s.downcase.to_sym } if options.key?(:redirect_protocols)
|
|
200
|
-
@__curb_safety_override[:max_body_bytes] = options[:max_body_bytes] if options.key?(:max_body_bytes)
|
|
206
|
+
@__curb_safety_override[:max_body_bytes] = options[:max_body_bytes] if options.key?(:max_body_bytes)
|
|
207
|
+
@__curb_safety_override[:network_policy] = options[:network_policy] if options.key?(:network_policy)
|
|
201
208
|
@__curb_safety_override_generation = __curb_safety_override_generation + 1
|
|
202
209
|
Curl.__send__(:apply_safety!, self) if Curl.respond_to?(:apply_safety!, true)
|
|
203
210
|
self
|
data/lib/curl/multi.rb
CHANGED
data/lib/curl.rb
CHANGED
|
@@ -23,6 +23,7 @@ module Curl
|
|
|
23
23
|
|
|
24
24
|
class SafetyConfig
|
|
25
25
|
DEFAULT_PROTOCOLS = [:http, :https].freeze
|
|
26
|
+
DEFAULT_MAX_BODY_BYTES = 10 * 1024 * 1024
|
|
26
27
|
|
|
27
28
|
attr_reader :max_body_bytes, :network_policy
|
|
28
29
|
attr_accessor :allow_proxies, :allow_resolve, :allow_connect_to, :allow_doh, :allow_unix_socket
|
|
@@ -30,8 +31,8 @@ module Curl
|
|
|
30
31
|
def initialize
|
|
31
32
|
@protocols = DEFAULT_PROTOCOLS
|
|
32
33
|
@redirect_protocols = nil
|
|
33
|
-
@max_body_bytes =
|
|
34
|
-
@network_policy =
|
|
34
|
+
@max_body_bytes = DEFAULT_MAX_BODY_BYTES
|
|
35
|
+
@network_policy = :public
|
|
35
36
|
@allowed_hosts = nil
|
|
36
37
|
@allowed_proxy_hosts = nil
|
|
37
38
|
@allowed_cidrs = nil
|
|
@@ -217,9 +218,11 @@ module Curl
|
|
|
217
218
|
apply_allowed_hosts!(easy, config.allowed_hosts) if config&.allowed_hosts
|
|
218
219
|
apply_allowed_cidrs!(easy, config) if config&.allowed_cidrs
|
|
219
220
|
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
221
|
+
network_policy = config&.network_policy
|
|
222
|
+
network_policy = :public if override && override[:network_policy] == :public
|
|
223
|
+
if network_policy
|
|
224
|
+
easy.network_policy = network_policy
|
|
225
|
+
apply_public_network_policy_controls!(easy, config || SafetyConfig.new) if network_policy == :public
|
|
223
226
|
end
|
|
224
227
|
|
|
225
228
|
apply_max_body_bytes!(easy, config.max_body_bytes) if config&.max_body_bytes
|
|
@@ -653,8 +656,7 @@ module Curl
|
|
|
653
656
|
|
|
654
657
|
http(verb, url, post_body, put_data) do |handle|
|
|
655
658
|
yield handle if block
|
|
656
|
-
handle.safe_http!
|
|
657
|
-
handle.max_body_bytes = options[:max_body_bytes] if options.key?(:max_body_bytes)
|
|
659
|
+
handle.safe_http!(options)
|
|
658
660
|
end
|
|
659
661
|
end
|
|
660
662
|
|