streamly 0.1.3 → 0.1.4

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.
@@ -1,5 +1,10 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.1.4 (September 2nd, 2010)
4
+ * Streamly now respects Encoding.default_internal on 1.9
5
+ * fixed a random segfault when applying a NULL auth field
6
+ * take advantage of rb_thread_blocking_region on 1.9 and emulate it's behavior on 1.8 for Ruby-aware thread-saftey
7
+
3
8
  ## 0.1.3 (August 19th, 2009)
4
9
  * Fixed a bug where a username or password was specified, but not both
5
10
 
@@ -3,14 +3,13 @@
3
3
  == Features
4
4
  * rest-client like API
5
5
  * Streaming API allows the caller to be handed chunks of the response while it's being received
6
+ * uses Encoding.default_internal (otherwise falls back to utf-8) for strings it hands back in 1.9
6
7
 
7
8
  == How to install
8
9
 
9
- Install it like any other gem hosted at the Githubs like so:
10
+ Nothing special about it, just:
10
11
 
11
- (more instructions here: http://gems.github.com)
12
-
13
- sudo gem install brianmario-streamly
12
+ sudo gem install streamly
14
13
 
15
14
  == Example of use
16
15
 
@@ -1,4 +1,5 @@
1
1
  ---
2
- :minor: 1
3
- :patch: 3
2
+ :patch: 4
4
3
  :major: 0
4
+ :build:
5
+ :minor: 1
@@ -1,4 +1,5 @@
1
1
  # encoding: UTF-8
2
+ $LOAD_PATH.unshift File.expand_path(File.dirname(__FILE__) + '/..')
2
3
  $LOAD_PATH.unshift File.expand_path(File.dirname(__FILE__) + '/../lib')
3
4
 
4
5
  require 'rubygems'
@@ -9,23 +10,23 @@ require 'benchmark'
9
10
 
10
11
  url = ARGV[0]
11
12
 
12
- Benchmark.bm do |x|
13
- puts "Streamly"
13
+ Benchmark.bmbm do |x|
14
14
  x.report do
15
+ puts "Streamly"
15
16
  (ARGV[1] || 1).to_i.times do
16
17
  Streamly.get(url)
17
18
  end
18
19
  end
19
-
20
- puts "Shell out to curl"
20
+
21
21
  x.report do
22
+ puts "Shell out to curl"
22
23
  (ARGV[1] || 1).to_i.times do
23
24
  `curl -s --compressed #{url}`
24
25
  end
25
26
  end
26
-
27
- puts "rest-client"
27
+
28
28
  x.report do
29
+ puts "rest-client"
29
30
  (ARGV[1] || 1).to_i.times do
30
31
  RestClient.get(url, {"Accept-Encoding" => "identity, deflate, gzip"})
31
32
  end
@@ -1,4 +1,5 @@
1
1
  # encoding: UTF-8
2
+ $LOAD_PATH.unshift File.expand_path(File.dirname(__FILE__) + '/..')
2
3
  $LOAD_PATH.unshift File.expand_path(File.dirname(__FILE__) + '/../lib')
3
4
 
4
5
  require 'rubygems'
@@ -10,31 +11,31 @@ require 'benchmark'
10
11
 
11
12
  url = ARGV[0]
12
13
 
13
- Benchmark.bm do |x|
14
- puts "Streamly"
14
+ Benchmark.bmbm do |x|
15
15
  parser = Yajl::Parser.new
16
16
  parser.on_parse_complete = lambda {|obj| }
17
17
  x.report do
18
+ puts "Streamly"
18
19
  (ARGV[1] || 1).to_i.times do
19
20
  Streamly.get(url) do |chunk|
20
21
  parser << chunk
21
22
  end
22
23
  end
23
24
  end
24
-
25
- puts "Shell out to curl"
25
+
26
26
  parser = Yajl::Parser.new
27
27
  parser.on_parse_complete = lambda {|obj| }
28
28
  x.report do
29
+ puts "Shell out to curl"
29
30
  (ARGV[1] || 1).to_i.times do
30
31
  parser.parse `curl -s --compressed #{url}`
31
32
  end
32
33
  end
33
-
34
- puts "rest-client"
34
+
35
35
  parser = Yajl::Parser.new
36
36
  parser.on_parse_complete = lambda {|obj| }
37
37
  x.report do
38
+ puts "rest-client"
38
39
  (ARGV[1] || 1).to_i.times do
39
40
  parser.parse RestClient.get(url, {"Accept-Encoding" => "identity, deflate, gzip"})
40
41
  end
@@ -2,6 +2,9 @@
2
2
  require 'mkmf'
3
3
  require 'rbconfig'
4
4
 
5
+ # 1.9-only
6
+ have_func('rb_thread_blocking_region')
7
+
5
8
  $CFLAGS << ' -DHAVE_RBTRAP' if have_var('rb_trap_immediate', ['ruby.h', 'rubysig.h'])
6
9
  # add_define 'HAVE_RBTRAP' if have_var('rb_trap_immediate', ['ruby.h', 'rubysig.h'])
7
10
 
@@ -19,7 +22,7 @@ elsif !have_library('curl') or !have_header('curl/curl.h')
19
22
  EOM
20
23
  end
21
24
 
22
- $CFLAGS << ' -Wall'
25
+ $CFLAGS << ' -Wall -Wextra -funroll-loops'
23
26
  # $CFLAGS << ' -O0 -ggdb'
24
27
 
25
28
  create_makefile("streamly_ext")
@@ -1,13 +1,13 @@
1
1
  // static size_t upload_data_handler(char * stream, size_t size, size_t nmemb, VALUE upload_stream) {
2
2
  // size_t result = 0;
3
- //
3
+ //
4
4
  // // TODO
5
5
  // // Change upload_stream back to a VALUE
6
6
  // // if TYPE(upload_stream) == T_STRING - read at most "len" continuously
7
7
  // // if upload_stream is IO-like, read chunks of it
8
8
  // // OR
9
9
  // // if upload_stream responds to "each", use that?
10
- //
10
+ //
11
11
  // TRAP_BEG;
12
12
  // // if (upload_stream != NULL && *upload_stream != NULL) {
13
13
  // // int len = size * nmemb;
@@ -16,334 +16,412 @@
16
16
  // // *upload_stream += result;
17
17
  // // }
18
18
  // TRAP_END;
19
- //
19
+ //
20
20
  // return result;
21
21
  // }
22
22
 
23
23
  #include "streamly.h"
24
+ #ifdef HAVE_RUBY_ENCODING_H
25
+ #include <ruby/encoding.h>
26
+ static rb_encoding *utf8Encoding;
27
+ #endif
24
28
 
25
29
  static size_t header_handler(char * stream, size_t size, size_t nmemb, VALUE handler) {
26
- TRAP_BEG;
27
- if(TYPE(handler) == T_STRING) {
28
- rb_str_buf_cat(handler, stream, size * nmemb);
30
+ size_t str_len = size * nmemb;
31
+
32
+ if(TYPE(handler) == T_STRING) {
33
+ #ifdef HAVE_RUBY_ENCODING_H
34
+ rb_encoding *default_internal_enc = rb_default_internal_encoding();
35
+ if (default_internal_enc) {
36
+ handler = rb_str_export_to_enc(handler, default_internal_enc);
37
+ } else {
38
+ handler = rb_str_export_to_enc(handler, utf8Encoding);
39
+ }
40
+ #endif
41
+ rb_str_buf_cat(handler, stream, str_len);
42
+ } else {
43
+ VALUE chunk = rb_str_new(stream, str_len);
44
+ #ifdef HAVE_RUBY_ENCODING_H
45
+ rb_encoding *default_internal_enc = rb_default_internal_encoding();
46
+ if (default_internal_enc) {
47
+ chunk = rb_str_export_to_enc(chunk, default_internal_enc);
29
48
  } else {
30
- rb_funcall(handler, rb_intern("call"), 1, rb_str_new(stream, size * nmemb));
49
+ chunk = rb_str_export_to_enc(chunk, utf8Encoding);
31
50
  }
32
- TRAP_END;
33
- return size * nmemb;
51
+ #endif
52
+ rb_funcall(handler, rb_intern("call"), 1, chunk);
53
+ }
54
+ return str_len;
34
55
  }
35
56
 
36
57
  static size_t data_handler(char * stream, size_t size, size_t nmemb, VALUE handler) {
37
- TRAP_BEG;
38
- if(TYPE(handler) == T_STRING) {
39
- rb_str_buf_cat(handler, stream, size * nmemb);
58
+ size_t str_len = size * nmemb;
59
+
60
+ if(TYPE(handler) == T_STRING) {
61
+ #ifdef HAVE_RUBY_ENCODING_H
62
+ rb_encoding *default_internal_enc = rb_default_internal_encoding();
63
+ if (default_internal_enc) {
64
+ handler = rb_str_export_to_enc(handler, default_internal_enc);
40
65
  } else {
41
- rb_funcall(handler, rb_intern("call"), 1, rb_str_new(stream, size * nmemb));
66
+ handler = rb_str_export_to_enc(handler, utf8Encoding);
42
67
  }
43
- TRAP_END;
44
- return size * nmemb;
68
+ #endif
69
+ rb_str_buf_cat(handler, stream, str_len);
70
+ } else {
71
+ VALUE chunk = rb_str_new(stream, str_len);
72
+ #ifdef HAVE_RUBY_ENCODING_H
73
+ rb_encoding *default_internal_enc = rb_default_internal_encoding();
74
+ if (default_internal_enc) {
75
+ chunk = rb_str_export_to_enc(chunk, default_internal_enc);
76
+ } else {
77
+ chunk = rb_str_export_to_enc(chunk, utf8Encoding);
78
+ }
79
+ #endif
80
+ rb_funcall(handler, rb_intern("call"), 1, chunk);
81
+ }
82
+ return str_len;
45
83
  }
46
84
 
47
- void streamly_instance_mark(struct curl_instance * instance) {
48
- rb_gc_mark(instance->request_method);
49
- rb_gc_mark(instance->request_payload_handler);
50
- rb_gc_mark(instance->response_header_handler);
51
- rb_gc_mark(instance->response_body_handler);
52
- rb_gc_mark(instance->options);
85
+ static void streamly_instance_mark(struct curl_instance * instance) {
86
+ rb_gc_mark(instance->request_method);
87
+ rb_gc_mark(instance->request_payload_handler);
88
+ rb_gc_mark(instance->response_header_handler);
89
+ rb_gc_mark(instance->response_body_handler);
90
+ rb_gc_mark(instance->options);
53
91
  }
54
92
 
55
- void streamly_instance_free(struct curl_instance * instance) {
56
- curl_easy_cleanup(instance->handle);
57
- free(instance);
93
+ static void streamly_instance_free(struct curl_instance * instance) {
94
+ curl_easy_cleanup(instance->handle);
95
+ free(instance);
58
96
  }
59
97
 
60
98
  // Initially borrowed from Patron - http://github.com/toland/patron
61
99
  // slightly modified for Streamly
62
100
  static VALUE each_http_header(VALUE header, VALUE self) {
63
- struct curl_instance * instance;
64
- GetInstance(self, instance);
65
- VALUE header_str = rb_str_new2("");
66
-
67
- rb_str_buf_cat(header_str, RSTRING_PTR(rb_ary_entry(header, 0)), RSTRING_LEN(rb_ary_entry(header, 0)));
68
- rb_str_buf_cat(header_str, ": ", 2);
69
- rb_str_buf_cat(header_str, RSTRING_PTR(rb_ary_entry(header, 1)), RSTRING_LEN(rb_ary_entry(header, 1)));
70
-
71
- instance->request_headers = curl_slist_append(instance->request_headers, RSTRING_PTR(header_str));
72
- rb_gc_mark(header_str);
73
- return Qnil;
101
+ struct curl_instance * instance;
102
+ GetInstance(self, instance);
103
+ size_t key_len, val_len, header_str_len;
104
+ VALUE key, val;
105
+
106
+ key = rb_ary_entry(header, 0);
107
+ key_len = RSTRING_LEN(key);
108
+
109
+ val = rb_ary_entry(header, 1);
110
+ val_len = RSTRING_LEN(val);
111
+
112
+ header_str_len = (key_len + val_len + 3);
113
+ unsigned char header_str[header_str_len];
114
+
115
+ memcpy(header_str, RSTRING_PTR(key), key_len);
116
+ memcpy(header_str+2, ": ", 2);
117
+ memcpy(header_str+val_len, RSTRING_PTR(val), val_len);
118
+
119
+ header_str[header_str_len+1] = '\0';
120
+ instance->request_headers = curl_slist_append(instance->request_headers, (char *)header_str);
121
+ return Qnil;
74
122
  }
75
123
 
76
124
  // Initially borrowed from Patron - http://github.com/toland/patron
77
125
  // slightly modified for Streamly
78
126
  static VALUE select_error(CURLcode code) {
79
- VALUE error = Qnil;
80
-
81
- switch (code) {
82
- case CURLE_UNSUPPORTED_PROTOCOL:
83
- error = eUnsupportedProtocol;
84
- break;
85
- case CURLE_URL_MALFORMAT:
86
- error = eURLFormatError;
87
- break;
88
- case CURLE_COULDNT_RESOLVE_HOST:
89
- error = eHostResolutionError;
90
- break;
91
- case CURLE_COULDNT_CONNECT:
92
- error = eConnectionFailed;
93
- break;
94
- case CURLE_PARTIAL_FILE:
95
- error = ePartialFileError;
96
- break;
97
- case CURLE_OPERATION_TIMEDOUT:
98
- error = eTimeoutError;
99
- break;
100
- case CURLE_TOO_MANY_REDIRECTS:
101
- error = eTooManyRedirects;
102
- break;
103
- default:
104
- error = eStreamlyError;
105
- }
127
+ VALUE error = Qnil;
128
+
129
+ switch (code) {
130
+ case CURLE_UNSUPPORTED_PROTOCOL:
131
+ error = eUnsupportedProtocol;
132
+ break;
133
+ case CURLE_URL_MALFORMAT:
134
+ error = eURLFormatError;
135
+ break;
136
+ case CURLE_COULDNT_RESOLVE_HOST:
137
+ error = eHostResolutionError;
138
+ break;
139
+ case CURLE_COULDNT_CONNECT:
140
+ error = eConnectionFailed;
141
+ break;
142
+ case CURLE_PARTIAL_FILE:
143
+ error = ePartialFileError;
144
+ break;
145
+ case CURLE_OPERATION_TIMEDOUT:
146
+ error = eTimeoutError;
147
+ break;
148
+ case CURLE_TOO_MANY_REDIRECTS:
149
+ error = eTooManyRedirects;
150
+ break;
151
+ default:
152
+ error = eStreamlyError;
153
+ }
106
154
 
107
- return error;
155
+ return error;
108
156
  }
109
157
 
110
158
  /*
111
- * Document-class: Streamly::Request
112
- *
113
- * A streaming REST client for Ruby that uses libcurl to do the heavy lifting.
114
- * The API is almost exactly like rest-client, so users of that library should find it very familiar.
115
- */
159
+ * Document-class: Streamly::Request
160
+ *
161
+ * A streaming REST client for Ruby that uses libcurl to do the heavy lifting.
162
+ * The API is almost exactly like rest-client, so users of that library should find it very familiar.
163
+ */
116
164
  /*
117
- * Document-method: new
118
- *
119
- * call-seq: new(args)
120
- *
121
- * +args+ should be a Hash and is required
122
- * This Hash should at least contain +:url+ and +:method+ keys.
123
- * You may also provide the following optional keys:
124
- * +:headers+ - should be a Hash of name/value pairs
125
- * +:response_header_handler+ - can be a string or object that responds to #call
126
- * If an object was passed, it's #call method will be called and passed the current chunk of data
127
- * +:response_body_handler+ - can be a string or object that responds to #call
128
- * If an object was passed, it's #call method will be called and passed the current chunk of data
129
- * +:payload+ - If +:method+ is either +:post+ or +:put+ this will be used as the request body
130
- *
131
- */
132
- VALUE rb_streamly_new(int argc, VALUE * argv, VALUE klass) {
133
- struct curl_instance * instance;
134
- VALUE obj = Data_Make_Struct(klass, struct curl_instance, streamly_instance_mark, streamly_instance_free, instance);
135
- rb_obj_call_init(obj, argc, argv);
136
- return obj;
165
+ * Document-method: new
166
+ *
167
+ * call-seq: new(args)
168
+ *
169
+ * +args+ should be a Hash and is required
170
+ * This Hash should at least contain +:url+ and +:method+ keys.
171
+ * You may also provide the following optional keys:
172
+ * +:headers+ - should be a Hash of name/value pairs
173
+ * +:response_header_handler+ - can be a string or object that responds to #call
174
+ * If an object was passed, it's #call method will be called and passed the current chunk of data
175
+ * +:response_body_handler+ - can be a string or object that responds to #call
176
+ * If an object was passed, it's #call method will be called and passed the current chunk of data
177
+ * +:payload+ - If +:method+ is either +:post+ or +:put+ this will be used as the request body
178
+ *
179
+ */
180
+ static VALUE rb_streamly_new(int argc, VALUE * argv, VALUE klass) {
181
+ struct curl_instance * instance;
182
+ VALUE obj = Data_Make_Struct(klass, struct curl_instance, streamly_instance_mark, streamly_instance_free, instance);
183
+ rb_obj_call_init(obj, argc, argv);
184
+ return obj;
137
185
  }
138
186
 
139
187
  /*
140
- * Document-method: initialize
141
- *
142
- * call-seq: initialize(args)
143
- *
144
- * +args+ should be a Hash and is required
145
- * This Hash should at least contain +:url+ and +:method+ keys.
146
- * You may also provide the following optional keys:
147
- * +:headers+ - should be a Hash of name/value pairs
148
- * +:response_header_handler+ - can be a string or object that responds to #call
149
- * If an object was passed, it's #call method will be called and passed the current chunk of data
150
- * +:response_body_handler+ - can be a string or object that responds to #call
151
- * If an object was passed, it's #call method will be called and passed the current chunk of data
152
- * +:payload+ - If +:method+ is either +:post+ or +:put+ this will be used as the request body
153
- *
154
- */
155
- VALUE rb_streamly_init(int argc, VALUE * argv, VALUE self) {
156
- struct curl_instance * instance;
157
- char * credential_sep = ":";
158
- VALUE args, url, payload, headers, username, password, credentials;
159
-
160
- GetInstance(self, instance);
161
- instance->handle = curl_easy_init();
162
- instance->request_headers = NULL;
163
- instance->request_method = Qnil;
164
- instance->request_payload_handler = Qnil;
165
- instance->response_header_handler = Qnil;
166
- instance->response_body_handler = Qnil;
167
- instance->options = Qnil;
168
-
169
- rb_scan_args(argc, argv, "10", &args);
170
-
188
+ * Document-method: initialize
189
+ *
190
+ * call-seq: initialize(args)
191
+ *
192
+ * +args+ should be a Hash and is required
193
+ * This Hash should at least contain +:url+ and +:method+ keys.
194
+ * You may also provide the following optional keys:
195
+ * +:headers+ - should be a Hash of name/value pairs
196
+ * +:response_header_handler+ - can be a string or object that responds to #call
197
+ * If an object was passed, it's #call method will be called and passed the current chunk of data
198
+ * +:response_body_handler+ - can be a string or object that responds to #call
199
+ * If an object was passed, it's #call method will be called and passed the current chunk of data
200
+ * +:payload+ - If +:method+ is either +:post+ or +:put+ this will be used as the request body
201
+ *
202
+ */
203
+ static VALUE rb_streamly_init(int argc, VALUE * argv, VALUE self) {
204
+ struct curl_instance * instance;
205
+ VALUE args, url, payload, headers, username, password, credentials;
206
+
207
+ GetInstance(self, instance);
208
+ instance->handle = curl_easy_init();
209
+ instance->request_headers = NULL;
210
+ instance->request_method = Qnil;
211
+ instance->request_payload_handler = Qnil;
212
+ instance->response_header_handler = Qnil;
213
+ instance->response_body_handler = Qnil;
214
+ instance->options = Qnil;
215
+
216
+ rb_scan_args(argc, argv, "10", &args);
217
+
171
218
  // Ensure our args parameter is a hash
172
- Check_Type(args, T_HASH);
173
-
174
- instance->request_method = rb_hash_aref(args, sym_method);
175
- url = rb_hash_aref(args, sym_url);
176
- payload = rb_hash_aref(args, sym_payload);
177
- headers = rb_hash_aref(args, sym_headers);
178
- username = rb_hash_aref(args, sym_username);
179
- password = rb_hash_aref(args, sym_password);
180
- instance->response_header_handler = rb_hash_aref(args, sym_response_header_handler);
181
- instance->response_body_handler = rb_hash_aref(args, sym_response_body_handler);
182
-
219
+ Check_Type(args, T_HASH);
220
+
221
+ instance->request_method = rb_hash_aref(args, sym_method);
222
+ url = rb_hash_aref(args, sym_url);
223
+ payload = rb_hash_aref(args, sym_payload);
224
+ headers = rb_hash_aref(args, sym_headers);
225
+ username = rb_hash_aref(args, sym_username);
226
+ password = rb_hash_aref(args, sym_password);
227
+ instance->response_header_handler = rb_hash_aref(args, sym_response_header_handler);
228
+ instance->response_body_handler = rb_hash_aref(args, sym_response_body_handler);
229
+
183
230
  // First lets verify we have a :method key
184
- if (NIL_P(instance->request_method)) {
185
- rb_raise(eStreamlyError, "You must specify a :method");
186
- } else {
231
+ if (NIL_P(instance->request_method)) {
232
+ rb_raise(eStreamlyError, "You must specify a :method");
233
+ } else {
187
234
  // OK, a :method was specified, but if it's POST or PUT we require a :payload
188
- if (instance->request_method == sym_post || instance->request_method == sym_put) {
189
- if (NIL_P(payload)) {
190
- rb_raise(eStreamlyError, "You must specify a :payload for POST and PUT requests");
191
- }
192
- }
235
+ if (instance->request_method == sym_post || instance->request_method == sym_put) {
236
+ if (NIL_P(payload)) {
237
+ rb_raise(eStreamlyError, "You must specify a :payload for POST and PUT requests");
238
+ }
193
239
  }
194
-
240
+ }
241
+
195
242
  // Now verify a :url was provided
196
- if (NIL_P(url)) {
197
- rb_raise(eStreamlyError, "You must specify a :url to request");
198
- }
199
-
200
- if (NIL_P(instance->response_header_handler)) {
201
- instance->response_header_handler = rb_str_new2("");
202
- }
203
- if (instance->request_method != sym_head && NIL_P(instance->response_body_handler)) {
204
- instance->response_body_handler = rb_str_new2("");
243
+ if (NIL_P(url)) {
244
+ rb_raise(eStreamlyError, "You must specify a :url to request");
245
+ }
246
+
247
+ if (NIL_P(instance->response_header_handler)) {
248
+ instance->response_header_handler = rb_str_new2("");
249
+ #ifdef HAVE_RUBY_ENCODING_H
250
+ rb_encoding *default_internal_enc = rb_default_internal_encoding();
251
+ if (default_internal_enc) {
252
+ instance->response_header_handler = rb_str_export_to_enc(instance->response_header_handler, default_internal_enc);
253
+ } else {
254
+ instance->response_header_handler = rb_str_export_to_enc(instance->response_header_handler, utf8Encoding);
205
255
  }
206
-
207
- if (!NIL_P(headers)) {
208
- Check_Type(headers, T_HASH);
209
- rb_iterate(rb_each, headers, each_http_header, self);
210
- curl_easy_setopt(instance->handle, CURLOPT_HTTPHEADER, instance->request_headers);
256
+ #endif
257
+ }
258
+ if (instance->request_method != sym_head && NIL_P(instance->response_body_handler)) {
259
+ instance->response_body_handler = rb_str_new2("");
260
+ #ifdef HAVE_RUBY_ENCODING_H
261
+ rb_encoding *default_internal_enc = rb_default_internal_encoding();
262
+ if (default_internal_enc) {
263
+ instance->response_body_handler = rb_str_export_to_enc(instance->response_body_handler, default_internal_enc);
264
+ } else {
265
+ instance->response_body_handler = rb_str_export_to_enc(instance->response_body_handler, utf8Encoding);
211
266
  }
212
-
267
+ #endif
268
+ }
269
+
270
+ if (!NIL_P(headers)) {
271
+ Check_Type(headers, T_HASH);
272
+ rb_iterate(rb_each, headers, each_http_header, self);
273
+ curl_easy_setopt(instance->handle, CURLOPT_HTTPHEADER, instance->request_headers);
274
+ }
275
+
213
276
  // So far so good, lets start setting up our request
214
-
277
+
215
278
  // Set the type of request
216
- if (instance->request_method == sym_head) {
217
- curl_easy_setopt(instance->handle, CURLOPT_NOBODY, 1);
218
- } else if (instance->request_method == sym_get) {
219
- curl_easy_setopt(instance->handle, CURLOPT_HTTPGET, 1);
220
- } else if (instance->request_method == sym_post) {
221
- curl_easy_setopt(instance->handle, CURLOPT_POST, 1);
222
- curl_easy_setopt(instance->handle, CURLOPT_POSTFIELDS, RSTRING_PTR(payload));
223
- curl_easy_setopt(instance->handle, CURLOPT_POSTFIELDSIZE, RSTRING_LEN(payload));
224
-
225
- // (multipart)
226
- // curl_easy_setopt(instance->handle, CURLOPT_HTTPPOST, 1);
227
-
228
- // TODO: get streaming upload working
229
- // curl_easy_setopt(instance->handle, CURLOPT_READFUNCTION, &upload_data_handler);
230
- // curl_easy_setopt(instance->handle, CURLOPT_READDATA, &instance->upload_stream);
231
- // curl_easy_setopt(instance->handle, CURLOPT_INFILESIZE, len);
232
- } else if (instance->request_method == sym_put) {
233
- curl_easy_setopt(instance->handle, CURLOPT_CUSTOMREQUEST, "PUT");
234
- curl_easy_setopt(instance->handle, CURLOPT_POSTFIELDS, RSTRING_PTR(payload));
235
- curl_easy_setopt(instance->handle, CURLOPT_POSTFIELDSIZE, RSTRING_LEN(payload));
236
-
237
- // TODO: get streaming upload working
238
- // curl_easy_setopt(instance->handle, CURLOPT_UPLOAD, 1);
239
- // curl_easy_setopt(instance->handle, CURLOPT_READFUNCTION, &upload_data_handler);
240
- // curl_easy_setopt(instance->handle, CURLOPT_READDATA, &instance->upload_stream);
241
- // curl_easy_setopt(instance->handle, CURLOPT_INFILESIZE, len);
242
- } else if (instance->request_method == sym_delete) {
243
- curl_easy_setopt(instance->handle, CURLOPT_CUSTOMREQUEST, "DELETE");
244
- }
245
-
246
- // Other common options
247
- curl_easy_setopt(instance->handle, CURLOPT_URL, RSTRING_PTR(url));
248
- curl_easy_setopt(instance->handle, CURLOPT_FOLLOWLOCATION, 1);
249
- curl_easy_setopt(instance->handle, CURLOPT_MAXREDIRS, 3);
250
-
251
- // Response header handling
252
- curl_easy_setopt(instance->handle, CURLOPT_HEADERFUNCTION, &header_handler);
253
- curl_easy_setopt(instance->handle, CURLOPT_HEADERDATA, instance->response_header_handler);
254
-
255
- // Response body handling
256
- if (instance->request_method != sym_head) {
257
- curl_easy_setopt(instance->handle, CURLOPT_ENCODING, "identity, deflate, gzip");
258
- curl_easy_setopt(instance->handle, CURLOPT_WRITEFUNCTION, &data_handler);
259
- curl_easy_setopt(instance->handle, CURLOPT_WRITEDATA, instance->response_body_handler);
279
+ if (instance->request_method == sym_head) {
280
+ curl_easy_setopt(instance->handle, CURLOPT_NOBODY, 1);
281
+ } else if (instance->request_method == sym_get) {
282
+ curl_easy_setopt(instance->handle, CURLOPT_HTTPGET, 1);
283
+ } else if (instance->request_method == sym_post) {
284
+ curl_easy_setopt(instance->handle, CURLOPT_POST, 1);
285
+ curl_easy_setopt(instance->handle, CURLOPT_POSTFIELDS, RSTRING_PTR(payload));
286
+ curl_easy_setopt(instance->handle, CURLOPT_POSTFIELDSIZE, RSTRING_LEN(payload));
287
+
288
+ // (multipart)
289
+ // curl_easy_setopt(instance->handle, CURLOPT_HTTPPOST, 1);
290
+
291
+ // TODO: get streaming upload working
292
+ // curl_easy_setopt(instance->handle, CURLOPT_READFUNCTION, &upload_data_handler);
293
+ // curl_easy_setopt(instance->handle, CURLOPT_READDATA, &instance->upload_stream);
294
+ // curl_easy_setopt(instance->handle, CURLOPT_INFILESIZE, len);
295
+ } else if (instance->request_method == sym_put) {
296
+ curl_easy_setopt(instance->handle, CURLOPT_CUSTOMREQUEST, "PUT");
297
+ curl_easy_setopt(instance->handle, CURLOPT_POSTFIELDS, RSTRING_PTR(payload));
298
+ curl_easy_setopt(instance->handle, CURLOPT_POSTFIELDSIZE, RSTRING_LEN(payload));
299
+
300
+ // TODO: get streaming upload working
301
+ // curl_easy_setopt(instance->handle, CURLOPT_UPLOAD, 1);
302
+ // curl_easy_setopt(instance->handle, CURLOPT_READFUNCTION, &upload_data_handler);
303
+ // curl_easy_setopt(instance->handle, CURLOPT_READDATA, &instance->upload_stream);
304
+ // curl_easy_setopt(instance->handle, CURLOPT_INFILESIZE, len);
305
+ } else if (instance->request_method == sym_delete) {
306
+ curl_easy_setopt(instance->handle, CURLOPT_CUSTOMREQUEST, "DELETE");
307
+ }
308
+
309
+ // Other common options
310
+ curl_easy_setopt(instance->handle, CURLOPT_URL, RSTRING_PTR(url));
311
+ curl_easy_setopt(instance->handle, CURLOPT_FOLLOWLOCATION, 1);
312
+ curl_easy_setopt(instance->handle, CURLOPT_MAXREDIRS, 3);
313
+
314
+ // Response header handling
315
+ curl_easy_setopt(instance->handle, CURLOPT_HEADERFUNCTION, &header_handler);
316
+ curl_easy_setopt(instance->handle, CURLOPT_HEADERDATA, instance->response_header_handler);
317
+
318
+ // Response body handling
319
+ if (instance->request_method != sym_head) {
320
+ curl_easy_setopt(instance->handle, CURLOPT_ENCODING, "identity, deflate, gzip");
321
+ curl_easy_setopt(instance->handle, CURLOPT_WRITEFUNCTION, &data_handler);
322
+ curl_easy_setopt(instance->handle, CURLOPT_WRITEDATA, instance->response_body_handler);
323
+ }
324
+
325
+ if (!NIL_P(username) || !NIL_P(password)) {
326
+ credentials = rb_str_new2("");
327
+ if (!NIL_P(username)) {
328
+ rb_str_buf_cat(credentials, RSTRING_PTR(username), RSTRING_LEN(username));
260
329
  }
261
-
262
- curl_easy_setopt(instance, CURLOPT_USERPWD, NULL);
263
- if (!NIL_P(username) || !NIL_P(password)) {
264
- credentials = rb_str_new2("");
265
- if (!NIL_P(username)) {
266
- rb_str_buf_cat(credentials, RSTRING_PTR(username), RSTRING_LEN(username));
267
- }
268
- rb_str_buf_cat(credentials, credential_sep, 1);
269
- if (!NIL_P(password)) {
270
- rb_str_buf_cat(credentials, RSTRING_PTR(password), RSTRING_LEN(password));
271
- }
272
- curl_easy_setopt(instance->handle, CURLOPT_HTTPAUTH, CURLAUTH_BASIC | CURLAUTH_DIGEST);
273
- curl_easy_setopt(instance->handle, CURLOPT_USERPWD, RSTRING_PTR(credentials));
274
- rb_gc_mark(credentials);
330
+ rb_str_buf_cat(credentials, ":", 1);
331
+ if (!NIL_P(password)) {
332
+ rb_str_buf_cat(credentials, RSTRING_PTR(password), RSTRING_LEN(password));
275
333
  }
276
-
277
- curl_easy_setopt(instance->handle, CURLOPT_SSL_VERIFYPEER, 0);
278
- curl_easy_setopt(instance->handle, CURLOPT_SSL_VERIFYHOST, 0);
279
-
280
- curl_easy_setopt(instance->handle, CURLOPT_ERRORBUFFER, instance->error_buffer);
281
-
282
- return self;
334
+ curl_easy_setopt(instance->handle, CURLOPT_HTTPAUTH, CURLAUTH_BASIC | CURLAUTH_DIGEST);
335
+ curl_easy_setopt(instance->handle, CURLOPT_USERPWD, RSTRING_PTR(credentials));
336
+ rb_gc_mark(credentials);
337
+ }
338
+
339
+ curl_easy_setopt(instance->handle, CURLOPT_SSL_VERIFYPEER, 0);
340
+ curl_easy_setopt(instance->handle, CURLOPT_SSL_VERIFYHOST, 0);
341
+
342
+ curl_easy_setopt(instance->handle, CURLOPT_ERRORBUFFER, instance->error_buffer);
343
+
344
+ return self;
345
+ }
346
+
347
+ static VALUE nogvl_perform(void *handle) {
348
+ CURLcode res;
349
+ VALUE status = Qnil;
350
+
351
+ res = curl_easy_perform(handle);
352
+ if (CURLE_OK != res) {
353
+ status = select_error(res);
354
+ }
355
+
356
+ return status;
283
357
  }
284
358
 
285
359
  /*
286
- * Document-method: rb_streamly_execute
287
- *
288
- * call-seq: rb_streamly_execute
289
- */
290
- VALUE rb_streamly_execute(int argc, VALUE * argv, VALUE self) {
291
- CURLcode res;
292
- struct curl_instance * instance;
293
- GetInstance(self, instance);
294
-
295
- // Done setting up, lets do this!
296
- res = curl_easy_perform(instance->handle);
297
- if (CURLE_OK != res) {
298
- rb_raise(select_error(res), instance->error_buffer);
299
- }
300
-
301
- // Cleanup
302
- if (instance->request_headers != NULL) {
303
- curl_slist_free_all(instance->request_headers);
304
- instance->request_headers = NULL;
305
- }
306
- curl_easy_reset(instance->handle);
307
- instance->request_payload_handler = Qnil;
360
+ * Document-method: rb_streamly_execute
361
+ *
362
+ * call-seq: rb_streamly_execute
363
+ */
364
+ static VALUE rb_streamly_execute(RB_STREAMLY_UNUSED int argc, RB_STREAMLY_UNUSED VALUE * argv, VALUE self) {
365
+ VALUE status;
366
+ struct curl_instance * instance;
367
+ GetInstance(self, instance);
308
368
 
309
- if (instance->request_method == sym_head && TYPE(instance->response_header_handler) == T_STRING) {
310
- return instance->response_header_handler;
311
- } else if (TYPE(instance->response_body_handler) == T_STRING) {
312
- return instance->response_body_handler;
313
- } else {
314
- return Qnil;
315
- }
369
+ // Done setting up, lets do this!
370
+ status = rb_thread_blocking_region(nogvl_perform, instance->handle, RUBY_UBF_IO, 0);
371
+ if (!NIL_P(status)) {
372
+ rb_raise(status, "%s", instance->error_buffer);
373
+ }
374
+
375
+ // Cleanup
376
+ if (instance->request_headers != NULL) {
377
+ curl_slist_free_all(instance->request_headers);
378
+ instance->request_headers = NULL;
379
+ }
380
+ curl_easy_reset(instance->handle);
381
+ instance->request_payload_handler = Qnil;
382
+
383
+ if (instance->request_method == sym_head && TYPE(instance->response_header_handler) == T_STRING) {
384
+ return instance->response_header_handler;
385
+ } else if (TYPE(instance->response_body_handler) == T_STRING) {
386
+ return instance->response_body_handler;
387
+ } else {
388
+ return Qnil;
389
+ }
316
390
  }
317
391
 
318
392
  // Ruby Extension initializer
319
393
  void Init_streamly_ext() {
320
- mStreamly = rb_define_module("Streamly");
321
-
322
- cRequest = rb_define_class_under(mStreamly, "Request", rb_cObject);
323
- rb_define_singleton_method(cRequest, "new", rb_streamly_new, -1);
324
- rb_define_method(cRequest, "initialize", rb_streamly_init, -1);
325
- rb_define_method(cRequest, "execute", rb_streamly_execute, -1);
326
-
327
- eStreamlyError = rb_define_class_under(mStreamly, "Error", rb_eStandardError);
328
- eUnsupportedProtocol = rb_define_class_under(mStreamly, "UnsupportedProtocol", rb_eStandardError);
329
- eURLFormatError = rb_define_class_under(mStreamly, "URLFormatError", rb_eStandardError);
330
- eHostResolutionError = rb_define_class_under(mStreamly, "HostResolutionError", rb_eStandardError);
331
- eConnectionFailed = rb_define_class_under(mStreamly, "ConnectionFailed", rb_eStandardError);
332
- ePartialFileError = rb_define_class_under(mStreamly, "PartialFileError", rb_eStandardError);
333
- eTimeoutError = rb_define_class_under(mStreamly, "TimeoutError", rb_eStandardError);
334
- eTooManyRedirects = rb_define_class_under(mStreamly, "TooManyRedirects", rb_eStandardError);
335
-
336
- sym_method = ID2SYM(rb_intern("method"));
337
- sym_url = ID2SYM(rb_intern("url"));
338
- sym_payload = ID2SYM(rb_intern("payload"));
339
- sym_headers = ID2SYM(rb_intern("headers"));
340
- sym_head = ID2SYM(rb_intern("head"));
341
- sym_get = ID2SYM(rb_intern("get"));
342
- sym_post = ID2SYM(rb_intern("post"));
343
- sym_put = ID2SYM(rb_intern("put"));
344
- sym_delete = ID2SYM(rb_intern("delete"));
345
- sym_username = ID2SYM(rb_intern("username"));
346
- sym_password = ID2SYM(rb_intern("password"));
347
- sym_response_header_handler = ID2SYM(rb_intern("response_header_handler"));
348
- sym_response_body_handler = ID2SYM(rb_intern("response_body_handler"));
394
+ mStreamly = rb_define_module("Streamly");
395
+
396
+ cRequest = rb_define_class_under(mStreamly, "Request", rb_cObject);
397
+ rb_define_singleton_method(cRequest, "new", rb_streamly_new, -1);
398
+ rb_define_method(cRequest, "initialize", rb_streamly_init, -1);
399
+ rb_define_method(cRequest, "execute", rb_streamly_execute, -1);
400
+
401
+ eStreamlyError = rb_define_class_under(mStreamly, "Error", rb_eStandardError);
402
+ eUnsupportedProtocol = rb_define_class_under(mStreamly, "UnsupportedProtocol", rb_eStandardError);
403
+ eURLFormatError = rb_define_class_under(mStreamly, "URLFormatError", rb_eStandardError);
404
+ eHostResolutionError = rb_define_class_under(mStreamly, "HostResolutionError", rb_eStandardError);
405
+ eConnectionFailed = rb_define_class_under(mStreamly, "ConnectionFailed", rb_eStandardError);
406
+ ePartialFileError = rb_define_class_under(mStreamly, "PartialFileError", rb_eStandardError);
407
+ eTimeoutError = rb_define_class_under(mStreamly, "TimeoutError", rb_eStandardError);
408
+ eTooManyRedirects = rb_define_class_under(mStreamly, "TooManyRedirects", rb_eStandardError);
409
+
410
+ sym_method = ID2SYM(rb_intern("method"));
411
+ sym_url = ID2SYM(rb_intern("url"));
412
+ sym_payload = ID2SYM(rb_intern("payload"));
413
+ sym_headers = ID2SYM(rb_intern("headers"));
414
+ sym_head = ID2SYM(rb_intern("head"));
415
+ sym_get = ID2SYM(rb_intern("get"));
416
+ sym_post = ID2SYM(rb_intern("post"));
417
+ sym_put = ID2SYM(rb_intern("put"));
418
+ sym_delete = ID2SYM(rb_intern("delete"));
419
+ sym_username = ID2SYM(rb_intern("username"));
420
+ sym_password = ID2SYM(rb_intern("password"));
421
+ sym_response_header_handler = ID2SYM(rb_intern("response_header_handler"));
422
+ sym_response_body_handler = ID2SYM(rb_intern("response_body_handler"));
423
+
424
+ #ifdef HAVE_RUBY_ENCODING_H
425
+ utf8Encoding = rb_utf8_encoding();
426
+ #endif
349
427
  }