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