codders-curb 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. data/LICENSE +51 -0
  2. data/README +194 -0
  3. data/Rakefile +320 -0
  4. data/doc.rb +42 -0
  5. data/ext/curb.c +977 -0
  6. data/ext/curb.h +52 -0
  7. data/ext/curb_easy.c +3404 -0
  8. data/ext/curb_easy.h +90 -0
  9. data/ext/curb_errors.c +647 -0
  10. data/ext/curb_errors.h +129 -0
  11. data/ext/curb_macros.h +159 -0
  12. data/ext/curb_multi.c +633 -0
  13. data/ext/curb_multi.h +26 -0
  14. data/ext/curb_postfield.c +523 -0
  15. data/ext/curb_postfield.h +40 -0
  16. data/ext/curb_upload.c +80 -0
  17. data/ext/curb_upload.h +30 -0
  18. data/ext/extconf.rb +399 -0
  19. data/lib/curb.rb +4 -0
  20. data/lib/curl.rb +57 -0
  21. data/lib/curl/easy.rb +468 -0
  22. data/lib/curl/multi.rb +248 -0
  23. data/tests/alltests.rb +3 -0
  24. data/tests/bug_crash_on_debug.rb +39 -0
  25. data/tests/bug_crash_on_progress.rb +33 -0
  26. data/tests/bug_curb_easy_blocks_ruby_threads.rb +52 -0
  27. data/tests/bug_curb_easy_post_with_string_no_content_length_header.rb +83 -0
  28. data/tests/bug_instance_post_differs_from_class_post.rb +53 -0
  29. data/tests/bug_multi_segfault.rb +14 -0
  30. data/tests/bug_postfields_crash.rb +26 -0
  31. data/tests/bug_postfields_crash2.rb +57 -0
  32. data/tests/bug_require_last_or_segfault.rb +40 -0
  33. data/tests/bugtests.rb +9 -0
  34. data/tests/helper.rb +199 -0
  35. data/tests/mem_check.rb +65 -0
  36. data/tests/require_last_or_segfault_script.rb +36 -0
  37. data/tests/tc_curl_download.rb +75 -0
  38. data/tests/tc_curl_easy.rb +1011 -0
  39. data/tests/tc_curl_easy_setopt.rb +31 -0
  40. data/tests/tc_curl_multi.rb +485 -0
  41. data/tests/tc_curl_postfield.rb +143 -0
  42. data/tests/timeout.rb +100 -0
  43. data/tests/timeout_server.rb +33 -0
  44. data/tests/unittests.rb +2 -0
  45. metadata +133 -0
