rhack 0.2.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (63) hide show
  1. data/.gemtest +0 -0
  2. data/CURB-LICENSE +51 -0
  3. data/Gemfile +4 -0
  4. data/History.txt +4 -0
  5. data/LICENSE +51 -0
  6. data/License.txt +17 -0
  7. data/Manifest.txt +61 -0
  8. data/README.txt +12 -0
  9. data/Rakefile +34 -0
  10. data/ext/curb-original/curb.c +977 -0
  11. data/ext/curb-original/curb.h +52 -0
  12. data/ext/curb-original/curb_config.h +235 -0
  13. data/ext/curb-original/curb_easy.c +3455 -0
  14. data/ext/curb-original/curb_easy.h +90 -0
  15. data/ext/curb-original/curb_errors.c +647 -0
  16. data/ext/curb-original/curb_errors.h +129 -0
  17. data/ext/curb-original/curb_macros.h +159 -0
  18. data/ext/curb-original/curb_multi.c +704 -0
  19. data/ext/curb-original/curb_multi.h +26 -0
  20. data/ext/curb-original/curb_postfield.c +523 -0
  21. data/ext/curb-original/curb_postfield.h +40 -0
  22. data/ext/curb-original/curb_upload.c +80 -0
  23. data/ext/curb-original/curb_upload.h +30 -0
  24. data/ext/curb/Makefile +157 -0
  25. data/ext/curb/curb.c +977 -0
  26. data/ext/curb/curb.h +52 -0
  27. data/ext/curb/curb_config.h +235 -0
  28. data/ext/curb/curb_easy.c +3430 -0
  29. data/ext/curb/curb_easy.h +94 -0
  30. data/ext/curb/curb_errors.c +647 -0
  31. data/ext/curb/curb_errors.h +129 -0
  32. data/ext/curb/curb_macros.h +159 -0
  33. data/ext/curb/curb_multi.c +710 -0
  34. data/ext/curb/curb_multi.h +26 -0
  35. data/ext/curb/curb_postfield.c +523 -0
  36. data/ext/curb/curb_postfield.h +40 -0
  37. data/ext/curb/curb_upload.c +80 -0
  38. data/ext/curb/curb_upload.h +30 -0
  39. data/ext/curb/extconf.rb +399 -0
  40. data/lib/cache.rb +44 -0
  41. data/lib/curl-global.rb +151 -0
  42. data/lib/extensions/browser/env.js +697 -0
  43. data/lib/extensions/browser/jquery.js +7180 -0
  44. data/lib/extensions/browser/xmlsax.js +1564 -0
  45. data/lib/extensions/browser/xmlw3cdom_1.js +1444 -0
  46. data/lib/extensions/browser/xmlw3cdom_2.js +2744 -0
  47. data/lib/extensions/curb.rb +125 -0
  48. data/lib/extensions/declarative.rb +153 -0
  49. data/lib/extensions/johnson.rb +63 -0
  50. data/lib/frame.rb +766 -0
  51. data/lib/init.rb +36 -0
  52. data/lib/rhack.rb +16 -0
  53. data/lib/rhack.yml.template +19 -0
  54. data/lib/rhack/proxy/checker.rb +226 -0
  55. data/lib/rhack/proxy/list.rb +196 -0
  56. data/lib/rhack/services.rb +445 -0
  57. data/lib/rhack_in.rb +2 -0
  58. data/lib/scout.rb +591 -0
  59. data/lib/words.rb +37 -0
  60. data/test/test_frame.rb +107 -0
  61. data/test/test_rhack.rb +5 -0
  62. data/test/test_scout.rb +53 -0
  63. metadata +195 -0
