fast_curl 0.1.0 → 0.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.
- checksums.yaml +4 -4
- data/ext/fast_curl/fast_curl.c +563 -498
- data/lib/fast_curl/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: aa97f9eaef2dd36adbff2816b3ab08ec7e924b7a7a23c01c7c38723c6f071ff3
|
|
4
|
+
data.tar.gz: 5c5e02905534b2c54602b9f970fab2d587603f7c31ab6a0ab251bd6a1857932b
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 54c9d82c53ab24b08129c38cda9a82e06d834cf16f4b2b210f62edd2361c77db84f49eb59e3cbcbfc04d94dc12a758d89bc6e65bed23183132e8365186892716
|
|
7
|
+
data.tar.gz: 354ee56ed5973a66f365f76eb0f2a01264ae3e14e238ff616cd34d6251f10830babda151037a713b7e17573bb209387e05713b19b291c573b31cf11099ecb3b7
|
data/ext/fast_curl/fast_curl.c
CHANGED
|
@@ -8,646 +8,711 @@
|
|
|
8
8
|
#include <stdlib.h>
|
|
9
9
|
#include <string.h>
|
|
10
10
|
|
|
11
|
-
#define MAX_RESPONSE_SIZE
|
|
12
|
-
#define MAX_REDIRECTS
|
|
13
|
-
#define MAX_TIMEOUT
|
|
11
|
+
#define MAX_RESPONSE_SIZE (100 * 1024 * 1024)
|
|
12
|
+
#define MAX_REDIRECTS 5
|
|
13
|
+
#define MAX_TIMEOUT 300
|
|
14
|
+
#define INITIAL_BUF_CAP 8192
|
|
15
|
+
#define INITIAL_HEADER_CAP 16
|
|
16
|
+
#define POLL_TIMEOUT_MS 50
|
|
17
|
+
#define FIBER_POLL_TIMEOUT_MS 10
|
|
18
|
+
#define HEADER_LINE_BUF_SIZE 512
|
|
19
|
+
|
|
20
|
+
static ID id_status;
|
|
21
|
+
static ID id_headers;
|
|
22
|
+
static ID id_body;
|
|
23
|
+
static ID id_error_code;
|
|
24
|
+
static ID id_url;
|
|
25
|
+
static ID id_method;
|
|
26
|
+
static ID id_timeout;
|
|
27
|
+
static ID id_connections;
|
|
28
|
+
static ID id_count;
|
|
29
|
+
static ID id_keys;
|
|
30
|
+
static VALUE sym_status;
|
|
31
|
+
static VALUE sym_headers;
|
|
32
|
+
static VALUE sym_body;
|
|
33
|
+
static VALUE sym_error_code;
|
|
34
|
+
static VALUE sym_url;
|
|
35
|
+
static VALUE sym_method;
|
|
36
|
+
static VALUE sym_timeout;
|
|
37
|
+
static VALUE sym_connections;
|
|
38
|
+
static VALUE sym_count;
|
|
14
39
|
|
|
15
40
|
typedef struct {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
41
|
+
char *data;
|
|
42
|
+
size_t len;
|
|
43
|
+
size_t cap;
|
|
44
|
+
size_t max_size;
|
|
20
45
|
} buffer_t;
|
|
21
46
|
|
|
22
|
-
static void buffer_init(buffer_t *buf) {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
47
|
+
static inline void buffer_init(buffer_t *buf) {
|
|
48
|
+
buf->data = NULL;
|
|
49
|
+
buf->len = 0;
|
|
50
|
+
buf->cap = 0;
|
|
51
|
+
buf->max_size = MAX_RESPONSE_SIZE;
|
|
27
52
|
}
|
|
28
53
|
|
|
29
|
-
static void buffer_free(buffer_t *buf) {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
54
|
+
static inline void buffer_free(buffer_t *buf) {
|
|
55
|
+
if (buf->data) {
|
|
56
|
+
free(buf->data);
|
|
57
|
+
buf->data = NULL;
|
|
58
|
+
}
|
|
59
|
+
buf->len = 0;
|
|
60
|
+
buf->cap = 0;
|
|
36
61
|
}
|
|
37
62
|
|
|
38
|
-
static size_t write_callback(char *ptr, size_t size, size_t nmemb,
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
size_t total = size * nmemb;
|
|
63
|
+
static size_t write_callback(char *ptr, size_t size, size_t nmemb, void *userdata) {
|
|
64
|
+
buffer_t *buf = (buffer_t *)userdata;
|
|
65
|
+
size_t total = size * nmemb;
|
|
42
66
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
67
|
+
if (buf->len + total > buf->max_size) {
|
|
68
|
+
return 0;
|
|
69
|
+
}
|
|
46
70
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
71
|
+
if (buf->len + total >= buf->cap) {
|
|
72
|
+
size_t new_cap = (buf->cap == 0) ? INITIAL_BUF_CAP : buf->cap;
|
|
73
|
+
while (new_cap <= buf->len + total)
|
|
74
|
+
new_cap *= 2;
|
|
51
75
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
}
|
|
76
|
+
if (new_cap > buf->max_size)
|
|
77
|
+
new_cap = buf->max_size;
|
|
55
78
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
79
|
+
char *new_data = realloc(buf->data, new_cap);
|
|
80
|
+
if (!new_data)
|
|
81
|
+
return 0;
|
|
82
|
+
buf->data = new_data;
|
|
83
|
+
buf->cap = new_cap;
|
|
84
|
+
}
|
|
62
85
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
86
|
+
memcpy(buf->data + buf->len, ptr, total);
|
|
87
|
+
buf->len += total;
|
|
88
|
+
return total;
|
|
66
89
|
}
|
|
67
90
|
|
|
68
91
|
typedef struct {
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
92
|
+
char *str;
|
|
93
|
+
size_t len;
|
|
94
|
+
} header_entry_t;
|
|
95
|
+
|
|
96
|
+
typedef struct {
|
|
97
|
+
header_entry_t *entries;
|
|
98
|
+
int count;
|
|
99
|
+
int cap;
|
|
72
100
|
} header_list_t;
|
|
73
101
|
|
|
74
|
-
static void header_list_init(header_list_t *h) {
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
102
|
+
static inline void header_list_init(header_list_t *h) {
|
|
103
|
+
h->entries = NULL;
|
|
104
|
+
h->count = 0;
|
|
105
|
+
h->cap = 0;
|
|
78
106
|
}
|
|
79
107
|
|
|
80
108
|
static void header_list_free(header_list_t *h) {
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
109
|
+
for (int i = 0; i < h->count; i++)
|
|
110
|
+
free(h->entries[i].str);
|
|
111
|
+
free(h->entries);
|
|
112
|
+
h->entries = NULL;
|
|
113
|
+
h->count = 0;
|
|
114
|
+
h->cap = 0;
|
|
87
115
|
}
|
|
88
116
|
|
|
89
|
-
static size_t header_callback(char *ptr, size_t size, size_t nmemb,
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
size_t total = size * nmemb;
|
|
117
|
+
static size_t header_callback(char *ptr, size_t size, size_t nmemb, void *userdata) {
|
|
118
|
+
header_list_t *h = (header_list_t *)userdata;
|
|
119
|
+
size_t total = size * nmemb;
|
|
93
120
|
|
|
94
|
-
|
|
95
|
-
|
|
121
|
+
if (total <= 2)
|
|
122
|
+
return total;
|
|
96
123
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
char *entry = malloc(total + 1);
|
|
107
|
-
if (!entry)
|
|
108
|
-
return 0;
|
|
109
|
-
memcpy(entry, ptr, total);
|
|
124
|
+
if (h->count >= h->cap) {
|
|
125
|
+
int new_cap = (h->cap == 0) ? INITIAL_HEADER_CAP : h->cap * 2;
|
|
126
|
+
header_entry_t *new_entries = realloc(h->entries, sizeof(header_entry_t) * new_cap);
|
|
127
|
+
if (!new_entries)
|
|
128
|
+
return 0;
|
|
129
|
+
h->entries = new_entries;
|
|
130
|
+
h->cap = new_cap;
|
|
131
|
+
}
|
|
110
132
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
133
|
+
size_t stripped = total;
|
|
134
|
+
while (stripped > 0 && (ptr[stripped - 1] == '\r' || ptr[stripped - 1] == '\n'))
|
|
135
|
+
stripped--;
|
|
136
|
+
|
|
137
|
+
char *entry = malloc(stripped + 1);
|
|
138
|
+
if (!entry)
|
|
139
|
+
return 0;
|
|
140
|
+
memcpy(entry, ptr, stripped);
|
|
141
|
+
entry[stripped] = '\0';
|
|
142
|
+
|
|
143
|
+
h->entries[h->count].str = entry;
|
|
144
|
+
h->entries[h->count].len = stripped;
|
|
145
|
+
h->count++;
|
|
146
|
+
|
|
147
|
+
return size * nmemb;
|
|
116
148
|
}
|
|
117
149
|
|
|
118
150
|
typedef struct {
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
151
|
+
CURL *easy;
|
|
152
|
+
int index;
|
|
153
|
+
buffer_t body;
|
|
154
|
+
header_list_t headers;
|
|
155
|
+
struct curl_slist *req_headers;
|
|
156
|
+
int done;
|
|
125
157
|
} request_ctx_t;
|
|
126
158
|
|
|
127
|
-
static void request_ctx_init(request_ctx_t *ctx, int index) {
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
159
|
+
static inline void request_ctx_init(request_ctx_t *ctx, int index) {
|
|
160
|
+
ctx->easy = curl_easy_init();
|
|
161
|
+
ctx->index = index;
|
|
162
|
+
buffer_init(&ctx->body);
|
|
163
|
+
header_list_init(&ctx->headers);
|
|
164
|
+
ctx->req_headers = NULL;
|
|
165
|
+
ctx->done = 0;
|
|
134
166
|
}
|
|
135
167
|
|
|
136
168
|
static void request_ctx_free(request_ctx_t *ctx) {
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
169
|
+
if (ctx->easy) {
|
|
170
|
+
curl_easy_cleanup(ctx->easy);
|
|
171
|
+
ctx->easy = NULL;
|
|
172
|
+
}
|
|
173
|
+
buffer_free(&ctx->body);
|
|
174
|
+
header_list_free(&ctx->headers);
|
|
175
|
+
if (ctx->req_headers) {
|
|
176
|
+
curl_slist_free_all(ctx->req_headers);
|
|
177
|
+
ctx->req_headers = NULL;
|
|
178
|
+
}
|
|
147
179
|
}
|
|
148
180
|
|
|
149
181
|
typedef struct {
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
182
|
+
CURLM *multi;
|
|
183
|
+
request_ctx_t *requests;
|
|
184
|
+
int count;
|
|
185
|
+
int still_running;
|
|
186
|
+
long timeout_ms;
|
|
187
|
+
int max_connections;
|
|
156
188
|
} multi_session_t;
|
|
157
189
|
|
|
158
190
|
static VALUE build_response(request_ctx_t *ctx) {
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
val_end
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
191
|
+
long status = 0;
|
|
192
|
+
curl_easy_getinfo(ctx->easy, CURLINFO_RESPONSE_CODE, &status);
|
|
193
|
+
|
|
194
|
+
VALUE headers_hash = rb_hash_new();
|
|
195
|
+
for (int i = 0; i < ctx->headers.count; i++) {
|
|
196
|
+
const char *hdr = ctx->headers.entries[i].str;
|
|
197
|
+
size_t hdr_len = ctx->headers.entries[i].len;
|
|
198
|
+
|
|
199
|
+
const char *colon = memchr(hdr, ':', hdr_len);
|
|
200
|
+
if (!colon)
|
|
201
|
+
continue;
|
|
202
|
+
|
|
203
|
+
VALUE key = rb_str_new(hdr, colon - hdr);
|
|
204
|
+
|
|
205
|
+
const char *val_start = colon + 1;
|
|
206
|
+
const char *val_end = hdr + hdr_len;
|
|
207
|
+
|
|
208
|
+
while (val_start < val_end && (*val_start == ' ' || *val_start == '\t'))
|
|
209
|
+
val_start++;
|
|
210
|
+
|
|
211
|
+
while (val_end > val_start && (*(val_end - 1) == ' ' || *(val_end - 1) == '\t'))
|
|
212
|
+
val_end--;
|
|
213
|
+
|
|
214
|
+
VALUE val = rb_str_new(val_start, val_end - val_start);
|
|
215
|
+
rb_hash_aset(headers_hash, key, val);
|
|
182
216
|
}
|
|
183
|
-
}
|
|
184
217
|
|
|
185
|
-
|
|
186
|
-
|
|
218
|
+
VALUE body_str =
|
|
219
|
+
ctx->body.data ? rb_str_new(ctx->body.data, ctx->body.len) : rb_str_new_cstr("");
|
|
187
220
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
221
|
+
VALUE result = rb_hash_new();
|
|
222
|
+
rb_hash_aset(result, sym_status, LONG2NUM(status));
|
|
223
|
+
rb_hash_aset(result, sym_headers, headers_hash);
|
|
224
|
+
rb_hash_aset(result, sym_body, body_str);
|
|
192
225
|
|
|
193
|
-
|
|
226
|
+
return result;
|
|
194
227
|
}
|
|
195
228
|
|
|
196
229
|
static VALUE build_error_response(const char *message) {
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
230
|
+
VALUE result = rb_hash_new();
|
|
231
|
+
rb_hash_aset(result, sym_status, INT2NUM(0));
|
|
232
|
+
rb_hash_aset(result, sym_headers, Qnil);
|
|
233
|
+
rb_hash_aset(result, sym_body, rb_str_new_cstr(message));
|
|
234
|
+
return result;
|
|
202
235
|
}
|
|
203
236
|
|
|
204
|
-
static
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
if (res != CURLE_OK) { \
|
|
212
|
-
return res; \
|
|
213
|
-
} \
|
|
214
|
-
} while (0)
|
|
215
|
-
|
|
216
|
-
static CURLcode setup_basic_options(CURL *easy, const char *url_str,
|
|
217
|
-
long timeout_sec, request_ctx_t *ctx) {
|
|
218
|
-
|
|
219
|
-
CURL_SETOPT_CHECK(easy, CURLOPT_URL, url_str);
|
|
220
|
-
CURL_SETOPT_CHECK(easy, CURLOPT_WRITEFUNCTION, write_callback);
|
|
221
|
-
CURL_SETOPT_CHECK(easy, CURLOPT_WRITEDATA, &ctx->body);
|
|
222
|
-
CURL_SETOPT_CHECK(easy, CURLOPT_HEADERFUNCTION, header_callback);
|
|
223
|
-
CURL_SETOPT_CHECK(easy, CURLOPT_HEADERDATA, &ctx->headers);
|
|
224
|
-
CURL_SETOPT_CHECK(easy, CURLOPT_TIMEOUT, timeout_sec);
|
|
225
|
-
CURL_SETOPT_CHECK(easy, CURLOPT_NOSIGNAL, 1L);
|
|
226
|
-
CURL_SETOPT_CHECK(easy, CURLOPT_FOLLOWLOCATION, 1L);
|
|
227
|
-
CURL_SETOPT_CHECK(easy, CURLOPT_MAXREDIRS, MAX_REDIRECTS);
|
|
228
|
-
CURL_SETOPT_CHECK(easy, CURLOPT_ACCEPT_ENCODING, "");
|
|
229
|
-
CURL_SETOPT_CHECK(easy, CURLOPT_PRIVATE, (char *)ctx);
|
|
230
|
-
|
|
231
|
-
return CURLE_OK;
|
|
237
|
+
static VALUE build_error_response_with_code(const char *message, int error_code) {
|
|
238
|
+
VALUE result = rb_hash_new();
|
|
239
|
+
rb_hash_aset(result, sym_status, INT2NUM(0));
|
|
240
|
+
rb_hash_aset(result, sym_headers, Qnil);
|
|
241
|
+
rb_hash_aset(result, sym_body, rb_str_new_cstr(message));
|
|
242
|
+
rb_hash_aset(result, sym_error_code, INT2NUM(error_code));
|
|
243
|
+
return result;
|
|
232
244
|
}
|
|
233
245
|
|
|
234
|
-
static
|
|
246
|
+
static int is_valid_url(const char *url) {
|
|
247
|
+
if (!url)
|
|
248
|
+
return 0;
|
|
249
|
+
|
|
250
|
+
size_t url_len = strlen(url);
|
|
251
|
+
|
|
252
|
+
if (url_len < 8 || url_len > 2048)
|
|
253
|
+
return 0;
|
|
254
|
+
|
|
255
|
+
if (strncmp(url, "https://", 8) == 0)
|
|
256
|
+
return 1;
|
|
257
|
+
if (url_len >= 7 && strncmp(url, "http://", 7) == 0)
|
|
258
|
+
return 1;
|
|
259
|
+
|
|
260
|
+
return 0;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
#define CURL_SETOPT_CHECK(handle, option, value) \
|
|
264
|
+
do { \
|
|
265
|
+
CURLcode res = curl_easy_setopt(handle, option, value); \
|
|
266
|
+
if (res != CURLE_OK) { \
|
|
267
|
+
return res; \
|
|
268
|
+
} \
|
|
269
|
+
} while (0)
|
|
270
|
+
|
|
271
|
+
static CURLcode setup_basic_options(CURL *easy, const char *url_str, long timeout_sec,
|
|
272
|
+
request_ctx_t *ctx) {
|
|
273
|
+
CURL_SETOPT_CHECK(easy, CURLOPT_URL, url_str);
|
|
274
|
+
CURL_SETOPT_CHECK(easy, CURLOPT_WRITEFUNCTION, write_callback);
|
|
275
|
+
CURL_SETOPT_CHECK(easy, CURLOPT_WRITEDATA, &ctx->body);
|
|
276
|
+
CURL_SETOPT_CHECK(easy, CURLOPT_HEADERFUNCTION, header_callback);
|
|
277
|
+
CURL_SETOPT_CHECK(easy, CURLOPT_HEADERDATA, &ctx->headers);
|
|
278
|
+
CURL_SETOPT_CHECK(easy, CURLOPT_TIMEOUT, timeout_sec);
|
|
279
|
+
CURL_SETOPT_CHECK(easy, CURLOPT_NOSIGNAL, 1L);
|
|
280
|
+
CURL_SETOPT_CHECK(easy, CURLOPT_FOLLOWLOCATION, 1L);
|
|
281
|
+
CURL_SETOPT_CHECK(easy, CURLOPT_MAXREDIRS, MAX_REDIRECTS);
|
|
282
|
+
CURL_SETOPT_CHECK(easy, CURLOPT_ACCEPT_ENCODING, "");
|
|
283
|
+
CURL_SETOPT_CHECK(easy, CURLOPT_PRIVATE, (char *)ctx);
|
|
284
|
+
CURL_SETOPT_CHECK(easy, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2TLS);
|
|
285
|
+
|
|
286
|
+
return CURLE_OK;
|
|
287
|
+
}
|
|
235
288
|
|
|
236
|
-
|
|
237
|
-
|
|
289
|
+
static CURLcode setup_security_options(CURL *easy) {
|
|
290
|
+
CURL_SETOPT_CHECK(easy, CURLOPT_SSL_VERIFYPEER, 1L);
|
|
291
|
+
CURL_SETOPT_CHECK(easy, CURLOPT_SSL_VERIFYHOST, 2L);
|
|
238
292
|
|
|
239
293
|
#ifdef CURLOPT_PROTOCOLS_STR
|
|
240
|
-
|
|
241
|
-
|
|
294
|
+
CURL_SETOPT_CHECK(easy, CURLOPT_PROTOCOLS_STR, "http,https");
|
|
295
|
+
CURL_SETOPT_CHECK(easy, CURLOPT_REDIR_PROTOCOLS_STR, "http,https");
|
|
242
296
|
#else
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
CURL_SETOPT_CHECK(easy, CURLOPT_REDIR_PROTOCOLS,
|
|
246
|
-
CURLPROTO_HTTP | CURLPROTO_HTTPS);
|
|
297
|
+
CURL_SETOPT_CHECK(easy, CURLOPT_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS);
|
|
298
|
+
CURL_SETOPT_CHECK(easy, CURLOPT_REDIR_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS);
|
|
247
299
|
#endif
|
|
248
300
|
|
|
249
|
-
|
|
301
|
+
return CURLE_OK;
|
|
250
302
|
}
|
|
251
303
|
|
|
252
304
|
static CURLcode setup_method_and_body(CURL *easy, VALUE method, VALUE body) {
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
305
|
+
if (!NIL_P(method)) {
|
|
306
|
+
const char *m = StringValueCStr(method);
|
|
307
|
+
if (strcmp(m, "POST") == 0) {
|
|
308
|
+
CURL_SETOPT_CHECK(easy, CURLOPT_POST, 1L);
|
|
309
|
+
} else if (strcmp(m, "PUT") == 0) {
|
|
310
|
+
CURL_SETOPT_CHECK(easy, CURLOPT_CUSTOMREQUEST, "PUT");
|
|
311
|
+
} else if (strcmp(m, "DELETE") == 0) {
|
|
312
|
+
CURL_SETOPT_CHECK(easy, CURLOPT_CUSTOMREQUEST, "DELETE");
|
|
313
|
+
} else if (strcmp(m, "PATCH") == 0) {
|
|
314
|
+
CURL_SETOPT_CHECK(easy, CURLOPT_CUSTOMREQUEST, "PATCH");
|
|
315
|
+
} else if (strcmp(m, "GET") != 0) {
|
|
316
|
+
CURL_SETOPT_CHECK(easy, CURLOPT_CUSTOMREQUEST, m);
|
|
317
|
+
}
|
|
266
318
|
}
|
|
267
|
-
}
|
|
268
319
|
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
320
|
+
if (!NIL_P(body)) {
|
|
321
|
+
CURL_SETOPT_CHECK(easy, CURLOPT_POSTFIELDS, StringValuePtr(body));
|
|
322
|
+
CURL_SETOPT_CHECK(easy, CURLOPT_POSTFIELDSIZE, RSTRING_LEN(body));
|
|
323
|
+
}
|
|
273
324
|
|
|
274
|
-
|
|
325
|
+
return CURLE_OK;
|
|
275
326
|
}
|
|
276
327
|
|
|
277
|
-
static int
|
|
278
|
-
|
|
279
|
-
VALUE url = rb_hash_aref(request, ID2SYM(rb_intern("url")));
|
|
280
|
-
VALUE method = rb_hash_aref(request, ID2SYM(rb_intern("method")));
|
|
281
|
-
VALUE headers = rb_hash_aref(request, ID2SYM(rb_intern("headers")));
|
|
282
|
-
VALUE body = rb_hash_aref(request, ID2SYM(rb_intern("body")));
|
|
328
|
+
static int header_iter_cb(VALUE key, VALUE val, VALUE arg) {
|
|
329
|
+
request_ctx_t *ctx = (request_ctx_t *)arg;
|
|
283
330
|
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
331
|
+
VALUE key_str = rb_String(key);
|
|
332
|
+
const char *k = RSTRING_PTR(key_str);
|
|
333
|
+
long klen = RSTRING_LEN(key_str);
|
|
287
334
|
|
|
288
|
-
|
|
335
|
+
if (NIL_P(val) || RSTRING_LEN(rb_String(val)) == 0) {
|
|
336
|
+
char stack_buf[HEADER_LINE_BUF_SIZE];
|
|
337
|
+
char *buf = stack_buf;
|
|
338
|
+
long need = klen + 2;
|
|
289
339
|
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
340
|
+
if (need > HEADER_LINE_BUF_SIZE)
|
|
341
|
+
buf = malloc(need);
|
|
342
|
+
if (!buf)
|
|
343
|
+
return ST_CONTINUE;
|
|
293
344
|
|
|
294
|
-
|
|
345
|
+
memcpy(buf, k, klen);
|
|
346
|
+
buf[klen] = ';';
|
|
347
|
+
buf[klen + 1] = '\0';
|
|
295
348
|
|
|
296
|
-
|
|
297
|
-
if (res != CURLE_OK)
|
|
298
|
-
return 0;
|
|
349
|
+
ctx->req_headers = curl_slist_append(ctx->req_headers, buf);
|
|
299
350
|
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
351
|
+
if (buf != stack_buf)
|
|
352
|
+
free(buf);
|
|
353
|
+
} else {
|
|
354
|
+
VALUE val_str = rb_String(val);
|
|
355
|
+
const char *v = RSTRING_PTR(val_str);
|
|
356
|
+
long vlen = RSTRING_LEN(val_str);
|
|
357
|
+
char stack_buf[HEADER_LINE_BUF_SIZE];
|
|
358
|
+
char *buf = stack_buf;
|
|
359
|
+
long need = klen + 2 + vlen + 1;
|
|
360
|
+
|
|
361
|
+
if (need > HEADER_LINE_BUF_SIZE)
|
|
362
|
+
buf = malloc(need);
|
|
363
|
+
if (!buf)
|
|
364
|
+
return ST_CONTINUE;
|
|
365
|
+
|
|
366
|
+
memcpy(buf, k, klen);
|
|
367
|
+
buf[klen] = ':';
|
|
368
|
+
buf[klen + 1] = ' ';
|
|
369
|
+
memcpy(buf + klen + 2, v, vlen);
|
|
370
|
+
buf[klen + 2 + vlen] = '\0';
|
|
371
|
+
|
|
372
|
+
ctx->req_headers = curl_slist_append(ctx->req_headers, buf);
|
|
373
|
+
|
|
374
|
+
if (buf != stack_buf)
|
|
375
|
+
free(buf);
|
|
376
|
+
}
|
|
303
377
|
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
378
|
+
return ST_CONTINUE;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
static int setup_easy_handle(request_ctx_t *ctx, VALUE request, long timeout_sec) {
|
|
382
|
+
VALUE url = rb_hash_aref(request, sym_url);
|
|
383
|
+
VALUE method = rb_hash_aref(request, sym_method);
|
|
384
|
+
VALUE headers = rb_hash_aref(request, sym_headers);
|
|
385
|
+
VALUE body = rb_hash_aref(request, sym_body);
|
|
307
386
|
|
|
308
|
-
|
|
309
|
-
VALUE keys = rb_funcall(headers, rb_intern("keys"), 0);
|
|
310
|
-
long hlen = RARRAY_LEN(keys);
|
|
311
|
-
for (long i = 0; i < hlen; i++) {
|
|
312
|
-
VALUE key = rb_ary_entry(keys, i);
|
|
313
|
-
VALUE val = rb_hash_aref(headers, key);
|
|
314
|
-
VALUE header_line;
|
|
315
|
-
|
|
316
|
-
if (NIL_P(val) || RSTRING_LEN(rb_String(val)) == 0) {
|
|
317
|
-
header_line = rb_sprintf("%" PRIsVALUE ";", key);
|
|
318
|
-
} else {
|
|
319
|
-
header_line = rb_sprintf("%" PRIsVALUE ": %" PRIsVALUE, key, val);
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
ctx->req_headers =
|
|
323
|
-
curl_slist_append(ctx->req_headers, StringValueCStr(header_line));
|
|
324
|
-
if (!ctx->req_headers) {
|
|
387
|
+
if (NIL_P(url))
|
|
325
388
|
return 0;
|
|
326
|
-
|
|
389
|
+
|
|
390
|
+
const char *url_str = StringValueCStr(url);
|
|
391
|
+
|
|
392
|
+
if (!is_valid_url(url_str)) {
|
|
393
|
+
rb_raise(rb_eArgError, "Invalid URL: %s", url_str);
|
|
327
394
|
}
|
|
328
395
|
|
|
329
|
-
res
|
|
396
|
+
CURLcode res;
|
|
397
|
+
|
|
398
|
+
res = setup_basic_options(ctx->easy, url_str, timeout_sec, ctx);
|
|
330
399
|
if (res != CURLE_OK)
|
|
331
|
-
|
|
332
|
-
}
|
|
400
|
+
return 0;
|
|
333
401
|
|
|
334
|
-
|
|
402
|
+
res = setup_security_options(ctx->easy);
|
|
403
|
+
if (res != CURLE_OK)
|
|
404
|
+
return 0;
|
|
405
|
+
|
|
406
|
+
res = setup_method_and_body(ctx->easy, method, body);
|
|
407
|
+
if (res != CURLE_OK)
|
|
408
|
+
return 0;
|
|
409
|
+
|
|
410
|
+
if (!NIL_P(headers) && rb_obj_is_kind_of(headers, rb_cHash)) {
|
|
411
|
+
rb_hash_foreach(headers, header_iter_cb, (VALUE)ctx);
|
|
412
|
+
|
|
413
|
+
if (ctx->req_headers) {
|
|
414
|
+
res = curl_easy_setopt(ctx->easy, CURLOPT_HTTPHEADER, ctx->req_headers);
|
|
415
|
+
if (res != CURLE_OK)
|
|
416
|
+
return 0;
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
return 1;
|
|
335
421
|
}
|
|
336
422
|
|
|
337
423
|
static void *perform_without_gvl(void *arg) {
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
424
|
+
multi_session_t *session = (multi_session_t *)arg;
|
|
425
|
+
|
|
426
|
+
while (session->still_running > 0) {
|
|
427
|
+
CURLMcode mc = curl_multi_perform(session->multi, &session->still_running);
|
|
428
|
+
if (mc != CURLM_OK)
|
|
429
|
+
break;
|
|
430
|
+
|
|
431
|
+
if (session->still_running > 0) {
|
|
432
|
+
int numfds = 0;
|
|
433
|
+
mc = curl_multi_poll(session->multi, NULL, 0, POLL_TIMEOUT_MS, &numfds);
|
|
434
|
+
if (mc != CURLM_OK)
|
|
435
|
+
break;
|
|
436
|
+
}
|
|
350
437
|
}
|
|
351
|
-
}
|
|
352
438
|
|
|
353
|
-
|
|
439
|
+
return NULL;
|
|
354
440
|
}
|
|
355
441
|
|
|
356
442
|
static void *poll_without_gvl(void *arg) {
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
443
|
+
multi_session_t *session = (multi_session_t *)arg;
|
|
444
|
+
int numfds = 0;
|
|
445
|
+
curl_multi_poll(session->multi, NULL, 0, POLL_TIMEOUT_MS, &numfds);
|
|
446
|
+
curl_multi_perform(session->multi, &session->still_running);
|
|
447
|
+
return NULL;
|
|
362
448
|
}
|
|
363
449
|
|
|
364
450
|
static void unblock_perform(void *arg) {
|
|
365
|
-
|
|
366
|
-
(void)session;
|
|
451
|
+
(void)arg;
|
|
367
452
|
}
|
|
368
453
|
|
|
369
454
|
static int has_fiber_scheduler(void) {
|
|
370
455
|
#ifdef HAVE_RB_FIBER_SCHEDULER_CURRENT
|
|
371
|
-
|
|
372
|
-
|
|
456
|
+
VALUE scheduler = rb_fiber_scheduler_current();
|
|
457
|
+
return scheduler != Qnil && scheduler != Qfalse;
|
|
373
458
|
#else
|
|
374
|
-
|
|
459
|
+
return 0;
|
|
375
460
|
#endif
|
|
376
461
|
}
|
|
377
462
|
|
|
378
463
|
typedef struct {
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
464
|
+
VALUE results;
|
|
465
|
+
int completed;
|
|
466
|
+
int target;
|
|
467
|
+
int stream;
|
|
383
468
|
} completion_ctx_t;
|
|
384
469
|
|
|
385
470
|
static int process_completed(multi_session_t *session, completion_ctx_t *cctx) {
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
471
|
+
CURLMsg *msg;
|
|
472
|
+
int msgs_left;
|
|
473
|
+
|
|
474
|
+
while ((msg = curl_multi_info_read(session->multi, &msgs_left))) {
|
|
475
|
+
if (msg->msg != CURLMSG_DONE)
|
|
476
|
+
continue;
|
|
477
|
+
|
|
478
|
+
request_ctx_t *ctx = NULL;
|
|
479
|
+
curl_easy_getinfo(msg->easy_handle, CURLINFO_PRIVATE, (char **)&ctx);
|
|
480
|
+
if (!ctx || ctx->done)
|
|
481
|
+
continue;
|
|
482
|
+
ctx->done = 1;
|
|
483
|
+
|
|
484
|
+
VALUE response;
|
|
485
|
+
if (msg->data.result == CURLE_OK) {
|
|
486
|
+
response = build_response(ctx);
|
|
487
|
+
} else {
|
|
488
|
+
response = build_error_response_with_code(curl_easy_strerror(msg->data.result),
|
|
489
|
+
(int)msg->data.result);
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
VALUE pair = rb_ary_new_from_args(2, INT2NUM(ctx->index), response);
|
|
493
|
+
|
|
494
|
+
if (cctx->stream) {
|
|
495
|
+
rb_yield(pair);
|
|
496
|
+
} else if (!NIL_P(cctx->results)) {
|
|
497
|
+
rb_ary_store(cctx->results, ctx->index, pair);
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
cctx->completed++;
|
|
501
|
+
|
|
502
|
+
if (cctx->target > 0 && cctx->completed >= cctx->target)
|
|
503
|
+
return 1;
|
|
406
504
|
}
|
|
407
505
|
|
|
408
|
-
VALUE pair = rb_ary_new_from_args(2, INT2NUM(ctx->index), response);
|
|
409
|
-
|
|
410
|
-
if (cctx->stream) {
|
|
411
|
-
rb_yield(pair);
|
|
412
|
-
} else if (!NIL_P(cctx->results)) {
|
|
413
|
-
rb_ary_store(cctx->results, ctx->index, pair);
|
|
414
|
-
}
|
|
415
|
-
|
|
416
|
-
cctx->completed++;
|
|
417
|
-
|
|
418
|
-
if (cctx->target > 0 && cctx->completed >= cctx->target) {
|
|
419
|
-
return 1;
|
|
420
|
-
}
|
|
421
|
-
}
|
|
422
|
-
|
|
423
|
-
return 0;
|
|
424
|
-
}
|
|
425
|
-
|
|
426
|
-
static VALUE build_error_response_with_code(const char *message,
|
|
427
|
-
int error_code) {
|
|
428
|
-
VALUE result = rb_hash_new();
|
|
429
|
-
rb_hash_aset(result, ID2SYM(rb_intern("status")), INT2NUM(0));
|
|
430
|
-
rb_hash_aset(result, ID2SYM(rb_intern("headers")), Qnil);
|
|
431
|
-
rb_hash_aset(result, ID2SYM(rb_intern("body")), rb_str_new_cstr(message));
|
|
432
|
-
rb_hash_aset(result, ID2SYM(rb_intern("error_code")), INT2NUM(error_code));
|
|
433
|
-
return result;
|
|
434
|
-
}
|
|
435
|
-
|
|
436
|
-
static int is_valid_url(const char *url) {
|
|
437
|
-
if (!url || strlen(url) == 0)
|
|
438
|
-
return 0;
|
|
439
|
-
|
|
440
|
-
size_t url_len = strlen(url);
|
|
441
|
-
|
|
442
|
-
if (url_len > 2048) {
|
|
443
|
-
return 0;
|
|
444
|
-
}
|
|
445
|
-
|
|
446
|
-
if (strncmp(url, "http://", 7) != 0 && strncmp(url, "https://", 8) != 0) {
|
|
447
|
-
return 0;
|
|
448
|
-
}
|
|
449
|
-
|
|
450
|
-
if (strncmp(url, "file://", 7) == 0 || strncmp(url, "ftp://", 6) == 0 ||
|
|
451
|
-
strncmp(url, "gopher://", 9) == 0 || strncmp(url, "ldap://", 7) == 0 ||
|
|
452
|
-
strncmp(url, "dict://", 7) == 0 || strncmp(url, "tftp://", 7) == 0) {
|
|
453
506
|
return 0;
|
|
454
|
-
}
|
|
455
|
-
|
|
456
|
-
return 1;
|
|
457
507
|
}
|
|
458
508
|
|
|
459
|
-
static
|
|
460
|
-
|
|
461
|
-
|
|
509
|
+
static void parse_options(VALUE options, long *timeout, int *max_conn) {
|
|
510
|
+
*timeout = 30;
|
|
511
|
+
*max_conn = 20;
|
|
462
512
|
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
VALUE c = rb_hash_aref(options, ID2SYM(rb_intern("connections")));
|
|
513
|
+
if (NIL_P(options) || !rb_obj_is_kind_of(options, rb_cHash))
|
|
514
|
+
return;
|
|
466
515
|
|
|
516
|
+
VALUE t = rb_hash_aref(options, sym_timeout);
|
|
467
517
|
if (!NIL_P(t)) {
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
timeout_val
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
}
|
|
475
|
-
*timeout = timeout_val;
|
|
518
|
+
long timeout_val = NUM2LONG(t);
|
|
519
|
+
if (timeout_val > MAX_TIMEOUT)
|
|
520
|
+
timeout_val = MAX_TIMEOUT;
|
|
521
|
+
else if (timeout_val <= 0)
|
|
522
|
+
timeout_val = 30;
|
|
523
|
+
*timeout = timeout_val;
|
|
476
524
|
}
|
|
477
525
|
|
|
526
|
+
VALUE c = rb_hash_aref(options, sym_connections);
|
|
478
527
|
if (!NIL_P(c)) {
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
conn_val
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
}
|
|
486
|
-
*max_conn = conn_val;
|
|
528
|
+
int conn_val = NUM2INT(c);
|
|
529
|
+
if (conn_val > 100)
|
|
530
|
+
conn_val = 100;
|
|
531
|
+
else if (conn_val <= 0)
|
|
532
|
+
conn_val = 20;
|
|
533
|
+
*max_conn = conn_val;
|
|
487
534
|
}
|
|
488
|
-
}
|
|
489
|
-
|
|
490
|
-
return Qnil;
|
|
491
535
|
}
|
|
492
536
|
|
|
493
|
-
static VALUE internal_execute(VALUE requests, VALUE options, int target,
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
537
|
+
static VALUE internal_execute(VALUE requests, VALUE options, int target, int stream) {
|
|
538
|
+
Check_Type(requests, T_ARRAY);
|
|
539
|
+
int count = (int)RARRAY_LEN(requests);
|
|
540
|
+
if (count == 0)
|
|
541
|
+
return rb_ary_new();
|
|
542
|
+
|
|
543
|
+
long timeout_sec;
|
|
544
|
+
int max_conn;
|
|
545
|
+
parse_options(options, &timeout_sec, &max_conn);
|
|
546
|
+
|
|
547
|
+
multi_session_t session;
|
|
548
|
+
session.multi = curl_multi_init();
|
|
549
|
+
session.count = count;
|
|
550
|
+
session.timeout_ms = timeout_sec * 1000;
|
|
551
|
+
session.max_connections = max_conn;
|
|
552
|
+
|
|
553
|
+
curl_multi_setopt(session.multi, CURLMOPT_MAXCONNECTS, (long)max_conn);
|
|
554
|
+
curl_multi_setopt(session.multi, CURLMOPT_MAX_TOTAL_CONNECTIONS, (long)max_conn);
|
|
555
|
+
#ifdef CURLPIPE_MULTIPLEX
|
|
556
|
+
curl_multi_setopt(session.multi, CURLMOPT_PIPELINING, CURLPIPE_MULTIPLEX);
|
|
557
|
+
#endif
|
|
558
|
+
|
|
559
|
+
session.requests = calloc(count, sizeof(request_ctx_t));
|
|
560
|
+
if (!session.requests) {
|
|
561
|
+
curl_multi_cleanup(session.multi);
|
|
562
|
+
rb_raise(rb_eNoMemError, "failed to allocate request contexts");
|
|
563
|
+
}
|
|
519
564
|
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
565
|
+
int valid_requests = 0;
|
|
566
|
+
for (int i = 0; i < count; i++) {
|
|
567
|
+
VALUE req = rb_ary_entry(requests, i);
|
|
568
|
+
request_ctx_init(&session.requests[i], i);
|
|
524
569
|
|
|
525
|
-
|
|
570
|
+
if (!setup_easy_handle(&session.requests[i], req, timeout_sec)) {
|
|
571
|
+
session.requests[i].done = 1;
|
|
572
|
+
continue;
|
|
573
|
+
}
|
|
526
574
|
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
575
|
+
CURLMcode mc = curl_multi_add_handle(session.multi, session.requests[i].easy);
|
|
576
|
+
if (mc != CURLM_OK) {
|
|
577
|
+
session.requests[i].done = 1;
|
|
578
|
+
continue;
|
|
579
|
+
}
|
|
530
580
|
|
|
531
|
-
|
|
532
|
-
curl_multi_add_handle(session.multi, session.requests[i].easy);
|
|
533
|
-
if (mc != CURLM_OK) {
|
|
534
|
-
session.requests[i].done = 1;
|
|
535
|
-
continue;
|
|
581
|
+
valid_requests++;
|
|
536
582
|
}
|
|
537
583
|
|
|
538
|
-
valid_requests
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
if (!stream) {
|
|
552
|
-
for (int i = 0; i < count; i++)
|
|
553
|
-
rb_ary_store(cctx.results, i, Qnil);
|
|
554
|
-
}
|
|
555
|
-
|
|
556
|
-
if (has_fiber_scheduler()) {
|
|
557
|
-
while (session.still_running > 0 || 1) {
|
|
558
|
-
CURLMcode mc = curl_multi_perform(session.multi, &session.still_running);
|
|
559
|
-
if (mc != CURLM_OK)
|
|
560
|
-
break;
|
|
561
|
-
if (process_completed(&session, &cctx))
|
|
562
|
-
break;
|
|
563
|
-
if (session.still_running == 0)
|
|
564
|
-
break;
|
|
565
|
-
|
|
566
|
-
int numfds = 0;
|
|
567
|
-
curl_multi_poll(session.multi, NULL, 0, 1, &numfds);
|
|
568
|
-
rb_thread_schedule();
|
|
584
|
+
if (valid_requests == 0)
|
|
585
|
+
session.still_running = 0;
|
|
586
|
+
|
|
587
|
+
completion_ctx_t cctx;
|
|
588
|
+
cctx.results = stream ? Qnil : rb_ary_new2(count);
|
|
589
|
+
cctx.completed = 0;
|
|
590
|
+
cctx.target = target;
|
|
591
|
+
cctx.stream = stream;
|
|
592
|
+
|
|
593
|
+
if (!stream) {
|
|
594
|
+
for (int i = 0; i < count; i++)
|
|
595
|
+
rb_ary_store(cctx.results, i, Qnil);
|
|
569
596
|
}
|
|
570
597
|
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
598
|
+
if (has_fiber_scheduler()) {
|
|
599
|
+
for (;;) {
|
|
600
|
+
CURLMcode mc = curl_multi_perform(session.multi, &session.still_running);
|
|
601
|
+
if (mc != CURLM_OK)
|
|
602
|
+
break;
|
|
603
|
+
if (process_completed(&session, &cctx))
|
|
604
|
+
break;
|
|
605
|
+
if (session.still_running == 0)
|
|
606
|
+
break;
|
|
607
|
+
|
|
608
|
+
int numfds = 0;
|
|
609
|
+
curl_multi_poll(session.multi, NULL, 0, FIBER_POLL_TIMEOUT_MS, &numfds);
|
|
610
|
+
rb_thread_schedule();
|
|
611
|
+
}
|
|
612
|
+
process_completed(&session, &cctx);
|
|
582
613
|
} else {
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
614
|
+
if (stream || target > 0) {
|
|
615
|
+
curl_multi_perform(session.multi, &session.still_running);
|
|
616
|
+
while (session.still_running > 0) {
|
|
617
|
+
rb_thread_call_without_gvl(poll_without_gvl, &session, unblock_perform, &session);
|
|
618
|
+
if (process_completed(&session, &cctx))
|
|
619
|
+
break;
|
|
620
|
+
}
|
|
621
|
+
process_completed(&session, &cctx);
|
|
622
|
+
} else {
|
|
623
|
+
session.still_running = 1;
|
|
624
|
+
curl_multi_perform(session.multi, &session.still_running);
|
|
625
|
+
rb_thread_call_without_gvl(perform_without_gvl, &session, unblock_perform, &session);
|
|
626
|
+
process_completed(&session, &cctx);
|
|
627
|
+
}
|
|
588
628
|
}
|
|
589
|
-
}
|
|
590
629
|
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
}
|
|
630
|
+
if (!stream) {
|
|
631
|
+
for (int i = 0; i < count; i++) {
|
|
632
|
+
if (session.requests[i].done && rb_ary_entry(cctx.results, i) == Qnil) {
|
|
633
|
+
VALUE error_response = build_error_response("Invalid request configuration");
|
|
634
|
+
VALUE pair = rb_ary_new_from_args(2, INT2NUM(i), error_response);
|
|
635
|
+
rb_ary_store(cctx.results, i, pair);
|
|
636
|
+
}
|
|
637
|
+
}
|
|
600
638
|
}
|
|
601
|
-
}
|
|
602
639
|
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
640
|
+
for (int i = 0; i < count; i++) {
|
|
641
|
+
curl_multi_remove_handle(session.multi, session.requests[i].easy);
|
|
642
|
+
request_ctx_free(&session.requests[i]);
|
|
643
|
+
}
|
|
644
|
+
free(session.requests);
|
|
645
|
+
curl_multi_cleanup(session.multi);
|
|
609
646
|
|
|
610
|
-
|
|
647
|
+
return stream ? Qnil : cctx.results;
|
|
611
648
|
}
|
|
612
649
|
|
|
613
650
|
static VALUE rb_fast_curl_execute(int argc, VALUE *argv, VALUE self) {
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
651
|
+
VALUE requests, options;
|
|
652
|
+
rb_scan_args(argc, argv, "1:", &requests, &options);
|
|
653
|
+
return internal_execute(requests, options, -1, 0);
|
|
617
654
|
}
|
|
618
655
|
|
|
619
656
|
static VALUE rb_fast_curl_first_execute(int argc, VALUE *argv, VALUE self) {
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
657
|
+
VALUE requests, options;
|
|
658
|
+
rb_scan_args(argc, argv, "1:", &requests, &options);
|
|
659
|
+
|
|
660
|
+
int count = 1;
|
|
661
|
+
if (!NIL_P(options)) {
|
|
662
|
+
VALUE c = rb_hash_aref(options, sym_count);
|
|
663
|
+
if (!NIL_P(c))
|
|
664
|
+
count = NUM2INT(c);
|
|
665
|
+
}
|
|
629
666
|
|
|
630
|
-
|
|
667
|
+
return internal_execute(requests, options, count, 0);
|
|
631
668
|
}
|
|
632
669
|
|
|
633
670
|
static VALUE rb_fast_curl_stream_execute(int argc, VALUE *argv, VALUE self) {
|
|
634
|
-
|
|
635
|
-
|
|
671
|
+
VALUE requests, options;
|
|
672
|
+
rb_scan_args(argc, argv, "1:", &requests, &options);
|
|
636
673
|
|
|
637
|
-
|
|
638
|
-
|
|
674
|
+
if (!rb_block_given_p())
|
|
675
|
+
rb_raise(rb_eArgError, "stream_execute requires a block");
|
|
639
676
|
|
|
640
|
-
|
|
677
|
+
return internal_execute(requests, options, -1, 1);
|
|
641
678
|
}
|
|
642
679
|
|
|
643
680
|
void Init_fast_curl(void) {
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
681
|
+
curl_global_init(CURL_GLOBAL_ALL);
|
|
682
|
+
|
|
683
|
+
id_status = rb_intern("status");
|
|
684
|
+
id_headers = rb_intern("headers");
|
|
685
|
+
id_body = rb_intern("body");
|
|
686
|
+
id_error_code = rb_intern("error_code");
|
|
687
|
+
id_url = rb_intern("url");
|
|
688
|
+
id_method = rb_intern("method");
|
|
689
|
+
id_timeout = rb_intern("timeout");
|
|
690
|
+
id_connections = rb_intern("connections");
|
|
691
|
+
id_count = rb_intern("count");
|
|
692
|
+
id_keys = rb_intern("keys");
|
|
693
|
+
|
|
694
|
+
sym_status = ID2SYM(id_status);
|
|
695
|
+
rb_gc_register_address(&sym_status);
|
|
696
|
+
sym_headers = ID2SYM(id_headers);
|
|
697
|
+
rb_gc_register_address(&sym_headers);
|
|
698
|
+
sym_body = ID2SYM(id_body);
|
|
699
|
+
rb_gc_register_address(&sym_body);
|
|
700
|
+
sym_error_code = ID2SYM(id_error_code);
|
|
701
|
+
rb_gc_register_address(&sym_error_code);
|
|
702
|
+
sym_url = ID2SYM(id_url);
|
|
703
|
+
rb_gc_register_address(&sym_url);
|
|
704
|
+
sym_method = ID2SYM(id_method);
|
|
705
|
+
rb_gc_register_address(&sym_method);
|
|
706
|
+
sym_timeout = ID2SYM(id_timeout);
|
|
707
|
+
rb_gc_register_address(&sym_timeout);
|
|
708
|
+
sym_connections = ID2SYM(id_connections);
|
|
709
|
+
rb_gc_register_address(&sym_connections);
|
|
710
|
+
sym_count = ID2SYM(id_count);
|
|
711
|
+
rb_gc_register_address(&sym_count);
|
|
712
|
+
|
|
713
|
+
VALUE mFastCurl = rb_define_module("FastCurl");
|
|
714
|
+
|
|
715
|
+
rb_define_module_function(mFastCurl, "execute", rb_fast_curl_execute, -1);
|
|
716
|
+
rb_define_module_function(mFastCurl, "first_execute", rb_fast_curl_first_execute, -1);
|
|
717
|
+
rb_define_module_function(mFastCurl, "stream_execute", rb_fast_curl_stream_execute, -1);
|
|
653
718
|
}
|
data/lib/fast_curl/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: fast_curl
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- roman-haidarov
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-03-
|
|
11
|
+
date: 2026-03-24 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: json
|