curl_ffi 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
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