@@ -0,0 +1,129 @@
1
+ /* curb_errors.h - Ruby exception types for curl errors
2
+ * Copyright (c)2006 Ross Bamford.
3
+ * Licensed under the Ruby License. See LICENSE for details.
4
+ *
5
+ * $Id: curb_errors.h 4 2006-11-17 18:35:31Z roscopeco $
6
+ */
7
+ #ifndef __CURB_ERRORS_H
8
+ #define __CURB_ERRORS_H
9
+
10
+ #include "curb.h"
11
+
12
+ /* base errors */
13
+ extern VALUE cCurlErr;
14
+
15
+ /* easy errors */
16
+ extern VALUE mCurlErr;
17
+ extern VALUE eCurlErrError;
18
+ extern VALUE eCurlErrFTPError;
19
+ extern VALUE eCurlErrHTTPError;
20
+ extern VALUE eCurlErrFileError;
21
+ extern VALUE eCurlErrLDAPError;
22
+ extern VALUE eCurlErrTelnetError;
23
+ extern VALUE eCurlErrTFTPError;
24
+
25
+ /* libcurl errors */
26
+ extern VALUE eCurlErrUnsupportedProtocol;
27
+ extern VALUE eCurlErrFailedInit;
28
+ extern VALUE eCurlErrMalformedURL;
29
+ extern VALUE eCurlErrMalformedURLUser;
30
+ extern VALUE eCurlErrProxyResolution;
31
+ extern VALUE eCurlErrHostResolution;
32
+ extern VALUE eCurlErrConnectFailed;
33
+ extern VALUE eCurlErrFTPWierdReply;
34
+ extern VALUE eCurlErrFTPAccessDenied;
35
+ extern VALUE eCurlErrFTPBadPassword;
36
+ extern VALUE eCurlErrFTPWierdPassReply;
37
+ extern VALUE eCurlErrFTPWierdUserReply;
38
+ extern VALUE eCurlErrFTPWierdPasvReply;
39
+ extern VALUE eCurlErrFTPWierd227Format;
40
+ extern VALUE eCurlErrFTPCantGetHost;
41
+ extern VALUE eCurlErrFTPCantReconnect;
42
+ extern VALUE eCurlErrFTPCouldntSetBinary;
43
+ extern VALUE eCurlErrPartialFile;
44
+ extern VALUE eCurlErrFTPCouldntRetrFile;
45
+ extern VALUE eCurlErrFTPWrite;
46
+ extern VALUE eCurlErrFTPQuote;
47
+ extern VALUE eCurlErrHTTPFailed;
48
+ extern VALUE eCurlErrWriteError;
49
+ extern VALUE eCurlErrMalformedUser;
50
+ extern VALUE eCurlErrFTPCouldntStorFile;
51
+ extern VALUE eCurlErrReadError;
52
+ extern VALUE eCurlErrOutOfMemory;
53
+ extern VALUE eCurlErrTimeout;
54
+ extern VALUE eCurlErrFTPCouldntSetASCII;
55
+ extern VALUE eCurlErrFTPPortFailed;
56
+ extern VALUE eCurlErrFTPCouldntUseRest;
57
+ extern VALUE eCurlErrFTPCouldntGetSize;
58
+ extern VALUE eCurlErrHTTPRange;
59
+ extern VALUE eCurlErrHTTPPost;
60
+ extern VALUE eCurlErrSSLConnectError;
61
+ extern VALUE eCurlErrBadResume;
62
+ extern VALUE eCurlErrFileCouldntRead;
63
+ extern VALUE eCurlErrLDAPCouldntBind;
64
+ extern VALUE eCurlErrLDAPSearchFailed;
65
+ extern VALUE eCurlErrLibraryNotFound;
66
+ extern VALUE eCurlErrFunctionNotFound;
67
+ extern VALUE eCurlErrAbortedByCallback;
68
+ extern VALUE eCurlErrBadFunctionArgument;
69
+ extern VALUE eCurlErrBadCallingOrder;
70
+ extern VALUE eCurlErrInterfaceFailed;
71
+ extern VALUE eCurlErrBadPasswordEntered;
72
+ extern VALUE eCurlErrTooManyRedirects;
73
+ extern VALUE eCurlErrTelnetUnknownOption;
74
+ extern VALUE eCurlErrTelnetBadOptionSyntax;
75
+ extern VALUE eCurlErrObsolete;
76
+ extern VALUE eCurlErrSSLPeerCertificate;
77
+ extern VALUE eCurlErrGotNothing;
78
+ extern VALUE eCurlErrSSLEngineNotFound;
79
+ extern VALUE eCurlErrSSLEngineSetFailed;
80
+ extern VALUE eCurlErrSendError;
81
+ extern VALUE eCurlErrRecvError;
82
+ extern VALUE eCurlErrShareInUse;
83
+ extern VALUE eCurlErrSSLCertificate;
84
+ extern VALUE eCurlErrSSLCipher;
85
+ extern VALUE eCurlErrSSLCACertificate;
86
+ extern VALUE eCurlErrBadContentEncoding;
87
+ extern VALUE eCurlErrLDAPInvalidURL;
88
+ extern VALUE eCurlErrFileSizeExceeded;
89
+ extern VALUE eCurlErrFTPSSLFailed;
90
+ extern VALUE eCurlErrSendFailedRewind;
91
+ extern VALUE eCurlErrSSLEngineInitFailed;
92
+ extern VALUE eCurlErrLoginDenied;
93
+ extern VALUE eCurlErrTFTPNotFound;
94
+ extern VALUE eCurlErrTFTPPermission;
95
+ extern VALUE eCurlErrTFTPDiskFull;
96
+ extern VALUE eCurlErrTFTPIllegalOperation;
97
+ extern VALUE eCurlErrTFTPUnknownID;
98
+ extern VALUE eCurlErrTFTPFileExists;
99
+ extern VALUE eCurlErrTFTPNoSuchUser;
100
+ extern VALUE eCurlErrConvFailed;
101
+ extern VALUE eCurlErrConvReqd;
102
+ extern VALUE eCurlErrSSLCacertBadfile;
103
+ extern VALUE eCurlErrRemoteFileNotFound;
104
+ extern VALUE eCurlErrSSH;
105
+ extern VALUE eCurlErrSSLShutdownFailed;
106
+ extern VALUE eCurlErrAgain;
107
+ extern VALUE eCurlErrSSLCRLBadfile;
108
+ extern VALUE eCurlErrSSLIssuerError;
109
+
110
+ /* multi errors */
111
+ extern VALUE mCurlErrFailedInit;
112
+ extern VALUE mCurlErrCallMultiPerform;
113
+ extern VALUE mCurlErrBadHandle;
114
+ extern VALUE mCurlErrBadEasyHandle;
115
+ extern VALUE mCurlErrOutOfMemory;
116
+ extern VALUE mCurlErrInternalError;
117
+ extern VALUE mCurlErrBadSocket;
118
+ extern VALUE mCurlErrUnknownOption;
119
+
120
+ /* binding errors */
121
+ extern VALUE eCurlErrInvalidPostField;
122
+
123
+ void init_curb_errors();
124
+ void raise_curl_easy_error_exception(CURLcode code);
125
+ void raise_curl_multi_error_exception(CURLMcode code);
126
+ VALUE rb_curl_easy_error(CURLcode code);
127
+ VALUE rb_curl_multi_error(CURLMcode code);
128
+
129
+ #endif
@@ -0,0 +1,159 @@
1
+ /* Curb - helper macros for ruby integration
2
+ * Copyright (c)2006 Ross Bamford.
3
+ * Licensed under the Ruby License. See LICENSE for details.
4
+ *
5
+ * $Id: curb_macros.h 13 2006-11-23 23:54:25Z roscopeco $
6
+ */
7
+
8
+ #ifndef __CURB_MACROS_H
9
+ #define __CURB_MACROS_H
10
+
11
+ #define rb_easy_sym(sym) ID2SYM(rb_intern(sym))
12
+ #define rb_easy_hkey(key) ID2SYM(rb_intern(key))
13
+ #define rb_easy_set(key,val) rb_hash_aset(rbce->opts, rb_easy_hkey(key) , val)
14
+ #define rb_easy_get(key) rb_hash_aref(rbce->opts, rb_easy_hkey(key))
15
+ #define rb_easy_del(key) rb_hash_delete(rbce->opts, rb_easy_hkey(key))
16
+ #define rb_easy_nil(key) (rb_hash_aref(rbce->opts, rb_easy_hkey(key)) == Qnil)
17
+ #define rb_easy_type_check(key,type) (rb_type(rb_hash_aref(rbce->opts, rb_easy_hkey(key))) == type)
18
+
19
+ // TODO: rb_sym_to_s may not be defined?
20
+ #define rb_easy_get_str(key) \
21
+ RSTRING_PTR((rb_easy_type_check(key,T_STRING) ? rb_easy_get(key) : rb_str_to_str(rb_easy_get(key))))
22
+
23
+ /* getter/setter macros for various things */
24
+ /* setter for anything that stores a ruby VALUE in the struct */
25
+ #define CURB_OBJECT_SETTER(type, attr) \
26
+ type *ptr; \
27
+ \
28
+ Data_Get_Struct(self, type, ptr); \
29
+ ptr->attr = attr; \
30
+ \
31
+ return attr;
32
+
33
+ /* getter for anything that stores a ruby VALUE */
34
+ #define CURB_OBJECT_GETTER(type, attr) \
35
+ type *ptr; \
36
+ \
37
+ Data_Get_Struct(self, type, ptr); \
38
+ return ptr->attr;
39
+
40
+ /* setter for anything that stores a ruby VALUE in the struct opts hash */
41
+ #define CURB_OBJECT_HSETTER(type, attr) \
42
+ type *ptr; \
43
+ \
44
+ Data_Get_Struct(self, type, ptr); \
45
+ rb_hash_aset(ptr->opts, rb_easy_hkey(#attr), attr); \
46
+ \
47
+ return attr;
48
+
49
+ /* getter for anything that stores a ruby VALUE in the struct opts hash */
50
+ #define CURB_OBJECT_HGETTER(type, attr) \
51
+ type *ptr; \
52
+ \
53
+ Data_Get_Struct(self, type, ptr); \
54
+ return rb_hash_aref(ptr->opts, rb_easy_hkey(#attr));
55
+
56
+ /* setter for bool flags */
57
+ #define CURB_BOOLEAN_SETTER(type, attr) \
58
+ type *ptr; \
59
+ Data_Get_Struct(self, type, ptr); \
60
+ \
61
+ if (attr == Qnil || attr == Qfalse) { \
62
+ ptr->attr = 0; \
63
+ } else { \
64
+ ptr->attr = 1; \
65
+ } \
66
+ \
67
+ return attr;
68
+
69
+ /* getter for bool flags */
70
+ #define CURB_BOOLEAN_GETTER(type, attr) \
71
+ type *ptr; \
72
+ Data_Get_Struct(self, type, ptr); \
73
+ \
74
+ return((ptr->attr) ? Qtrue : Qfalse);
75
+
76
+ /* special setter for on_event handlers that take a block */
77
+ #define CURB_HANDLER_PROC_SETTER(type, handler) \
78
+ type *ptr; \
79
+ VALUE oldproc; \
80
+ \
81
+ Data_Get_Struct(self, type, ptr); \
82
+ \
83
+ oldproc = ptr->handler; \
84
+ rb_scan_args(argc, argv, "0&", &ptr->handler); \
85
+ \
86
+ return oldproc; \
87
+
88
+ /* special setter for on_event handlers that take a block, same as above but stores int he opts hash */
89
+ #define CURB_HANDLER_PROC_HSETTER(type, handler) \
90
+ type *ptr; \
91
+ VALUE oldproc, newproc; \
92
+ \
93
+ Data_Get_Struct(self, type, ptr); \
94
+ \
95
+ oldproc = rb_hash_aref(ptr->opts, rb_easy_hkey(#handler)); \
96
+ rb_scan_args(argc, argv, "0&", &newproc); \
97
+ \
98
+ rb_hash_aset(ptr->opts, rb_easy_hkey(#handler), newproc); \
99
+ \
100
+ return oldproc;
101
+
102
+ /* setter for numerics that are kept in c ints */
103
+ #define CURB_IMMED_SETTER(type, attr, nilval) \
104
+ type *ptr; \
105
+ \
106
+ Data_Get_Struct(self, type, ptr); \
107
+ if (attr == Qnil) { \
108
+ ptr->attr = nilval; \
109
+ } else { \
110
+ ptr->attr = NUM2INT(attr); \
111
+ } \
112
+ \
113
+ return attr; \
114
+
115
+ /* setter for numerics that are kept in c ints */
116
+ #define CURB_IMMED_GETTER(type, attr, nilval) \
117
+ type *ptr; \
118
+ \
119
+ Data_Get_Struct(self, type, ptr); \
120
+ if (ptr->attr == nilval) { \
121
+ return Qnil; \
122
+ } else { \
123
+ return INT2NUM(ptr->attr); \
124
+ }
125
+
126
+ /* special setter for port / port ranges */
127
+ #define CURB_IMMED_PORT_SETTER(type, attr, msg) \
128
+ type *ptr; \
129
+ \
130
+ Data_Get_Struct(self, type, ptr); \
131
+ if (attr == Qnil) { \
132
+ ptr->attr = 0; \
133
+ } else { \
134
+ int port = FIX2INT(attr); \
135
+ \
136
+ if ((port) && ((port & 0xFFFF) == port)) { \
137
+ ptr->attr = port; \
138
+ } else { \
139
+ rb_raise(rb_eArgError, "Invalid " msg " %d (expected between 1 and 65535)", port); \
140
+ } \
141
+ } \
142
+ \
143
+ return attr; \
144
+
145
+ /* special getter for port / port ranges */
146
+ #define CURB_IMMED_PORT_GETTER(type, attr) \
147
+ type *ptr; \
148
+ \
149
+ Data_Get_Struct(self, type, ptr); \
150
+ if (ptr->attr == 0) { \
151
+ return Qnil; \
152
+ } else { \
153
+ return INT2FIX(ptr->attr); \
154
+ }
155
+
156
+ #define CURB_DEFINE(name) \
157
+ rb_define_const(mCurl, #name, INT2FIX(name))
158
+
159
+ #endif
@@ -0,0 +1,710 @@
1
+ /* curb_multi.c - Curl multi mode
2
+ * Copyright (c)2008 Todd A. Fisher.
3
+ * Licensed under the Ruby License. See LICENSE for details.
4
+ *
5
+ */
6
+ #include "curb_config.h"
7
+ #ifdef HAVE_RUBY19_ST_H
8
+ #include <ruby.h>
9
+ #include <ruby/st.h>
10
+ #else
11
+ #include <ruby.h>
12
+ #include <st.h>
13
+ #endif
14
+ #include "curb_easy.h"
15
+ #include "curb_errors.h"
16
+ #include "curb_postfield.h"
17
+ #include "curb_multi.h"
18
+
19
+ #include <errno.h>
20
+
21
+ #ifdef _WIN32
22
+ // for O_RDWR and O_BINARY
23
+ #include <fcntl.h>
24
+ #endif
25
+
26
+ extern VALUE mCurl;
27
+ static VALUE idCall;
28
+
29
+ #ifdef RDOC_NEVER_DEFINED
30
+ mCurl = rb_define_module("Curl");
31
+ #endif
32
+
33
+ VALUE cCurlMulti;
34
+ static void ruby_log(const char* str)
35
+ {
36
+ rb_funcall(rb_const_get(mCurl, rb_intern("L")), rb_intern("debug"), 1, rb_str_new2(str));
37
+ }
38
+ static void ruby_log_obj(VALUE obj)
39
+ {
40
+ rb_funcall(rb_const_get(mCurl, rb_intern("L")), rb_intern("debug"), 1, obj);
41
+ }
42
+
43
+ static long cCurlMutiDefaulttimeout = 100; /* milliseconds */
44
+
45
+ static void rb_curl_multi_remove(ruby_curl_multi *rbcm, VALUE easy);
46
+ static void rb_curl_multi_read_info(VALUE self, CURLM *mptr);
47
+ static void rb_curl_multi_run(VALUE self, CURLM *multi_handle, int *still_running);
48
+
49
+ static VALUE callback_exception(VALUE unused) {
50
+ return Qfalse;
51
+ }
52
+
53
+ static void curl_multi_mark(ruby_curl_multi *rbcm) {
54
+ rb_gc_mark(rbcm->requests);
55
+ }
56
+
57
+ static void curl_multi_flush_easy(VALUE key, VALUE easy, ruby_curl_multi *rbcm) {
58
+ CURLMcode result;
59
+ ruby_curl_easy *rbce;
60
+
61
+ Data_Get_Struct(easy, ruby_curl_easy, rbce);
62
+ result = curl_multi_remove_handle(rbcm->handle, rbce->curl);
63
+ if (result != 0) {
64
+ raise_curl_multi_error_exception(result);
65
+ }
66
+ }
67
+
68
+ static int
69
+ rb_hash_clear_i(VALUE key, VALUE value, VALUE dummy) {
70
+ return ST_DELETE;
71
+ }
72
+
73
+ static void curl_multi_free(ruby_curl_multi *rbcm) {
74
+
75
+ if (rbcm && !rbcm->requests == Qnil && rb_type(rbcm->requests) == T_HASH && RHASH_LEN(rbcm->requests) > 0) {
76
+
77
+ rb_hash_foreach( rbcm->requests, (int (*)())curl_multi_flush_easy, (VALUE)rbcm );
78
+
79
+ rb_hash_foreach(rbcm->requests, rb_hash_clear_i, 0); //rb_hash_clear(rbcm->requests);
80
+ rbcm->requests = Qnil;
81
+ }
82
+ curl_multi_cleanup(rbcm->handle);
83
+ free(rbcm);
84
+ }
85
+
86
+ /*
87
+ * call-seq:
88
+ * Curl::Multi.new => #&lt;Curl::Easy...&gt;
89
+ *
90
+ * Create a new Curl::Multi instance
91
+ */
92
+ VALUE ruby_curl_multi_new(VALUE klass) {
93
+ VALUE new_curlm;
94
+
95
+ ruby_curl_multi *rbcm = ALLOC(ruby_curl_multi);
96
+
97
+ rbcm->handle = curl_multi_init();
98
+ if (!rbcm->handle) {
99
+ rb_raise(mCurlErrFailedInit, "Failed to initialize multi handle");
100
+ }
101
+
102
+ rbcm->requests = rb_hash_new();
103
+
104
+ rbcm->active = 0;
105
+ rbcm->running = 0;
106
+
107
+ new_curlm = Data_Wrap_Struct(klass, curl_multi_mark, curl_multi_free, rbcm);
108
+
109
+ return new_curlm;
110
+ }
111
+
112
+ /*
113
+ * call-seq:
114
+ * Curl::Multi.default_timeout = 4 => 4
115
+ *
116
+ * Set the global default time out for all Curl::Multi Handles. This value is used
117
+ * when libcurl cannot determine a timeout value when calling curl_multi_timeout.
118
+ *
119
+ */
120
+ VALUE ruby_curl_multi_set_default_timeout(VALUE klass, VALUE timeout) {
121
+ cCurlMutiDefaulttimeout = FIX2LONG(timeout);
122
+ return timeout;
123
+ }
124
+
125
+ /*
126
+ * call-seq:
127
+ * Curl::Multi.default_timeout = 4 => 4
128
+ *
129
+ * Get the global default time out for all Curl::Multi Handles.
130
+ *
131
+ */
132
+ VALUE ruby_curl_multi_get_default_timeout(VALUE klass) {
133
+ return INT2FIX(cCurlMutiDefaulttimeout);
134
+ }
135
+
136
+ /* Hash#foreach callback for ruby_curl_multi_requests */
137
+ static int ruby_curl_multi_requests_callback(VALUE key, VALUE value, VALUE result_array) {
138
+ rb_ary_push(result_array, value);
139
+
140
+ return ST_CONTINUE;
141
+ }
142
+
143
+ /*
144
+ * call-seq:
145
+ * multi.requests => [#&lt;Curl::Easy...&gt;, ...]
146
+ *
147
+ * Returns an array containing all the active requests on this Curl::Multi object.
148
+ */
149
+ static VALUE ruby_curl_multi_requests(VALUE self) {
150
+ ruby_curl_multi *rbcm;
151
+ VALUE result_array;
152
+
153
+ Data_Get_Struct(self, ruby_curl_multi, rbcm);
154
+
155
+ result_array = rb_ary_new();
156
+
157
+ /* iterate over the requests hash, and stuff references into the array. */
158
+ rb_hash_foreach(rbcm->requests, ruby_curl_multi_requests_callback, result_array);
159
+
160
+ return result_array;
161
+ }
162
+ static VALUE ruby_curl_multi_running(VALUE self) {
163
+ ruby_curl_multi *rbcm;
164
+
165
+ Data_Get_Struct(self, ruby_curl_multi, rbcm);
166
+
167
+ return INT2FIX(rbcm->running);
168
+ }
169
+
170
+ /*
171
+ * call-seq:
172
+ * multi.idle? => true or false
173
+ *
174
+ * Returns whether or not this Curl::Multi handle is processing any requests. E.g. this returns
175
+ * true when multi.requests.length == 0.
176
+ */
177
+ static VALUE ruby_curl_multi_idle(VALUE self) {
178
+ ruby_curl_multi *rbcm;
179
+
180
+ Data_Get_Struct(self, ruby_curl_multi, rbcm);
181
+
182
+ if ( FIX2INT( rb_funcall(rbcm->requests, rb_intern("length"), 0) ) == 0 ) {
183
+ return Qtrue;
184
+ } else {
185
+ return Qfalse;
186
+ }
187
+ }
188
+
189
+ /*
190
+ * call-seq:
191
+ * multi = Curl::Multi.new
192
+ * multi.max_connects = 800
193
+ *
194
+ * Set the max connections in the cache for a multi handle
195
+ */
196
+ static VALUE ruby_curl_multi_max_connects(VALUE self, VALUE count) {
197
+ #ifdef HAVE_CURLMOPT_MAXCONNECTS
198
+ ruby_curl_multi *rbcm;
199
+
200
+ Data_Get_Struct(self, ruby_curl_multi, rbcm);
201
+ curl_multi_setopt(rbcm->handle, CURLMOPT_MAXCONNECTS, NUM2INT(count));
202
+ #endif
203
+
204
+ return count;
205
+ }
206
+
207
+ /*
208
+ * call-seq:
209
+ * multi = Curl::Multi.new
210
+ * multi.pipeline = true
211
+ *
212
+ * Pass a long set to 1 to enable or 0 to disable. Enabling pipelining on a multi handle will make it
213
+ * attempt to perform HTTP Pipelining as far as possible for transfers using this handle. This means
214
+ * that if you add a second request that can use an already existing connection, the second request will
215
+ * be "piped" on the same connection rather than being executed in parallel. (Added in 7.16.0)
216
+ *
217
+ */
218
+ static VALUE ruby_curl_multi_pipeline(VALUE self, VALUE onoff) {
219
+ #ifdef HAVE_CURLMOPT_PIPELINING
220
+ ruby_curl_multi *rbcm;
221
+
222
+ Data_Get_Struct(self, ruby_curl_multi, rbcm);
223
+ curl_multi_setopt(rbcm->handle, CURLMOPT_PIPELINING, onoff == Qtrue ? 1 : 0);
224
+ #endif
225
+ return onoff;
226
+ }
227
+
228
+ /*
229
+ * call-seq:
230
+ * multi = Curl::Multi.new
231
+ * easy = Curl::Easy.new('url')
232
+ *
233
+ * multi.add(easy)
234
+ *
235
+ * Add an easy handle to the multi stack
236
+ */
237
+ VALUE ruby_curl_multi_add(VALUE self, VALUE easy) {
238
+ CURLMcode mcode;
239
+ ruby_curl_easy *rbce;
240
+ ruby_curl_multi *rbcm;
241
+
242
+ Data_Get_Struct(self, ruby_curl_multi, rbcm);
243
+ Data_Get_Struct(easy, ruby_curl_easy, rbce);
244
+
245
+ /* setup the easy handle */
246
+ ruby_curl_easy_setup( rbce );
247
+
248
+ mcode = curl_multi_add_handle(rbcm->handle, rbce->curl);
249
+ if (mcode != CURLM_CALL_MULTI_PERFORM && mcode != CURLM_OK) {
250
+ raise_curl_multi_error_exception(mcode);
251
+ }
252
+
253
+ rbcm->active++;
254
+
255
+ /* Increase the running count, so that the perform loop keeps running.
256
+ * If this number is not correct, the next call to curl_multi_perform will correct it. */
257
+ rbcm->running++;
258
+
259
+ rb_hash_aset( rbcm->requests, easy, easy );
260
+
261
+ return self;
262
+ }
263
+
264
+ /*
265
+ * call-seq:
266
+ * multi = Curl::Multi.new
267
+ * easy = Curl::Easy.new('url')
268
+ *
269
+ * multi.add(easy)
270
+ *
271
+ * # sometime later
272
+ * multi.remove(easy)
273
+ *
274
+ * Remove an easy handle from a multi stack.
275
+ *
276
+ * Will raise an exception if the easy handle is not found
277
+ */
278
+ VALUE ruby_curl_multi_remove(VALUE self, VALUE easy) {
279
+ ruby_curl_multi *rbcm;
280
+ ruby_curl_easy *rbce;
281
+
282
+ Data_Get_Struct(self, ruby_curl_multi, rbcm);
283
+
284
+ Data_Get_Struct(easy, ruby_curl_easy, rbce);
285
+
286
+ rb_curl_multi_remove(rbcm,easy);
287
+
288
+ return self;
289
+ }
290
+ static void rb_curl_multi_remove(ruby_curl_multi *rbcm, VALUE easy) {
291
+ CURLMcode result;
292
+ ruby_curl_easy *rbce;
293
+ VALUE r;
294
+
295
+ Data_Get_Struct(easy, ruby_curl_easy, rbce);
296
+
297
+ result = curl_multi_remove_handle(rbcm->handle, rbce->curl);
298
+ if (result != 0) {
299
+ raise_curl_multi_error_exception(result);
300
+ }
301
+
302
+ rbcm->active--;
303
+
304
+ ruby_curl_easy_cleanup( easy, rbce );
305
+
306
+ // active should equal INT2FIX(RHASH(rbcm->requests)->tbl->num_entries)
307
+ r = rb_hash_delete( rbcm->requests, easy );
308
+ if( r != easy || r == Qnil ) {
309
+ rb_warn("Possibly lost track of Curl::Easy VALUE, it may not be reclaimed by GC");
310
+ }
311
+ }
312
+
313
+ /* Hash#foreach callback for ruby_curl_multi_cancel */
314
+ static int ruby_curl_multi_cancel_callback(VALUE key, VALUE value, ruby_curl_multi *rbcm) {
315
+ rb_curl_multi_remove(rbcm, value);
316
+
317
+ return ST_CONTINUE;
318
+ }
319
+
320
+ /*
321
+ * call-seq:
322
+ * multi.cancel!
323
+ *
324
+ * Cancels all requests currently being made on this Curl::Multi handle.
325
+ */
326
+ static VALUE ruby_curl_multi_cancel(VALUE self) {
327
+ ruby_curl_multi *rbcm;
328
+
329
+ Data_Get_Struct(self, ruby_curl_multi, rbcm);
330
+
331
+ rb_hash_foreach( rbcm->requests, ruby_curl_multi_cancel_callback, (VALUE)rbcm );
332
+
333
+ /* for chaining */
334
+ return self;
335
+ }
336
+
337
+ // on_success, on_failure, on_complete
338
+ static VALUE call_status_handler1(VALUE ary) {
339
+ return rb_funcall(rb_ary_entry(ary, 0), idCall, 1, rb_ary_entry(ary, 1));
340
+ }
341
+ static VALUE call_status_handler2(VALUE ary) {
342
+ return rb_funcall(rb_ary_entry(ary, 0), idCall, 2, rb_ary_entry(ary, 1), rb_ary_entry(ary, 2));
343
+ }
344
+
345
+ static void rb_curl_mutli_handle_complete(VALUE self, CURL *easy_handle, int result) {
346
+ long response_code = -1;
347
+ VALUE easy;
348
+ ruby_curl_easy *rbce = NULL;
349
+ VALUE callargs, val = Qtrue;
350
+
351
+ CURLcode ecode = curl_easy_getinfo(easy_handle, CURLINFO_PRIVATE, (char**)&easy);
352
+
353
+ Data_Get_Struct(easy, ruby_curl_easy, rbce);
354
+
355
+ rbce->last_result = result; /* save the last easy result code */
356
+
357
+ ruby_curl_multi_remove( self, easy );
358
+
359
+ /* after running a request cleanup the headers, these are set before each request */
360
+ if (rbce->curl_headers) {
361
+ curl_slist_free_all(rbce->curl_headers);
362
+ rbce->curl_headers = NULL;
363
+ }
364
+
365
+ if (ecode != 0) {
366
+ raise_curl_easy_error_exception(ecode);
367
+ }
368
+
369
+ curl_easy_getinfo(rbce->curl, CURLINFO_RESPONSE_CODE, &response_code);
370
+
371
+ if (result != 0) {
372
+ if (!rb_easy_nil("failure_proc")) {
373
+ rbce->callback_active = 1;
374
+ //callargs = rb_ary_new3(3, rb_easy_get("failure_proc"), easy, rb_curl_easy_error(result));
375
+ //val = rb_rescue(call_status_handler2, callargs, callback_exception, Qnil);
376
+ rb_funcall( rb_easy_get("failure_proc"), idCall, 2, easy, rb_curl_easy_error(result) );
377
+ rbce->callback_active = 0;
378
+ }
379
+ }
380
+ else if (!rb_easy_nil("success_proc") &&
381
+ ((response_code >= 200 && response_code < 300) || response_code == 0)) {
382
+ /* NOTE: we allow response_code == 0, in the case of non http requests e.g. reading from disk */
383
+ rbce->callback_active = 1;
384
+ //callargs = rb_ary_new3(2, rb_easy_get("success_proc"), easy);
385
+ //val = rb_rescue(call_status_handler1, callargs, callback_exception, Qnil);
386
+ rb_funcall( rb_easy_get("success_proc"), idCall, 1, easy );
387
+ rbce->callback_active = 0;
388
+ }
389
+ else if (!rb_easy_nil("redirect_proc") &&
390
+ (response_code >= 300 && response_code < 400)) {
391
+ rbce->callback_active = 1;
392
+ callargs = rb_ary_new3(3, rb_easy_get("redirect_proc"), easy, rb_curl_easy_error(result));
393
+ rbce->callback_active = 0;
394
+ val = rb_rescue(call_status_handler2, callargs, callback_exception, Qnil);
395
+ }
396
+ else if (!rb_easy_nil("missing_proc") &&
397
+ (response_code >= 400 && response_code < 500)) {
398
+ rbce->callback_active = 1;
399
+ callargs = rb_ary_new3(3, rb_easy_get("missing_proc"), easy, rb_curl_easy_error(result));
400
+ rbce->callback_active = 0;
401
+ val = rb_rescue(call_status_handler2, callargs, callback_exception, Qnil);
402
+ }/*
403
+ else if (!rb_easy_nil("failure_proc") &&
404
+ (response_code >= 500 && response_code <= 999)) {
405
+ callargs = rb_ary_new3(3, rb_easy_get("failure_proc"), easy, rb_curl_easy_error(result));
406
+ rbce->callback_active = 1;
407
+ val = rb_rescue(call_status_handler2, callargs, callback_exception, Qnil);
408
+ rbce->callback_active = 0;
409
+ //rb_funcall( rb_easy_get("failure_proc"), idCall, 2, easy, rb_curl_easy_error(result) );
410
+ }*/
411
+
412
+ if (!rb_easy_nil("complete_proc") || !rb_easy_nil("callback")) {
413
+ rbce->callback_active = 1;
414
+ if (!rb_easy_nil("complete_proc")) {
415
+ //callargs = rb_ary_new3(2, rb_easy_get("complete_proc"), easy);
416
+ //val = rb_rescue(call_status_handler1, callargs, callback_exception, Qnil);
417
+ rb_funcall( rb_easy_get("complete_proc"), idCall, 1, easy );
418
+ }
419
+ if (!rb_easy_nil("callback")) {
420
+ //callargs = rb_ary_new3(2, rb_easy_get("callback"), easy);
421
+ //val = rb_rescue(call_status_handler1, callargs, callback_exception, Qnil);
422
+ rb_funcall( rb_easy_get("callback"), idCall, 1, easy );
423
+ }
424
+ rbce->callback_active = 0;
425
+ }
426
+
427
+ if (val == Qfalse) {
428
+ rb_warn("uncaught exception from callback");
429
+ // exception was raised?
430
+ //fprintf(stderr, "exception raised from callback\n");
431
+ }
432
+
433
+ }
434
+
435
+ static void rb_curl_multi_read_info(VALUE self, CURLM *multi_handle) {
436
+ int msgs_left, result;
437
+ CURLMsg *msg;
438
+ CURL *easy_handle;
439
+
440
+ /* check for finished easy handles and remove from the multi handle */
441
+ while ((msg = curl_multi_info_read(multi_handle, &msgs_left))) {
442
+ if (msg->msg == CURLMSG_DONE) {
443
+ easy_handle = msg->easy_handle;
444
+ result = msg->data.result;
445
+ if (easy_handle) {
446
+ rb_curl_mutli_handle_complete(self, easy_handle, result);
447
+ }
448
+ }
449
+ }
450
+ }
451
+
452
+ /* called within ruby_curl_multi_perform */
453
+ static void rb_curl_multi_run(VALUE self, CURLM *multi_handle, int *still_running) {
454
+ CURLMcode mcode;
455
+
456
+ do {
457
+ mcode = curl_multi_perform(multi_handle, still_running);
458
+ } while (mcode == CURLM_CALL_MULTI_PERFORM);
459
+
460
+ if (mcode != CURLM_OK) {
461
+ raise_curl_multi_error_exception(mcode);
462
+ }
463
+
464
+ rb_curl_multi_read_info( self, multi_handle );
465
+ if (rb_block_given_p()) rb_yield(self);
466
+ }
467
+
468
+ #ifdef _WIN32
469
+ void create_crt_fd(fd_set *os_set, fd_set *crt_set)
470
+ {
471
+ int i;
472
+ crt_set->fd_count = os_set->fd_count;
473
+ for (i = 0; i < os_set->fd_count; i++) {
474
+ WSAPROTOCOL_INFO wsa_pi;
475
+ // dupicate the SOCKET
476
+ int r = WSADuplicateSocket(os_set->fd_array[i], GetCurrentProcessId(), &wsa_pi);
477
+ SOCKET s = WSASocket(wsa_pi.iAddressFamily, wsa_pi.iSocketType, wsa_pi.iProtocol, &wsa_pi, 0, 0);
478
+ // create the CRT fd so ruby can get back to the SOCKET
479
+ int fd = _open_osfhandle(s, O_RDWR|O_BINARY);
480
+ os_set->fd_array[i] = s;
481
+ crt_set->fd_array[i] = fd;
482
+ }
483
+ }
484
+
485
+ void cleanup_crt_fd(fd_set *os_set, fd_set *crt_set)
486
+ {
487
+ int i;
488
+ for (i = 0; i < os_set->fd_count; i++) {
489
+ // cleanup the CRT fd
490
+ _close(crt_set->fd_array[i]);
491
+ // cleanup the duplicated SOCKET
492
+ closesocket(os_set->fd_array[i]);
493
+ }
494
+ }
495
+ #endif
496
+
497
+ #ifdef HAVE_RB_THREAD_BLOCKING_REGION
498
+ struct _select_set {
499
+ int maxfd;
500
+ fd_set *fdread, *fdwrite, *fdexcep;
501
+ struct timeval *tv;
502
+ };
503
+
504
+ static VALUE curb_select(void *args) {
505
+ struct _select_set* set = args;
506
+ int rc = select(set->maxfd, set->fdread, set->fdwrite, set->fdexcep, set->tv);
507
+ return INT2FIX(rc);
508
+ }
509
+ #endif
510
+
511
+ /*
512
+ * call-seq:
513
+ * multi = Curl::Multi.new
514
+ * easy1 = Curl::Easy.new('url')
515
+ * easy2 = Curl::Easy.new('url')
516
+ *
517
+ * multi.add(easy1)
518
+ * multi.add(easy2)
519
+ *
520
+ * multi.perform do
521
+ * # while idle other code my execute here
522
+ * end
523
+ *
524
+ * Run multi handles, looping selecting when data can be transfered
525
+ */
526
+ void rb_curl_multi_perform(VALUE self, ruby_curl_multi *rbcm) {
527
+ CURLMcode mcode;
528
+ int maxfd, rc;
529
+ fd_set fdread, fdwrite, fdexcep;
530
+ #ifdef _WIN32
531
+ fd_set crt_fdread, crt_fdwrite, crt_fdexcep;
532
+ #endif
533
+ long timeout_milliseconds;
534
+ struct timeval tv = {0, 0};
535
+ #ifdef HAVE_RB_THREAD_BLOCKING_REGION
536
+ struct _select_set fdset_args;
537
+ #endif
538
+ timeout_milliseconds = cCurlMutiDefaulttimeout;
539
+
540
+ rb_curl_multi_run( self, rbcm->handle, &(rbcm->running) );
541
+
542
+ while (rbcm->running) {
543
+ #ifdef HAVE_CURL_MULTI_TIMEOUT
544
+ /* get the curl suggested time out */
545
+ mcode = curl_multi_timeout(rbcm->handle, &timeout_milliseconds);
546
+ if (mcode != CURLM_OK) {
547
+ raise_curl_multi_error_exception(mcode);
548
+ }
549
+ #else
550
+ /* libcurl doesn't have a timeout method defined, initialize to -1 we'll pick up the default later */
551
+ timeout_milliseconds = -1;
552
+ #endif
553
+
554
+ if (timeout_milliseconds == 0) { /* no delay */
555
+ rb_curl_multi_run( self, rbcm->handle, &(rbcm->running) );
556
+ continue;
557
+ }
558
+
559
+ if (timeout_milliseconds < 0 || timeout_milliseconds > cCurlMutiDefaulttimeout) {
560
+ timeout_milliseconds = cCurlMutiDefaulttimeout; /* libcurl doesn't know how long to wait, use a default timeout */
561
+ /* or buggy versions libcurl sometimes reports huge timeouts... let's cap it */
562
+ }
563
+
564
+ tv.tv_sec = 0; /* never wait longer than 1 second */
565
+ tv.tv_usec = (int)(timeout_milliseconds * 1000); /* XXX: int is the right type for OSX, what about linux? */
566
+
567
+ FD_ZERO(&fdread);
568
+ FD_ZERO(&fdwrite);
569
+ FD_ZERO(&fdexcep);
570
+
571
+ /* load the fd sets from the multi handle */
572
+ mcode = curl_multi_fdset(rbcm->handle, &fdread, &fdwrite, &fdexcep, &maxfd);
573
+ if (mcode != CURLM_OK) {
574
+ raise_curl_multi_error_exception(mcode);
575
+ }
576
+ //ruby_log_obj(INT2FIX(maxfd));
577
+
578
+ #ifdef _WIN32
579
+ create_crt_fd(&fdread, &crt_fdread);
580
+ create_crt_fd(&fdwrite, &crt_fdwrite);
581
+ create_crt_fd(&fdexcep, &crt_fdexcep);
582
+ #endif
583
+
584
+ #ifdef HAVE_RB_THREAD_BLOCKING_REGION
585
+ fdset_args.maxfd = maxfd+1;
586
+ fdset_args.fdread = &fdread;
587
+ fdset_args.fdwrite = &fdwrite;
588
+ fdset_args.fdexcep = &fdexcep;
589
+ fdset_args.tv = &tv;
590
+ rc = rb_thread_blocking_region(curb_select, &fdset_args, RUBY_UBF_IO, 0);
591
+ #else
592
+ rc = rb_thread_select(maxfd+1, &fdread, &fdwrite, &fdexcep, &tv);
593
+ #endif
594
+
595
+ #ifdef _WIN32
596
+ cleanup_crt_fd(&fdread, &crt_fdread);
597
+ cleanup_crt_fd(&fdwrite, &crt_fdwrite);
598
+ cleanup_crt_fd(&fdexcep, &crt_fdexcep);
599
+ #endif
600
+
601
+ switch(rc) {
602
+ case -1:
603
+ rb_raise(rb_eRuntimeError, "select(): %s", strerror(errno));
604
+ break;
605
+ case 0: /* timeout */
606
+ default: /* action */
607
+ rb_curl_multi_run( self, rbcm->handle, &(rbcm->running) );
608
+ break;
609
+ }
610
+ }
611
+
612
+ rb_curl_multi_read_info( self, rbcm->handle );
613
+ if (rb_block_given_p()) {rb_yield(self);}
614
+ }
615
+
616
+ static void rb_curl_multi_idle_perform(VALUE self, ruby_curl_multi *rbcm) {
617
+ struct timeval tv = {1, 0}; /* sleep time must not be lesser 1 second, otherwise multi thread will always "run" */
618
+ int rc, maxfd;
619
+ #ifdef _WIN32
620
+ fd_set crt_fdread, crt_fdwrite, crt_fdexcep;
621
+ #endif
622
+ #ifdef HAVE_RB_THREAD_BLOCKING_REGION
623
+ struct _select_set fdset_args;
624
+ #endif
625
+ fd_set fdread, fdwrite, fdexcep;
626
+ FD_ZERO(&fdread);
627
+ FD_ZERO(&fdwrite);
628
+ FD_ZERO(&fdexcep);
629
+ #ifdef _WIN32
630
+ create_crt_fd(&fdread, &crt_fdread);
631
+ create_crt_fd(&fdwrite, &crt_fdwrite);
632
+ create_crt_fd(&fdexcep, &crt_fdexcep);
633
+ #endif
634
+
635
+ do {
636
+ #ifdef HAVE_RB_THREAD_BLOCKING_REGION
637
+ fdset_args.maxfd = 0;
638
+ fdset_args.fdread = &fdread;
639
+ fdset_args.fdwrite = &fdwrite;
640
+ fdset_args.fdexcep = &fdexcep;
641
+ fdset_args.tv = &tv;
642
+ rc = rb_thread_blocking_region(curb_select, &fdset_args, RUBY_UBF_IO, 0);
643
+ #else
644
+ rc = rb_thread_select(0, &fdread, &fdwrite, &fdexcep, &tv);
645
+ #endif
646
+ if (rc == -1)
647
+ rb_raise(rb_eRuntimeError, "select(): %s", strerror(errno));
648
+
649
+ } while (!(RHASH_TBL(rbcm->requests)->num_entries));
650
+
651
+ #ifdef _WIN32
652
+ cleanup_crt_fd(&fdread, &crt_fdread);
653
+ cleanup_crt_fd(&fdwrite, &crt_fdwrite);
654
+ cleanup_crt_fd(&fdexcep, &crt_fdexcep);
655
+ #endif
656
+ rb_curl_multi_perform(self, rbcm);
657
+ }
658
+
659
+ static VALUE ruby_curl_multi_perform(int argc, VALUE *argv, VALUE self) {
660
+ ruby_curl_multi *rbcm;
661
+ VALUE idle;
662
+
663
+ Data_Get_Struct(self, ruby_curl_multi, rbcm);
664
+
665
+ if (!(rbcm->active || rbcm->running)) {
666
+ rb_scan_args(argc, argv, "01", &idle);
667
+ if (idle == Qtrue) {
668
+
669
+ if (rb_gv_get("$CarierThreadIsJoined") == Qtrue) {
670
+ ruby_log("Nothing to perform; recalling...");
671
+ return Qfalse;
672
+ }
673
+ //ruby_log("Nothing to perform; idling...");
674
+ rb_curl_multi_idle_perform(self, rbcm);
675
+
676
+ }
677
+ else {
678
+ rb_raise(rb_eRuntimeError, "Nothing to perform");
679
+ }
680
+ return Qtrue;
681
+ }
682
+
683
+ rb_curl_multi_perform(self, rbcm);
684
+ return Qtrue;
685
+ }
686
+
687
+ /* =================== INIT LIB =====================*/
688
+ void init_curb_multi() {
689
+ idCall = rb_intern("call");
690
+
691
+ cCurlMulti = rb_define_class_under(mCurl, "Multi", rb_cObject);
692
+
693
+ /* Class methods */
694
+ rb_define_singleton_method(cCurlMulti, "new", ruby_curl_multi_new, 0);
695
+ rb_define_singleton_method(cCurlMulti, "default_timeout=", ruby_curl_multi_set_default_timeout, 1);
696
+ rb_define_singleton_method(cCurlMulti, "default_timeout", ruby_curl_multi_get_default_timeout, 0);
697
+
698
+ /* "Attributes" */
699
+ rb_define_method(cCurlMulti, "requests", ruby_curl_multi_requests, 0);
700
+ rb_define_method(cCurlMulti, "running", ruby_curl_multi_running, 0);
701
+ rb_define_method(cCurlMulti, "idle?", ruby_curl_multi_idle, 0);
702
+
703
+ /* Instnace methods */
704
+ rb_define_method(cCurlMulti, "max_connects=", ruby_curl_multi_max_connects, 1);
705
+ rb_define_method(cCurlMulti, "pipeline=", ruby_curl_multi_pipeline, 1);
706
+ rb_define_method(cCurlMulti, "add", ruby_curl_multi_add, 1);
707
+ rb_define_method(cCurlMulti, "remove", ruby_curl_multi_remove, 1);
708
+ rb_define_method(cCurlMulti, "cancel!", ruby_curl_multi_cancel, 0);
709
+ rb_define_method(cCurlMulti, "perform", ruby_curl_multi_perform, -1);
710
+ }