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 ADDED
@@ -0,0 +1,3 @@
1
+ pkg/*
2
+ *.gem
3
+ .bundle
data/Gemfile ADDED
@@ -0,0 +1,9 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in gsolr.gemspec
4
+ gemspec
5
+
6
+ group :test do
7
+ gem 'rspec'
8
+ gem 'rspec-core'
9
+ end
data/curl_ffi.gemspec ADDED
@@ -0,0 +1,24 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "curl_ffi/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "curl_ffi"
7
+ s.version = CurlFFI::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.authors = ["Arthur Schreiber", "Scott Gonyea"]
10
+ s.email = ["schreiber.arthur@gmail.com"]
11
+ s.homepage = "http://github.com/nokarma/curl-ffi"
12
+ s.summary = "An FFI based libCurl interface"
13
+ s.description = "An FFI based libCurl interface, intended to serve as a common backend for existing interfaces to libcurl"
14
+
15
+ s.required_rubygems_version = ">= 1.3.6"
16
+ s.rubyforge_project = "curl-ffi"
17
+
18
+ s.add_dependency "ffi"
19
+ s.add_development_dependency "rspec"
20
+
21
+ s.files = `git ls-files`.split("\n")
22
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
23
+ s.require_paths = ["lib"]
24
+ end
@@ -0,0 +1,223 @@
1
+ # Shows how to use the libcurl-ffi interface in combination with eventmachine.
2
+ # Inspired by the different hiperfifo examples on the libcurl site.
3
+ require "rubygems"
4
+ require "benchmark"
5
+ require "eventmachine"
6
+
7
+ require File.expand_path("../lib/curl", File.dirname(__FILE__))
8
+
9
+ if FFI::Platform.windows?
10
+ # Sockets returned by curl are WinSock SOCKETs
11
+ # As we want to wrap these sockets in Ruby's IO interface (using IO.for_fd),
12
+ # we have to first get the SOCKET's filehandle using _get_osfhandle.
13
+ #
14
+ # To later get the SOCKET again, we can use _open_osfhandle again.
15
+ module WinSock
16
+ extend FFI::Library
17
+ ffi_lib FFI::Library::LIBC
18
+ attach_function :_get_osfhandle, [:int], :long
19
+ attach_function :_open_osfhandle, [:long, :int], :int
20
+ end
21
+
22
+ def get_socket(io)
23
+ WinSock._get_osfhandle(io.fileno)
24
+ end
25
+
26
+ def get_io(socket)
27
+ FFI::IO.for_fd(WinSock._open_osfhandle(socket, 0), "r")
28
+ end
29
+ else
30
+ def get_socket(io)
31
+ io.fileno
32
+ end
33
+
34
+ def get_io(socket)
35
+ IO.for_fd(socket, "r")
36
+ end
37
+ end
38
+
39
+ $sockets = {}
40
+
41
+ module CurlHandler
42
+ def notify_readable
43
+ begin
44
+ rc = $multi.socket_action(get_socket(@io), 1)
45
+ end while rc == :CALL_MULTI_PERFORM
46
+ mcode_or_die("event_cb: curl_multi_socket", rc)
47
+ check_run_count
48
+ if $multi.running <= 0
49
+ puts "last transfer done, kill timeout\n"
50
+ EM.stop
51
+ end
52
+ end
53
+
54
+ def notify_writable
55
+ begin
56
+ rc = $multi.socket_action(get_socket(@io), 2)
57
+ end while rc == :CALL_MULTI_PERFORM
58
+ mcode_or_die("event_cb: curl_multi_socket", rc)
59
+ check_run_count
60
+ if $multi.running <= 0
61
+ puts "last transfer done, kill timeout\n"
62
+ EM.stop
63
+ end
64
+ end
65
+ end
66
+
67
+ def addsock(socket, easy, action)
68
+ io = get_io(socket)
69
+ $sockets[socket] = {
70
+ :io => io,
71
+ :action => action,
72
+ :connection => EM.watch(io, CurlHandler)
73
+ }
74
+ setsock(socket, easy, action)
75
+ end
76
+
77
+ def setsock(socket, easy, action)
78
+ $sockets[socket][:action] = action
79
+ conn = $sockets[socket][:connection]
80
+ conn.notify_readable = action & 1 != 0
81
+ conn.notify_writable = action & 2 != 0
82
+ end
83
+
84
+ def remsock(socket)
85
+ puts "Removing Socket #{socket}"
86
+ $sockets[socket][:connection].detach
87
+ $sockets.delete(socket)
88
+ end
89
+
90
+ sock_callback = FFI::Function.new(:int, [:pointer, :int, :int]) do |easy_ptr, socket, what|
91
+ whatstr = [ "none", "IN", "OUT", "INOUT", "REMOVE" ]
92
+ puts("socket callback: s=%d e=%p what=%s " % [socket, easy_ptr, whatstr[what]])
93
+
94
+ if what == 4
95
+ remsock(socket)
96
+ puts ""
97
+ else
98
+ if $sockets[socket].nil?
99
+ puts "Adding data: %s\n" % whatstr[what]
100
+ addsock(socket, easy_ptr, what)
101
+ else
102
+ puts "Changing action from %s to %s\n" % [whatstr[$sockets[socket][:action]], whatstr[what]]
103
+ setsock(socket, easy_ptr, what)
104
+ end
105
+ end
106
+
107
+ 0
108
+ end
109
+
110
+ def mcode_or_die(where, code)
111
+ return if code == :OK
112
+ puts "ERROR: %s returns %s\n" % [where, code]
113
+ exit unless code == :BAD_SOCKET
114
+ end
115
+
116
+ $prev_running = 0
117
+ def check_run_count
118
+ if $prev_running > $multi.running
119
+ puts "REMAINING: %d\n" % $multi.running
120
+
121
+ while message = $multi.info_read_next
122
+ if message[:msg] == :DONE
123
+ puts "Done!"
124
+ end
125
+ end
126
+ end
127
+ $prev_running = $multi.running
128
+ end
129
+
130
+ $timer = nil
131
+
132
+ multi_timer_callback = FFI::Function.new(:int, [:pointer, :long]) do |multi_ptr, timeout_ms|
133
+ puts "multi_timer_cb: Setting timeout to %d ms\n" % timeout_ms
134
+
135
+ $timer && $timer.cancel
136
+ $timer = EventMachine::Timer.new(timeout_ms / 1000.0) {
137
+ begin
138
+ rc = $multi.socket_action(CurlFFI::SOCKET_TIMEOUT, 0)
139
+ puts rc
140
+ end while rc == :CALL_MULTI_PERFORM
141
+ mcode_or_die("timer_cb: curl_multi_socket_action", rc)
142
+ check_run_count
143
+ }
144
+
145
+ 0
146
+ end
147
+
148
+ def progress_callback(url_pointer, dltotal, dlnow, ultotal, ulnow)
149
+ puts "[#{Time.now}]Progress: %s (%g/%g)\n" % [url_pointer.read_string, dlnow, dltotal]
150
+ return 0
151
+ end
152
+
153
+ def write_callback(easy, size, nmemb, data)
154
+ realsize = size * nmemb
155
+ return realsize
156
+ end
157
+
158
+ PROGRESS_CALLBACK = FFI::Function.new(:int, [:pointer, :double, :double, :double, :double], &self.method(:progress_callback))
159
+ WRITE_CALLBACK = FFI::Function.new(:size_t, [:pointer, :size_t, :size_t, :pointer], &self.method(:write_callback))
160
+
161
+ $multi = CurlFFI::Multi.new
162
+ $multi.setopt(:SOCKETFUNCTION, sock_callback)
163
+ $multi.setopt(:TIMERFUNCTION, multi_timer_callback)
164
+
165
+ EventMachine::run {
166
+ [ "http://www.microsoft.com",
167
+ "http://www.opensource.org",
168
+ "http://www.google.com",
169
+ "http://www.yahoo.com",
170
+ "http://www.ibm.com",
171
+ "http://www.mysql.com",
172
+ "http://www.oracle.com",
173
+ "http://www.ripe.net",
174
+ "http://www.iana.org",
175
+ "http://www.amazon.com",
176
+ "http://www.netcraft.com",
177
+ "http://www.heise.de",
178
+ "http://www.chip.de",
179
+ "http://www.ca.com",
180
+ "http://www.cnet.com",
181
+ "http://www.news.com",
182
+ "http://www.cnn.com",
183
+ "http://www.wikipedia.org",
184
+ "http://www.dell.com",
185
+ "http://www.hp.com",
186
+ "http://www.cert.org",
187
+ "http://www.mit.edu",
188
+ "http://www.nist.gov",
189
+ "http://www.ebay.com",
190
+ "http://www.playstation.com",
191
+ "http://www.uefa.com",
192
+ "http://www.ieee.org",
193
+ "http://www.apple.com",
194
+ "http://www.sony.com",
195
+ "http://www.symantec.com",
196
+ "http://www.zdnet.com",
197
+ "http://www.fujitsu.com",
198
+ "http://www.supermicro.com",
199
+ "http://www.hotmail.com",
200
+ "http://www.ecma.com",
201
+ "http://www.bbc.co.uk",
202
+ "http://news.google.com",
203
+ "http://www.foxnews.com",
204
+ "http://www.msn.com",
205
+ "http://www.wired.com",
206
+ "http://www.sky.com",
207
+ "http://www.usatoday.com",
208
+ "http://www.cbs.com",
209
+ "http://www.nbc.com",
210
+ "http://slashdot.org",
211
+ "http://www.bloglines.com",
212
+ "http://www.techweb.com",
213
+ "http://www.newslink.org" ].each do |url|
214
+ e = CurlFFI::Easy.new
215
+ e.setopt(:PROXY, "")
216
+ e.setopt(:URL, url)
217
+ e.setopt(:NOPROGRESS, 0)
218
+ e.setopt(:PROGRESSFUNCTION, PROGRESS_CALLBACK)
219
+ e.setopt(:WRITEFUNCTION, WRITE_CALLBACK)
220
+ e.setopt(:PROGRESSDATA, url)
221
+ $multi.add_handle(e)
222
+ end
223
+ }
@@ -0,0 +1,22 @@
1
+ require "rubygems"
2
+
3
+ require File.expand_path("../lib/curl", File.dirname(__FILE__))
4
+
5
+ multi = CurlFFI::Multi.new
6
+
7
+ e = CurlFFI::Easy.new
8
+ e.setopt(:PROXY, "")
9
+ e.setopt(:URL, "http://www.un.org")
10
+
11
+ multi.add_handle(e)
12
+
13
+
14
+ e = CurlFFI::Easy.new
15
+ e.setopt(:PROXY, "")
16
+ e.setopt(:URL, "http://www.google.com")
17
+
18
+ multi.add_handle(e)
19
+
20
+ begin
21
+ multi.perform
22
+ end while multi.running != 0
@@ -0,0 +1,219 @@
1
+ # Shows how to use the libcurl-ffi interface in combination with eventmachine.
2
+ # Inspired by the different hiperfifo examples on the libcurl site.
3
+ require "rubygems"
4
+ require "benchmark"
5
+ require "eventmachine"
6
+
7
+ require File.expand_path("../lib/curl", File.dirname(__FILE__))
8
+
9
+ if FFI::Platform.windows?
10
+ # Sockets returned by curl are WinSock SOCKETs
11
+ # As we want to wrap these sockets in Ruby's IO interface (using IO.for_fd),
12
+ # we have to first get the SOCKET's filehandle using _get_osfhandle.
13
+ #
14
+ # To later get the SOCKET again, we can use _open_osfhandle again.
15
+ module WinSock
16
+ extend FFI::Library
17
+ ffi_lib FFI::Library::LIBC
18
+ attach_function :_get_osfhandle, [:int], :long
19
+ attach_function :_open_osfhandle, [:long, :int], :int
20
+ end
21
+
22
+ def get_socket(io)
23
+ WinSock._get_osfhandle(io.fileno)
24
+ end
25
+
26
+ def get_io(socket)
27
+ FFI::IO.for_fd(WinSock._open_osfhandle(socket, 0), "r")
28
+ end
29
+ else
30
+ def get_socket(io)
31
+ io.fileno
32
+ end
33
+
34
+ def get_io(socket)
35
+ IO.for_fd(socket, "r")
36
+ end
37
+ end
38
+
39
+ $sockets = { }
40
+
41
+ def addsock(socket, easy, action)
42
+ io = get_io(socket)
43
+ $sockets[socket] = {
44
+ :io => io,
45
+ :action => action,
46
+ }
47
+ setsock(socket, easy, action)
48
+ end
49
+
50
+ def setsock(socket, easy, action)
51
+ $sockets[socket][:action] = action
52
+ end
53
+
54
+ def remsock(socket)
55
+ puts "Removing Socket #{socket}"
56
+ $sockets.delete(socket)
57
+ end
58
+
59
+ def progress_callback(url_pointer, dltotal, dlnow, ultotal, ulnow)
60
+ puts "[#{Time.now}]Progress: %s (%g/%g)\n" % [url_pointer.read_string, dlnow, dltotal]
61
+ return 0
62
+ end
63
+
64
+ def write_callback(easy, size, nmemb, data)
65
+ realsize = size * nmemb
66
+ return realsize
67
+ end
68
+
69
+ def mcode_or_die(where, code)
70
+ return if code == :OK
71
+ puts "ERROR: %s returns %s\n" % [where, code]
72
+ exit unless code == :BAD_SOCKET
73
+ end
74
+
75
+ PROGRESS_CALLBACK = FFI::Function.new(:int, [:pointer, :double, :double, :double, :double], &self.method(:progress_callback))
76
+ WRITE_CALLBACK = FFI::Function.new(:size_t, [:pointer, :size_t, :size_t, :pointer], &self.method(:write_callback))
77
+
78
+ sock_callback = FFI::Function.new(:int, [:pointer, :int, :int]) do |easy_ptr, socket, what|
79
+ whatstr = [ "none", "IN", "OUT", "INOUT", "REMOVE" ]
80
+ puts("socket callback: s=%d e=%p what=%s " % [socket, easy_ptr, whatstr[what]])
81
+
82
+ if what == 4
83
+ remsock(socket)
84
+ puts ""
85
+ else
86
+ if $sockets[socket].nil?
87
+ puts "Adding data: %s\n" % whatstr[what]
88
+ addsock(socket, easy_ptr, what)
89
+ else
90
+ puts "Changing action from %s to %s\n" % [whatstr[$sockets[socket][:action]], whatstr[what]]
91
+ setsock(socket, easy_ptr, what)
92
+ end
93
+ end
94
+
95
+ 0
96
+ end
97
+
98
+ multi_timer_callback = FFI::Function.new(:int, [:pointer, :long]) do |multi_ptr, timeout_ms|
99
+ puts "multi_timer_cb: Setting timeout to %d ms\n" % timeout_ms
100
+
101
+ $timeout = timeout_ms
102
+
103
+ 0
104
+ end
105
+
106
+ $prev_running = 0
107
+ def check_run_count
108
+ if $prev_running > $multi.running
109
+ puts "REMAINING: %d\n" % $multi.running
110
+
111
+ while message = $multi.info_read_next
112
+ if message[:msg] == :DONE
113
+ puts "Done!"
114
+ end
115
+ end
116
+ end
117
+ $prev_running = $multi.running
118
+ end
119
+
120
+ $timeout = 0
121
+ $multi = CurlFFI::Multi.new
122
+ $multi.setopt(:SOCKETFUNCTION, sock_callback)
123
+ $multi.setopt(:TIMERFUNCTION, multi_timer_callback)
124
+
125
+ [ "http://www.microsoft.com",
126
+ "http://www.opensource.org",
127
+ "http://www.google.com",
128
+ "http://www.yahoo.com",
129
+ "http://www.ibm.com",
130
+ "http://www.mysql.com",
131
+ "http://www.oracle.com",
132
+ "http://www.ripe.net",
133
+ "http://www.iana.org",
134
+ "http://www.amazon.com",
135
+ "http://www.netcraft.com",
136
+ "http://www.heise.de",
137
+ "http://www.chip.de",
138
+ "http://www.ca.com",
139
+ "http://www.cnet.com",
140
+ "http://www.news.com",
141
+ "http://www.cnn.com",
142
+ "http://www.wikipedia.org",
143
+ "http://www.dell.com",
144
+ "http://www.hp.com",
145
+ "http://www.cert.org",
146
+ "http://www.mit.edu",
147
+ "http://www.nist.gov",
148
+ "http://www.ebay.com",
149
+ "http://www.playstation.com",
150
+ "http://www.uefa.com",
151
+ "http://www.ieee.org",
152
+ "http://www.apple.com",
153
+ "http://www.sony.com",
154
+ "http://www.symantec.com",
155
+ "http://www.zdnet.com",
156
+ "http://www.fujitsu.com",
157
+ "http://www.supermicro.com",
158
+ "http://www.hotmail.com",
159
+ "http://www.ecma.com",
160
+ "http://www.bbc.co.uk",
161
+ "http://news.google.com",
162
+ "http://www.foxnews.com",
163
+ "http://www.msn.com",
164
+ "http://www.wired.com",
165
+ "http://www.sky.com",
166
+ "http://www.usatoday.com",
167
+ "http://www.cbs.com",
168
+ "http://www.nbc.com",
169
+ "http://slashdot.org",
170
+ "http://www.bloglines.com",
171
+ "http://www.techweb.com",
172
+ "http://www.newslink.org" ].each do |url|
173
+ e = CurlFFI::Easy.new
174
+ e.setopt(:PROXY, "")
175
+ e.setopt(:URL, url)
176
+ e.setopt(:NOPROGRESS, 0)
177
+ e.setopt(:PROGRESSFUNCTION, PROGRESS_CALLBACK)
178
+ e.setopt(:WRITEFUNCTION, WRITE_CALLBACK)
179
+ e.setopt(:PROGRESSDATA, url)
180
+ $multi.add_handle(e)
181
+ end
182
+
183
+ begin
184
+ to_read = $sockets.select { |k, v| v[:action] & 1 != 0 }.map { |x| x[1][:io] }
185
+ to_write = $sockets.select { |k, v| v[:action] & 2 != 0 }.map { |x| x[1][:io] }
186
+
187
+ read, write, err = IO.select(to_read, to_write, [], $timeout / 1000.0)
188
+
189
+ if read
190
+ read.each do |io|
191
+ begin
192
+ rc = $multi.socket_action(get_socket(io), 1)
193
+ end while rc == :CALL_MULTI_PERFORM
194
+ mcode_or_die("event_cb: curl_multi_socket", rc)
195
+ check_run_count
196
+ end
197
+ end
198
+
199
+ if write
200
+ write.each do |io|
201
+ begin
202
+ rc = $multi.socket_action(get_socket(io), 2)
203
+ end while rc == :CALL_MULTI_PERFORM
204
+ mcode_or_die("event_cb: curl_multi_socket", rc)
205
+ check_run_count
206
+ end
207
+ end
208
+
209
+ if !read && !write
210
+ puts "!!! Socket timeout"
211
+ begin
212
+ rc = $multi.socket_action(CurlFFI::SOCKET_TIMEOUT, 0)
213
+ puts rc
214
+ end while rc == :CALL_MULTI_PERFORM
215
+ mcode_or_die("timer_cb: curl_multi_socket_action", rc)
216
+ check_run_count
217
+ end
218
+
219
+ end while $multi.running > 0