Class: SimpleHttp

Inherits:
Object
  • Object
show all
Defined in:
lib/simplehttp.rb

Overview

Wrapper around ruby’s standard net/http classes. Currently, only GET and POST https methods are supported. SimpleHttp provides class methods get and post to handle basic functionality. In case more complicated requests need to be made or default settings need to be overriden, it’s possible to instantiate SimpleHttp and use instance methods get and put.

Features:

  • Handles Redirects automatically

  • Proxy used transparently if http_proxy environment variable is set.

  • SSL handled automatically

  • fault tolerant uri, e.g. all of these would work: “www.example.com”, “www.example.com/”, “www.example.com

Some usage examples:

# plain GET (using class methods)
SimpleHttp.get "www.example.com"

# POST using the instance methods
uri = URI.parse "https://www.example.com/index.html"
sh = SimpleHttp uri
sh.set_proxy "my.proxy", "8080"
sh.post {"query" => "query_data"}

# POST using class methods.
binaryData = getImage
SimpleData.post binaryData, "image/png"

# GET requst with a custom request_header
sh = SimpleHttp.new "http://www.example.com"
sh.request_headers= {'X-Special-Http-Header'=>'my-value'}
sh.get

Constant Summary collapse

VERSION =
'0.1.5'
RESPONSE_HANDLERS =
{
  Net::HTTPResponse => lambda { |request, response, http| 
    http._update_response_headers(response)
    raise "#{response.to_s} : #{response.code} : #{http.uri}"
  },
  Net::HTTPSuccess => lambda { |request, response, http|
    http._update_response_headers(response)
    #http.cookies += response.cookies
    case request
    when Net::HTTP::Head, Net::HTTP::Options
      http.response_headers 
    else
      response.body
    end
  },
  Net::HTTPRedirection =>  lambda { |request, response, http|
    raise "too many redirects!" unless http.follow_num_redirects > 0  
    # create a new SimpleHttp for the location
    # refered to decreasing the remaining redirects
    # by one.
    
    if (location = response['location']) !~ /^https?:\/\//
      new_location = "#{http.uri.scheme}://#{http.uri.host}"
      if location =~ /^\//
        new_location += location
      else
        new_location += "/#{http.uri.path}/#{location}"
      end
      location = new_location  
    end

    sh = SimpleHttp.new location 
    #STDERR.puts location  
    sh.follow_num_redirects = http.follow_num_redirects-1

    # copy the response handlers used in the current
    # request in case they were non standard.
    sh.response_handlers = http.response_handlers

    # copy the request headers
    sh.request_headers=http.request_headers
    sh.response_headers=http.response_headers
    #sh.cookies+=http.cookies

    # copy host and port
    sh.uri.host = http.uri.host
    sh.uri.port = http.uri.port

    # HTTP doesn't permit redirects for methods other than
    # GET or HEAD. The exception is 303 redirects, which
    # should automatically follow the redirect URI using a
    # GET method regardless of the initial method. For
    # other classes of redirection, the client is required
    # to prompt the user before redirection occurs. Because
    # that's not a feasible action for this library, all
    # 3xx redirect URIs are followed using a GET method. 
    #
    # http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html

    case request
    when   Net::HTTP::Get, 
          Net::HTTP::Head,
          Net::HTTP::Options
      sh.get
    when Net::HTTP::Post
      sh.request_headers['content-length']=nil
      sh.get
     else 
       raise "Not a valid HTTP method for redirection: #{request.class}"
    end

  }

}

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(uri) ⇒ SimpleHttp

SimpleHttp can either be used directly through the get and post class methods or be instantiated, in case you need to to add custom behaviour to the requests.

Example:

http = SimpleHttp.new(URI.parse("http://www.example.com"))
http = SimpleHttp.new "www.example.com"
http = SimpleHttp.new "http://usr:[email protected]:1234"

Parameters:

  • may

    be a URI or a String.



156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
# File 'lib/simplehttp.rb', line 156

def initialize uri
  set_proxy ENV['http_proxy'] if ENV['http_proxy']
          
  if uri.class == String
    unless uri =~ /^https?:\/\//
      uri = "http://#{uri}"
    end
    uri = URI.parse uri
  end
  @uri = uri

  if !@uri.path || "" == @uri.path.strip
    @uri.path="/"
  end

  @request_headers={}
  @response_headers={}
  @cookies=[]
  @response_handlers=RESPONSE_HANDLERS.clone
  @follow_num_redirects=5

  if @uri.user
    basic_authentication @uri.user, @uri.password
  end

end

Instance Attribute Details

#follow_num_redirectsObject

The number of redirects we should follow. Default 5. An exception gets raised after the fifth redirect.



69
70
71
# File 'lib/simplehttp.rb', line 69

def follow_num_redirects
  @follow_num_redirects
end

#proxy_hostObject

Host component of proxy uri



52
53
54
# File 'lib/simplehttp.rb', line 52

def proxy_host
  @proxy_host
end

#proxy_portObject

Port component of proxy uri



54
55
56
# File 'lib/simplehttp.rb', line 54

def proxy_port
  @proxy_port
end

