Skip to content

Instantly share code, notes, and snippets.

@slyphon
Created October 6, 2009 05:23
Show Gist options
  • Save slyphon/202785 to your computer and use it in GitHub Desktop.
Save slyphon/202785 to your computer and use it in GitHub Desktop.
require 'rubygems'
gem 'ffi', '= 0.3.5'
require 'ffi'
require 'fileutils'
# if defined?(JRUBY_VERSION)
# jsys = java.lang.System
# $stderr.puts "#{jsys.get_property('java.vm.name')} #{jsys.get_property('java.version')}"
# end
module CurlFfi
module Easy
extend FFI::Library
class CurlFfiError < StandardError
end
COTYPE_LONG = 0
COTYPE_OBJECTPOINT = 10_000
COTYPE_FUNCTIONPOINT = 20_000
COTYPE_OFF_T = 30_000
# constants defined in curl.h
CURL_GLOBAL_SSL = (1 << 0)
CURL_GLOBAL_WIN32 = (1 << 1)
CURL_GLOBAL_ALL = (CURL_GLOBAL_SSL|CURL_GLOBAL_WIN32)
CURL_GLOBAL_NOTHING = 0
CURL_GLOBAL_DEFAULT = CURL_GLOBAL_ALL
# options
CURLOPT_FILE = COTYPE_OBJECTPOINT + 1
CURLOPT_URL = COTYPE_OBJECTPOINT + 2
CURLOPT_ERRORBUFFER = COTYPE_OBJECTPOINT + 10
CURLOPT_WRITEFUNCTION = COTYPE_FUNCTIONPOINT + 11
# status
CURLE_OK = 0
ffi_lib "curl"
attach_function :_global_init, :curl_global_init, [:long], :int
attach_function :_init, :curl_easy_init, [], :pointer
attach_function :_cleanup, :curl_easy_cleanup, [:pointer], :void
attach_function :_setopt, :curl_easy_setopt, [:pointer, :int, :pointer], :int
attach_function :_setoptstr, :curl_easy_setopt, [:pointer, :int, :string], :int
attach_function :_setoptva, :curl_easy_setopt, [:pointer, :int, :varargs], :int
attach_function :_perform, :curl_easy_perform, [:pointer], :int
attach_function :_strerror, :curl_easy_strerror, [:int], :string
callback(:data_write_callback, [:pointer, :size_t, :size_t, :pointer], :size_t)
attach_function :_setwritefunc, :curl_easy_setopt, [ :pointer, :int, :data_write_callback ], :int
@@global_init_done = false unless defined?(@@global_init_done)
@@mutex = Mutex.new unless defined?(@@mutex)
attr_reader :url
def self.global_init!
@@mutex.synchronize do
return if @@global_init_done
@@global_init_done = true
_global_init(CURL_GLOBAL_DEFAULT)
end
end
class << self
def download(url)
fp = nil
handle = self._init
url_ptr = MemoryPointer.from_string(url)
self._setopt(handle, CURLOPT_URL, url_ptr)
body_str = ''
write_func = lambda do |ptr,size,nmemb,stream|
body_str << ptr.get_string(0, size*nmemb)
size * nmemb
end
self._setwritefunc(handle, CURLOPT_WRITEFUNCTION, write_func)
self._perform(handle)
$stdout << body_str
true
ensure
self._cleanup(handle) if handle
end
private
def raise_curl_errno!(val)
raise CurlFfiError, self._strerror(val)
end
end
end
end
def main
if ARGV.empty?
$stderr.puts "Usage: #{File.basename($0)} http://example.com/"
exit 1
end
CurlFfi::Easy.global_init!
url = ARGV.first
outpath = ARGV[1]
CurlFfi::Easy.download(url)
end
main if __FILE__ == $0
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment