lib/ethon/easy.rb
# frozen_string_literal: true require 'ethon/easy/informations' require 'ethon/easy/features' require 'ethon/easy/callbacks' require 'ethon/easy/options' require 'ethon/easy/header' require 'ethon/easy/util' require 'ethon/easy/params' require 'ethon/easy/form' require 'ethon/easy/http' require 'ethon/easy/operations' require 'ethon/easy/response_callbacks' require 'ethon/easy/debug_info' require 'ethon/easy/mirror' module Ethon # This is the class representing the libcurl easy interface # See http://curl.haxx.se/libcurl/c/libcurl-easy.html for more informations. # # @example You can access the libcurl easy interface through this class, every request is based on it. The simplest setup looks like that: # # e = Ethon::Easy.new(url: "www.example.com") # e.perform # #=> :ok # # @example You can the reuse this Easy for the next request: # # e.reset # reset easy handle # e.url = "www.google.com" # e.followlocation = true # e.perform # #=> :ok # # @see initialize class Easy include Ethon::Easy::Informations include Ethon::Easy::Callbacks include Ethon::Easy::Options include Ethon::Easy::Header include Ethon::Easy::Http include Ethon::Easy::Operations include Ethon::Easy::ResponseCallbacks extend Ethon::Easy::Features # Returns the curl return code. # # @return [ Symbol ] The return code. # * :ok: All fine. Proceed as usual. # * :unsupported_protocol: The URL you passed to libcurl used a # protocol that this libcurl does not support. The support # might be a compile-time option that you didn't use, it can # be a misspelled protocol string or just a protocol # libcurl has no code for. # * :failed_init: Very early initialization code failed. This # is likely to be an internal error or problem, or a # resource problem where something fundamental couldn't # get done at init time. # * :url_malformat: The URL was not properly formatted. # * :not_built_in: A requested feature, protocol or option # was not found built-in in this libcurl due to a build-time # decision. This means that a feature or option was not enabled # or explicitly disabled when libcurl was built and in # order to get it to function you have to get a rebuilt libcurl. # * :couldnt_resolve_proxy: Couldn't resolve proxy. The given # proxy host could not be resolved. # * :couldnt_resolve_host: Couldn't resolve host. The given remote # host was not resolved. # * :couldnt_connect: Failed to connect() to host or proxy. # * :ftp_weird_server_reply: After connecting to a FTP server, # libcurl expects to get a certain reply back. This error # code implies that it got a strange or bad reply. The given # remote server is probably not an OK FTP server. # * :remote_access_denied: We were denied access to the resource # given in the URL. For FTP, this occurs while trying to # change to the remote directory. # * :ftp_accept_failed: While waiting for the server to connect # back when an active FTP session is used, an error code was # sent over the control connection or similar. # * :ftp_weird_pass_reply: After having sent the FTP password to # the server, libcurl expects a proper reply. This error code # indicates that an unexpected code was returned. # * :ftp_accept_timeout: During an active FTP session while # waiting for the server to connect, the CURLOPT_ACCEPTTIMOUT_MS # (or the internal default) timeout expired. # * :ftp_weird_pasv_reply: libcurl failed to get a sensible result # back from the server as a response to either a PASV or a # EPSV command. The server is flawed. # * :ftp_weird_227_format: FTP servers return a 227-line as a response # to a PASV command. If libcurl fails to parse that line, # this return code is passed back. # * :ftp_cant_get_host: An internal failure to lookup the host used # for the new connection. # * :ftp_couldnt_set_type: Received an error when trying to set # the transfer mode to binary or ASCII. # * :partial_file: A file transfer was shorter or larger than # expected. This happens when the server first reports an expected # transfer size, and then delivers data that doesn't match the # previously given size. # * :ftp_couldnt_retr_file: This was either a weird reply to a # 'RETR' command or a zero byte transfer complete. # * :quote_error: When sending custom "QUOTE" commands to the # remote server, one of the commands returned an error code that # was 400 or higher (for FTP) or otherwise indicated unsuccessful # completion of the command. # * :http_returned_error: This is returned if CURLOPT_FAILONERROR is # set TRUE and the HTTP server returns an error code that is >= 400. # * :write_error: An error occurred when writing received data to a # local file, or an error was returned to libcurl from a write callback. # * :upload_failed: Failed starting the upload. For FTP, the server # typically denied the STOR command. The error buffer usually # contains the server's explanation for this. # * :read_error: There was a problem reading a local file or an error # returned by the read callback. # * :out_of_memory: A memory allocation request failed. This is serious # badness and things are severely screwed up if this ever occurs. # * :operation_timedout: Operation timeout. The specified time-out # period was reached according to the conditions. # * :ftp_port_failed: The FTP PORT command returned error. This mostly # happens when you haven't specified a good enough address for # libcurl to use. See CURLOPT_FTPPORT. # * :ftp_couldnt_use_rest: The FTP REST command returned error. This # should never happen if the server is sane. # * :range_error: The server does not support or accept range requests. # * :http_post_error: This is an odd error that mainly occurs due to # internal confusion. # * :ssl_connect_error: A problem occurred somewhere in the SSL/TLS # handshake. You really want the error buffer and read the message # there as it pinpoints the problem slightly more. Could be # certificates (file formats, paths, permissions), passwords, and others. # * :bad_download_resume: The download could not be resumed because # the specified offset was out of the file boundary. # * :file_couldnt_read_file: A file given with FILE:// couldn't be # opened. Most likely because the file path doesn't identify an # existing file. Did you check file permissions? # * :ldap_cannot_bind: LDAP cannot bind. LDAP bind operation failed. # * :ldap_search_failed: LDAP search failed. # * :function_not_found: Function not found. A required zlib function was not found. # * :aborted_by_callback: Aborted by callback. A callback returned # "abort" to libcurl. # * :bad_function_argument: Internal error. A function was called with # a bad parameter. # * :interface_failed: Interface error. A specified outgoing interface # could not be used. Set which interface to use for outgoing # connections' source IP address with CURLOPT_INTERFACE. # * :too_many_redirects: Too many redirects. When following redirects, # libcurl hit the maximum amount. Set your limit with CURLOPT_MAXREDIRS. # * :unknown_option: An option passed to libcurl is not recognized/known. # Refer to the appropriate documentation. This is most likely a # problem in the program that uses libcurl. The error buffer might # contain more specific information about which exact option it concerns. # * :telnet_option_syntax: A telnet option string was Illegally formatted. # * :peer_failed_verification: The remote server's SSL certificate or # SSH md5 fingerprint was deemed not OK. # * :got_nothing: Nothing was returned from the server, and under the # circumstances, getting nothing is considered an error. # * :ssl_engine_notfound: The specified crypto engine wasn't found. # * :ssl_engine_setfailed: Failed setting the selected SSL crypto engine as default! # * :send_error: Failed sending network data. # * :recv_error: Failure with receiving network data. # * :ssl_certproblem: problem with the local client certificate. # * :ssl_cipher: Couldn't use specified cipher. # * :bad_content_encoding: Unrecognized transfer encoding. # * :ldap_invalid_url: Invalid LDAP URL. # * :filesize_exceeded: Maximum file size exceeded. # * :use_ssl_failed: Requested FTP SSL level failed. # * :send_fail_rewind: When doing a send operation curl had to rewind the data to # retransmit, but the rewinding operation failed. # * :ssl_engine_initfailed: Initiating the SSL Engine failed. # * :login_denied: The remote server denied curl to login # * :tftp_notfound: File not found on TFTP server. # * :tftp_perm: Permission problem on TFTP server. # * :remote_disk_full: Out of disk space on the server. # * :tftp_illegal: Illegal TFTP operation. # * :tftp_unknownid: Unknown TFTP transfer ID. # * :remote_file_exists: File already exists and will not be overwritten. # * :tftp_nosuchuser: This error should never be returned by a properly # functioning TFTP server. # * :conv_failed: Character conversion failed. # * :conv_reqd: Caller must register conversion callbacks. # * :ssl_cacert_badfile: Problem with reading the SSL CA cert (path? access rights?): # * :remote_file_not_found: The resource referenced in the URL does not exist. # * :ssh: An unspecified error occurred during the SSH session. # * :ssl_shutdown_failed: Failed to shut down the SSL connection. # * :again: Socket is not ready for send/recv wait till it's ready and try again. # This return code is only returned from curl_easy_recv(3) and curl_easy_send(3) # * :ssl_crl_badfile: Failed to load CRL file # * :ssl_issuer_error: Issuer check failed # * :ftp_pret_failed: The FTP server does not understand the PRET command at # all or does not support the given argument. Be careful when # using CURLOPT_CUSTOMREQUEST, a custom LIST command will be sent with PRET CMD # before PASV as well. # * :rtsp_cseq_error: Mismatch of RTSP CSeq numbers. # * :rtsp_session_error: Mismatch of RTSP Session Identifiers. # * :ftp_bad_file_list: Unable to parse FTP file list (during FTP wildcard downloading). # * :chunk_failed: Chunk callback reported error. # * :obsolete: These error codes will never be returned. They were used in an old # libcurl version and are currently unused. # # @see http://curl.haxx.se/libcurl/c/libcurl-errors.html attr_accessor :return_code # Initialize a new Easy. # It initializes curl, if not already done and applies the provided options. # Look into {Ethon::Easy::Options Options} to see what you can provide in the # options hash. # # @example Create a new Easy. # Easy.new(url: "www.google.de") # # @param [ Hash ] options The options to set. # @option options :headers [ Hash ] Request headers. # # @return [ Easy ] A new Easy. # # @see Ethon::Easy::Options # @see http://curl.haxx.se/libcurl/c/curl_easy_setopt.html def initialize(options = {}) Curl.init set_attributes(options) set_callbacks end # Set given options. # # @example Set options. # easy.set_attributes(options) # # @param [ Hash ] options The options. # # @raise InvalidOption # # @see initialize def set_attributes(options) options.each_pair do |key, value| method = "#{key}=" unless respond_to?(method) raise Errors::InvalidOption.new(key) end send(method, value) end end # Reset easy. This means resetting all options and instance variables. # Also the easy handle is resetted. # # @example Reset. # easy.reset def reset @url = nil @escape = nil @hash = nil @on_complete = nil @on_headers = nil @on_body = nil @on_progress = nil @procs = nil @mirror = nil Curl.easy_reset(handle) set_callbacks end # Clones libcurl session handle. This means that all options that is set in # the current handle will be set on duplicated handle. def dup e = super e.handle = Curl.easy_duphandle(handle) e.instance_variable_set(:@body_write_callback, nil) e.instance_variable_set(:@header_write_callback, nil) e.instance_variable_set(:@debug_callback, nil) e.instance_variable_set(:@progress_callback, nil) e.set_callbacks e end # Url escapes the value. # # @example Url escape. # easy.escape(value) # # @param [ String ] value The value to escape. # # @return [ String ] The escaped value. # # @api private def escape(value) string_pointer = Curl.easy_escape(handle, value, value.bytesize) returned_string = string_pointer.read_string Curl.free(string_pointer) returned_string end # Returns the informations available through libcurl as # a hash. # # @return [ Hash ] The informations hash. def to_hash Kernel.warn("Ethon: Easy#to_hash is deprecated and will be removed, please use #mirror.") mirror.to_hash end def mirror @mirror ||= Mirror.from_easy(self) end # Return pretty log out. # # @example Return log out. # easy.log_inspect # # @return [ String ] The log out. def log_inspect "EASY #{mirror.log_informations.map{|k, v| "#{k}=#{v}"}.flatten.join(' ')}" end end end