#proxy_pwdObject

Proxy Password



58
59
60
# File 'lib/simplehttp.rb', line 58

def proxy_pwd
  @proxy_pwd
end

#proxy_userObject

Proxy User



56
57
58
# File 'lib/simplehttp.rb', line 56

def proxy_user
  @proxy_user
end

#request_headersObject

Hash of headers that will be sent in the request.



62
63
64
# File 'lib/simplehttp.rb', line 62

def request_headers
  @request_headers
end

#response_handlersObject

A Hash of handlers for each class of HTTPResponse.



66
67
68
# File 'lib/simplehttp.rb', line 66

def response_handlers
  @response_handlers
end

#response_headersObject

Hash of headers that were set in the response.



64
65
66
# File 'lib/simplehttp.rb', line 64

def response_headers
  @response_headers
end

#uriObject (readonly)

The URI object to connect to



60
61
62
# File 'lib/simplehttp.rb', line 60

def uri
  @uri
end

Class Method Details

.get(uri, query = nil) ⇒ Object

Make a simple GET request to the provided URI.

Parameter

uri

the uri to connect to, may be a URI or a String

query

the query part of the get, may be a String or Hash

Usage:

puts(SimpleHttp.get("www.example.com"))
puts(SimpleHttp.get("www.example.com", "param"=>"value")


363
364
365
366
# File 'lib/simplehttp.rb', line 363

def self.get uri, query=nil
  http = SimpleHttp.new uri
  http.get query  
end

.head(uri, query = nil) ⇒ Object

Make a simple HEAD request

Parameter

see get



372
373
374
375
# File 'lib/simplehttp.rb', line 372

def self.head uri, query=nil
  http = SimpleHttp.new uri
  http.head query
end

.options(uri) ⇒ Object

Make a simple OPTIONS request



378
379
380
381
# File 'lib/simplehttp.rb', line 378

def self.options uri
  http = SimpleHttp.new uri
  http.options 
end

.post(uri, query = nil, content_type = 'application/x-www-form-urlencoded') ⇒ Object

Make a POST request to the provided URI.

Example:

puts(SimpleHttp.post("www.example.com", "query"=>"my_query"))

Alternatively, to post arbitrary data, all you need to do is set the appriate content_type:

SimpleHttp.post("http://www.example.com/", binary_data, "img/png")


399
400
401
402
# File 'lib/simplehttp.rb', line 399

def self.post uri, query=nil, content_type='application/x-www-form-urlencoded'
  http = SimpleHttp.new uri
  http.post query, content_type
end

.trace(uri) ⇒ Object

Make a simple TRACE request



384
385
386
387
# File 'lib/simplehttp.rb', line 384

def self.trace uri
  http = SimpleHttp.new uri
  http.trace
end

Instance Method Details

#_do_http(request) ⇒ Object

Internal

Parameter

request the Net::HTTPRequest to process.



329
330
331
332
333
334
335
336
337
338
339
340
341
342
# File 'lib/simplehttp.rb', line 329

def _do_http request
  http = Net::HTTP.new(@uri.host, @uri.port, 
                       @proxy_host, @proxy_port, @proxy_user, @proxy_pwd)
  http.use_ssl = @uri.scheme == 'https'
  request.basic_auth(@basic_auth[0], @basic_auth[1]) if @basic_auth
  
  # add custom request headers.
  @request_headers.each {|key,value|
    request[key]=value;
  }
  
  response = http.request(request)
  _handle_response(request, response);
end

#_handle_path(query = nil) ⇒ Object



463
464
465
466
467
468
# File 'lib/simplehttp.rb', line 463

def _handle_path query=nil
  if (query = _make_query query)
    @uri.query = @uri.query ? @uri.query+"&"+query : query
  end
  path = @uri.query ? "#{uri.path}?#{@uri.query}" : @uri.path
end

#_handle_response(http_request, http_response) ⇒ Object

Internal

Takes a HTTPResponse (or subclass) and determines how to handle the response. Default behaviour is:

HTTPSuccess : return the body of the response
HTTPRedirection : follow the redirect until success.
default : raise the HTTPResponse.

the default behaviour can be overidden by registering a response handler using the register_response_handler method.



304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
# File 'lib/simplehttp.rb', line 304

def _handle_response http_request, http_response
  raise "Not a Net::HTTPResponse" unless http_response.is_a? Net::HTTPResponse
  
  c = http_response.class
  # Go up the inheritance chain to find the most specific handler
  # for the class of response we received.
  while c!=Object
    # the response_handlers hash contains a handler
    # for the specific response class.
    if @response_handlers[c]
      return @response_handlers[c].call(http_request, http_response, self)
    end
    c=c.superclass
  end  

  # if we reached this place, no handler was registered
  # for this response. default is to return the response.
  
  return http_response
end

#_make_query(query) ⇒ Object

Internal



345
346
347
348
349
350
# File 'lib/simplehttp.rb', line 345

def _make_query query
  return query unless query && query.class == Hash
  query.inject([]) do |s, (key, value)|
    s << CGI::escape(key) + "=" + CGI::escape(value)
  end.join('&')
end

#_update_response_headers(http_response) ⇒ Object

Internal

Used in the response handler to set the value of the response header fields.



473
474
475
476
477
# File 'lib/simplehttp.rb', line 473

def _update_response_headers http_response
    http_response.each_header {|key, value|
      self.response_headers[key]=value  
    }
end

#basic_authentication(usr, pwd) ⇒ Object

Provides facilities to perform http basic authentication. You don’t need to provide usr and pwd if they are already included in the uri, i.e. user:[email protected]/

Usage:

sh = SimpleHttp.new "www.example.com/password_protected_resource"
sh.basic_authentication "user_name", "secret_password"
sh.get


194
195
196
# File 'lib/simplehttp.rb', line 194

def basic_authentication usr, pwd
  @basic_auth = [usr, pwd]
end

#get(query = nil) ⇒ Object

Call the get method as an instance method if you need to modify the default behaviour of the library, or set special headers:

http = SimpleHttp.new "www.example.com"
http.request_headers["X-Special"]="whatever"
str = http.get


411
412
413
414
415
# File 'lib/simplehttp.rb', line 411

def get query = nil
  req = Net::HTTP::Get.new( _handle_path(query) )
  # puts Net::HTTP::Proxy(@proxy_host, @proxy_port, @proxy_user, @proxy_pwd).get(@uri)
  _do_http req
end

#head(query = nil) ⇒ Object

Call the head method as an instance method. see head



420
421
422
423
424
# File 'lib/simplehttp.rb', line 420

def head query = nil
  req = Net::HTTP::Head.new( _handle_path(query) )
  # puts Net::HTTP::Proxy(@proxy_host, @proxy_port, @proxy_user, @proxy_pwd).get(@uri)
  _do_http req
end

#optionsObject

Call http options method. Returns the response see options



428
429
430
431
432
# File 'lib/simplehttp.rb', line 428

def options
  # we don't support sending a payload in options' body.
  req = Net::HTTP::Options.new(@uri.path)
  _do_http req
end

#post(query = nil, content_type = 'application/x-www-form-urlencoded') ⇒ Object

Post the query data to the url.

The body of the request remains empty if query=nil.

In case +query+ is a +Hash+, it's assumed that we are
sending a form.

In case +query+ is a +String+, it's also assumed that a
form is being sent, UNLESS the +content_type+ parameter
is set.


454
455
456
457
458
459
460
461
# File 'lib/simplehttp.rb', line 454

def post query=nil, content_type='application/x-www-form-urlencoded'
  req = Net::HTTP::Post.new( _handle_path() )
  req.body= _make_query query if query
  req.content_type=content_type if query
  req.content_length=query ? req.body.length : 0

  _do_http req
end

#register_response_handler(clazz, &block) ⇒ Object

This method can be used to register response handlers for specific http responses in case you need to override the default behaviour. Defaults are:

HTTPSuccess (200-206)

return the body of the response

HTTPRedirection (300-307)

follow the redirection until success

Others

raise an exception

Parameters:

clazz is the subclass of Net::HTTPResponse (or HTTPResponse in case you want to define “default” behaviour) that you are registering the handler for. E.g. to register a handler for a HTTP 303 response, clazz needs to be HTTPSeeOther.

block is the handler itself. When a response of the appropriate class is received by the library, block is called with three parameters: the ‘raw’ Net::HTTPRequest, the actual HTTPResponse object that was received and a reference to the instance of SimpleHttp that is executing the call.

Example:

# to override the default action of following a HTTP
# redirect, you could register the folllowing handler:

sh = SimpleHttp "www.example.com" 
sh.register_response_handler Net::HTTPRedirection {|request, response, shttp| 
  response['location'] 
}


231
232
233
234
235
236
237
238
239
# File 'lib/simplehttp.rb', line 231

def register_response_handler clazz, &block
  # completely unnecessary sanity check to make sure parameter
  # `clazz` is in fact a HTTPResponse ...
  unless clazz.ancestors.include? Net::HTTPResponse
    raise "Trying to register a response handler for non-response class: #{clazz}"   
  end
  @response_handlers[clazz]=block 

end

#set_proxy(proxy, port = nil, user = nil, pwd = nil) ⇒ Object



265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
# File 'lib/simplehttp.rb', line 265

def set_proxy proxy, port=nil, user=nil, pwd=nil
  
  
  if !proxy  
    @proxy_host=@proxy_port=@proxy_user=@proxy_pwd=nil 
    return
  end

  if String === proxy 
    if !port && !user && !pwd
      proxy = URI.parse(proxy)
    else 
      @proxy_host= host
      @proxy_port= port
      @proxy_user= user
      @proxy_pwd = pwd
    end
  end
  
  if URI::HTTP === proxy 
    @proxy_host= proxy.host
    @proxy_port= proxy.port
    @proxy_user= proxy.user
    @proxy_pwd = proxy.password
  end
end

#traceObject

Call http trace method. Returns the response see trace



436
437
438
439
440
# File 'lib/simplehttp.rb', line 436

def trace
  # payload? 
  req = Net::HTTP::Trace.new(@uri.path)
  _do_http req
end