Created
December 31, 2009 10:09
-
-
Save chrismcg/266686 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require 'rubygems' | |
require 'ffi' | |
class Kurl | |
extend ::FFI::Library | |
ffi_lib 'libcurl' | |
CURLOPT_URL = 10002 | |
CURLOPT_WRITEFUNCTION = 20011 | |
CURLOPT_TIMEOUT = 13 | |
CURLOPT_NOPROGRESS = 43 | |
CURLOPT_PROGRESSFUNCTION = 20056 | |
CURLOPT_FOLLOWLOCATION = 52 | |
CURLOPT_FORBID_REUSE = 75 | |
CURLOPT_VERBOSE = 41 | |
CURLOPT_ERRORBUFFER = 10010 | |
# libcurl error codes | |
CURLcode = enum [ | |
:ok, 0, | |
:unsupported_protocol, # 1 | |
:failed_init, # 2 | |
:url_malformat, # 3 | |
:obsolete4, # 4 - NOT USED | |
:couldnt_resolve_proxy, # 5 | |
:couldnt_resolve_host, # 6 | |
:couldnt_connect, # 7 | |
:ftp_weird_server_reply, # 8 | |
:remote_access_denied, # 9 a service was denied by the server | |
# due to lack of access - when login fails | |
# this is not returned. | |
:obsolete10, # 10 - NOT USED | |
:ftp_weird_pass_reply, # 11 | |
:obsolete12, # 12 - NOT USED | |
:ftp_weird_pasv_reply, # 13 | |
:ftp_weird_227_format, # 14 | |
:ftp_cant_get_host, # 15 | |
:obsolete16, # 16 - NOT USED | |
:ftp_couldnt_set_type, # 17 | |
:partial_file, # 18 | |
:ftp_couldnt_retr_file, # 19 | |
:obsolete20, # 20 - NOT USED | |
:quote_error, # 21 - quote command failure | |
:http_returned_error, # 22 | |
:write_error, # 23 | |
:obsolete24, # 24 - NOT USED | |
:upload_failed, # 25 - failed upload "command" | |
:read_error, # 26 - couldn't open/read from file | |
:out_of_memory, # 27 | |
# Note: CURLE_OUT_OF_MEMORY may sometimes indicate a conversion error | |
# instead of a memory allocation error if CURL_DOES_CONVERSIONS | |
# is defined | |
:operation_timedout, # 28 - the timeout time was reached | |
:obsolete29, # 29 - NOT USED | |
:ftp_port_failed, # 30 - FTP PORT operation failed | |
:ftp_couldnt_use_rest, # 31 - the REST command failed | |
:obsolete32, # 32 - NOT USED | |
:range_error, # 33 - RANGE "command" didn't work | |
:http_post_error, # 34 | |
:ssl_connect_error, # 35 - wrong when connecting with SSL | |
:bad_download_resume, # 36 - couldn't resume download | |
:file_couldnt_read_file, # 37 | |
:ldap_cannot_bind, # 38 | |
:ldap_search_failed, # 39 | |
:obsolete40, # 40 - NOT USED | |
:function_not_found, # 41 | |
:aborted_by_callback, # 42 | |
:bad_function_argument, # 43 | |
:obsolete44, # 44 - NOT USED | |
:interface_failed, # 45 - CURLOPT_INTERFACE failed | |
:obsolete46, # 46 - NOT USED | |
:too_many_redirects, # 47 - catch endless re-direct loops | |
:unknown_telnet_option, # 48 - User specified an unknown option | |
:telnet_option_syntax, # 49 - Malformed telnet option | |
:obsolete50, # 50 - NOT USED | |
:peer_failed_verification, # 51 - peer's certificate or fingerprint wasn't verified fine | |
:got_nothing, # 52 - when this is a specific error | |
:ssl_engine_notfound, # 53 - SSL crypto engine not found | |
:ssl_engine_setfailed, # 54 - can not set SSL crypto engine as default | |
:send_error, # 55 - failed sending network data | |
:recv_error, # 56 - failure in receiving network data | |
:obsolete57, # 57 - NOT IN USE | |
:ssl_certproblem, # 58 - problem with the local certificate | |
:ssl_cipher, # 59 - couldn't use specified cipher | |
:ssl_cacert, # 60 - problem with the CA cert (path?) | |
:bad_content_encoding, # 61 - Unrecognized transfer encoding | |
:ldap_invalid_url, # 62 - Invalid LDAP URL | |
:filesize_exceeded, # 63 - Maximum file size exceeded | |
:use_ssl_failed, # 64 - Requested FTP SSL level failed | |
:send_fail_rewind, # 65 - Sending the data requires a rewind that failed | |
:ssl_engine_initfailed, # 66 - failed to initialise ENGINE | |
:login_denied, # 67 - user password or similar was not accepted and we failed to login | |
:tftp_notfound, # 68 - file not found on server | |
:tftp_perm, # 69 - permission problem on server | |
:remote_disk_full, # 70 - out of disk space on server | |
:tftp_illegal, # 71 - Illegal TFTP operation | |
:tftp_unknownid, # 72 - Unknown transfer ID | |
:remote_file_exists, # 73 - File already exists | |
:tftp_nosuchuser, # 74 - No such user | |
:conv_failed, # 75 - conversion failed | |
:conv_reqd, # 76 - caller must register conversion callbacks using curl_easy_setopt options | |
# CURLOPT_CONV_FROM_NETWORK_FUNCTION, | |
# CURLOPT_CONV_TO_NETWORK_FUNCTION, and | |
# CURLOPT_CONV_FROM_UTF8_FUNCTION | |
:ssl_cacert_badfile, # 77 - could not load cacert file missing or wrong format | |
:remote_file_not_found, # 78 - remote file not found | |
:ssh, # 79 - error from the ssh layer somewhat | |
# generic so the error message will be of | |
# interest when this has happened | |
:ssl_shutdown_failed, # 80 - Failed to shut down the SSL connection | |
:again, # 81 - socket is not ready for send/recv | |
# wait till it's ready and try again (Added in 7.18.2) | |
:ssl_crl_badfile, # 82 - could not load crl file missing or wrong format (Added in 7.19.0) | |
:ssl_issuer_error, # 83 - Issuer check failed. (Added in 7.19.0) | |
:curl_last # never use! | |
] | |
attach_function 'curl_global_init', [:long], :int | |
attach_function 'curl_easy_init', [], :pointer | |
attach_function 'curl_easy_cleanup', [:pointer], :pointer | |
attach_function 'curl_easy_perform', [:pointer], :int | |
attach_function 'curl_easy_strerror', [:int], :string | |
# curl_easy_setopt has different parameter types | |
attach_function 'curl_easy_setopt', [:pointer, :int, :pointer], :int | |
attach_function 'curl_easy_setopt_long', 'curl_easy_setopt', [:pointer, :int, :long], :int | |
# size_t write_data(void *buffer, size_t size, size_t nmemb, void *userp); | |
callback :write_data_callback, [:pointer, :int, :int, :pointer], :int | |
attach_function 'curl_easy_setopt_write_callback', 'curl_easy_setopt', [:pointer, :int, :write_data_callback], :int | |
# typedef int (*curl_progress_callback)(void *clientp, double dltotal, double dlnow, double ultotal, double ulnow); | |
callback :progress_callback, [:pointer, :double, :double, :double], :int | |
attach_function 'curl_easy_setopt_progress_callback', 'curl_easy_setopt', [:pointer, :int, :progress_callback], :int | |
def on_write(&block) | |
curl_easy_setopt_write_callback @handle, CURLOPT_WRITEFUNCTION, block | |
end | |
def on_progress(&block) | |
curl_easy_setopt_long @handle, CURLOPT_NOPROGRESS, 0 | |
curl_easy_setopt_progress_callback @handle, CURLOPT_PROGRESSFUNCTION, block | |
end | |
def timeout=(value) | |
curl_easy_setopt_long @handle, CURLOPT_TIMEOUT, value | |
end | |
def forbid_reuse! | |
curl_easy_setopt_long @handle, CURLOPT_FORBID_REUSE, 1 | |
end | |
def follow_location! | |
curl_easy_setopt_long @handle, CURLOPT_FOLLOWLOCATION, 1 | |
end | |
def verbose! | |
curl_easy_setopt_long @handle, CURLOPT_VERBOSE, 1 | |
end | |
def url=(url) | |
curl_easy_setopt @handle, CURLOPT_URL, url | |
end | |
def initialize | |
result = curl_global_init(0) | |
puts "Error initializing libcurl: #{result}" && exit unless result.zero? | |
@handle = curl_easy_init | |
@error_buffer = '' | |
curl_easy_setopt @handle, CURLOPT_ERRORBUFFER, @error_buffer | |
end | |
def perform | |
result = curl_easy_perform(@handle) | |
if result != CURLcode[:ok] | |
puts curl_easy_strerror(result) | |
if @error_buffer != '' | |
puts @error_buffer | |
end | |
end | |
curl_easy_cleanup(@handle) | |
end | |
def self.download(url, filename) | |
kurl = new | |
kurl.url = url | |
kurl.follow_location! | |
kurl.forbid_reuse! | |
yield kurl | |
file = File.open(filename, 'w+') | |
kurl.on_write do |buffer, size, nmemb, userp| | |
file << buffer.read_string(size * nmemb) | |
size * nmemb | |
end | |
kurl.perform | |
file.close | |
end | |
end | |
Kurl.download(ARGV[0], ARGV[1]) do |kurl| | |
kurl.timeout = 10 * 60 | |
# kurl.verbose! | |
kurl.on_progress do |clientp, download_total, downloaded, upload_total, uploaded| | |
if download_total > 0 | |
puts "Downloaded #{downloaded} of #{download_total} bytes" | |
else | |
puts "Downloaded #{downloaded} bytes" | |
end | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment