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,704 @@
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
+
347
+ long response_code = -1;
348
+ VALUE easy;
349
+ ruby_curl_easy *rbce = NULL;
350
+ VALUE callargs, val = Qtrue;
351
+
352
+ CURLcode ecode = curl_easy_getinfo(easy_handle, CURLINFO_PRIVATE, (char**)&easy);
353
+
354
+ Data_Get_Struct(easy, ruby_curl_easy, rbce);
355
+
356
+ rbce->last_result = result; /* save the last easy result code */
357
+
358
+ ruby_curl_multi_remove( self, easy );
359
+
360
+ /* after running a request cleanup the headers, these are set before each request */
361
+ if (rbce->curl_headers) {
362
+ curl_slist_free_all(rbce->curl_headers);
363
+ rbce->curl_headers = NULL;
364
+ }
365
+
366
+ if (ecode != 0) {
367
+ raise_curl_easy_error_exception(ecode);
368
+ }
369
+
370
+ if (!rb_easy_nil("complete_proc")) {
371
+ callargs = rb_ary_new3(2, rb_easy_get("complete_proc"), easy);
372
+ rbce->callback_active = 1;
373
+ val = rb_rescue(call_status_handler1, callargs, callback_exception, Qnil);
374
+ rbce->callback_active = 0;
375
+ //rb_funcall( rb_easy_get("complete_proc"), idCall, 1, easy );
376
+ }
377
+
378
+ curl_easy_getinfo(rbce->curl, CURLINFO_RESPONSE_CODE, &response_code);
379
+
380
+ if (result != 0) {
381
+ if (!rb_easy_nil("failure_proc")) {
382
+ callargs = rb_ary_new3(3, rb_easy_get("failure_proc"), easy, rb_curl_easy_error(result));
383
+ rbce->callback_active = 1;
384
+ val = rb_rescue(call_status_handler2, callargs, callback_exception, Qnil);
385
+ rbce->callback_active = 0;
386
+ //rb_funcall( rb_easy_get("failure_proc"), idCall, 2, easy, rb_curl_easy_error(result) );
387
+ }
388
+ }
389
+ else if (!rb_easy_nil("success_proc") &&
390
+ ((response_code >= 200 && response_code < 300) || response_code == 0)) {
391
+ /* NOTE: we allow response_code == 0, in the case of non http requests e.g. reading from disk */
392
+ callargs = rb_ary_new3(2, rb_easy_get("success_proc"), easy);
393
+ rbce->callback_active = 1;
394
+ val = rb_rescue(call_status_handler1, callargs, callback_exception, Qnil);
395
+ rbce->callback_active = 0;
396
+ //rb_funcall( rb_easy_get("success_proc"), idCall, 1, easy );
397
+ }
398
+ else if (!rb_easy_nil("redirect_proc") &&
399
+ (response_code >= 300 && response_code < 400)) {
400
+ rbce->callback_active = 1;
401
+ callargs = rb_ary_new3(3, rb_easy_get("redirect_proc"), easy, rb_curl_easy_error(result));
402
+ rbce->callback_active = 0;
403
+ val = rb_rescue(call_status_handler2, callargs, callback_exception, Qnil);
404
+ }
405
+ else if (!rb_easy_nil("missing_proc") &&
406
+ (response_code >= 400 && response_code < 500)) {
407
+ rbce->callback_active = 1;
408
+ callargs = rb_ary_new3(3, rb_easy_get("missing_proc"), easy, rb_curl_easy_error(result));
409
+ rbce->callback_active = 0;
410
+ val = rb_rescue(call_status_handler2, callargs, callback_exception, Qnil);
411
+ }
412
+ else if (!rb_easy_nil("failure_proc") &&
413
+ (response_code >= 500 && response_code <= 999)) {
414
+ callargs = rb_ary_new3(3, rb_easy_get("failure_proc"), easy, rb_curl_easy_error(result));
415
+ rbce->callback_active = 1;
416
+ val = rb_rescue(call_status_handler2, callargs, callback_exception, Qnil);
417
+ rbce->callback_active = 0;
418
+ //rb_funcall( rb_easy_get("failure_proc"), idCall, 2, easy, rb_curl_easy_error(result) );
419
+ }
420
+
421
+ if (val == Qfalse) {
422
+ rb_warn("uncaught exception from callback");
423
+ // exception was raised?
424
+ //fprintf(stderr, "exception raised from callback\n");
425
+ }
426
+
427
+ }
428
+
429
+ static void rb_curl_multi_read_info(VALUE self, CURLM *multi_handle) {
430
+ int msgs_left, result;
431
+ CURLMsg *msg;
432
+ CURL *easy_handle;
433
+
434
+ /* check for finished easy handles and remove from the multi handle */
435
+ while ((msg = curl_multi_info_read(multi_handle, &msgs_left))) {
436
+ if (msg->msg == CURLMSG_DONE) {
437
+ easy_handle = msg->easy_handle;
438
+ result = msg->data.result;
439
+ if (easy_handle) {
440
+ rb_curl_mutli_handle_complete(self, easy_handle, result);
441
+ }
442
+ }
443
+ }
444
+ }
445
+
446
+ /* called within ruby_curl_multi_perform */
447
+ static void rb_curl_multi_run(VALUE self, CURLM *multi_handle, int *still_running) {
448
+ CURLMcode mcode;
449
+
450
+ do {
451
+ mcode = curl_multi_perform(multi_handle, still_running);
452
+ } while (mcode == CURLM_CALL_MULTI_PERFORM);
453
+
454
+ if (mcode != CURLM_OK) {
455
+ raise_curl_multi_error_exception(mcode);
456
+ }
457
+
458
+ rb_curl_multi_read_info( self, multi_handle );
459
+ if (rb_block_given_p()) rb_yield(self);
460
+ }
461
+
462
+ #ifdef _WIN32
463
+ void create_crt_fd(fd_set *os_set, fd_set *crt_set)
464
+ {
465
+ int i;
466
+ crt_set->fd_count = os_set->fd_count;
467
+ for (i = 0; i < os_set->fd_count; i++) {
468
+ WSAPROTOCOL_INFO wsa_pi;
469
+ // dupicate the SOCKET
470
+ int r = WSADuplicateSocket(os_set->fd_array[i], GetCurrentProcessId(), &wsa_pi);
471
+ SOCKET s = WSASocket(wsa_pi.iAddressFamily, wsa_pi.iSocketType, wsa_pi.iProtocol, &wsa_pi, 0, 0);
472
+ // create the CRT fd so ruby can get back to the SOCKET
473
+ int fd = _open_osfhandle(s, O_RDWR|O_BINARY);
474
+ os_set->fd_array[i] = s;
475
+ crt_set->fd_array[i] = fd;
476
+ }
477
+ }
478
+
479
+ void cleanup_crt_fd(fd_set *os_set, fd_set *crt_set)
480
+ {
481
+ int i;
482
+ for (i = 0; i < os_set->fd_count; i++) {
483
+ // cleanup the CRT fd
484
+ _close(crt_set->fd_array[i]);
485
+ // cleanup the duplicated SOCKET
486
+ closesocket(os_set->fd_array[i]);
487
+ }
488
+ }
489
+ #endif
490
+
491
+ #ifdef HAVE_RB_THREAD_BLOCKING_REGION
492
+ struct _select_set {
493
+ int maxfd;
494
+ fd_set *fdread, *fdwrite, *fdexcep;
495
+ struct timeval *tv;
496
+ };
497
+
498
+ static VALUE curb_select(void *args) {
499
+ struct _select_set* set = args;
500
+ int rc = select(set->maxfd, set->fdread, set->fdwrite, set->fdexcep, set->tv);
501
+ return INT2FIX(rc);
502
+ }
503
+ #endif
504
+
505
+ /*
506
+ * call-seq:
507
+ * multi = Curl::Multi.new
508
+ * easy1 = Curl::Easy.new('url')
509
+ * easy2 = Curl::Easy.new('url')
510
+ *
511
+ * multi.add(easy1)
512
+ * multi.add(easy2)
513
+ *
514
+ * multi.perform do
515
+ * # while idle other code my execute here
516
+ * end
517
+ *
518
+ * Run multi handles, looping selecting when data can be transfered
519
+ */
520
+ static VALUE ruby_curl_multi_perform(int argc, VALUE *argv, VALUE self) {
521
+ ruby_curl_multi *rbcm;
522
+ VALUE idle;
523
+
524
+ Data_Get_Struct(self, ruby_curl_multi, rbcm);
525
+
526
+ if (!(rbcm->active || rbcm->running)) {
527
+ rb_scan_args(argc, argv, "01", &idle);
528
+ if (idle == Qtrue) {
529
+
530
+ if (rb_gv_get("$CarierThreadIsJoined") == Qtrue) {
531
+ ruby_log("Nothing to perform; recalling...");
532
+ return Qfalse;
533
+ }
534
+ ruby_log("Nothing to perform; idling...");
535
+ rb_curl_multi_idle_perform(self, rbcm);
536
+
537
+ }
538
+ else {
539
+ rb_raise(rb_eRuntimeError, "Nothing to perform");
540
+ }
541
+ return Qtrue;
542
+ }
543
+
544
+ rb_curl_multi_perform(self, rbcm);
545
+ return Qtrue;
546
+ }
547
+
548
+ static void rb_curl_multi_idle_perform(VALUE self, ruby_curl_multi *rbcm) {
549
+ struct timeval tv = {1, 0}; /* sleep time must not be lesser 1 second, otherwise multi thread will always "run" */
550
+ int rc, maxfd;
551
+ #ifdef _WIN32
552
+ fd_set crt_fdread, crt_fdwrite, crt_fdexcep;
553
+ #endif
554
+ #ifdef HAVE_RB_THREAD_BLOCKING_REGION
555
+ struct _select_set fdset_args;
556
+ #endif
557
+ fd_set fdread, fdwrite, fdexcep;
558
+ FD_ZERO(&fdread);
559
+ FD_ZERO(&fdwrite);
560
+ FD_ZERO(&fdexcep);
561
+ #ifdef _WIN32
562
+ create_crt_fd(&fdread, &crt_fdread);
563
+ create_crt_fd(&fdwrite, &crt_fdwrite);
564
+ create_crt_fd(&fdexcep, &crt_fdexcep);
565
+ #endif
566
+
567
+ do {
568
+ #ifdef HAVE_RB_THREAD_BLOCKING_REGION
569
+ fdset_args.maxfd = 0;
570
+ fdset_args.fdread = &fdread;
571
+ fdset_args.fdwrite = &fdwrite;
572
+ fdset_args.fdexcep = &fdexcep;
573
+ fdset_args.tv = &tv;
574
+ rc = rb_thread_blocking_region(curb_select, &fdset_args, RUBY_UBF_IO, 0);
575
+ #else
576
+ rc = rb_thread_select(0, &fdread, &fdwrite, &fdexcep, &tv);
577
+ #endif
578
+ if (rc == -1)
579
+ rb_raise(rb_eRuntimeError, "select(): %s", strerror(errno));
580
+
581
+ } while (!(RHASH_TBL(rbcm->requests)->num_entries));
582
+
583
+ #ifdef _WIN32
584
+ cleanup_crt_fd(&fdread, &crt_fdread);
585
+ cleanup_crt_fd(&fdwrite, &crt_fdwrite);
586
+ cleanup_crt_fd(&fdexcep, &crt_fdexcep);
587
+ #endif
588
+ rb_curl_multi_perform(self, rbcm);
589
+ }
590
+
591
+ void rb_curl_multi_perform(VALUE self, ruby_curl_multi *rbcm) {
592
+ CURLMcode mcode;
593
+ ruby_curl_multi *rbcm;
594
+ int maxfd, rc;
595
+ fd_set fdread, fdwrite, fdexcep;
596
+ #ifdef _WIN32
597
+ fd_set crt_fdread, crt_fdwrite, crt_fdexcep;
598
+ #endif
599
+ long timeout_milliseconds;
600
+ struct timeval tv = {0, 0};
601
+ #ifdef HAVE_RB_THREAD_BLOCKING_REGION
602
+ struct _select_set fdset_args;
603
+ #endif
604
+ timeout_milliseconds = cCurlMutiDefaulttimeout;
605
+
606
+ rb_curl_multi_run( self, rbcm->handle, &(rbcm->running) );
607
+
608
+ while (rbcm->running) {
609
+ #ifdef HAVE_CURL_MULTI_TIMEOUT
610
+ /* get the curl suggested time out */
611
+ mcode = curl_multi_timeout(rbcm->handle, &timeout_milliseconds);
612
+ if (mcode != CURLM_OK) {
613
+ raise_curl_multi_error_exception(mcode);
614
+ }
615
+ #else
616
+ /* libcurl doesn't have a timeout method defined, initialize to -1 we'll pick up the default later */
617
+ timeout_milliseconds = -1;
618
+ #endif
619
+
620
+ if (timeout_milliseconds == 0) { /* no delay */
621
+ rb_curl_multi_run( self, rbcm->handle, &(rbcm->running) );
622
+ continue;
623
+ }
624
+
625
+ if (timeout_milliseconds < 0 || timeout_milliseconds > cCurlMutiDefaulttimeout) {
626
+ timeout_milliseconds = cCurlMutiDefaulttimeout; /* libcurl doesn't know how long to wait, use a default timeout */
627
+ /* or buggy versions libcurl sometimes reports huge timeouts... let's cap it */
628
+ }
629
+
630
+ tv.tv_sec = 0; /* never wait longer than 1 second */
631
+ tv.tv_usec = (int)(timeout_milliseconds * 1000); /* XXX: int is the right type for OSX, what about linux? */
632
+
633
+ FD_ZERO(&fdread);
634
+ FD_ZERO(&fdwrite);
635
+ FD_ZERO(&fdexcep);
636
+
637
+ /* load the fd sets from the multi handle */
638
+ mcode = curl_multi_fdset(rbcm->handle, &fdread, &fdwrite, &fdexcep, &maxfd);
639
+ if (mcode != CURLM_OK) {
640
+ raise_curl_multi_error_exception(mcode);
641
+ }
642
+ ruby_log_obj(INT2FIX(maxfd));
643
+
644
+ #ifdef _WIN32
645
+ create_crt_fd(&fdread, &crt_fdread);
646
+ create_crt_fd(&fdwrite, &crt_fdwrite);
647
+ create_crt_fd(&fdexcep, &crt_fdexcep);
648
+ #endif
649
+
650
+ #ifdef HAVE_RB_THREAD_BLOCKING_REGION
651
+ fdset_args.maxfd = maxfd+1;
652
+ fdset_args.fdread = &fdread;
653
+ fdset_args.fdwrite = &fdwrite;
654
+ fdset_args.fdexcep = &fdexcep;
655
+ fdset_args.tv = &tv;
656
+ rc = rb_thread_blocking_region(curb_select, &fdset_args, RUBY_UBF_IO, 0);
657
+ #else
658
+ rc = rb_thread_select(maxfd+1, &fdread, &fdwrite, &fdexcep, &tv);
659
+ #endif
660
+
661
+ #ifdef _WIN32
662
+ cleanup_crt_fd(&fdread, &crt_fdread);
663
+ cleanup_crt_fd(&fdwrite, &crt_fdwrite);
664
+ cleanup_crt_fd(&fdexcep, &crt_fdexcep);
665
+ #endif
666
+
667
+ switch(rc) {
668
+ case -1:
669
+ rb_raise(rb_eRuntimeError, "select(): %s", strerror(errno));
670
+ break;
671
+ case 0: /* timeout */
672
+ default: /* action */
673
+ rb_curl_multi_run( self, rbcm->handle, &(rbcm->running) );
674
+ break;
675
+ }
676
+ }
677
+
678
+ rb_curl_multi_read_info( self, rbcm->handle );
679
+ if (rb_block_given_p()) {rb_yield(self);}
680
+ }
681
+
682
+ /* =================== INIT LIB =====================*/
683
+ void init_curb_multi() {
684
+ idCall = rb_intern("call");
685
+
686
+ cCurlMulti = rb_define_class_under(mCurl, "Multi", rb_cObject);
687
+
688
+ /* Class methods */
689
+ rb_define_singleton_method(cCurlMulti, "new", ruby_curl_multi_new, 0);
690
+ rb_define_singleton_method(cCurlMulti, "default_timeout=", ruby_curl_multi_set_default_timeout, 1);
691
+ rb_define_singleton_method(cCurlMulti, "default_timeout", ruby_curl_multi_get_default_timeout, 0);
692
+
693
+ /* "Attributes" */
694
+ rb_define_method(cCurlMulti, "requests", ruby_curl_multi_requests, 0);
695
+ rb_define_method(cCurlMulti, "idle?", ruby_curl_multi_idle, 0);
696
+
697
+ /* Instnace methods */
698
+ rb_define_method(cCurlMulti, "max_connects=", ruby_curl_multi_max_connects, 1);
699
+ rb_define_method(cCurlMulti, "pipeline=", ruby_curl_multi_pipeline, 1);
700
+ rb_define_method(cCurlMulti, "add", ruby_curl_multi_add, 1);
701
+ rb_define_method(cCurlMulti, "remove", ruby_curl_multi_remove, 1);
702
+ rb_define_method(cCurlMulti, "cancel!", ruby_curl_multi_cancel, 0);
703
+ rb_define_method(cCurlMulti, "perform", ruby_curl_multi_perform, -1);
704
+ }