httpi 1.1.1 → 2.0.0.rc1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,126 @@
1
+ require "httpi/adapter/base"
2
+ require "httpi/response"
3
+
4
+ module HTTPI
5
+ module Adapter
6
+
7
+ # An HTTPI adapter for `EventMachine::HttpRequest`. Due to limitations of
8
+ # the em-httprequest library, not all features are supported. In particular,
9
+ #
10
+ # * CA files,
11
+ # * certificate verification modes other than "none" and "peer,"
12
+ # * NTLM authentication,
13
+ # * digest authentication, and
14
+ # * password-protected certificate keys
15
+ #
16
+ # are supported by HTTPI but not em-httprequest.
17
+ #
18
+ # In addition, some features of em-httprequest are not represented in HTTPI
19
+ # and are therefore not supported. In particular,
20
+ #
21
+ # * SOCKS5 proxying,
22
+ # * automatic redirect following,
23
+ # * response streaming,
24
+ # * file body streaming,
25
+ # * keepalive,
26
+ # * pipelining, and
27
+ # * multi-request
28
+ #
29
+ # are supported by em-httprequest but not HTTPI.
30
+ class EmHttpRequest < Base
31
+
32
+ register :em_http, :deps => %w(em-synchrony em-synchrony/em-http em-http)
33
+
34
+ def initialize(request)
35
+ @request = request
36
+ @client = EventMachine::HttpRequest.new build_request_url(request.url)
37
+ end
38
+
39
+ attr_reader :client
40
+
41
+ def cert_directory
42
+ @cert_directory ||= "/tmp"
43
+ end
44
+
45
+ attr_writer :cert_directory
46
+
47
+ # Executes arbitrary HTTP requests.
48
+ # @see HTTPI.request
49
+ def request(method)
50
+ _request { |options| @client.send method, options }
51
+ end
52
+
53
+ private
54
+
55
+ def _request
56
+ options = client_options
57
+ setup_proxy(options) if @request.proxy
58
+ setup_http_auth(options) if @request.auth.http?
59
+
60
+ if @request.auth.ssl?
61
+ raise NotSupportedError, "EM-HTTP-Request does not support SSL client auth"
62
+ end
63
+
64
+ start_time = Time.now
65
+ respond_with yield(options), start_time
66
+ end
67
+
68
+ def client_options
69
+ {
70
+ :query => @request.url.query,
71
+ :connect_timeout => @request.open_timeout,
72
+ :inactivity_timeout => @request.read_timeout,
73
+ :head => @request.headers.to_hash,
74
+ :body => @request.body
75
+ }
76
+ end
77
+
78
+ def setup_proxy(options)
79
+ options[:proxy] = {
80
+ :host => @request.proxy.host,
81
+ :port => @request.proxy.port,
82
+ :authorization => [@request.proxy.user, @request.proxy.password]
83
+ }
84
+ end
85
+
86
+ def setup_http_auth(options)
87
+ unless @request.auth.type == :basic
88
+ raise NotSupportedError, "EM-HTTP-Request does only support HTTP basic auth"
89
+ end
90
+
91
+ options[:head] ||= {}
92
+ options[:head][:authorization] = @request.auth.credentials
93
+ end
94
+
95
+ def respond_with(http, start_time)
96
+ raise TimeoutError, "EM-HTTP-Request connection timed out: #{Time.now - start_time} sec" if http.response_header.status.zero?
97
+
98
+ Response.new http.response_header.status,
99
+ convert_headers(http.response_header), http.response
100
+ end
101
+
102
+ def build_request_url(url)
103
+ "%s://%s:%s%s" % [url.scheme, url.host, url.port, url.path]
104
+ end
105
+
106
+ # Takes any header names with an underscore as a word separator and
107
+ # converts the name to camel case, where words are separated by a dash.
108
+ #
109
+ # E.g. CONTENT_TYPE becomes Content-Type.
110
+ def convert_headers(headers)
111
+ return headers unless headers.keys.any? { |k| k =~ /_/ }
112
+
113
+ result = {}
114
+
115
+ headers.each do |k, v|
116
+ words = k.split("_")
117
+ key = words.map { |w| w.downcase.capitalize }.join("-")
118
+ result[key] = v
119
+ end
120
+
121
+ result
122
+ end
123
+ end
124
+
125
+ end
126
+ end
@@ -1,3 +1,4 @@
1
+ require "httpi/adapter/base"
1
2
  require "httpi/response"
