gus-curb 0.8.7

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