Skip to content

Instantly share code, notes, and snippets.

@thinkerbot
Created November 5, 2009 20:57
Show Gist options
  • Save thinkerbot/227373 to your computer and use it in GitHub Desktop.
Save thinkerbot/227373 to your computer and use it in GitHub Desktop.
A session wrapper for curb
require 'curb'
class Session
# The internal Curl::Easy instance
attr_reader :curb
# The last response, or nil
attr_reader :last
def initialize
@curb ||= Curl::Easy.new
@curb.enable_cookies = true
@last = nil
end
# Make a get request using the query and headers. The query may be a query
# string, a hash, or an array of arrays. Yields curb to the block, if
# given, before the request is made.
#
# Returns a Response.
def get(url, query={}, headers={})
unless query.kind_of?(String)
query = query.collect do |key, value|
"#{curb.escape(key.to_s)}=#{curb.escape(value.to_s)}"
end.join('&')
end
curb.url = query.empty? ? url : "#{url}?#{build_query(query)}"
curb.headers = headers
yield(curb) if block_given?
curb.http_get
@last = Response.new(curb)
end
# Make a post request using the query and headers. The query may be a query
# string, a hash, or an array of arrays. Yields curb and the post fields to
# the block, if given, before submitting the request.
#
# If a query value is a File, then the file contents will be uploaded using
# 'multipart/formdata'. Otherwise the content type will be
# 'application/x-www-form-urlencoded'.
#
# Returns a Response.
def post(url, query={}, headers={})
curb.url = url
curb.headers = headers
curb.multipart_form_post = false
fields = []
query.each do |key, value|
case value
when File
curb.multipart_form_post = true
# it is not very efficient to close the file and re-open it
# for the post, but the block form does not appear to work
#
# Curl::PostField.file(key, value.path) { value.read }
fields << Curl::PostField.file(key, value.path)
value.close
else
fields << Curl::PostField.content(key, value)
end
end
yield(curb, fields) if block_given?
curb.http_post(*fields)
@last = Response.new(curb)
end
class Response
def initialize(curb)
@curb = curb
reset
end
# Returns the status line of the response.
def status_line
@status_line || parse_header(false)
end
# Returns a hash of the headers.
def headers
@headers || parse_header(true)
end
# Returns the response body.
def body
@curb.body_str
end
# Returns the response code.
def code
@curb.response_code
end
# Returns a hash of cookies in the header.
def cookies
cookies = {}
headers["Set-Cookie"].to_s.split(/;\s*/).each do |cookie|
key, value = cookie.split("=", 2)
cookies[key] = value
end
cookies
end
# Returns true if the response code == 302
def redirect?
code == 302
end
# Returns the redirect location.
def redirect_location
headers["Location"]
end
# Performs an HTTP get for the redirect_location and refreshes self to
# reflect the new response. The current state of self is overwritten.
def follow_redirect!
@curb.url = redirect_location
@curb.http_get
reset
self
end
private
# resets self so the status_line and headers will be re-parsed
def reset # :nodoc:
@status_line = nil
@headers = nil
end
# parses the headers and status_line, returns headers if return_headers is
# true and status_line otherwise
def parse_header(return_headers) # :nodoc:
lines = @curb.header_str.split(/\r\n/)
@status_line = lines.shift
@headers = {}
lines.each do |line|
key, value = line.split(/:\s*/, 2)
@headers[key] = value
end
return_headers ? @headers : @status_line
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment