curb 0.7.15 → 1.0.0

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