2
3
 
3
4
  module HTTPI
@@ -7,86 +8,55 @@ module HTTPI
7
8
  #
8
9
  # Adapter for the HTTPClient client.
9
10
  # http://rubygems.org/gems/httpclient
10
- class HTTPClient
11
+ class HTTPClient < Base
11
12
 
12
- def initialize(request = nil)
13
- end
13
+ register :httpclient, :deps => %w(httpclient)
14
14
 
15
- # Returns a memoized <tt>HTTPClient</tt> instance.
16
- def client
17
- @client ||= ::HTTPClient.new
15
+ def initialize(request)
16
+ @request = request
17
+ @client = ::HTTPClient.new
18
18
  end
19
19
 
20
- # Executes an HTTP GET request.
21
- # @see HTTPI.get
22
- def get(request)
23
- do_request request do |url, headers|
24
- client.get url, nil, headers
25
- end
26
- end
20
+ attr_reader :client
27
21
 
28
- # Executes an HTTP POST request.
29
- # @see HTTPI.post
30
- def post(request)
31
- do_request request do |url, headers, body|
32
- client.post url, body, headers
33
- end
22
+ # Executes arbitrary HTTP requests.
23
+ # @see HTTPI.request
24
+ def request(method)
25
+ setup_client
26
+ respond_with @client.request(method, @request.url, nil, @request.body, @request.headers)
27
+ rescue OpenSSL::SSL::SSLError
28
+ raise SSLError
34
29
  end
35
30
 
36
- # Executes an HTTP HEAD request.
37
- # @see HTTPI.head
38
- def head(request)
39
- do_request request do |url, headers|
40
- client.head url, nil, headers
41
- end
42
- end
31
+ private
43
32
 
44
- # Executes an HTTP PUT request.
45
- # @see HTTPI.put
46
- def put(request)
47
- do_request request do |url, headers, body|
48
- client.put url, body, headers
49
- end
33
+ def setup_client
34
+ basic_setup
35
+ setup_auth if @request.auth.http?
36
+ setup_ssl_auth if @request.auth.ssl?
50
37
  end
51
38
 
52
- # Executes an HTTP DELETE request.
53
- # @see HTTPI.delete
54
- def delete(request)
55
- do_request request do |url, headers|
56
- client.delete url, headers
57
- end
39
+ def basic_setup
40
+ @client.proxy = @request.proxy if @request.proxy
41
+ @client.connect_timeout = @request.open_timeout if @request.open_timeout
42
+ @client.receive_timeout = @request.read_timeout if @request.read_timeout
58
43
  end
59
44
 
60
- private
61
-
62
- def do_request(request)
63
- setup_client request
64
- respond_with yield(request.url, request.headers, request.body)
65
- end
66
-
67
- def setup_client(request)
68
- basic_setup request
69
- setup_auth request if request.auth.http?
70
- setup_ssl_auth request.auth.ssl if request.auth.ssl?
45
+ def setup_auth
46
+ @client.set_auth @request.url, *@request.auth.credentials
71
47
  end
72
48
 
73
- def basic_setup(request)
74
- client.proxy = request.proxy if request.proxy
75
- client.connect_timeout = request.open_timeout if request.open_timeout
76
- client.receive_timeout = request.read_timeout if request.read_timeout
77
- end
49
+ def setup_ssl_auth
50
+ ssl = @request.auth.ssl
78
51
 
79
- def setup_auth(request)
80
- client.set_auth request.url, *request.auth.credentials
81
- end
82
-
83
- def setup_ssl_auth(ssl)
84
52
  unless ssl.verify_mode == :none
85
- client.ssl_config.client_cert = ssl.cert
86
- client.ssl_config.client_key = ssl.cert_key
87
- client.ssl_config.client_ca = ssl.ca_cert if ssl.ca_cert_file
53
+ @client.ssl_config.client_cert = ssl.cert
54
+ @client.ssl_config.client_key = ssl.cert_key
55
+ @client.ssl_config.add_trust_ca(ssl.ca_cert_file) if ssl.ca_cert_file
88
56
  end
