http_client 0.5.0-universal-java-1.7

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: a120508893a08fdf41e1cd67421138ae3e043088
4
+ data.tar.gz: eca3be3cb0a0f5c12f112ceb8a384a99269b6081
5
+ SHA512:
6
+ metadata.gz: 3d4477baa911fbfdd8750816571d862f4a55e892e7e90fd39e9483668f9e634ff62970f1d722f6651fe9ea5b27ae9867556bfb8a4e23760d59e49b7384343eb4
7
+ data.tar.gz: b1dd2383ee8362db2199e942cf297872315e3e90f3d0cdadac17602a1e8810b2bf090ac34984537f4ad5f8496dd499038fab013cfaa35eb0d512e63d1c45379e
data/README.md ADDED
@@ -0,0 +1,79 @@
1
+ # A simple yet powerful HTTP client for JRuby
2
+
3
+ This library wraps the Apache HTTPClient (4.3) in a simple fashion.
4
+ It currently implements all common HTTP verbs, connection pooling, retries and transparent gzip response handling. All common exceptions from Javaland are wrapped as Ruby exceptions. The library is intended to be used in a multithreaded environment.
5
+
6
+ ## Examples
7
+
8
+ #### A simple GET request
9
+
10
+ ```ruby
11
+ require "http_client"
12
+
13
+ client = HTTPClient.new
14
+ response = client.get("http://www.google.com/robots.txt")
15
+
16
+ response.status
17
+ # => 200
18
+
19
+ response.body
20
+ # =>
21
+ # User-agent: *
22
+ # ...
23
+ ```
24
+
25
+ #### POST a form
26
+
27
+ ```ruby
28
+ response = client.post("http://geocities.com/darrensden/guestbook",
29
+ :form => {
30
+ :name => "Joe Stub",
31
+ :email => "joey@ymail.com",
32
+ :comment => "Hey, I really like your site! Awesome stuff"
33
+ }
34
+ )
35
+ ```
36
+
37
+ #### POST JSON data
38
+
39
+ ```ruby
40
+ response = client.post("http://webtwoopointo.com/v1/api/guestbooks/123/comments",
41
+ :json => {
42
+ :name => "Jason",
43
+ :email => "jaz0r@gmail.com",
44
+ :comment => "Yo dawg, luv ur site!"
45
+ }
46
+ )
47
+ ```
48
+
49
+ #### Provide request headers
50
+
51
+ ```ruby
52
+ response = client.get("http://secretservice.com/users/123",
53
+ :headers => { "X-Auth": "deadbeef23" }
54
+ )
55
+ ```
56
+
57
+ #### Using a connection pool
58
+
59
+ ```ruby
60
+ $client = HTTPClient.new(
61
+ :use_connection_pool => true,
62
+ :max_connections => 10,
63
+ )
64
+
65
+ %[www.google.de www.yahoo.com www.altavista.com].each do |host|
66
+ Thread.new do
67
+ response = $client.get("http://#{host}/robots.txt")
68
+ puts response.body
69
+ end
70
+ end
71
+ ```
72
+
73
+ ## Contribute
74
+
75
+ This library covers just what I need. I wanted to have a thread safe HTTP client that has a fixed connection pool with fine grained timeout configurations.
76
+
77
+ Before you start hacking away, have a look at the issues. There might be stuff that is already in the making. If so, there will be a published branch you can contribute to.
78
+
79
+ Just create a fork and send me a pull request. I would be honored to look at your input.
Binary file
@@ -0,0 +1,15 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = "http_client"
3
+ s.version = "0.5.0"
4
+ s.date = "2013-11-07"
5
+ s.summary = "HTTP client for JRuby"
6
+ s.description = "This library wraps the Apache HTTPClient (4.3) in a simple fashion. The library is intended to be used in a multithreaded environment."
7
+ s.platform = Gem::Platform::CURRENT
8
+ s.requirements << "JRuby"
9
+ s.requirements << "Java, since this library wraps a Java library"
10
+ s.authors = ["Lukas Rieder"]
11
+ s.email = "l.rieder@gmail.com"
12
+ s.files = Dir["**/*"]
13
+ s.homepage = "https://github.com/Overbryd/http_client"
14
+ s.license = "MIT"
15
+ end
@@ -0,0 +1,223 @@
1
+ # coding: utf-8
2
+
3
+ require "uri"
4
+ require "java"
5
+ %w[httpcore-4.3 httpclient-4.3.1 httpmime-4.3.1 commons-logging-1.1.3].each do |jar|
6
+ require_relative "../vendor/#{jar}.jar"
7
+ end
8
+
9
+ class HttpClient
10
+ import org.apache.http.impl.client.HttpClients
11
+ import org.apache.http.impl.conn.BasicHttpClientConnectionManager
12
+ import org.apache.http.impl.conn.PoolingHttpClientConnectionManager
13
+ import org.apache.http.client.methods.HttpGet
14
+ import org.apache.http.client.methods.HttpPost
15
+ import org.apache.http.client.methods.HttpPut
16
+ import org.apache.http.client.methods.HttpPatch
17
+ import org.apache.http.client.methods.HttpDelete
18
+ import org.apache.http.client.methods.HttpHead
19
+ import org.apache.http.client.methods.HttpOptions
20
+ import org.apache.http.client.config.RequestConfig
21
+ import org.apache.http.entity.StringEntity
22
+ import org.apache.http.client.entity.UrlEncodedFormEntity
23
+ import org.apache.http.client.entity.GzipDecompressingEntity
24
+ import org.apache.http.client.entity.DeflateDecompressingEntity
25
+ import org.apache.http.message.BasicNameValuePair
26
+ import org.apache.http.entity.ContentType
27
+ import org.apache.http.util.EntityUtils
28
+ import org.apache.http.HttpException
29
+ import org.apache.http.conn.ConnectTimeoutException
30
+ import java.io.IOException
31
+ import java.net.SocketTimeoutException
32
+ import java.util.concurrent.TimeUnit
33
+
34
+ class Error < StandardError; end
35
+ class Timeout < Error; end
36
+ class IOError < Error; end
37
+
38
+ class Response
39
+ attr_reader :status, :body, :headers
40
+
41
+ def initialize(closeable_response)
42
+ @status = closeable_response.status_line.status_code
43
+ @headers = closeable_response.get_all_headers.inject({}) do |headers, header|
44
+ headers[header.name] = header.value
45
+ headers
46
+ end
47
+ @body = read_body(closeable_response)
48
+ end
49
+
50
+ def success?
51
+ @status >= 200 && @status <= 206
52
+ end
53
+
54
+ def json_body(options = {})
55
+ @json_body ||= JSON.parse(body, options)
56
+ end
57
+
58
+ private
59
+
60
+ def read_body(closeable_response)
61
+ return "" unless entity = closeable_response.entity
62
+ return "" unless entity.content_length > 0
63
+ if content_encoding = entity.content_encoding
64
+ entity = case content_encoding.value
65
+ when "gzip", "x-gzip" then
66
+ GzipDecompressingEntity.new(entity)
67
+ when "deflate" then
68
+ DeflateDecompressingEntity.new(entity)
69
+ else
70
+ entity
71
+ end
72
+ end
73
+ EntityUtils.to_string(entity, "UTF-8")
74
+ end
75
+ end
76
+
77
+ attr_reader :client, :max_retries, :response_class, :default_request_options
78
+
79
+ def initialize(options = {})
80
+ options = {
81
+ :use_connection_pool => false,
82
+ :max_connections => 20,
83
+ :max_connections_per_route => nil,
84
+ :max_retries => 0,
85
+ :response_class => Response,
86
+ :connection_request_timeout => 100,
87
+ :connect_timeout => 1000,
88
+ :socket_timeout => 2000,
89
+ :default_request_options => {}
90
+ }.merge(options)
91
+ @request_config = create_request_config(options)
92
+ @connection_manager = create_connection_manager(options)
93
+ @client = HttpClients.create_minimal(@connection_manager)
94
+ @max_retries = options[:max_retries]
95
+ @response_class = options[:response_class]
96
+ @default_request_options = options[:default_request_options]
97
+ end
98
+
99
+ def get(uri, options = {})
100
+ uri = uri.sub(/\?.+$|$/, "?#{URI.encode_www_form(options[:params])}") if options[:params]
101
+ request = create_request(HttpGet, uri, options)
102
+ execute(request)
103
+ end
104
+
105
+ def post(uri, options = {})
106
+ request = create_request(HttpPost, uri, options)
107
+ entity = create_entity(options)
108
+ request.set_entity(entity) if entity
109
+ execute(request)
110
+ end
111
+
112
+ def put(uri, options = {})
113
+ request = create_request(HttpPut, uri, options)
114
+ entity = create_entity(options)
115
+ request.set_entity(entity) if entity
116
+ execute(request)
117
+ end
118
+
119
+ def patch(uri, options = {})
120
+ request = create_request(HttpPatch, uri, options)
121
+ entity = create_entity(options)
122
+ request.set_entity(entity) if entity
123
+ execute(request)
124
+ end
125
+
126
+ def delete(uri, options = {})
127
+ request = create_request(HttpDelete, uri, options)
128
+ execute(request)
129
+ end
130
+
131
+ def head(uri, options = {})
132
+ request = create_request(HttpHead, uri, options)
133
+ execute(request)
134
+ end
135
+
136
+ def options(uri, options = {})
137
+ request = create_request(HttpOptions, uri, options)
138
+ execute(request)
139
+ end
140
+
141
+ def pool_stats
142
+ raise "#{self.class.name}#pool_stats is supported only when using a connection pool" unless @connection_manager.is_a?(PoolingHttpClientConnectionManager)
143
+ total_stats = @connection_manager.total_stats
144
+ Hash(
145
+ :idle => total_stats.available,
146
+ :in_use => total_stats.leased,
147
+ :max => total_stats.max,
148
+ :waiting => total_stats.pending
149
+ )
150
+ end
151
+
152
+ def cleanup_connections(max_idle = 5)
153
+ @connection_manager.close_idle_connections(max_idle, TimeUnit::SECONDS)
154
+ end
155
+
156
+ def shutdown
157
+ @connection_manager.shutdown
158
+ end
159
+
160
+ private
161
+
162
+ def execute(request)
163
+ retries = max_retries
164
+ begin
165
+ closeable_response = client.execute(request)
166
+ response_class.new(closeable_response)
167
+ rescue ConnectTimeoutException, SocketTimeoutException => e
168
+ retry if (retries -= 1) > 0
169
+ raise Timeout, "#{e.message}"
170
+ rescue IOException => e
171
+ retry if (retries -= 1) > 0
172
+ raise IOError, "#{e.message}"
173
+ rescue HttpException => e
174
+ raise Error, "#{e.message}"
175
+ ensure
176
+ closeable_response.close if closeable_response
177
+ end
178
+ end
179
+
180
+ def create_request(method_class, uri, options)
181
+ request = method_class.new(uri)
182
+ request.config = @request_config
183
+ options = default_request_options.merge(options)
184
+ options[:headers].each do |name, value|
185
+ request.set_header(name.to_s.gsub("_", "-"), value)
186
+ end if options[:headers]
187
+ request
188
+ end
189
+
190
+ def create_entity(options)
191
+ if options[:body]
192
+ StringEntity.new(options[:body], ContentType.create(options[:content_type] || "text/plain", "UTF-8"))
193
+ elsif options[:json]
194
+ json = options[:json].to_json
195
+ StringEntity.new(json, ContentType.create("application/json", "UTF-8"))
196
+ elsif options[:form]
197
+ form = options[:form].map { |k, v| BasicNameValuePair.new(k.to_s, v.to_s) }
198
+ UrlEncodedFormEntity.new(form, "UTF-8")
199
+ else
200
+ nil
201
+ end
202
+ end
203
+
204
+ def create_request_config(options)
205
+ config = RequestConfig.custom
206
+ config.set_stale_connection_check_enabled(true)
207
+ config.set_connection_request_timeout(options[:connection_request_timeout])
208
+ config.set_connect_timeout(options[:connect_timeout])
209
+ config.set_socket_timeout(options[:socket_timeout])
210
+ config.build
211
+ end
212
+
213
+ def create_connection_manager(options)
214
+ options[:use_connection_pool] ? create_pooling_connection_manager(options) : BasicHttpClientConnectionManager.new
215
+ end
216
+
217
+ def create_pooling_connection_manager(options)
218
+ connection_manager = PoolingHttpClientConnectionManager.new
219
+ connection_manager.max_total = options[:max_connections]
220
+ connection_manager.default_max_per_route = options[:max_connections_per_route] || options[:max_connections]
221
+ end
222
+
223
+ end
@@ -0,0 +1,95 @@
1
+ # coding: utf-8
2
+
3
+ require "rubygems"
4
+ gem "minitest"
5
+ require "minitest/pride"
6
+ require "minitest/autorun"
7
+
8
+ require "json"
9
+ require_relative "../lib/http_client"
10
+
11
+ module Minitest
12
+ class Test
13
+ def self.test(name, &block)
14
+ define_method("test_#{name.gsub(" ", " ")}", &block)
15
+ end
16
+ end
17
+ end
18
+
19
+ class HttpClientTest < Minitest::Test
20
+ attr_reader :client
21
+
22
+ def setup
23
+ @client = HttpClient.new
24
+ end
25
+
26
+ test "GET request with params in uri" do
27
+ response = client.get("http://httpbin.org/get?foo=bar")
28
+ assert_equal Hash("foo" => "bar"), response.json_body["args"]
29
+ end
30
+
31
+ test "GET request with params in options" do
32
+ response = client.get("http://httpbin.org/get", :params => { :foo => "bar" })
33
+ assert_equal Hash("foo" => "bar"), response.json_body["args"]
34
+ end
35
+
36
+ test "GET request with headers" do
37
+ response = client.get("http://httpbin.org/get", :headers => { "X-Send-By" => "foobar" })
38
+ assert_equal "foobar", response.json_body["headers"]["X-Send-By"]
39
+ end
40
+
41
+ test "GET request with gzipped response" do
42
+ response = client.get("http://httpbin.org/gzip")
43
+ assert_equal true, response.json_body["gzipped"]
44
+ assert_nil response.json_body["headers"]["Content-Encoding"]
45
+ end
46
+
47
+ test "POST request with string body" do
48
+ response = client.post("http://httpbin.org/post", :body => "foo:bar|zig:zag")
49
+ assert_equal "text/plain; charset=UTF-8", response.json_body["headers"]["Content-Type"]
50
+ assert_equal "foo:bar|zig:zag", response.json_body["data"]
51
+ end
52
+
53
+ test "POST request with form data" do
54
+ response = client.post("http://httpbin.org/post", :form => { :foo => "bar"})
55
+ assert_equal "application/x-www-form-urlencoded; charset=UTF-8", response.json_body["headers"]["Content-Type"]
56
+ assert_equal Hash("foo" => "bar"), response.json_body["form"]
57
+ end
58
+
59
+ test "POST request with json data" do
60
+ response = client.post("http://httpbin.org/post", :json => { :foo => "bar"})
61
+ assert_equal "application/json; charset=UTF-8", response.json_body["headers"]["Content-Type"]
62
+ assert_equal Hash("foo" => "bar"), response.json_body["json"]
63
+ end
64
+
65
+ test "POST request Content-Type in header takes precedence" do
66
+ response = client.post("http://httpbin.org/post",
67
+ :json => { "foo" => "bar" },
68
+ :headers => { "Content-Type" => "application/x-json" }
69
+ )
70
+ assert_equal "application/x-json", response.json_body["headers"]["Content-Type"]
71
+ end
72
+ end
73
+
74
+ # # Content-Type precedence
75
+ #
76
+ # client.post("http://httpbin.org/post",
77
+ # :headers => {
78
+ # :content_type => "application/xml"
79
+ # },
80
+ # :body => %Q'<?xml version="1.0" encoding="utf-8"?><foo>bar</foo>'
81
+ # )
82
+ # client.post("http://httpbin.org/post",
83
+ # :content_type => "application/xml+foo",
84
+ # :headers => {
85
+ # :content_type => "application/xml"
86
+ # },
87
+ # :body => %Q'<?xml version="1.0" encoding="utf-8"?><foo>bar</foo>'
88
+ # )
89
+ # client.post("http://httpbin.org/post",
90
+ # :content_type => "application/xml+foo",
91
+ # :headers => {
92
+ # :content_type => "application/xml"
93
+ # },
94
+ # :json => %Q'{"foo":"bar"}'
95
+ # )
Binary file
Binary file
Binary file
Binary file
metadata ADDED
@@ -0,0 +1,55 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: http_client
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.5.0
5
+ platform: universal-java-1.7
6
+ authors:
7
+ - Lukas Rieder
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-11-07 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: This library wraps the Apache HTTPClient (4.3) in a simple fashion. The library is intended to be used in a multithreaded environment.
14
+ email: l.rieder@gmail.com
15
+ executables: []
16
+ extensions: []
17
+ extra_rdoc_files: []
18
+ files:
19
+ - http_client-0.5.0-universal-java-1.7.gem
20
+ - http_client-0.5.0.gem
21
+ - http_client.gemspec
22
+ - README.md
23
+ - lib/http_client.rb
24
+ - test/http_client_test.rb
25
+ - vendor/commons-logging-1.1.3.jar
26
+ - vendor/httpclient-4.3.1.jar
27
+ - vendor/httpcore-4.3.jar
28
+ - vendor/httpmime-4.3.1.jar
29
+ homepage: https://github.com/Overbryd/http_client
30
+ licenses:
31
+ - MIT
32
+ metadata: {}
33
+ post_install_message:
34
+ rdoc_options: []
35
+ require_paths:
36
+ - lib
37
+ required_ruby_version: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - '>='
40
+ - !ruby/object:Gem::Version
41
+ version: '0'
42
+ required_rubygems_version: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - '>='
45
+ - !ruby/object:Gem::Version
46
+ version: '0'
47
+ requirements:
48
+ - JRuby
49
+ - Java, since this library wraps a Java library
50
+ rubyforge_project:
51
+ rubygems_version: 2.1.9
52
+ signing_key:
53
+ specification_version: 4
54
+ summary: HTTP client for JRuby
55
+ test_files: []