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_class
converts the:get
request intoGet
which 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.