89
- client.ssl_config.verify_mode = ssl.openssl_verify_mode
57
+
58
+ @client.ssl_config.verify_mode = ssl.openssl_verify_mode
59
+ @client.ssl_config.ssl_version = ssl.ssl_version if ssl.ssl_version
90
60
  end
91
61
 
92
62
  def respond_with(response)
@@ -1,4 +1,6 @@
1
1
  require "uri"
2
+
3
+ require "httpi/adapter/base"
2
4
  require "httpi/response"
3
5
 
4
6
  module HTTPI
@@ -8,89 +10,69 @@ module HTTPI
8
10
  #
9
11
  # Adapter for the Net::HTTP client.
10
12
  # http://ruby-doc.org/stdlib/libdoc/net/http/rdoc/
11
- class NetHTTP
13
+ class NetHTTP < Base
14
+
15
+ register :net_http, :deps => %w(net/https)
12
16
 
13
17
  def initialize(request)
14
- self.client = new_client request
18
+ @request = request
19
+ @client = create_client
15
20
  end
16
21
 
17
22
  attr_reader :client
18
23
 
19
- # Executes an HTTP GET request.
20
- # @see HTTPI.get
21
- def get(request)
22
- do_request :get, request do |http, get|
23
- http.request get
24
- end
25
- end
26
-
27
- # Executes an HTTP POST request.
28
- # @see HTTPI.post
29
- def post(request)
30
- do_request :post, request do |http, post|
31
- post.body = request.body
32
- http.request post
33
- end
34
- end
35
-
36
- # Executes an HTTP HEAD request.
37
- # @see HTTPI.head
38
- def head(request)
39
- do_request :head, request do |http, head|
40
- http.request head
24
+ # Executes arbitrary HTTP requests.
25
+ # @see HTTPI.request
26
+ def request(method)
27
+ unless REQUEST_METHODS.include? method
28
+ raise NotSupportedError, "Net::HTTP does not support custom HTTP methods"
41
29
  end
42
- end
43
-
44
- # Executes an HTTP PUT request.
45
- # @see HTTPI.put
46
- def put(request)
47
- do_request :put, request do |http, put|
48
- put.body = request.body
49
- http.request put
50
- end
51
- end
52
30
 
53
- # Executes an HTTP DELETE request.
54
- # @see HTTPI.delete
55
- def delete(request)
56
- do_request :delete, request do |http, delete|
57
- http.request delete
31
+ do_request(method) do |http, http_request|
32
+ http_request.body = @request.body
33
+ http.request http_request
58
34
  end
35
+ rescue OpenSSL::SSL::SSLError
36
+ raise SSLError
59
37
  end
60
38
 
61
- private
39
+ private
62
40
 
63
- attr_writer :client
64
-
65
- def new_client(request)
66
- proxy_url = request.proxy || URI("")
41
+ def create_client
42
+ proxy_url = @request.proxy || URI("")
67
43
  proxy = Net::HTTP::Proxy(proxy_url.host, proxy_url.port, proxy_url.user, proxy_url.password)
68
- proxy.new request.url.host, request.url.port
44
+ proxy.new(@request.url.host, @request.url.port)
69
45
  end
70
46
 
71
- def do_request(type, request)
72
- setup_client request
73
- setup_ssl_auth request.auth.ssl if request.auth.ssl?
47
+ def do_request(type)
48
+ setup_client
49
+ setup_ssl_auth if @request.auth.ssl?
74
50
 
