curl_ffi 0.0.2
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/.gitignore +3 -0
- data/Gemfile +9 -0
- data/curl_ffi.gemspec +24 -0
- data/examples/evented_multi.rb +223 -0
- data/examples/perform_multi.rb +22 -0
- data/examples/select_multi.rb +219 -0
- data/lib/curl_ffi/curl_bindings.rb +985 -0
- data/lib/curl_ffi/easy.rb +86 -0
- data/lib/curl_ffi/multi.rb +66 -0
- data/lib/curl_ffi/version.rb +3 -0
- data/lib/curl_ffi.rb +13 -0
- data/spec/curl/easy_spec.rb +84 -0
- data/spec/curl/multi_spec.rb +81 -0
- data/spec/spec_helper.rb +3 -0
- metadata +108 -0
@@ -0,0 +1,86 @@
|
|
1
|
+
require "curl_ffi"
|
2
|
+
|
3
|
+
module CurlFFI
|
4
|
+
class Easy
|
5
|
+
attr_reader :pointer
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
@pointer = FFI::AutoPointer.new(CurlFFI.easy_init, CurlFFI.method(:easy_cleanup))
|
9
|
+
end
|
10
|
+
|
11
|
+
def reset
|
12
|
+
CurlFFI.easy_reset(@pointer)
|
13
|
+
end
|
14
|
+
|
15
|
+
def initialize_copy(other)
|
16
|
+
@pointer = FFI::AutoPointer.new(CurlFFI.easy_duphandle(other.pointer), CurlFFI.method(:easy_cleanup))
|
17
|
+
end
|
18
|
+
|
19
|
+
def escape(string)
|
20
|
+
str_pointer = CurlFFI.easy_escape(@pointer, string, string.length)
|
21
|
+
result = str_pointer.null? ? nil : str_pointer.read_string
|
22
|
+
CurlFFI.free(str_pointer)
|
23
|
+
result
|
24
|
+
end
|
25
|
+
|
26
|
+
def unescape(string)
|
27
|
+
int_pointer = FFI::MemoryPointer.new(:int)
|
28
|
+
str_pointer = CurlFFI.easy_unescape(@pointer, string, string.length, int_pointer)
|
29
|
+
result = str_pointer.read_string(int_pointer.read_int)
|
30
|
+
CurlFFI.free(str_pointer)
|
31
|
+
result
|
32
|
+
end
|
33
|
+
|
34
|
+
def error_string(error_code)
|
35
|
+
CurlFFI.easy_strerror(error_code)
|
36
|
+
end
|
37
|
+
|
38
|
+
def perform
|
39
|
+
check_code(CurlFFI.easy_perform(@pointer))
|
40
|
+
end
|
41
|
+
|
42
|
+
def setopt(option, value)
|
43
|
+
check_code(CurlFFI.easy_setopt(@pointer, option, value))
|
44
|
+
end
|
45
|
+
|
46
|
+
def getinfo(info)
|
47
|
+
info = INFO[info] if info.is_a?(Symbol)
|
48
|
+
|
49
|
+
if info > CurlFFI::INFO_SLIST
|
50
|
+
raise "Not implemented yet"
|
51
|
+
elsif info > CurlFFI::INFO_DOUBLE
|
52
|
+
getinfo_double(info)
|
53
|
+
elsif info > CurlFFI::INFO_LONG
|
54
|
+
getinfo_long(info)
|
55
|
+
elsif info > CurlFFI::INFO_STRING
|
56
|
+
getinfo_string(info)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
protected
|
61
|
+
def check_code(result)
|
62
|
+
if result != :OK
|
63
|
+
raise "Error - #{result}"
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def getinfo_double(info)
|
68
|
+
double_ptr = FFI::MemoryPointer.new(:double)
|
69
|
+
check_code(CurlFFI.easy_getinfo(@pointer, info, double_ptr))
|
70
|
+
double_ptr.read_double
|
71
|
+
end
|
72
|
+
|
73
|
+
def getinfo_string(info)
|
74
|
+
pointer = FFI::MemoryPointer.new(:pointer)
|
75
|
+
check_code(CurlFFI.easy_getinfo(@pointer, info, pointer))
|
76
|
+
string_ptr = pointer.read_pointer
|
77
|
+
string_ptr.null? ? nil : string_ptr.read_string
|
78
|
+
end
|
79
|
+
|
80
|
+
def getinfo_long(info)
|
81
|
+
long_ptr = FFI::MemoryPointer.new(:long)
|
82
|
+
check_code(CurlFFI.easy_getinfo(@pointer, info, long_ptr))
|
83
|
+
long_ptr.read_long
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
require "curl_ffi"
|
2
|
+
|
3
|
+
module CurlFFI
|
4
|
+
class Multi
|
5
|
+
attr_reader :pointer, :running
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
@pointer = FFI::AutoPointer.new(CurlFFI.multi_init, CurlFFI.method(:multi_cleanup))
|
9
|
+
@running = -1
|
10
|
+
@handles = []
|
11
|
+
@messages_in_queue = 0
|
12
|
+
end
|
13
|
+
|
14
|
+
# @todo handle return code
|
15
|
+
def add_handle(easy)
|
16
|
+
CurlFFI.multi_add_handle(@pointer, easy.pointer)
|
17
|
+
@handles << easy # Save the handle so it won't be gc'ed
|
18
|
+
end
|
19
|
+
|
20
|
+
# @todo handle return code
|
21
|
+
def remove_handle(easy)
|
22
|
+
CurlFFI.multi_remove_handle(@pointer, easy.pointer)
|
23
|
+
@handles.delete(easy) # Save the handle so it won't be gc'ed
|
24
|
+
end
|
25
|
+
|
26
|
+
def setopt(option, param)
|
27
|
+
CurlFFI.multi_setopt(@pointer, option, param)
|
28
|
+
end
|
29
|
+
|
30
|
+
def info_read_all()
|
31
|
+
messages = []
|
32
|
+
while message = info_read_next
|
33
|
+
messages << message
|
34
|
+
end
|
35
|
+
messages
|
36
|
+
end
|
37
|
+
|
38
|
+
def info_read_next()
|
39
|
+
int_pointer = FFI::MemoryPointer.new(:int)
|
40
|
+
message_pointer = CurlFFI.multi_info_read(@pointer, int_pointer)
|
41
|
+
@messages_in_queue = int_pointer.read_int
|
42
|
+
message_pointer.null? ? nil : CurlFFI::Message.new(message_pointer)
|
43
|
+
end
|
44
|
+
|
45
|
+
# @todo handle return code
|
46
|
+
def socket_action(sockfd, ev_bitmask = 0)
|
47
|
+
int_pointer = FFI::MemoryPointer.new(:int)
|
48
|
+
result = CurlFFI.multi_socket_action(@pointer, sockfd, ev_bitmask, int_pointer)
|
49
|
+
@running = int_pointer.read_int
|
50
|
+
result
|
51
|
+
end
|
52
|
+
|
53
|
+
def perform()
|
54
|
+
int_pointer = FFI::MemoryPointer.new(:int)
|
55
|
+
result = CurlFFI.multi_perform(@pointer, int_pointer)
|
56
|
+
@running = int_pointer.read_int
|
57
|
+
result
|
58
|
+
end
|
59
|
+
|
60
|
+
def timeout
|
61
|
+
long_pointer = FFI::MemoryPointer.new(:long)
|
62
|
+
result = CurlFFI.multi_timeout(@pointer, long_pointer)
|
63
|
+
long_pointer.read_long
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
data/lib/curl_ffi.rb
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
$:.unshift(File.dirname(__FILE__)) unless
|
2
|
+
$:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
|
3
|
+
|
4
|
+
require 'ffi'
|
5
|
+
|
6
|
+
require "curl_ffi/ffi_bindings"
|
7
|
+
|
8
|
+
module CurlFFI
|
9
|
+
extend CurlFFI::CurlBindings
|
10
|
+
|
11
|
+
autoload :Easy, "curl_ffi/easy"
|
12
|
+
autoload :Multi, "curl_ffi/multi"
|
13
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
module Curl
|
4
|
+
describe Easy, ".new" do
|
5
|
+
it "should return a new Curl::Easy object" do
|
6
|
+
Easy.new.should be_a(Easy)
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
describe Easy do
|
11
|
+
before :each do
|
12
|
+
@easy = Easy.new
|
13
|
+
end
|
14
|
+
|
15
|
+
describe "#dup" do
|
16
|
+
it "should return a new Curl::Easy object" do
|
17
|
+
@easy.dup.should be_a(Easy)
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should have a seperate libCurl handle" do
|
21
|
+
@easy.dup.pointer.should_not equal(@easy.pointer)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
describe "#reset" do
|
26
|
+
it "should reset all set options" do
|
27
|
+
# TODO how would one test that?
|
28
|
+
@easy.should respond_to(:reset)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
describe "#setopt" do
|
33
|
+
it "should correctly set string options" do
|
34
|
+
@easy.setopt(OPTION[:URL], "http://google.de")
|
35
|
+
end
|
36
|
+
|
37
|
+
it "should correctly set string options" do
|
38
|
+
@easy.setopt(OPTION[:PORT], 123)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
describe "#getinfo" do
|
43
|
+
it "should return internal information" do
|
44
|
+
@easy.getinfo(INFO[:EFFECTIVE_URL]).should == ""
|
45
|
+
|
46
|
+
@easy.setopt(OPTION[:URL], "http://google.de")
|
47
|
+
@easy.getinfo(INFO[:EFFECTIVE_URL]).should == "http://google.de"
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
describe "#perform" do
|
52
|
+
# it "should perform the request" do
|
53
|
+
# @easy.setopt(OPTION[:URL], "http://google.de")
|
54
|
+
# @easy.perform
|
55
|
+
# end
|
56
|
+
end
|
57
|
+
|
58
|
+
describe "#escape" do
|
59
|
+
it "should escape the passed string" do
|
60
|
+
str = @easy.escape("Hello World!")
|
61
|
+
str.should == "Hello%20World%21"
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
describe "#unescape" do
|
66
|
+
it "should unescape the passed string" do
|
67
|
+
str = @easy.unescape("Hello%20World%21")
|
68
|
+
str.should == "Hello World!"
|
69
|
+
end
|
70
|
+
|
71
|
+
it "should correctly unescape strings containing '%00'" do
|
72
|
+
str = @easy.unescape("%00Test")
|
73
|
+
str.should == "\00Test"
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
describe "#error_string" do
|
78
|
+
it "should return a string describing the passed error code" do
|
79
|
+
@easy.error_string(:OK).should == "No error"
|
80
|
+
@easy.error_string(:UNSUPPORTED_PROTOCOL).should == "Unsupported protocol"
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
module Curl
|
4
|
+
describe Multi, ".new" do
|
5
|
+
it "should return a new Curl::Easy object" do
|
6
|
+
Multi.new.should be_a(Multi)
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
describe Multi do
|
11
|
+
before :each do
|
12
|
+
@multi = Multi.new
|
13
|
+
@easy = Easy.new
|
14
|
+
@easy.setopt(OPTION[:PROXY], "")
|
15
|
+
end
|
16
|
+
|
17
|
+
describe "#add_handle" do
|
18
|
+
it "should add the easy object to this multi objects handles" do
|
19
|
+
@multi.add_handle(@easy)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
describe "#info_read_next" do
|
24
|
+
it "should return nil if there are no messages" do
|
25
|
+
@multi.info_read_next.should be_nil
|
26
|
+
end
|
27
|
+
|
28
|
+
it "should return Curl::Message objects when messages are available" do
|
29
|
+
@easy.setopt(OPTION[:URL], "http://google.de")
|
30
|
+
@multi.add_handle(@easy)
|
31
|
+
|
32
|
+
@multi.perform while @multi.running != 0
|
33
|
+
message = @multi.info_read_next
|
34
|
+
message.should be_a(Curl::Message)
|
35
|
+
message[:msg].should == :DONE
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
describe "#info_read_all" do
|
40
|
+
it "should return an empty array if there are no messages" do
|
41
|
+
@multi.info_read_all.should == []
|
42
|
+
end
|
43
|
+
|
44
|
+
it "should return an array of Curl::Message objects when messages are available" do
|
45
|
+
@easy.setopt(OPTION[:URL], "http://google.de")
|
46
|
+
@multi.add_handle(@easy)
|
47
|
+
|
48
|
+
@multi.perform while @multi.running != 0
|
49
|
+
messages = @multi.info_read_all
|
50
|
+
messages.size.should == 1
|
51
|
+
|
52
|
+
messages.first.should be_a(Curl::Message)
|
53
|
+
messages.first[:msg].should == :DONE
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
describe "#timeout" do
|
58
|
+
it "should return -1 if there is no timer set yet" do
|
59
|
+
@multi.timeout.should == -1
|
60
|
+
end
|
61
|
+
|
62
|
+
it "should return the timeout till the next call to #perform" do
|
63
|
+
@easy.setopt(OPTION[:URL], "http://google.de")
|
64
|
+
@multi.add_handle(@easy)
|
65
|
+
|
66
|
+
@multi.timeout.should == 1
|
67
|
+
@multi.perform
|
68
|
+
@multi.timeout.should == 1
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
describe "#perform" do
|
73
|
+
before :each do
|
74
|
+
@easy.setopt(OPTION[:URL], "http://google.de")
|
75
|
+
@multi.add_handle(@easy)
|
76
|
+
end
|
77
|
+
|
78
|
+
it "should perform the request"
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,108 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: curl_ffi
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 0
|
8
|
+
- 2
|
9
|
+
version: 0.0.2
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- Arthur Schreiber
|
13
|
+
- Scott Gonyea
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2010-11-05 00:00:00 -07:00
|
19
|
+
default_executable:
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
22
|
+
name: ffi
|
23
|
+
prerelease: false
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ">="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
segments:
|
30
|
+
- 0
|
31
|
+
version: "0"
|
32
|
+
type: :runtime
|
33
|
+
version_requirements: *id001
|
34
|
+
- !ruby/object:Gem::Dependency
|
35
|
+
name: rspec
|
36
|
+
prerelease: false
|
37
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
38
|
+
none: false
|
39
|
+
requirements:
|
40
|
+
- - ">="
|
41
|
+
- !ruby/object:Gem::Version
|
42
|
+
segments:
|
43
|
+
- 0
|
44
|
+
version: "0"
|
45
|
+
type: :development
|
46
|
+
version_requirements: *id002
|
47
|
+
description: An FFI based libCurl interface, intended to serve as a common backend for existing interfaces to libcurl
|
48
|
+
email:
|
49
|
+
- schreiber.arthur@gmail.com
|
50
|
+
executables: []
|
51
|
+
|
52
|
+
extensions: []
|
53
|
+
|
54
|
+
extra_rdoc_files: []
|
55
|
+
|
56
|
+
files:
|
57
|
+
- .gitignore
|
58
|
+
- Gemfile
|
59
|
+
- curl_ffi.gemspec
|
60
|
+
- examples/evented_multi.rb
|
61
|
+
- examples/perform_multi.rb
|
62
|
+
- examples/select_multi.rb
|
63
|
+
- lib/curl_ffi.rb
|
64
|
+
- lib/curl_ffi/curl_bindings.rb
|
65
|
+
- lib/curl_ffi/easy.rb
|
66
|
+
- lib/curl_ffi/multi.rb
|
67
|
+
- lib/curl_ffi/version.rb
|
68
|
+
- spec/curl/easy_spec.rb
|
69
|
+
- spec/curl/multi_spec.rb
|
70
|
+
- spec/spec_helper.rb
|
71
|
+
has_rdoc: true
|
72
|
+
homepage: http://github.com/nokarma/curl-ffi
|
73
|
+
licenses: []
|
74
|
+
|
75
|
+
post_install_message:
|
76
|
+
rdoc_options: []
|
77
|
+
|
78
|
+
require_paths:
|
79
|
+
- lib
|
80
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
81
|
+
none: false
|
82
|
+
requirements:
|
83
|
+
- - ">="
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
segments:
|
86
|
+
- 0
|
87
|
+
version: "0"
|
88
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
90
|
+
requirements:
|
91
|
+
- - ">="
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
segments:
|
94
|
+
- 1
|
95
|
+
- 3
|
96
|
+
- 6
|
97
|
+
version: 1.3.6
|
98
|
+
requirements: []
|
99
|
+
|
100
|
+
rubyforge_project: curl-ffi
|
101
|
+
rubygems_version: 1.3.7
|
102
|
+
signing_key:
|
103
|
+
specification_version: 3
|
104
|
+
summary: An FFI based libCurl interface
|
105
|
+
test_files:
|
106
|
+
- spec/curl/easy_spec.rb
|
107
|
+
- spec/curl/multi_spec.rb
|
108
|
+
- spec/spec_helper.rb
|