data/lib/curb.rb ADDED
@@ -0,0 +1,4 @@
1
+ require 'curb_core'
2
+ require 'curl'
3
+ require 'curl/easy'
4
+ require 'curl/multi'
data/lib/curl.rb ADDED
@@ -0,0 +1,57 @@
1
+ require 'curb'
2
+ require 'uri'
3
+
4
+ # expose shortcut methods
5
+ module Curl
6
+
7
+ def self.http(verb, url, post_body=nil, put_data=nil, &block)
8
+ handle = Curl::Easy.new(url)
9
+ handle.post_body = post_body if post_body
10
+ handle.put_data = put_data if put_data
11
+ yield handle if block_given?
12
+ handle.http(verb)
13
+ handle
14
+ end
15
+
16
+ def self.get(url, params={}, &block)
17
+ http :GET, urlalize(url, params), nil, nil, &block
18
+ end
19
+
20
+ def self.post(url, params={}, &block)
21
+ http :POST, url, postalize(params), nil, &block
22
+ end
23
+
24
+ def self.put(url, params={}, &block)
25
+ http :PUT, url, nil, postalize(params), &block
26
+ end
27
+
28
+ def self.delete(url, params={}, &block)
29
+ http :DELETE, url, postalize(params), nil, &block
30
+ end
31
+
32
+ def self.patch(url, params={}, &block)
33
+ http :PATCH, url, postalize(params), nil, &block
34
+ end
35
+
36
+ def self.head(url, params={}, &block)
37
+ http :OPTIONS, urlalize(url, params), nil, nil, &block
38
+ end
39
+
40
+ def self.options(url, params={}, &block)
41
+ http :OPTIONS, urlalize(url, params), nil, nil, &block
42
+ end
43
+
44
+ def self.urlalize(url, params={})
45
+ query_str = params.map {|k,v| "#{URI.escape(k.to_s)}=#{URI.escape(v.to_s)}" }.join('&')
46
+ if url.match(/\?/)
47
+ "#{url}&#{query_str}"
48
+ else
49
+ "#{url}?#{query_str}"
50
+ end
51
+ end
52
+
53
+ def self.postalize(params={})
54
+ URI.encode_www_form(params)
55
+ end
56
+
57
+ end
data/lib/curl/easy.rb ADDED
@@ -0,0 +1,468 @@
1
+ module Curl
2
+ class Easy
3
+
4
+ alias post http_post
5
+ alias put http_put
6
+
7
+ #
8
+ # call-seq:
9
+ # easy.status => String
10
+ #
11
+ def status
12
+ parts = self.header_str.split(/\s/)
13
+ status = []
14
+ parts.shift
15
+ while parts.size > 0 && parts.first != ''
16
+ status << parts.shift
17
+ end
18
+ status.join(' ')
19
+ end
20
+
21
+ #
22
+ # call-seq:
23
+ # easy.set :sym|Fixnum, value
24
+ #
25
+ # set options on the curl easy handle see http://curl.haxx.se/libcurl/c/curl_easy_setopt.html
26
+ #
27
+ def set(opt,val)
28
+ if opt.is_a?(Symbol)
29
+ setopt(sym2curl(opt), val)
30
+ else
31
+ setopt(opt.to_i, val)
32
+ end
33
+ end
34
+
35
+ #
36
+ # call-seq:
37
+ # easy.sym2curl :symbol => Fixnum
38
+ #
39
+ # translates ruby symbols to libcurl options
40
+ #
41
+ def sym2curl(opt)
42
+ Curl.const_get("CURLOPT_#{opt.to_s.upcase}")
43
+ end
44
+
45
+ #
46
+ # call-seq:
47
+ # easy.perform => true
48
+ #
49
+ # Transfer the currently configured URL using the options set for this
50
+ # Curl::Easy instance. If this is an HTTP URL, it will be transferred via
51
+ # the configured HTTP Verb.
52
+ #
53
+ def perform
54
+ self.multi = Curl::Multi.new if self.multi.nil?
55
+ self.multi.add self
56
+ ret = self.multi.perform
57
+
58
+ if self.last_result != 0 && self.on_failure.nil?
59
+ error = Curl::Easy.error(self.last_result)
60
+ raise error.first
61
+ end
62
+
63
+ ret
64
+ end
65
+
66
+ #
67
+ # call-seq:
68
+ #
69
+ # easy = Curl::Easy.new
70
+ # easy.nosignal = true
71
+ #
72
+ def nosignal=(onoff)
73
+ set :nosignal, !!onoff
74
+ end
75
+
76
+ #
77
+ # call-seq:
78
+ # easy = Curl::Easy.new("url") do|c|
79
+ # c.delete = true
80
+ # end
81
+ # easy.perform
82
+ #
83
+ def delete=(onoff)
84
+ set :customrequest, onoff ? 'delete' : nil
85
+ onoff
86
+ end
87
+ #
88
+ # call-seq:
89
+ #
90
+ # easy = Curl::Easy.new("url")
91
+ # easy.version = Curl::HTTP_1_1
92
+ # easy.version = Curl::HTTP_1_0
93
+ # easy.version = Curl::HTTP_NONE
94
+ #
95
+ def version=(http_version)
96
+ set :http_version, http_version
97
+ end
98
+
99
+ #
100
+ # call-seq:
101
+ # easy.url = "http://some.url/" => "http://some.url/"
102
+ #
103
+ # Set the URL for subsequent calls to +perform+. It is acceptable
104
+ # (and even recommended) to reuse Curl::Easy instances by reassigning
105
+ # the URL between calls to +perform+.
106
+ #
107
+ def url=(u)
108
+ set :url, u
109
+ end
110
+
111
+ #
112
+ # call-seq:
113
+ # easy.proxy_url = string => string
114
+ #
115
+ # Set the URL of the HTTP proxy to use for subsequent calls to +perform+.
116
+ # The URL should specify the the host name or dotted IP address. To specify
117
+ # port number in this string, append :[port] to the end of the host name.
118
+ # The proxy string may be prefixed with [protocol]:// since any such prefix
119
+ # will be ignored. The proxy's port number may optionally be specified with
120
+ # the separate option proxy_port .
121
+ #
122
+ # When you tell the library to use an HTTP proxy, libcurl will transparently
123
+ # convert operations to HTTP even if you specify an FTP URL etc. This may have
124
+ # an impact on what other features of the library you can use, such as
125
+ # FTP specifics that don't work unless you tunnel through the HTTP proxy. Such
126
+ # tunneling is activated with proxy_tunnel = true.
127
+ #
128
+ # libcurl respects the environment variables *http_proxy*, *ftp_proxy*,
129
+ # *all_proxy* etc, if any of those is set. The proxy_url option does however
130
+ # override any possibly set environment variables.
131
+ #
132
+ # Starting with libcurl 7.14.1, the proxy host string given in environment
133
+ # variables can be specified the exact same way as the proxy can be set with
134
+ # proxy_url, including protocol prefix (http://) and embedded user + password.
135
+ #
136
+ def proxy_url=(url)
137
+ set :proxy, url
138
+ end
139
+
140
+ def ssl_verify_host=(value)
141
+ value = 1 if value.class == TrueClass
142
+ value = 0 if value.class == FalseClass
143
+ self.ssl_verify_host_integer=value
144
+ end
145
+
146
+ #
147
+ # call-seq:
148
+ # easy.ssl_verify_host? => boolean
149
+ #
150
+ # Deprecated: call easy.ssl_verify_host instead
151
+ # can be one of [0,1,2]
152
+ #
153
+ # Determine whether this Curl instance will verify that the server cert
154
+ # is for the server it is known as.
155
+ #
156
+ def ssl_verify_host?
157
+ ssl_verify_host.nil? ? false : (ssl_verify_host > 0)
158
+ end
159
+
160
+ #
161
+ # call-seq:
162
+ # easy.interface = string => string
163
+ #
164
+ # Set the interface name to use as the outgoing network interface.
165
+ # The name can be an interface name, an IP address or a host name.
166
+ #
167
+ def interface=(value)
168
+ set :interface, value
169
+ end
170
+
171
+ #
172
+ # call-seq:
173
+ # easy.userpwd = string => string
174
+ #
175
+ # Set the username/password string to use for subsequent calls to +perform+.
176
+ # The supplied string should have the form "username:password"
177
+ #
178
+ def userpwd=(value)
179
+ set :userpwd, value
180
+ end
181
+
182
+ #
183
+ # call-seq:
184
+ # easy.proxypwd = string => string
185
+ #
186
+ # Set the username/password string to use for proxy connection during
187
+ # subsequent calls to +perform+. The supplied string should have the
188
+ # form "username:password"
189
+ #
190
+ def proxypwd=(value)
191
+ set :proxyuserpwd, value
192
+ end
193
+
194
+ #
195
+ # call-seq:
196
+ # easy.cookies = "name1=content1; name2=content2;" => string
197
+ #
198
+ # Set cookies to be sent by this Curl::Easy instance. The format of the string should
199
+ # be NAME=CONTENTS, where NAME is the cookie name and CONTENTS is what the cookie should contain.
200
+ # Set multiple cookies in one string like this: "name1=content1; name2=content2;" etc.
201
+ #
202
+ def cookies=(value)
203
+ set :cookie, value
204
+ end
205
+
206
+ #
207
+ # call-seq:
208
+ # easy.cookiefile = string => string
209
+ #
210
+ # Set a file that contains cookies to be sent in subsequent requests by this Curl::Easy instance.
211
+ #
212
+ # *Note* that you must set enable_cookies true to enable the cookie
213
+ # engine, or this option will be ignored.
214
+ #
215
+ def cookiefile=(value)
216
+ set :cookiefile, value
217
+ end
218
+
219
+ #
220
+ # call-seq:
221
+ # easy.cookiejar = string => string
222
+ #
223
+ # Set a cookiejar file to use for this Curl::Easy instance.
224
+ # Cookies from the response will be written into this file.
225
+ #
226
+ # *Note* that you must set enable_cookies true to enable the cookie
227
+ # engine, or this option will be ignored.
228
+ #
229
+ def cookiejar=(value)
230
+ set :cookiejar, value
231
+ end
232
+
233
+ #
234
+ # call-seq:
235
+ # easy = Curl::Easy.new("url") do|c|
236
+ # c.head = true
237
+ # end
238
+ # easy.perform
239
+ #
240
+ def head=(onoff)
241
+ set :nobody, onoff
242
+ end
243
+
244
+ #
245
+ # call-seq:
246
+ # easy.follow_location = boolean => boolean
247
+ #
248
+ # Configure whether this Curl instance will follow Location: headers
249
+ # in HTTP responses. Redirects will only be followed to the extent
250
+ # specified by +max_redirects+.
251
+ #
252
+ def follow_location=(onoff)
253
+ set :followlocation, onoff
254
+ end
255
+
256
+ #
257
+ # call-seq:
258
+ # easy.http_head => true
259
+ #
260
+ # Request headers from the currently configured URL using the HEAD
261
+ # method and current options set for this Curl::Easy instance. This
262
+ # method always returns true, or raises an exception (defined under
263
+ # Curl::Err) on error.
264
+ #
265
+ def http_head
266
+ set :nobody, true
267
+ ret = self.perform
268
+ set :nobody, false
269
+ ret
270
+ end
271
+
272
+ #
273
+ # call-seq:
274
+ # easy.http_get => true
275
+ #
276
+ # GET the currently configured URL using the current options set for
277
+ # this Curl::Easy instance. This method always returns true, or raises
278
+ # an exception (defined under Curl::Err) on error.
279
+ #
280
+ def http_get
281
+ set :httpget, true
282
+ http :GET
283
+ end
284
+ alias get http_get
285
+
286
+ #
287
+ # call-seq:
288
+ # easy.http_delete
289
+ #
290
+ # DELETE the currently configured URL using the current options set for
291
+ # this Curl::Easy instance. This method always returns true, or raises
292
+ # an exception (defined under Curl::Err) on error.
293
+ #
294
+ def http_delete
295
+ self.http :DELETE
296
+ end
297
+ alias delete http_delete
298
+
299
+ class << self
300
+
301
+ #
302
+ # call-seq:
303
+ # Curl::Easy.perform(url) { |easy| ... } => #&lt;Curl::Easy...&gt;
304
+ #
305
+ # Convenience method that creates a new Curl::Easy instance with
306
+ # the specified URL and calls the general +perform+ method, before returning
307
+ # the new instance. For HTTP URLs, this is equivalent to calling +http_get+.
308
+ #
309
+ # If a block is supplied, the new instance will be yielded just prior to
310
+ # the +http_get+ call.
311
+ #
312
+ def perform(*args)
313
+ c = Curl::Easy.new *args
314
+ yield c if block_given?
315
+ c.perform
316
+ c
317
+ end
318
+
319
+ #
320
+ # call-seq:
321
+ # Curl::Easy.http_get(url) { |easy| ... } => #&lt;Curl::Easy...&gt;
322
+ #
323
+ # Convenience method that creates a new Curl::Easy instance with
324
+ # the specified URL and calls +http_get+, before returning the new instance.
325
+ #
326
+ # If a block is supplied, the new instance will be yielded just prior to
327
+ # the +http_get+ call.
328
+ #
329
+ def http_get(*args)
330
+ c = Curl::Easy.new *args
331
+ yield c if block_given?
332
+ c.http_get
333
+ c
334
+ end
335
+
336
+ #
337
+ # call-seq:
338
+ # Curl::Easy.http_head(url) { |easy| ... } => #&lt;Curl::Easy...&gt;
339
+ #
340
+ # Convenience method that creates a new Curl::Easy instance with
341
+ # the specified URL and calls +http_head+, before returning the new instance.
342
+ #
343
+ # If a block is supplied, the new instance will be yielded just prior to
344
+ # the +http_head+ call.
345
+ #
346
+ def http_head(*args)
347
+ c = Curl::Easy.new *args
348
+ yield c if block_given?
349
+ c.http_head
350
+ c
351
+ end
352
+
353
+ #
354
+ # call-seq:
355
+ # Curl::Easy.http_put(url, data) {|c| ... }
356
+ #
357
+ # see easy.http_put
358
+ #
359
+ def http_put(url, data)
360
+ c = Curl::Easy.new url
361
+ yield c if block_given?
362
+ c.http_put data
363
+ c
364
+ end
365
+
366
+ #
367
+ # call-seq:
368
+ # Curl::Easy.http_post(url, "some=urlencoded%20form%20data&and=so%20on") => true
369
+ # Curl::Easy.http_post(url, "some=urlencoded%20form%20data", "and=so%20on", ...) => true
370
+ # Curl::Easy.http_post(url, "some=urlencoded%20form%20data", Curl::PostField, "and=so%20on", ...) => true
371
+ # Curl::Easy.http_post(url, Curl::PostField, Curl::PostField ..., Curl::PostField) => true
372
+ #
373
+ # POST the specified formdata to the currently configured URL using
374
+ # the current options set for this Curl::Easy instance. This method
375
+ # always returns true, or raises an exception (defined under
376
+ # Curl::Err) on error.
377
+ #
378
+ # If you wish to use multipart form encoding, you'll need to supply a block
379
+ # in order to set multipart_form_post true. See #http_post for more
380
+ # information.
381
+ #
382
+ def http_post(*args)
383
+ url = args.shift
384
+ c = Curl::Easy.new url
385
+ yield c if block_given?
386
+ c.http_post *args
387
+ c
388
+ end
389
+
390
+ #
391
+ # call-seq:
392
+ # Curl::Easy.http_delete(url) { |easy| ... } => #&lt;Curl::Easy...&gt;
393
+ #
394
+ # Convenience method that creates a new Curl::Easy instance with
395
+ # the specified URL and calls +http_delete+, before returning the new instance.
396
+ #
397
+ # If a block is supplied, the new instance will be yielded just prior to
398
+ # the +http_delete+ call.
399
+ #
400
+ def http_delete(*args)
401
+ c = Curl::Easy.new *args
402
+ yield c if block_given?
403
+ c.http_delete
404
+ c
405
+ end
406
+
407
+ # call-seq:
408
+ # Curl::Easy.download(url, filename = url.split(/\?/).first.split(/\//).last) { |curl| ... }
409
+ #
410
+ # Stream the specified url (via perform) and save the data directly to the
411
+ # supplied filename (defaults to the last component of the URL path, which will
412
+ # usually be the filename most simple urls).
413
+ #
414
+ # If a block is supplied, it will be passed the curl instance prior to the
415
+ # perform call.
416
+ #
417
+ # *Note* that the semantics of the on_body handler are subtly changed when using
418
+ # download, to account for the automatic routing of data to the specified file: The
419
+ # data string is passed to the handler *before* it is written
420
+ # to the file, allowing the handler to perform mutative operations where
421
+ # necessary. As usual, the transfer will be aborted if the on_body handler
422
+ # returns a size that differs from the data chunk size - in this case, the
423
+ # offending chunk will *not* be written to the file, the file will be closed,
424
+ # and a Curl::Err::AbortedByCallbackError will be raised.
425
+ def download(url, filename = url.split(/\?/).first.split(/\//).last, &blk)
426
+ curl = Curl::Easy.new(url, &blk)
427
+
428
+ output = if filename.is_a? IO
429
+ filename.binmode if filename.respond_to?(:binmode)
430
+ filename
431
+ else
432
+ File.open(filename, 'wb')
433
+ end
434
+
435
+ begin
436
+ old_on_body = curl.on_body do |data|
437
+ result = old_on_body ? old_on_body.call(data) : data.length
438
+ output << data if result == data.length
439
+ result
440
+ end
441
+ curl.perform
442
+ ensure
443
+ output.close rescue IOError
444
+ end
445
+
446
+ return curl
447
+ end
448
+ end
449
+
450
+ # Allow the incoming cert string to be file:password
451
+ # but be careful to not use a colon from a windows file path
452
+ # as the split point. Mimic what curl's main does
453
+ if respond_to?(:cert=)
454
+ alias_method :native_cert=, :cert=
455
+ def cert=(cert_file)
456
+ pos = cert_file.rindex(':')
457
+ if pos && pos > 1
458
+ self.native_cert= cert_file[0..pos-1]
459
+ self.certpassword= cert_file[pos+1..-1]
460
+ else
461
+ self.native_cert= cert_file
462
+ end
463
+ self.cert
464
+ end
465
+ end
466
+
467
+ end
468
+ end