ovirt-engine-sdk 4.1.5 → 4.1.6
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/CHANGES.adoc +37 -0
- data/ext/ovirtsdk4c/ov_http_client.c +531 -353
- data/ext/ovirtsdk4c/ov_http_client.h +39 -2
- data/ext/ovirtsdk4c/ov_http_request.c +130 -102
- data/ext/ovirtsdk4c/ov_http_request.h +10 -2
- data/ext/ovirtsdk4c/ov_http_response.c +63 -50
- data/ext/ovirtsdk4c/ov_http_response.h +9 -2
- data/ext/ovirtsdk4c/ov_http_transfer.c +80 -0
- data/ext/ovirtsdk4c/ov_http_transfer.h +47 -0
- data/ext/ovirtsdk4c/ov_string.c +43 -0
- data/ext/ovirtsdk4c/ov_string.h +25 -0
- data/ext/ovirtsdk4c/ov_xml_reader.c +115 -99
- data/ext/ovirtsdk4c/ov_xml_reader.h +20 -3
- data/ext/ovirtsdk4c/ov_xml_writer.c +95 -77
- data/ext/ovirtsdk4c/ov_xml_writer.h +18 -3
- data/ext/ovirtsdk4c/ovirtsdk4c.c +2 -0
- data/lib/ovirtsdk4.rb +1 -0
- data/lib/ovirtsdk4/connection.rb +176 -26
- data/lib/ovirtsdk4/error.rb +38 -0
- data/lib/ovirtsdk4/probe.rb +4 -7
- data/lib/ovirtsdk4/readers.rb +9 -1
- data/lib/ovirtsdk4/service.rb +267 -36
- data/lib/ovirtsdk4/services.rb +7342 -15734
- data/lib/ovirtsdk4/types.rb +50 -4
- data/lib/ovirtsdk4/version.rb +1 -1
- data/lib/ovirtsdk4/writer.rb +27 -1
- data/lib/ovirtsdk4/writers.rb +3 -1
- metadata +7 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e8d3c58ff70eecb169d5103a4eeb2f7f9fed5a77
|
4
|
+
data.tar.gz: 302c8ab4c8c915eac19558b9813f91831d634687
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 508ab6b1baa739ef1837e0ca7548075d2874d98b4ba887ab1e2ffcc85c60370516d4a7e7e7c67ffd2a49bc669eeaa5a8581c98989d0611ad7a0d3372eefaac0a
|
7
|
+
data.tar.gz: 098cec88d486935c3b5aecb9b2435cd669f9369a700ca197824d7ce81e211d249335919aa87ad9144aff21e41bb752e785139fbd837cb422589133b868ea83f1
|
data/CHANGES.adoc
CHANGED
@@ -2,6 +2,43 @@
|
|
2
2
|
|
3
3
|
This document describes the relevant changes between releases of the SDK.
|
4
4
|
|
5
|
+
== 4.1.6 / May 31 2017
|
6
|
+
|
7
|
+
Update to model 4.1.35:
|
8
|
+
|
9
|
+
* Replace generic assigned networks services with services specific to
|
10
|
+
the type of object that they are assigned to, in particular data
|
11
|
+
centers and clusters.
|
12
|
+
|
13
|
+
* Add `driver` attribute to `HostDevice` type.
|
14
|
+
|
15
|
+
* Add common concepts document.
|
16
|
+
|
17
|
+
* Add appendix containing changes from version 3 to version 4 of
|
18
|
+
the API.
|
19
|
+
|
20
|
+
* Add `readOnly` attribute to the `DiskAttachment` type.
|
21
|
+
|
22
|
+
* Fix the type of the `Host.nics` link. It should be of type
|
23
|
+
`HostNic[]`, not `Nic[]`.
|
24
|
+
|
25
|
+
New features:
|
26
|
+
|
27
|
+
* Add support for asynchronous requests.
|
28
|
+
|
29
|
+
* Automatically replace bad tokens
|
30
|
+
https://bugzilla.redhat.com/1434831[#1434831].
|
31
|
+
|
32
|
+
* Improve error message for wrong content type
|
33
|
+
https://bugzilla.redhat.com/1440292[#1440292].
|
34
|
+
|
35
|
+
* Add `Error.code`
|
36
|
+
https://bugzilla.redhat.com/1443420[#1443420].
|
37
|
+
|
38
|
+
* Add `Error.fault`.
|
39
|
+
|
40
|
+
* Add support for request timeout.
|
41
|
+
|
5
42
|
== 4.1.5 / Mar 15 2017
|
6
43
|
|
7
44
|
Update to model 4.1.33:
|
@@ -1,5 +1,5 @@
|
|
1
1
|
/*
|
2
|
-
Copyright (c) 2016 Red Hat, Inc.
|
2
|
+
Copyright (c) 2016-2017 Red Hat, Inc.
|
3
3
|
|
4
4
|
Licensed under the Apache License, Version 2.0 (the "License");
|
5
5
|
you may not use this file except in compliance with the License.
|
@@ -26,9 +26,11 @@ limitations under the License.
|
|
26
26
|
|
27
27
|
#include "ov_module.h"
|
28
28
|
#include "ov_error.h"
|
29
|
+
#include "ov_string.h"
|
29
30
|
#include "ov_http_client.h"
|
30
31
|
#include "ov_http_request.h"
|
31
32
|
#include "ov_http_response.h"
|
33
|
+
#include "ov_http_transfer.h"
|
32
34
|
|
33
35
|
/* Class: */
|
34
36
|
VALUE ov_http_client_class;
|
@@ -36,18 +38,22 @@ VALUE ov_http_client_class;
|
|
36
38
|
/* Symbols: */
|
37
39
|
static VALUE CA_FILE_SYMBOL;
|
38
40
|
static VALUE COMPRESS_SYMBOL;
|
41
|
+
static VALUE CONNECTIONS_SYMBOL;
|
39
42
|
static VALUE DEBUG_SYMBOL;
|
40
43
|
static VALUE INSECURE_SYMBOL;
|
41
44
|
static VALUE LOG_SYMBOL;
|
42
45
|
static VALUE PASSWORD_SYMBOL;
|
43
|
-
static VALUE
|
44
|
-
static VALUE
|
46
|
+
static VALUE PIPELINE_SYMBOL;
|
47
|
+
static VALUE PROXY_PASSWORD_SYMBOL;
|
45
48
|
static VALUE PROXY_URL_SYMBOL;
|
46
49
|
static VALUE PROXY_USERNAME_SYMBOL;
|
47
|
-
static VALUE
|
50
|
+
static VALUE TIMEOUT_SYMBOL;
|
51
|
+
static VALUE USERNAME_SYMBOL;
|
48
52
|
|
49
53
|
/* Method identifiers: */
|
54
|
+
static ID COMPARE_BY_IDENTITY_ID;
|
50
55
|
static ID DEBUG_ID;
|
56
|
+
static ID DOWNCASE_ID;
|
51
57
|
static ID ENCODE_WWW_FORM_ID;
|
52
58
|
static ID INFO_ID;
|
53
59
|
static ID INFO_Q_ID;
|
@@ -70,31 +76,22 @@ const char LF = '\x0A';
|
|
70
76
|
#define CURLAUTH_NEGOTIATE CURLAUTH_GSSNEGOTIATE
|
71
77
|
#endif
|
72
78
|
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
typedef struct {
|
79
|
-
ov_http_client_object* object;
|
80
|
-
ov_http_response_object* response;
|
81
|
-
VALUE in; /* IO */
|
82
|
-
VALUE out; /* IO */
|
83
|
-
bool cancel;
|
84
|
-
CURLcode result;
|
85
|
-
} ov_http_client_perform_context;
|
79
|
+
/* Before version 7.43.0 of libcurl the CURLPIPE_HTTP1 constant didn't exist, the value 1 was used
|
80
|
+
directly instead: */
|
81
|
+
#ifndef CURLPIPE_HTTP1
|
82
|
+
#define CURLPIPE_HTTP1 1
|
83
|
+
#endif
|
86
84
|
|
87
85
|
typedef struct {
|
88
|
-
|
86
|
+
VALUE io; /* IO */
|
89
87
|
char* ptr;
|
90
88
|
size_t size;
|
91
89
|
size_t nmemb;
|
92
|
-
VALUE io; /* IO */
|
93
90
|
size_t result;
|
94
91
|
} ov_http_client_io_context;
|
95
92
|
|
96
93
|
typedef struct {
|
97
|
-
|
94
|
+
VALUE response; /* HttpResponse */
|
98
95
|
char* buffer;
|
99
96
|
size_t size;
|
100
97
|
size_t nitems;
|
@@ -102,57 +99,104 @@ typedef struct {
|
|
102
99
|
} ov_http_client_header_context;
|
103
100
|
|
104
101
|
typedef struct {
|
105
|
-
|
102
|
+
VALUE client; /* HttpClient */
|
106
103
|
curl_infotype type;
|
107
104
|
char* data;
|
108
105
|
size_t size;
|
109
106
|
} ov_http_client_debug_context;
|
110
107
|
|
108
|
+
typedef struct {
|
109
|
+
CURLM* handle;
|
110
|
+
CURLcode code;
|
111
|
+
bool cancel;
|
112
|
+
} ov_http_client_wait_context;
|
113
|
+
|
114
|
+
|
115
|
+
static void ov_http_client_log_info(VALUE log, const char* format, ...) {
|
116
|
+
VALUE enabled;
|
117
|
+
VALUE message;
|
118
|
+
va_list args;
|
119
|
+
|
120
|
+
if (!NIL_P(log)) {
|
121
|
+
enabled = rb_funcall(log, INFO_Q_ID, 0);
|
122
|
+
if (RTEST(enabled)) {
|
123
|
+
va_start(args, format);
|
124
|
+
message = rb_vsprintf(format, args);
|
125
|
+
rb_funcall(log, INFO_ID, 1, message);
|
126
|
+
va_end(args);
|
127
|
+
}
|
128
|
+
}
|
129
|
+
}
|
130
|
+
|
111
131
|
static void ov_http_client_check_closed(ov_http_client_object* object) {
|
112
|
-
if (object->
|
132
|
+
if (object->handle == NULL) {
|
113
133
|
rb_raise(ov_error_class, "The client is already closed");
|
114
134
|
}
|
115
135
|
}
|
116
136
|
|
117
|
-
static void ov_http_client_mark(
|
118
|
-
|
137
|
+
static void ov_http_client_mark(void* vptr) {
|
138
|
+
ov_http_client_object* ptr;
|
139
|
+
|
140
|
+
ptr = vptr;
|
141
|
+
rb_gc_mark(ptr->log);
|
142
|
+
rb_gc_mark(ptr->pending);
|
143
|
+
rb_gc_mark(ptr->completed);
|
119
144
|
}
|
120
145
|
|
121
|
-
static void ov_http_client_free(
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
146
|
+
static void ov_http_client_free(void* vptr) {
|
147
|
+
ov_http_client_object* ptr;
|
148
|
+
|
149
|
+
/* Get the pointer to the object: */
|
150
|
+
ptr = vptr;
|
151
|
+
|
152
|
+
/* Release the resources used by libcurl: */
|
153
|
+
if (ptr->handle != NULL) {
|
154
|
+
curl_multi_cleanup(ptr->handle);
|
155
|
+
ptr->handle = NULL;
|
130
156
|
}
|
131
157
|
|
158
|
+
/* Free the strings: */
|
159
|
+
ov_string_free(ptr->ca_file);
|
160
|
+
ov_string_free(ptr->proxy_url);
|
161
|
+
ov_string_free(ptr->proxy_username);
|
162
|
+
ov_string_free(ptr->proxy_password);
|
163
|
+
|
132
164
|
/* Free this object: */
|
133
|
-
xfree(
|
165
|
+
xfree(ptr);
|
134
166
|
}
|
135
167
|
|
168
|
+
rb_data_type_t ov_http_client_type = {
|
169
|
+
.wrap_struct_name = "OVHTTPCLIENT",
|
170
|
+
.function = {
|
171
|
+
.dmark = ov_http_client_mark,
|
172
|
+
.dfree = ov_http_client_free,
|
173
|
+
.dsize = NULL,
|
174
|
+
.reserved = { NULL, NULL }
|
175
|
+
},
|
176
|
+
#ifdef RUBY_TYPED_FREE_IMMEDIATELY
|
177
|
+
.parent = NULL,
|
178
|
+
.data = NULL,
|
179
|
+
.flags = RUBY_TYPED_FREE_IMMEDIATELY,
|
180
|
+
#endif
|
181
|
+
};
|
182
|
+
|
136
183
|
static VALUE ov_http_client_alloc(VALUE klass) {
|
137
|
-
ov_http_client_object*
|
184
|
+
ov_http_client_object* ptr;
|
138
185
|
|
139
|
-
|
140
|
-
|
141
|
-
object->log = Qnil;
|
142
|
-
return Data_Wrap_Struct(klass, ov_http_client_mark, ov_http_client_free, object);
|
186
|
+
ptr = ALLOC(ov_http_client_object);
|
187
|
+
return TypedData_Wrap_Struct(klass, &ov_http_client_type, ptr);
|
143
188
|
}
|
144
189
|
|
145
190
|
static VALUE ov_http_client_close(VALUE self) {
|
146
|
-
ov_http_client_object*
|
191
|
+
ov_http_client_object* ptr;
|
147
192
|
|
148
193
|
/* Get the pointer to the native object and check that it isn't closed: */
|
149
|
-
|
150
|
-
ov_http_client_check_closed(
|
194
|
+
ov_http_client_ptr(self, ptr);
|
195
|
+
ov_http_client_check_closed(ptr);
|
151
196
|
|
152
|
-
/* Release the resources used by libcurl
|
153
|
-
|
154
|
-
|
155
|
-
object->curl = NULL;
|
197
|
+
/* Release the resources used by libcurl: */
|
198
|
+
curl_multi_cleanup(ptr->handle);
|
199
|
+
ptr->handle = NULL;
|
156
200
|
|
157
201
|
return Qnil;
|
158
202
|
}
|
@@ -160,73 +204,91 @@ static VALUE ov_http_client_close(VALUE self) {
|
|
160
204
|
static void* ov_http_client_read_task(void* data) {
|
161
205
|
VALUE bytes;
|
162
206
|
VALUE count;
|
163
|
-
ov_http_client_io_context*
|
207
|
+
ov_http_client_io_context* context_ptr;
|
208
|
+
|
209
|
+
/* The passed data is a pointer to the IO context: */
|
210
|
+
context_ptr = (ov_http_client_io_context*) data;
|
164
211
|
|
165
212
|
/* Read the data using the "read" method and write the raw bytes to the buffer provided by libcurl: */
|
166
|
-
count = INT2NUM(
|
167
|
-
bytes = rb_funcall(
|
213
|
+
count = INT2NUM(context_ptr->size * context_ptr->nmemb);
|
214
|
+
bytes = rb_funcall(context_ptr->io, READ_ID, 1, count);
|
168
215
|
if (NIL_P(bytes)) {
|
169
|
-
|
216
|
+
context_ptr->result = 0;
|
170
217
|
}
|
171
218
|
else {
|
172
|
-
|
173
|
-
memcpy(
|
219
|
+
context_ptr->result = RSTRING_LEN(bytes);
|
220
|
+
memcpy(context_ptr->ptr, StringValuePtr(bytes), context_ptr->result);
|
174
221
|
}
|
175
222
|
|
176
223
|
return NULL;
|
177
224
|
}
|
178
225
|
|
179
226
|
static size_t ov_http_client_read_function(char *ptr, size_t size, size_t nmemb, void *userdata) {
|
180
|
-
|
181
|
-
ov_http_client_io_context
|
227
|
+
VALUE transfer;
|
228
|
+
ov_http_client_io_context context;
|
229
|
+
ov_http_transfer_object* transfer_ptr;
|
230
|
+
|
231
|
+
/* The passed user data is the transfer: */
|
232
|
+
transfer = (VALUE) userdata;
|
233
|
+
|
234
|
+
/* Get the pointer to the transfer: */
|
235
|
+
ov_http_transfer_ptr(transfer, transfer_ptr);
|
182
236
|
|
183
237
|
/* Check if the operation has been cancelled, and return immediately, this will cause the perform method to
|
184
238
|
return an error to the caller: */
|
185
|
-
if (
|
239
|
+
if (transfer_ptr->cancel) {
|
186
240
|
return CURL_READFUNC_ABORT;
|
187
241
|
}
|
188
242
|
|
189
243
|
/* Execute the read with the global interpreter lock acquired, as it needs to call Ruby methods: */
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
return io_context.result;
|
244
|
+
context.ptr = ptr;
|
245
|
+
context.size = size;
|
246
|
+
context.nmemb = nmemb;
|
247
|
+
context.io = transfer_ptr->in;
|
248
|
+
rb_thread_call_with_gvl(ov_http_client_read_task, &context);
|
249
|
+
return context.result;
|
197
250
|
}
|
198
251
|
|
199
252
|
static void* ov_http_client_write_task(void* data) {
|
200
253
|
VALUE bytes;
|
201
254
|
VALUE count;
|
202
|
-
ov_http_client_io_context*
|
255
|
+
ov_http_client_io_context* context_ptr;
|
256
|
+
|
257
|
+
/* The passed data is a pointer to the IO context: */
|
258
|
+
context_ptr = (ov_http_client_io_context*) data;
|
203
259
|
|
204
260
|
/* Convert the buffer to a Ruby string and write it to the IO object, using the "write" method: */
|
205
|
-
bytes = rb_str_new(
|
206
|
-
count = rb_funcall(
|
207
|
-
|
261
|
+
bytes = rb_str_new(context_ptr->ptr, context_ptr->size * context_ptr->nmemb);
|
262
|
+
count = rb_funcall(context_ptr->io, WRITE_ID, 1, bytes);
|
263
|
+
context_ptr->result = NUM2INT(count);
|
208
264
|
|
209
265
|
return NULL;
|
210
266
|
}
|
211
267
|
|
212
268
|
static size_t ov_http_client_write_function(char *ptr, size_t size, size_t nmemb, void *userdata) {
|
213
|
-
|
214
|
-
ov_http_client_io_context
|
269
|
+
VALUE transfer;
|
270
|
+
ov_http_client_io_context context;
|
271
|
+
ov_http_transfer_object* transfer_ptr;
|
272
|
+
|
273
|
+
/* The passed user data is the transfer: */
|
274
|
+
transfer = (VALUE) userdata;
|
275
|
+
|
276
|
+
/* Get the pointer to the transfer: */
|
277
|
+
ov_http_transfer_ptr(transfer, transfer_ptr);
|
215
278
|
|
216
279
|
/* Check if the operation has been cancelled, and return immediately, this will cause the perform method to
|
217
280
|
return an error to the caller: */
|
218
|
-
if (
|
281
|
+
if (transfer_ptr->cancel) {
|
219
282
|
return 0;
|
220
283
|
}
|
221
284
|
|
222
285
|
/* Execute the write with the global interpreter lock acquired, as it needs to call Ruby methods: */
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
return io_context.result;
|
286
|
+
context.ptr = ptr;
|
287
|
+
context.size = size;
|
288
|
+
context.nmemb = nmemb;
|
289
|
+
context.io = transfer_ptr->out;
|
290
|
+
rb_thread_call_with_gvl(ov_http_client_write_task, &context);
|
291
|
+
return context.result;
|
230
292
|
}
|
231
293
|
|
232
294
|
static void* ov_http_client_header_task(void* data) {
|
@@ -234,15 +296,22 @@ static void* ov_http_client_header_task(void* data) {
|
|
234
296
|
VALUE value;
|
235
297
|
char* buffer;
|
236
298
|
char* pointer;
|
299
|
+
ov_http_client_header_context* context_ptr;
|
300
|
+
ov_http_response_object* response_ptr;
|
237
301
|
size_t length;
|
238
|
-
|
302
|
+
|
303
|
+
/* The passed data is the pointer to the context: */
|
304
|
+
context_ptr = (ov_http_client_header_context*) data;
|
305
|
+
|
306
|
+
/* Get the pointer to the response: */
|
307
|
+
ov_http_response_ptr(context_ptr->response, response_ptr);
|
239
308
|
|
240
309
|
/* We should always tell the library that we processed all the data: */
|
241
|
-
|
310
|
+
context_ptr->result = context_ptr->size * context_ptr->nitems;
|
242
311
|
|
243
312
|
/* Remove trailing white space: */
|
244
|
-
length =
|
245
|
-
buffer =
|
313
|
+
length = context_ptr->result;
|
314
|
+
buffer = context_ptr->buffer;
|
246
315
|
while (length > 0 && isspace(buffer[length - 1])) {
|
247
316
|
length--;
|
248
317
|
}
|
@@ -251,56 +320,71 @@ static void* ov_http_client_header_task(void* data) {
|
|
251
320
|
pointer = memchr(buffer, ':', length);
|
252
321
|
if (pointer != NULL) {
|
253
322
|
name = rb_str_new(buffer, pointer - buffer);
|
323
|
+
name = rb_funcall(name, DOWNCASE_ID, 0);
|
254
324
|
pointer++;
|
255
325
|
while (pointer - buffer < length && isspace(*pointer)) {
|
256
326
|
pointer++;
|
257
327
|
}
|
258
328
|
value = rb_str_new(pointer, length - (pointer - buffer));
|
259
|
-
rb_hash_aset(
|
329
|
+
rb_hash_aset(response_ptr->headers, name, value);
|
260
330
|
}
|
261
331
|
|
262
332
|
return NULL;
|
263
333
|
}
|
264
334
|
|
265
335
|
static size_t ov_http_client_header_function(char *buffer, size_t size, size_t nitems, void *userdata) {
|
266
|
-
|
267
|
-
|
336
|
+
VALUE transfer;
|
337
|
+
ov_http_client_header_context context;
|
338
|
+
ov_http_transfer_object* transfer_ptr;
|
339
|
+
|
340
|
+
/* The passed user data is a pointer to the transfer: */
|
341
|
+
transfer = (VALUE) userdata;
|
342
|
+
|
343
|
+
/* Get the pointer to the transfer: */
|
344
|
+
ov_http_transfer_ptr(transfer, transfer_ptr);
|
268
345
|
|
269
346
|
/* Check if the operation has been cancelled, and return immediately, this will cause the perform method to
|
270
347
|
return an error to the caller: */
|
271
|
-
if (
|
348
|
+
if (transfer_ptr->cancel) {
|
272
349
|
return 0;
|
273
350
|
}
|
274
351
|
|
275
|
-
/* Parse the header with the global
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
rb_thread_call_with_gvl(ov_http_client_header_task, &
|
281
|
-
return
|
352
|
+
/* Parse the header with the global interpreter lock acquired, as it needs to call Ruby methods: */
|
353
|
+
context.response = transfer_ptr->response;
|
354
|
+
context.buffer = buffer;
|
355
|
+
context.size = size;
|
356
|
+
context.nitems = nitems;
|
357
|
+
rb_thread_call_with_gvl(ov_http_client_header_task, &context);
|
358
|
+
return context.result;
|
282
359
|
}
|
283
360
|
|
284
361
|
static void* ov_http_client_debug_task(void* data) {
|
285
362
|
VALUE line;
|
286
363
|
VALUE log;
|
287
|
-
int c;
|
288
364
|
char* text;
|
289
|
-
|
365
|
+
int c;
|
366
|
+
int s;
|
367
|
+
ov_http_client_debug_context* context_ptr;
|
368
|
+
ov_http_client_object* client_ptr;
|
290
369
|
size_t i;
|
291
370
|
size_t j;
|
292
371
|
size_t size;
|
293
|
-
|
372
|
+
|
373
|
+
/* The passed data is a pointer to the context: */
|
374
|
+
context_ptr = (ov_http_client_debug_context*) data;
|
375
|
+
|
376
|
+
/* Get the pointer to the client: */
|
377
|
+
ov_http_client_ptr(context_ptr->client, client_ptr);
|
294
378
|
|
295
379
|
/* Do nothing if there is no log: */
|
296
|
-
log =
|
380
|
+
log = client_ptr->log;
|
297
381
|
if (NIL_P(log)) {
|
298
382
|
return NULL;
|
299
383
|
}
|
300
384
|
|
301
385
|
/* Split the text into lines, and send a debug message for each line: */
|
302
|
-
text =
|
303
|
-
size =
|
386
|
+
text = context_ptr->data;
|
387
|
+
size = context_ptr->size;
|
304
388
|
i = 0;
|
305
389
|
s = 0;
|
306
390
|
for (j = 0; j <= size; j++) {
|
@@ -329,33 +413,34 @@ static void* ov_http_client_debug_task(void* data) {
|
|
329
413
|
}
|
330
414
|
|
331
415
|
static int ov_http_client_debug_function(CURL* handle, curl_infotype type, char* data, size_t size, void* userptr) {
|
332
|
-
|
333
|
-
|
416
|
+
VALUE transfer;
|
417
|
+
ov_http_client_debug_context context;
|
418
|
+
ov_http_transfer_object* transfer_ptr;
|
419
|
+
|
420
|
+
/* The passed user pointer is the transfer: */
|
421
|
+
transfer = (VALUE) userptr;
|
422
|
+
|
423
|
+
/* Get the pointer to the transfer: */
|
424
|
+
ov_http_transfer_ptr(transfer, transfer_ptr);
|
334
425
|
|
335
426
|
/* Execute the debug code with the global interpreter lock acquired, as it needs to call Ruby methods: */
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
rb_thread_call_with_gvl(ov_http_client_debug_task, &
|
427
|
+
context.client = transfer_ptr->client;
|
428
|
+
context.type = type;
|
429
|
+
context.data = data;
|
430
|
+
context.size = size;
|
431
|
+
rb_thread_call_with_gvl(ov_http_client_debug_task, &context);
|
341
432
|
return 0;
|
342
433
|
}
|
343
434
|
|
344
435
|
static VALUE ov_http_client_initialize(int argc, VALUE* argv, VALUE self) {
|
345
436
|
VALUE opt;
|
346
437
|
VALUE opts;
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
char* ca_file;
|
351
|
-
char* proxy_password;
|
352
|
-
char* proxy_url;
|
353
|
-
char* proxy_username;
|
354
|
-
int timeout;
|
355
|
-
ov_http_client_object* object;
|
438
|
+
long connections;
|
439
|
+
long pipeline;
|
440
|
+
ov_http_client_object* ptr;
|
356
441
|
|
357
442
|
/* Get the pointer to the native object: */
|
358
|
-
|
443
|
+
ov_http_client_ptr(self, ptr);
|
359
444
|
|
360
445
|
/* Check the number of arguments: */
|
361
446
|
if (argc > 1) {
|
@@ -369,146 +454,96 @@ static VALUE ov_http_client_initialize(int argc, VALUE* argv, VALUE self) {
|
|
369
454
|
Check_Type(opts, T_HASH);
|
370
455
|
}
|
371
456
|
|
372
|
-
/* Get the value of the 'insecure' parameter: */
|
373
|
-
opt = rb_hash_aref(opts, INSECURE_SYMBOL);
|
374
|
-
if (NIL_P(opt)) {
|
375
|
-
insecure = false;
|
376
|
-
}
|
377
|
-
else {
|
378
|
-
insecure = RTEST(opt);
|
379
|
-
}
|
380
|
-
|
381
457
|
/* Get the value of the 'ca_file' parameter: */
|
382
458
|
opt = rb_hash_aref(opts, CA_FILE_SYMBOL);
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
ca_file = StringValueCStr(opt);
|
389
|
-
}
|
459
|
+
ptr->ca_file = ov_string_dup(opt);
|
460
|
+
|
461
|
+
/* Get the value of the 'insecure' parameter: */
|
462
|
+
opt = rb_hash_aref(opts, INSECURE_SYMBOL);
|
463
|
+
ptr->insecure = NIL_P(opt)? false: RTEST(opt);
|
390
464
|
|
391
465
|
/* Get the value of the 'debug' parameter: */
|
392
466
|
opt = rb_hash_aref(opts, DEBUG_SYMBOL);
|
393
|
-
|
394
|
-
debug = false;
|
395
|
-
}
|
396
|
-
else {
|
397
|
-
debug = RTEST(opt);
|
398
|
-
}
|
467
|
+
ptr->debug = NIL_P(opt)? false: RTEST(opt);
|
399
468
|
|
400
|
-
/* Get the value of the '
|
401
|
-
opt = rb_hash_aref(opts,
|
402
|
-
|
403
|
-
object->log = Qnil;
|
404
|
-
}
|
405
|
-
else {
|
406
|
-
object->log = opt;
|
407
|
-
}
|
469
|
+
/* Get the value of the 'compress' parameter: */
|
470
|
+
opt = rb_hash_aref(opts, COMPRESS_SYMBOL);
|
471
|
+
ptr->compress = NIL_P(opt)? true: RTEST(opt);
|
408
472
|
|
409
473
|
/* Get the value of the 'timeout' parameter: */
|
410
474
|
opt = rb_hash_aref(opts, TIMEOUT_SYMBOL);
|
411
475
|
if (NIL_P(opt)) {
|
412
|
-
timeout = 0;
|
476
|
+
ptr->timeout = 0;
|
413
477
|
}
|
414
478
|
else {
|
415
479
|
Check_Type(opt, T_FIXNUM);
|
416
|
-
timeout = NUM2INT(opt);
|
417
|
-
}
|
418
|
-
|
419
|
-
/* Get the value of the 'compress' parameter: */
|
420
|
-
opt = rb_hash_aref(opts, COMPRESS_SYMBOL);
|
421
|
-
if (NIL_P(opt)) {
|
422
|
-
compress = false;
|
423
|
-
}
|
424
|
-
else {
|
425
|
-
compress = RTEST(opt);
|
480
|
+
ptr->timeout = NUM2INT(opt);
|
426
481
|
}
|
427
482
|
|
428
483
|
/* Get the value of the 'proxy_url' parameter: */
|
429
484
|
opt = rb_hash_aref(opts, PROXY_URL_SYMBOL);
|
430
|
-
|
431
|
-
proxy_url = NULL;
|
432
|
-
}
|
433
|
-
else {
|
434
|
-
Check_Type(opt, T_STRING);
|
435
|
-
proxy_url = StringValueCStr(opt);
|
436
|
-
}
|
485
|
+
ptr->proxy_url = ov_string_dup(opt);
|
437
486
|
|
438
487
|
/* Get the value of the 'proxy_username' parameter: */
|
439
488
|
opt = rb_hash_aref(opts, PROXY_USERNAME_SYMBOL);
|
440
|
-
|
441
|
-
proxy_username = NULL;
|
442
|
-
}
|
443
|
-
else {
|
444
|
-
Check_Type(opt, T_STRING);
|
445
|
-
proxy_username = StringValueCStr(opt);
|
446
|
-
}
|
489
|
+
ptr->proxy_username = ov_string_dup(opt);
|
447
490
|
|
448
491
|
/* Get the value of the 'proxy_password' parameter: */
|
449
492
|
opt = rb_hash_aref(opts, PROXY_PASSWORD_SYMBOL);
|
493
|
+
ptr->proxy_password = ov_string_dup(opt);
|
494
|
+
|
495
|
+
/* Get the value of the 'log' parameter: */
|
496
|
+
opt = rb_hash_aref(opts, LOG_SYMBOL);
|
497
|
+
ptr->log = opt;
|
498
|
+
|
499
|
+
/* Get the value of the 'pipeline' parameter: */
|
500
|
+
opt = rb_hash_aref(opts, PIPELINE_SYMBOL);
|
450
501
|
if (NIL_P(opt)) {
|
451
|
-
|
502
|
+
pipeline = 0;
|
452
503
|
}
|
453
504
|
else {
|
454
|
-
Check_Type(opt,
|
455
|
-
|
456
|
-
}
|
457
|
-
|
458
|
-
/* Create the libcurl object: */
|
459
|
-
object->curl = curl_easy_init();
|
460
|
-
if (object->curl == NULL) {
|
461
|
-
rb_raise(ov_error_class, "Can't create libcurl object");
|
505
|
+
Check_Type(opt, T_FIXNUM);
|
506
|
+
pipeline = NUM2LONG(opt);
|
462
507
|
}
|
463
508
|
|
464
|
-
/*
|
465
|
-
|
466
|
-
|
467
|
-
|
509
|
+
/* Get the value of the 'connections' parameter: */
|
510
|
+
opt = rb_hash_aref(opts, CONNECTIONS_SYMBOL);
|
511
|
+
if (NIL_P(opt)) {
|
512
|
+
connections = 0;
|
468
513
|
}
|
469
|
-
|
470
|
-
|
514
|
+
else {
|
515
|
+
Check_Type(opt, T_FIXNUM);
|
516
|
+
connections = NUM2LONG(opt);
|
471
517
|
}
|
472
518
|
|
473
|
-
/*
|
474
|
-
|
519
|
+
/* Create the hash that contains the transfers are pending an completed. Both use the identity of the request
|
520
|
+
as key. */
|
521
|
+
ptr->completed = rb_funcall(rb_hash_new(), COMPARE_BY_IDENTITY_ID, 0);
|
522
|
+
ptr->pending = rb_funcall(rb_hash_new(), COMPARE_BY_IDENTITY_ID, 0);
|
475
523
|
|
476
|
-
/*
|
477
|
-
|
478
|
-
if (
|
479
|
-
|
524
|
+
/* Create the libcurl multi handle: */
|
525
|
+
ptr->handle = curl_multi_init();
|
526
|
+
if (ptr->handle == NULL) {
|
527
|
+
rb_raise(ov_error_class, "Can't create libcurl object");
|
480
528
|
}
|
481
529
|
|
482
|
-
/*
|
483
|
-
if (
|
484
|
-
|
485
|
-
|
530
|
+
/* Enable pipelining: */
|
531
|
+
if (pipeline > 0) {
|
532
|
+
curl_multi_setopt(ptr->handle, CURLMOPT_PIPELINING, CURLPIPE_HTTP1);
|
533
|
+
#ifdef CURLMOPT_MAX_PIPELINE_LENGTH
|
534
|
+
curl_multi_setopt(ptr->handle, CURLMOPT_MAX_PIPELINE_LENGTH, pipeline);
|
535
|
+
#endif
|
486
536
|
}
|
487
|
-
|
488
|
-
|
489
|
-
|
490
|
-
|
491
|
-
if (proxy_username != NULL && proxy_password != NULL) {
|
492
|
-
curl_easy_setopt(object->curl, CURLOPT_PROXYUSERNAME, proxy_username);
|
493
|
-
curl_easy_setopt(object->curl, CURLOPT_PROXYPASSWORD, proxy_password);
|
494
|
-
}
|
537
|
+
if (connections > 0) {
|
538
|
+
#ifdef CURLMOPT_MAX_HOST_CONNECTIONS
|
539
|
+
curl_multi_setopt(ptr->handle, CURLMOPT_MAX_HOST_CONNECTIONS, connections);
|
540
|
+
#endif
|
495
541
|
}
|
496
542
|
|
497
|
-
/* Configure callbacks: */
|
498
|
-
curl_easy_setopt(object->curl, CURLOPT_READFUNCTION, ov_http_client_read_function);
|
499
|
-
curl_easy_setopt(object->curl, CURLOPT_WRITEFUNCTION, ov_http_client_write_function);
|
500
|
-
curl_easy_setopt(object->curl, CURLOPT_HEADERFUNCTION, ov_http_client_header_function);
|
501
|
-
|
502
543
|
return self;
|
503
544
|
}
|
504
545
|
|
505
|
-
static VALUE ov_http_client_build_url(
|
506
|
-
ov_http_client_object* object;
|
507
|
-
|
508
|
-
/* Get the pointer to the native object and check that it isn't closed: */
|
509
|
-
Data_Get_Struct(self, ov_http_client_object, object);
|
510
|
-
ov_http_client_check_closed(object);
|
511
|
-
|
546
|
+
static VALUE ov_http_client_build_url( VALUE url, VALUE query) {
|
512
547
|
/* Copy the URL: */
|
513
548
|
if (NIL_P(url)) {
|
514
549
|
rb_raise(ov_error_class, "The 'url' parameter can't be nil");
|
@@ -535,178 +570,317 @@ static int ov_http_client_add_header(VALUE name, VALUE value, struct curl_slist*
|
|
535
570
|
return ST_CONTINUE;
|
536
571
|
}
|
537
572
|
|
538
|
-
static void*
|
539
|
-
|
573
|
+
static void* ov_http_client_complete_task(void* data) {
|
574
|
+
CURLM* handle;
|
575
|
+
CURLMsg* message;
|
576
|
+
VALUE error;
|
577
|
+
VALUE transfer;
|
578
|
+
long code;
|
579
|
+
ov_http_client_object* client_ptr;
|
580
|
+
ov_http_response_object* response_ptr;
|
581
|
+
ov_http_transfer_object* transfer_ptr;
|
582
|
+
|
583
|
+
/* The passed pointer is the libcurl message describing the completed transfer: */
|
584
|
+
message = (CURLMsg*) data;
|
585
|
+
handle = message->easy_handle;
|
586
|
+
|
587
|
+
/* The transfer is stored as the private data of the libcurl easy handle: */
|
588
|
+
curl_easy_getinfo(handle, CURLINFO_PRIVATE, &transfer);
|
589
|
+
|
590
|
+
/* Get the pointers to the transfer, client and response: */
|
591
|
+
ov_http_transfer_ptr(transfer, transfer_ptr);
|
592
|
+
ov_http_client_ptr(transfer_ptr->client, client_ptr);
|
593
|
+
ov_http_response_ptr(transfer_ptr->response, response_ptr);
|
594
|
+
|
595
|
+
/* Remove the transfer from the pending hash: */
|
596
|
+
rb_hash_delete(client_ptr->pending, transfer_ptr->request);
|
597
|
+
|
598
|
+
if (message->data.result == CURLE_OK) {
|
599
|
+
/* Copy the response code and the response body to the response object: */
|
600
|
+
curl_easy_getinfo(handle, CURLINFO_RESPONSE_CODE, &code);
|
601
|
+
response_ptr->code = LONG2NUM(code);
|
602
|
+
response_ptr->body = rb_funcall(transfer_ptr->out, STRING_ID, 0);
|
603
|
+
|
604
|
+
/* Put the request and the response in the completed transfers hash: */
|
605
|
+
rb_hash_aset(client_ptr->completed, transfer_ptr->request, transfer_ptr->response);
|
606
|
+
|
607
|
+
/* Send a summary of the response to the log: */
|
608
|
+
ov_http_client_log_info(
|
609
|
+
client_ptr->log,
|
610
|
+
"Received response code '%"PRIsVALUE"'.",
|
611
|
+
response_ptr->code
|
612
|
+
);
|
613
|
+
}
|
614
|
+
else {
|
615
|
+
/* Put the request and error in the completed transfers hash: */
|
616
|
+
error = rb_sprintf("Can't send request: %s", curl_easy_strerror(message->data.result));
|
617
|
+
error = rb_class_new_instance(1, &error, ov_error_class);
|
618
|
+
rb_hash_aset(client_ptr->completed, transfer_ptr->request, error);
|
619
|
+
}
|
540
620
|
|
541
|
-
/*
|
542
|
-
|
621
|
+
/* Now that the libcurl easy handle is released, we can release the headers as well: */
|
622
|
+
curl_slist_free_all(transfer_ptr->headers);
|
543
623
|
|
544
624
|
return NULL;
|
545
625
|
}
|
546
626
|
|
547
|
-
static void
|
548
|
-
|
627
|
+
static void* ov_http_client_wait_task(void* data) {
|
628
|
+
CURLMsg* message;
|
629
|
+
int count;
|
630
|
+
int pending;
|
631
|
+
long timeout;
|
632
|
+
ov_http_client_wait_context* context_ptr;
|
549
633
|
|
550
|
-
/*
|
551
|
-
|
552
|
-
perform_context->cancel = true;
|
553
|
-
}
|
634
|
+
/* The passed data is the wait context: */
|
635
|
+
context_ptr = data;
|
554
636
|
|
555
|
-
|
556
|
-
|
557
|
-
|
558
|
-
|
637
|
+
/* Get the timeout preferred by libcurl, or one second by default: */
|
638
|
+
curl_multi_timeout(context_ptr->handle, &timeout);
|
639
|
+
if (timeout < 0) {
|
640
|
+
timeout = 1000;
|
641
|
+
}
|
559
642
|
|
560
|
-
|
561
|
-
|
562
|
-
|
563
|
-
|
564
|
-
|
565
|
-
|
566
|
-
|
643
|
+
/* Wait till there is activity: */
|
644
|
+
context_ptr->code = curl_multi_wait(context_ptr->handle, NULL, 0, timeout, NULL);
|
645
|
+
if (context_ptr->code != CURLE_OK) {
|
646
|
+
return NULL;
|
647
|
+
}
|
648
|
+
|
649
|
+
/* Let libcurl do its work, even if no file descriptor needs attention. This is necessary because some of its
|
650
|
+
activities can't be monitored using file descriptors. */
|
651
|
+
context_ptr->code = curl_multi_perform(context_ptr->handle, &pending);
|
652
|
+
if (context_ptr->code != CURLE_OK) {
|
653
|
+
return NULL;
|
654
|
+
}
|
655
|
+
|
656
|
+
/* Check if there are finished transfers. For each of them call the function that completes them, with the global
|
657
|
+
interpreter lock acquired, as it will call Ruby code. */
|
658
|
+
while ((message = curl_multi_info_read(context_ptr->handle, &count)) != NULL) {
|
659
|
+
if (message->msg == CURLMSG_DONE) {
|
660
|
+
/* Call the Ruby code that completes the transfer: */
|
661
|
+
rb_thread_call_with_gvl(ov_http_client_complete_task, message);
|
662
|
+
|
663
|
+
/* Remove the easy handle from the multi handle and discard it: */
|
664
|
+
curl_multi_remove_handle(context_ptr->handle, message->easy_handle);
|
665
|
+
curl_easy_cleanup(message->easy_handle);
|
567
666
|
}
|
568
667
|
}
|
668
|
+
|
669
|
+
/* Everything worked correctly: */
|
670
|
+
context_ptr->code = CURLE_OK;
|
671
|
+
return NULL;
|
672
|
+
}
|
673
|
+
|
674
|
+
static void ov_http_client_wait_cancel(void* data) {
|
675
|
+
ov_http_client_wait_context* context_ptr;
|
676
|
+
|
677
|
+
/* The passed data is the wait context: */
|
678
|
+
context_ptr = data;
|
679
|
+
|
680
|
+
/* Set the cancel flag so that the operation will be actually aborted in the next operation of the wait loop: */
|
681
|
+
context_ptr->cancel = true;
|
569
682
|
}
|
570
683
|
|
571
|
-
static
|
684
|
+
static void ov_http_client_prepare_handle(ov_http_client_object* client_ptr, ov_http_request_object* request_ptr,
|
685
|
+
struct curl_slist** headers, CURL* handle) {
|
572
686
|
VALUE header;
|
573
687
|
VALUE url;
|
574
|
-
|
575
|
-
ov_http_client_object* object;
|
576
|
-
ov_http_client_perform_context perform_context;
|
577
|
-
ov_http_request_object* request_object;
|
578
|
-
ov_http_response_object* response_object;
|
579
|
-
struct curl_slist* headers;
|
688
|
+
int timeout;
|
580
689
|
|
581
|
-
/*
|
582
|
-
|
583
|
-
|
690
|
+
/* Configure TLS parameters: */
|
691
|
+
if (client_ptr->insecure) {
|
692
|
+
curl_easy_setopt(handle, CURLOPT_SSL_VERIFYPEER, 0L);
|
693
|
+
curl_easy_setopt(handle, CURLOPT_SSL_VERIFYHOST, 0L);
|
694
|
+
}
|
695
|
+
if (client_ptr->ca_file != NULL) {
|
696
|
+
curl_easy_setopt(handle, CURLOPT_CAINFO, client_ptr->ca_file);
|
697
|
+
}
|
584
698
|
|
585
|
-
/*
|
586
|
-
|
587
|
-
|
699
|
+
/* Configure the timeout: */
|
700
|
+
timeout = client_ptr->timeout;
|
701
|
+
if (!NIL_P(request_ptr->timeout)) {
|
702
|
+
timeout = NUM2INT(request_ptr->timeout);
|
588
703
|
}
|
589
|
-
|
590
|
-
|
704
|
+
curl_easy_setopt(handle, CURLOPT_TIMEOUT, timeout);
|
705
|
+
|
706
|
+
/* Configure compression of responses (setting the value to zero length string means accepting all the
|
707
|
+
compression types that libcurl supports): */
|
708
|
+
if (client_ptr->compress) {
|
709
|
+
curl_easy_setopt(handle, CURLOPT_ENCODING, "");
|
591
710
|
}
|
592
|
-
Data_Get_Struct(request, ov_http_request_object, request_object);
|
593
711
|
|
594
|
-
/*
|
595
|
-
if (
|
596
|
-
|
712
|
+
/* Configure debug mode: */
|
713
|
+
if (client_ptr->debug) {
|
714
|
+
curl_easy_setopt(handle, CURLOPT_VERBOSE, 1L);
|
715
|
+
curl_easy_setopt(handle, CURLOPT_DEBUGFUNCTION, ov_http_client_debug_function);
|
597
716
|
}
|
598
|
-
|
599
|
-
|
717
|
+
|
718
|
+
/* Configure the proxy: */
|
719
|
+
if (client_ptr->proxy_url != NULL) {
|
720
|
+
curl_easy_setopt(handle, CURLOPT_PROXY, client_ptr->proxy_url);
|
721
|
+
if (client_ptr->proxy_username != NULL && client_ptr->proxy_password != NULL) {
|
722
|
+
curl_easy_setopt(handle, CURLOPT_PROXYUSERNAME, client_ptr->proxy_username);
|
723
|
+
curl_easy_setopt(handle, CURLOPT_PROXYPASSWORD, client_ptr->proxy_password);
|
724
|
+
}
|
600
725
|
}
|
601
|
-
Data_Get_Struct(response, ov_http_response_object, response_object);
|
602
726
|
|
603
|
-
/*
|
604
|
-
|
605
|
-
curl_easy_setopt(
|
727
|
+
/* Configure callbacks: */
|
728
|
+
curl_easy_setopt(handle, CURLOPT_READFUNCTION, ov_http_client_read_function);
|
729
|
+
curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, ov_http_client_write_function);
|
730
|
+
curl_easy_setopt(handle, CURLOPT_HEADERFUNCTION, ov_http_client_header_function);
|
606
731
|
|
607
|
-
/*
|
608
|
-
|
732
|
+
/* Build and set the URL: */
|
733
|
+
url = ov_http_client_build_url(request_ptr->url, request_ptr->query);
|
734
|
+
curl_easy_setopt(handle, CURLOPT_URL, StringValueCStr(url));
|
609
735
|
|
610
736
|
/* Set the method: */
|
611
|
-
if (rb_eql(
|
612
|
-
headers = curl_slist_append(headers, "Transfer-Encoding: chunked");
|
613
|
-
headers = curl_slist_append(headers, "Expect:");
|
614
|
-
curl_easy_setopt(
|
737
|
+
if (rb_eql(request_ptr->method, POST_SYMBOL)) {
|
738
|
+
*headers = curl_slist_append(*headers, "Transfer-Encoding: chunked");
|
739
|
+
*headers = curl_slist_append(*headers, "Expect:");
|
740
|
+
curl_easy_setopt(handle, CURLOPT_POST, 1L);
|
615
741
|
}
|
616
|
-
else if (rb_eql(
|
617
|
-
curl_easy_setopt(
|
618
|
-
curl_easy_setopt(
|
742
|
+
else if (rb_eql(request_ptr->method, PUT_SYMBOL)) {
|
743
|
+
curl_easy_setopt(handle, CURLOPT_UPLOAD, 1L);
|
744
|
+
curl_easy_setopt(handle, CURLOPT_PUT, 1L);
|
619
745
|
}
|
620
|
-
else if (rb_eql(
|
621
|
-
curl_easy_setopt(
|
622
|
-
curl_easy_setopt(
|
746
|
+
else if (rb_eql(request_ptr->method, DELETE_SYMBOL)) {
|
747
|
+
curl_easy_setopt(handle, CURLOPT_HTTPGET, 1L);
|
748
|
+
curl_easy_setopt(handle, CURLOPT_CUSTOMREQUEST, "DELETE");
|
623
749
|
}
|
624
|
-
else if (rb_eql(
|
625
|
-
curl_easy_setopt(
|
750
|
+
else if (rb_eql(request_ptr->method, GET_SYMBOL)) {
|
751
|
+
curl_easy_setopt(handle, CURLOPT_HTTPGET, 1L);
|
626
752
|
}
|
627
753
|
|
628
754
|
/* Set authentication details: */
|
629
|
-
if (!NIL_P(
|
630
|
-
header = rb_sprintf("Authorization: Bearer %"PRIsVALUE"",
|
631
|
-
headers = curl_slist_append(headers, StringValueCStr(header));
|
755
|
+
if (!NIL_P(request_ptr->token)) {
|
756
|
+
header = rb_sprintf("Authorization: Bearer %"PRIsVALUE"", request_ptr->token);
|
757
|
+
*headers = curl_slist_append(*headers, StringValueCStr(header));
|
632
758
|
}
|
633
|
-
else if (!NIL_P(
|
634
|
-
curl_easy_setopt(
|
635
|
-
curl_easy_setopt(
|
636
|
-
curl_easy_setopt(
|
759
|
+
else if (!NIL_P(request_ptr->username) && !NIL_P(request_ptr->password)) {
|
760
|
+
curl_easy_setopt(handle, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
|
761
|
+
curl_easy_setopt(handle, CURLOPT_USERNAME, StringValueCStr(request_ptr->username));
|
762
|
+
curl_easy_setopt(handle, CURLOPT_PASSWORD, StringValueCStr(request_ptr->password));
|
637
763
|
}
|
638
|
-
else if (RTEST(
|
639
|
-
curl_easy_setopt(
|
764
|
+
else if (RTEST(request_ptr->kerberos)) {
|
765
|
+
curl_easy_setopt(handle, CURLOPT_HTTPAUTH, CURLAUTH_NEGOTIATE);
|
640
766
|
}
|
641
767
|
|
642
768
|
/* Set the headers: */
|
643
|
-
if (!NIL_P(
|
644
|
-
rb_hash_foreach(
|
769
|
+
if (!NIL_P(request_ptr->headers)) {
|
770
|
+
rb_hash_foreach(request_ptr->headers, ov_http_client_add_header, (VALUE) headers);
|
645
771
|
}
|
646
|
-
curl_easy_setopt(
|
772
|
+
curl_easy_setopt(handle, CURLOPT_HTTPHEADER, *headers);
|
647
773
|
|
648
774
|
/* Send a summary of the request to the log: */
|
649
775
|
ov_http_client_log_info(
|
650
|
-
|
776
|
+
client_ptr->log,
|
651
777
|
"Sending '%"PRIsVALUE"' request to URL '%"PRIsVALUE"'.",
|
652
|
-
|
778
|
+
request_ptr->method,
|
653
779
|
url
|
654
780
|
);
|
781
|
+
}
|
782
|
+
|
783
|
+
static VALUE ov_http_client_send(VALUE self, VALUE request) {
|
784
|
+
CURL* handle;
|
785
|
+
VALUE response;
|
786
|
+
VALUE transfer;
|
787
|
+
ov_http_client_object* ptr;
|
788
|
+
ov_http_request_object* request_ptr;
|
789
|
+
ov_http_transfer_object* transfer_ptr;
|
790
|
+
struct curl_slist* headers;
|
655
791
|
|
656
|
-
/*
|
657
|
-
|
658
|
-
|
659
|
-
|
660
|
-
|
661
|
-
if (NIL_P(
|
662
|
-
|
792
|
+
/* Get the pointer to the native object and check that it isn't closed: */
|
793
|
+
ov_http_client_ptr(self, ptr);
|
794
|
+
ov_http_client_check_closed(ptr);
|
795
|
+
|
796
|
+
/* Check the type of request and get the pointer to the native object: */
|
797
|
+
if (NIL_P(request)) {
|
798
|
+
rb_raise(ov_error_class, "The 'request' parameter can't be nil");
|
663
799
|
}
|
664
|
-
|
665
|
-
|
666
|
-
}
|
667
|
-
|
668
|
-
|
669
|
-
|
670
|
-
|
671
|
-
|
672
|
-
|
673
|
-
|
674
|
-
|
675
|
-
|
676
|
-
|
677
|
-
|
800
|
+
if (!rb_obj_is_instance_of(request, ov_http_request_class)) {
|
801
|
+
rb_raise(ov_error_class, "The 'request' parameter isn't an instance of class 'HttpRequest'");
|
802
|
+
}
|
803
|
+
ov_http_request_ptr(request, request_ptr);
|
804
|
+
|
805
|
+
/* Create the libcurl easy handle: */
|
806
|
+
handle = curl_easy_init();
|
807
|
+
if (ptr->handle == NULL) {
|
808
|
+
rb_raise(ov_error_class, "Can't create libcurl object");
|
809
|
+
}
|
810
|
+
|
811
|
+
/* The headers used by the libcurl easy handle can't be released till the handle is released itself, so we need
|
812
|
+
to initialize here, and add it to the context so that we can release it later: */
|
813
|
+
headers = NULL;
|
814
|
+
|
815
|
+
/* Configure the libcurl easy handle with the data from the client and from the request: */
|
816
|
+
ov_http_client_prepare_handle(ptr, request_ptr, &headers, handle);
|
678
817
|
|
679
|
-
/*
|
680
|
-
|
681
|
-
curl_easy_setopt(object->curl, CURLOPT_URL, NULL);
|
682
|
-
curl_easy_setopt(object->curl, CURLOPT_READDATA, NULL);
|
683
|
-
curl_easy_setopt(object->curl, CURLOPT_WRITEDATA, NULL);
|
684
|
-
curl_easy_setopt(object->curl, CURLOPT_HEADERDATA, NULL);
|
685
|
-
curl_easy_setopt(object->curl, CURLOPT_DEBUGDATA, NULL);
|
686
|
-
curl_easy_setopt(object->curl, CURLOPT_CUSTOMREQUEST, NULL);
|
687
|
-
curl_easy_setopt(object->curl, CURLOPT_UPLOAD, 0L);
|
688
|
-
curl_easy_setopt(object->curl, CURLOPT_HTTPAUTH, 0L);
|
689
|
-
curl_easy_setopt(object->curl, CURLOPT_USERNAME, "");
|
690
|
-
curl_easy_setopt(object->curl, CURLOPT_PASSWORD, "");
|
818
|
+
/* Allocate a ne empty response: */
|
819
|
+
response = rb_class_new_instance(0, NULL, ov_http_response_class);
|
691
820
|
|
692
|
-
/*
|
693
|
-
|
694
|
-
|
821
|
+
/* Allocate a new empty transfer: */
|
822
|
+
transfer = rb_class_new_instance(0, NULL, ov_http_transfer_class);
|
823
|
+
ov_http_transfer_ptr(transfer, transfer_ptr);
|
824
|
+
transfer_ptr->client = self;
|
825
|
+
transfer_ptr->request = request;
|
826
|
+
transfer_ptr->response = response;
|
827
|
+
transfer_ptr->headers = headers;
|
828
|
+
transfer_ptr->cancel = false;
|
829
|
+
if (NIL_P(request_ptr->body)) {
|
830
|
+
transfer_ptr->in = rb_class_new_instance(0, NULL, STRING_IO_CLASS);
|
831
|
+
}
|
832
|
+
else {
|
833
|
+
transfer_ptr->in = rb_class_new_instance(1, &request_ptr->body, STRING_IO_CLASS);
|
695
834
|
}
|
835
|
+
transfer_ptr->out = rb_class_new_instance(0, NULL, STRING_IO_CLASS);
|
696
836
|
|
697
|
-
/*
|
698
|
-
|
699
|
-
response_object->code = LONG2NUM(response_code);
|
837
|
+
/* Put the request and the transfer in the hash of pending transfers: */
|
838
|
+
rb_hash_aset(ptr->pending, request, transfer);
|
700
839
|
|
701
|
-
/*
|
702
|
-
|
840
|
+
/* Set the transfer as the data for all the callbacks, so we can access it from any place where it is needed: */
|
841
|
+
curl_easy_setopt(handle, CURLOPT_PRIVATE, transfer);
|
842
|
+
curl_easy_setopt(handle, CURLOPT_READDATA, transfer);
|
843
|
+
curl_easy_setopt(handle, CURLOPT_WRITEDATA, transfer);
|
844
|
+
curl_easy_setopt(handle, CURLOPT_HEADERDATA, transfer);
|
845
|
+
curl_easy_setopt(handle, CURLOPT_DEBUGDATA, transfer);
|
703
846
|
|
704
|
-
/*
|
705
|
-
|
706
|
-
|
707
|
-
|
708
|
-
|
709
|
-
|
847
|
+
/* Add the easy handle to the multi handle: */
|
848
|
+
curl_multi_add_handle(ptr->handle, handle);
|
849
|
+
|
850
|
+
return Qnil;
|
851
|
+
}
|
852
|
+
|
853
|
+
static VALUE ov_http_client_wait(VALUE self, VALUE request) {
|
854
|
+
VALUE result;
|
855
|
+
ov_http_client_object* ptr;
|
856
|
+
ov_http_client_wait_context context;
|
857
|
+
|
858
|
+
/* Get the pointer to the native object and check that it isn't closed: */
|
859
|
+
ov_http_client_ptr(self, ptr);
|
860
|
+
ov_http_client_check_closed(ptr);
|
861
|
+
|
862
|
+
/* Work till the transfer has been completed: */
|
863
|
+
context.handle = ptr->handle;
|
864
|
+
context.code = CURLE_OK;
|
865
|
+
context.cancel = false;
|
866
|
+
for (;;) {
|
867
|
+
result = rb_hash_delete(ptr->completed, request);
|
868
|
+
if (!NIL_P(result)) {
|
869
|
+
return result;
|
870
|
+
}
|
871
|
+
rb_thread_call_without_gvl(
|
872
|
+
ov_http_client_wait_task,
|
873
|
+
&context,
|
874
|
+
ov_http_client_wait_cancel,
|
875
|
+
&context
|
876
|
+
);
|
877
|
+
if (context.cancel) {
|
878
|
+
return Qnil;
|
879
|
+
}
|
880
|
+
if (context.code != CURLE_OK) {
|
881
|
+
rb_raise(ov_error_class, "Unexpected error while waiting: %s", curl_easy_strerror(context.code));
|
882
|
+
}
|
883
|
+
}
|
710
884
|
|
711
885
|
return Qnil;
|
712
886
|
}
|
@@ -726,33 +900,37 @@ void ov_http_client_define(void) {
|
|
726
900
|
rb_define_method(ov_http_client_class, "initialize", ov_http_client_initialize, -1);
|
727
901
|
|
728
902
|
/* Define the methods: */
|
729
|
-
rb_define_method(ov_http_client_class, "
|
730
|
-
rb_define_method(ov_http_client_class, "
|
731
|
-
rb_define_method(ov_http_client_class, "
|
903
|
+
rb_define_method(ov_http_client_class, "close", ov_http_client_close, 0);
|
904
|
+
rb_define_method(ov_http_client_class, "send", ov_http_client_send, 1);
|
905
|
+
rb_define_method(ov_http_client_class, "wait", ov_http_client_wait, 1);
|
732
906
|
|
733
907
|
/* Define the symbols: */
|
734
|
-
USERNAME_SYMBOL = ID2SYM(rb_intern("username"));
|
735
|
-
PASSWORD_SYMBOL = ID2SYM(rb_intern("password"));
|
736
|
-
INSECURE_SYMBOL = ID2SYM(rb_intern("insecure"));
|
737
908
|
CA_FILE_SYMBOL = ID2SYM(rb_intern("ca_file"));
|
909
|
+
COMPRESS_SYMBOL = ID2SYM(rb_intern("compress"));
|
910
|
+
CONNECTIONS_SYMBOL = ID2SYM(rb_intern("connections"));
|
738
911
|
DEBUG_SYMBOL = ID2SYM(rb_intern("debug"));
|
912
|
+
INSECURE_SYMBOL = ID2SYM(rb_intern("insecure"));
|
739
913
|
LOG_SYMBOL = ID2SYM(rb_intern("log"));
|
740
|
-
|
741
|
-
|
914
|
+
PASSWORD_SYMBOL = ID2SYM(rb_intern("password"));
|
915
|
+
PIPELINE_SYMBOL = ID2SYM(rb_intern("pipeline"));
|
916
|
+
PROXY_PASSWORD_SYMBOL = ID2SYM(rb_intern("proxy_password"));
|
742
917
|
PROXY_URL_SYMBOL = ID2SYM(rb_intern("proxy_url"));
|
743
918
|
PROXY_USERNAME_SYMBOL = ID2SYM(rb_intern("proxy_username"));
|
744
|
-
|
919
|
+
TIMEOUT_SYMBOL = ID2SYM(rb_intern("timeout"));
|
920
|
+
USERNAME_SYMBOL = ID2SYM(rb_intern("username"));
|
745
921
|
|
746
922
|
/* Define the method identifiers: */
|
747
|
-
|
748
|
-
|
749
|
-
|
750
|
-
|
751
|
-
|
752
|
-
|
753
|
-
|
754
|
-
|
755
|
-
|
923
|
+
COMPARE_BY_IDENTITY_ID = rb_intern("compare_by_identity");
|
924
|
+
DEBUG_ID = rb_intern("debug");
|
925
|
+
DOWNCASE_ID = rb_intern("downcase");
|
926
|
+
ENCODE_WWW_FORM_ID = rb_intern("encode_www_form");
|
927
|
+
INFO_ID = rb_intern("info");
|
928
|
+
INFO_Q_ID = rb_intern("info?");
|
929
|
+
READ_ID = rb_intern("read");
|
930
|
+
STRING_ID = rb_intern("string");
|
931
|
+
STRING_IO_ID = rb_intern("StringIO");
|
932
|
+
URI_ID = rb_intern("URI");
|
933
|
+
WRITE_ID = rb_intern("write");
|
756
934
|
|
757
935
|
/* Locate classes: */
|
758
936
|
STRING_IO_CLASS = rb_const_get(rb_cObject, STRING_IO_ID);
|