After looking at the other RestClient::Resource methods from yesterday, I noticed that they are all almost identical to #get. So today I’m going to dig into the code one level down, RestClient::Request.
The Code
RestClient::Request#execute
1 2 3 4 5 6 7 |
module RestClient class Request def self.execute(args, &block) new(args).execute &block end end end |
RestClient::Resource#get calls this method, which is just a wrapper for creating a new Request object and calling it’s #execute method.
RestClient::Request#initialize
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
def initialize args @method = args[:method] or raise ArgumentError, "must pass :method" @url = args[:url] or raise ArgumentError, "must pass :url" @headers = args[:headers] || {} @cookies = @headers.delete(:cookies) || args[:cookies] || {} @payload = Payload.generate(args[:payload]) @user = args[:user] @password = args[:password] @timeout = args[:timeout] @open_timeout = args[:open_timeout] @raw_response = args[:raw_response] || false @verify_ssl = args[:verify_ssl] || false @ssl_client_cert = args[:ssl_client_cert] || nil @ssl_client_key = args[:ssl_client_key] || nil @ssl_ca_file = args[:ssl_ca_file] || nil @tf = nil # If you are a raw request, this is your tempfile @processed_headers = make_headers headers @args = args end |
#initialize just checks the args and sets a bunch of attributes. The :method and :url attributes are required, which if you remember #get sets automatically. It’s also good that the original arguments are stored away into @args, I always forget to do that.
RestClient::Request#execute
1 2 3 4 |
def execute &block uri = parse_url_with_auth(url) transmit uri, net_http_request_class(method).new(uri.request_uri, processed_headers), payload, &block end |
After initialize, the class method #execute calls the object method #execute above. There is a lot happening in these short lines so I need to break it out a bit:
- the request uri is parsed out using
parse_url_with_auth. -
net_http_request_classconverts the:getrequest intoGetwhich is an actual[net/http](http://www.ensta.fr/~diam/ruby/online/ruby-doc-stdlib/libdoc/net/http/rdoc/classes/Net/HTTP.html)class. - then a new Net/HTTP object is created, given the uri and request headers.
- finally the uri, Net/HTTP request object, payload, and block is passed to
#transmit
RestClient::Request#transmit
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
def transmit uri, req, payload, &block setup_credentials req net = net_http_class.new(uri.host, uri.port) net.use_ssl = uri.is_a?(URI::HTTPS) if @verify_ssl == false net.verify_mode = OpenSSL::SSL::VERIFY_NONE elsif @verify_ssl.is_a? Integer net.verify_mode = @verify_ssl end net.cert = @ssl_client_cert if @ssl_client_cert net.key = @ssl_client_key if @ssl_client_key net.ca_file = @ssl_ca_file if @ssl_ca_file net.read_timeout = @timeout if @timeout net.open_timeout = @open_timeout if @open_timeout RestClient.before_execution_procs.each do |before_proc| before_proc.call(req, args) end log_request net.start do |http| res = http.request(req, payload) { |http_response| fetch_body(http_response) } log_response res process_result res, &block end rescue EOFError raise RestClient::ServerBrokeConnection rescue Timeout::Error raise RestClient::RequestTimeout end end end |
Now I’ve found where the request is sent.
This is one of the core methods of RestClient. It starts with a bit of setup for the credentials and SSL. Sending the actual request is pretty simple and just uses net/http and a few helper methods:
1 2 3 4 5 |
net.start do |http| res = http.request(req, payload) { |http_response| fetch_body(http_response) } log_response res process_result res, &block end |
It also looks like transmit is use two separate net/http objects; req and net. I’m not really sure why they needed two objects, especially since only one of them is actually used to connect to the server.
Now that I’ve seen the Request side, I think I’ll take a look at the response side.