75
- respond_with(client.start do |http|
76
- yield http, request_client(type, request)
51
+ respond_with(@client.start do |http|
52
+ yield http, request_client(type)
77
53
  end)
78
54
  end
79
55
 
80
- def setup_client(request)
81
- client.use_ssl = request.ssl?
82
- client.open_timeout = request.open_timeout if request.open_timeout
83
- client.read_timeout = request.read_timeout if request.read_timeout
56
+ def setup_client
57
+ @client.use_ssl = @request.ssl?
58
+ @client.open_timeout = @request.open_timeout if @request.open_timeout
59
+ @client.read_timeout = @request.read_timeout if @request.read_timeout
84
60
  end
85
61
 
86
- def setup_ssl_auth(ssl)
87
- client.key = ssl.cert_key
88
- client.cert = ssl.cert
89
- client.ca_file = ssl.ca_cert_file if ssl.ca_cert_file
90
- client.verify_mode = ssl.openssl_verify_mode
62
+ def setup_ssl_auth
63
+ ssl = @request.auth.ssl
64
+
65
+ unless ssl.verify_mode == :none
66
+ @client.key = ssl.cert_key
67
+ @client.cert = ssl.cert
68
+ @client.ca_file = ssl.ca_cert_file if ssl.ca_cert_file
69
+ end
70
+
71
+ @client.verify_mode = ssl.openssl_verify_mode
72
+ @client.ssl_version = ssl.ssl_version if ssl.ssl_version
91
73
  end
92
74
 
93
- def request_client(type, request)
75
+ def request_client(type)
94
76
  request_class = case type
95
77
  when :get then Net::HTTP::Get
96
78
  when :post then Net::HTTP::Post
@@ -99,8 +81,8 @@ module HTTPI
99
81
  when :delete then Net::HTTP::Delete
100
82
  end
101
83
 
102
- request_client = request_class.new request.url.request_uri, request.headers
103
- request_client.basic_auth *request.auth.credentials if request.auth.basic?
84
+ request_client = request_class.new @request.url.request_uri, @request.headers
85
+ request_client.basic_auth *@request.auth.credentials if @request.auth.basic?
104
86
 
105
87
  request_client
106
88
  end
@@ -10,6 +10,7 @@ module HTTPI
10
10
 
11
11
  VERIFY_MODES = [:none, :peer, :fail_if_no_peer_cert, :client_once]
12
12
  CERT_TYPES = [:pem, :der]
13
+ SSL_VERSIONS = [:TLSv1, :SSLv2, :SSLv3]
13
14
 
14
15
  # Returns whether SSL configuration is present.
15
16
  def present?
@@ -37,7 +38,11 @@ module HTTPI
37
38
 
38
39
  # Sets the cert type to validate SSL certificates PEM|DER.
39
40
  def cert_type=(type)
40
- raise ArgumentError, "Invalid SSL cert type: #{type}" unless CERT_TYPES.include? type
41
+ unless CERT_TYPES.include? type
42
+ raise ArgumentError, "Invalid SSL cert type #{type.inspect}\n" +
43
+ "Please specify one of #{CERT_TYPES.inspect}"
44
+ end
45
+
41
46
  @cert_type = type
42
47
  end
43
48
 
@@ -48,10 +53,29 @@ module HTTPI
48
53
 
49
54
  # Sets the SSL verify mode. Expects one of <tt>HTTPI::Auth::SSL::VERIFY_MODES</tt>.
50
55
  def verify_mode=(mode)
51
- raise ArgumentError, "Invalid SSL verify mode: #{mode}" unless VERIFY_MODES.include? mode
56
+ unless VERIFY_MODES.include? mode
57
+ raise ArgumentError, "Invalid SSL verify mode #{mode.inspect}\n" +
58
+ "Please specify one of #{VERIFY_MODES.inspect}"
59
+ end
60
+
52
61
  @verify_mode = mode
53
62
  end
54
63
 
64
+ # Returns the SSL version number. Defaults to <tt>nil</tt> (auto-negotiate).
65
+ def ssl_version
66
+ @ssl_version
67
+ end
68
+
69
+ # Sets the SSL version number. Expects one of <tt>HTTPI::Auth::SSL::SSL_VERSIONS</tt>.
70
+ def ssl_version=(version)
71
+ unless SSL_VERSIONS.include? version
72
+ raise ArgumentError, "Invalid SSL version #{version.inspect}\n" +
73
+ "Please specify one of #{SSL_VERSIONS.inspect}"
74
+ end
75
+
76
+ @ssl_version = version
77
+ end
78
+
55
79
  # Returns an <tt>OpenSSL::X509::Certificate</tt> for the +cert_file+.
56
80
  def cert
57
81
  @cert ||= (OpenSSL::X509::Certificate.new File.read(cert_file) if cert_file)