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
    
        data/.gitignore
    ADDED
    
    
    
        data/Gemfile
    ADDED
    
    
    
        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
         |