http_client 0.5.3-java

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: b421fafaae4821bf74f136d92e5ba9c54da80482
4
+ data.tar.gz: 767e58bcfc6e22c21093ba51dc1f8995cab036b9
5
+ SHA512:
6
+ metadata.gz: f2b280ef32020b5b4a14b1eeae03afe94dd24520c55e11108ad347a22d826d49226ddef862d2ffa8153d893d60c78bd1d3b90309a0009c48019bbcf8a71df68b
7
+ data.tar.gz: c91ad4500a47825a572ff51dcab97cc37239fa1ebded2a69d44e3549cd8d2e9f462513a8d39ba8b475b36085be03ffc0689ed9ef4cde8d390cc39857a0513db2
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source "https://rubygems.org"
2
+
3
+ group :test do
4
+ gem "minitest"
5
+ gem "json"
6
+ end
@@ -0,0 +1,12 @@
1
+ GEM
2
+ remote: https://rubygems.org/
3
+ specs:
4
+ json (1.8.1-java)
5
+ minitest (5.0.8)
6
+
7
+ PLATFORMS
8
+ java
9
+
10
+ DEPENDENCIES
11
+ json
12
+ minitest
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2013 Lukas Rieder
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,85 @@
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](https://github.com/Overbryd/http_client/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.
80
+
81
+ ![Build Status](https://travis-ci.org/Overbryd/http_client.png)
82
+
83
+ ## Legal
84
+
85
+ Copyright by Lukas Rieder, 2013, Licensed under the MIT License, see LICENSE
@@ -0,0 +1,15 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = "http_client"
3
+ s.version = "0.5.3"
4
+ s.date = "2013-11-08"
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 = "java"
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["**/*"].reject { |path| path =~ /\.gem$/ }
13
+ s.homepage = "https://github.com/Overbryd/http_client"
14
+ s.license = "MIT"
15
+ end
@@ -0,0 +1,231 @@
1
+ # coding: utf-8
2
+
3
+ require "uri"
4
+ require "base64"
5
+ require "java"
6
+ %w[httpcore-4.3 httpclient-4.3.1 httpmime-4.3.1 commons-logging-1.1.3].each do |jar|
7
+ require File.expand_path("../../vendor/#{jar}.jar", __FILE__)
8
+ end
9
+
10
+ class HttpClient
11
+ import org.apache.http.impl.client.HttpClients
12
+ import org.apache.http.impl.conn.BasicHttpClientConnectionManager
13
+ import org.apache.http.impl.conn.PoolingHttpClientConnectionManager
14
+ import org.apache.http.client.methods.HttpGet
15
+ import org.apache.http.client.methods.HttpPost
16
+ import org.apache.http.client.methods.HttpPut
17
+ import org.apache.http.client.methods.HttpPatch
18
+ import org.apache.http.client.methods.HttpDelete
19
+ import org.apache.http.client.methods.HttpHead
20
+ import org.apache.http.client.methods.HttpOptions
21
+ import org.apache.http.client.config.RequestConfig
22
+ import org.apache.http.entity.StringEntity
23
+ import org.apache.http.client.entity.UrlEncodedFormEntity
24
+ import org.apache.http.client.entity.GzipDecompressingEntity
25
+ import org.apache.http.client.entity.DeflateDecompressingEntity
26
+ import org.apache.http.message.BasicNameValuePair
27
+ import org.apache.http.entity.ContentType
28
+ import org.apache.http.util.EntityUtils
29
+ import org.apache.http.HttpException
30
+ import org.apache.http.conn.ConnectTimeoutException
31
+ import java.io.IOException
32
+ import java.net.SocketTimeoutException
33
+ import java.util.concurrent.TimeUnit
34
+
35
+ class Error < StandardError; end
36
+ class Timeout < Error; end
37
+ class IOError < Error; end
38
+
39
+ class Response
40
+ attr_reader :status, :body, :headers
41
+
42
+ def initialize(closeable_response)
43
+ @status = closeable_response.status_line.status_code
44
+ @headers = closeable_response.get_all_headers.inject({}) do |headers, header|
45
+ headers[header.name] = header.value
46
+ headers
47
+ end
48
+ @body = read_body(closeable_response)
49
+ end
50
+
51
+ def success?
52
+ @status >= 200 && @status <= 206
53
+ end
54
+
55
+ def json_body(options = {})
56
+ @json_body ||= JSON.parse(body, options)
57
+ end
58
+
59
+ private
60
+
61
+ def read_body(closeable_response)
62
+ return "" unless entity = closeable_response.entity
63
+ return "" unless entity.is_chunked? || entity.content_length > 0
64
+ if content_encoding = entity.content_encoding
65
+ entity = case content_encoding.value
66
+ when "gzip", "x-gzip" then
67
+ GzipDecompressingEntity.new(entity)
68
+ when "deflate" then
69
+ DeflateDecompressingEntity.new(entity)
70
+ else
71
+ entity
72
+ end
73
+ end
74
+ EntityUtils.to_string(entity, "UTF-8")
75
+ end
76
+ end
77
+
78
+ attr_reader :client, :max_retries, :response_class, :default_request_options
79
+
80
+ def initialize(options = {})
81
+ options = {
82
+ :use_connection_pool => false,
83
+ :max_connections => 20,
84
+ :max_connections_per_route => nil,
85
+ :max_retries => 0,
86
+ :response_class => Response,
87
+ :connection_request_timeout => 100,
88
+ :connect_timeout => 1000,
89
+ :socket_timeout => 2000,
90
+ :default_request_options => {}
91
+ }.merge(options)
92
+ @request_config = create_request_config(options)
93
+ @connection_manager = create_connection_manager(options)
94
+ @client = HttpClients.create_minimal(@connection_manager)
95
+ @max_retries = options[:max_retries]
96
+ @response_class = options[:response_class]
97
+ @default_request_options = options[:default_request_options]
98
+ end
99
+
100
+ def get(uri, options = {})
101
+ uri = uri.sub(/\?.+$|$/, "?#{URI.encode_www_form(options[:params])}") if options[:params]
102
+ request = create_request(HttpGet, uri, options)
103
+ execute(request)
104
+ end
105
+
106
+ def post(uri, options = {})
107
+ request = create_request(HttpPost, uri, options)
108
+ entity = create_entity(options)
109
+ request.set_entity(entity) if entity
110
+ execute(request)
111
+ end
112
+
113
+ def put(uri, options = {})
114
+ request = create_request(HttpPut, uri, options)
115
+ entity = create_entity(options)
116
+ request.set_entity(entity) if entity
117
+ execute(request)
118
+ end
119
+
120
+ def patch(uri, options = {})
121
+ request = create_request(HttpPatch, uri, options)
122
+ entity = create_entity(options)
123
+ request.set_entity(entity) if entity
124
+ execute(request)
125
+ end
126
+
127
+ def delete(uri, options = {})
128
+ request = create_request(HttpDelete, uri, options)
129
+ execute(request)
130
+ end
131
+
132
+ def head(uri, options = {})
133
+ request = create_request(HttpHead, uri, options)
134
+ execute(request)
135
+ end
136
+
137
+ def options(uri, options = {})
138
+ request = create_request(HttpOptions, uri, options)
139
+ execute(request)
140
+ end
141
+
142
+ def pool_stats
143
+ raise "#{self.class.name}#pool_stats is supported only when using a connection pool" unless @connection_manager.is_a?(PoolingHttpClientConnectionManager)
144
+ total_stats = @connection_manager.total_stats
145
+ Hash(
146
+ :idle => total_stats.available,
147
+ :in_use => total_stats.leased,
148
+ :max => total_stats.max,
149
+ :waiting => total_stats.pending
150
+ )
151
+ end
152
+
153
+ def cleanup_connections(max_idle = 5)
154
+ @connection_manager.close_idle_connections(max_idle, TimeUnit::SECONDS)
155
+ end
156
+
157
+ def shutdown
158
+ @connection_manager.shutdown
159
+ end
160
+
161
+ private
162
+
163
+ def execute(request)
164
+ retries = max_retries
165
+ begin
166
+ closeable_response = client.execute(request)
167
+ response_class.new(closeable_response)
168
+ rescue ConnectTimeoutException, SocketTimeoutException => e
169
+ retry if (retries -= 1) > 0
170
+ raise Timeout, "#{e.message}"
171
+ rescue IOException => e
172
+ retry if (retries -= 1) > 0
173
+ raise IOError, "#{e.message}"
174
+ rescue HttpException => e
175
+ raise Error, "#{e.message}"
176
+ ensure
177
+ closeable_response.close if closeable_response
178
+ end
179
+ end
180
+
181
+ def create_request(method_class, uri, options)
182
+ request = method_class.new(uri)
183
+ request.config = @request_config
184
+ options = default_request_options.merge(options)
185
+ set_basic_auth(request, options[:basic_auth])
186
+ options[:headers].each do |name, value|
187
+ request.set_header(name.to_s.gsub("_", "-"), value)
188
+ end if options[:headers]
189
+ request
190
+ end
191
+
192
+ def set_basic_auth(request, basic_auth)
193
+ return unless basic_auth.is_a?(Hash)
194
+ credentials = "#{basic_auth[:user]}:#{basic_auth[:password]}"
195
+ request.set_header("Authorization", "Basic #{Base64.strict_encode64(credentials)}")
196
+ end
197
+
198
+ def create_entity(options)
199
+ if options[:body]
200
+ StringEntity.new(options[:body], ContentType.create(options[:content_type] || "text/plain", "UTF-8"))
201
+ elsif options[:json]
202
+ json = options[:json].to_json
203
+ StringEntity.new(json, ContentType.create("application/json", "UTF-8"))
204
+ elsif options[:form]
205
+ form = options[:form].map { |k, v| BasicNameValuePair.new(k.to_s, v.to_s) }
206
+ UrlEncodedFormEntity.new(form, "UTF-8")
207
+ else
208
+ nil
209
+ end
210
+ end
211
+
212
+ def create_request_config(options)
213
+ config = RequestConfig.custom
214
+ config.set_stale_connection_check_enabled(true)
215
+ config.set_connection_request_timeout(options[:connection_request_timeout])
216
+ config.set_connect_timeout(options[:connect_timeout])
217
+ config.set_socket_timeout(options[:socket_timeout])
218
+ config.build
219
+ end
220
+
221
+ def create_connection_manager(options)
222
+ options[:use_connection_pool] ? create_pooling_connection_manager(options) : BasicHttpClientConnectionManager.new
223
+ end
224
+
225
+ def create_pooling_connection_manager(options)
226
+ connection_manager = PoolingHttpClientConnectionManager.new
227
+ connection_manager.max_total = options[:max_connections]
228
+ connection_manager.default_max_per_route = options[:max_connections_per_route] || options[:max_connections]
229
+ end
230
+
231
+ end
@@ -0,0 +1,109 @@
1
+ # coding: utf-8
2
+
3
+ require "rubygems"
4
+ require "bundler/setup"
5
+ require "minitest/pride"
6
+ require "minitest/autorun"
7
+ require "json"
8
+
9
+ require File.expand_path("../../lib/http_client", __FILE__)
10
+
11
+ def Hash(h); h; end
12
+
13
+ module Minitest
14
+ class Test
15
+ def self.test(name, &block)
16
+ define_method("test_#{name.gsub(" ", "_")}", &block)
17
+ end
18
+ end
19
+ end
20
+
21
+ class HttpClientTest < Minitest::Test
22
+ attr_reader :client
23
+
24
+ def setup
25
+ @client = HttpClient.new(:socket_timeout => 5000, :connect_timeout => 5000, :max_retries => 2)
26
+ end
27
+
28
+ test "GET request with params in uri" do
29
+ response = client.get("http://httpbin.org/get?foo=bar")
30
+ assert_equal Hash("foo" => "bar"), response.json_body["args"]
31
+ end
32
+
33
+ test "GET request with params in options" do
34
+ response = client.get("http://httpbin.org/get", :params => { :foo => "bar" })
35
+ assert_equal Hash("foo" => "bar"), response.json_body["args"]
36
+ end
37
+
38
+ test "GET request with headers" do
39
+ response = client.get("http://httpbin.org/get", :headers => { "X-Send-By" => "foobar" })
40
+ assert_equal "foobar", response.json_body["headers"]["X-Send-By"]
41
+ end
42
+
43
+ test "GET request with gzipped response" do
44
+ response = client.get("http://httpbin.org/gzip")
45
+ assert_equal true, response.json_body["gzipped"]
46
+ assert_nil response.json_body["headers"]["Content-Encoding"]
47
+ end
48
+
49
+ test "GET request with chunked response" do
50
+ response = client.get("http://httpbin.org/stream/10")
51
+ assert_equal 10, response.body.split("\n").size
52
+ end
53
+
54
+ test "GET request with basic auth" do
55
+ response = client.get("http://httpbin.org/basic-auth/foo/bar",
56
+ :basic_auth => { :user => "foo", :password => "bar" }
57
+ )
58
+ assert_equal 200, response.status
59
+ end
60
+
61
+ test "POST request with string body" do
62
+ response = client.post("http://httpbin.org/post", :body => "foo:bar|zig:zag")
63
+ assert_equal "text/plain; charset=UTF-8", response.json_body["headers"]["Content-Type"]
64
+ assert_equal "foo:bar|zig:zag", response.json_body["data"]
65
+ end
66
+
67
+ test "POST request with form data" do
68
+ response = client.post("http://httpbin.org/post", :form => { :foo => "bar"})
69
+ assert_equal "application/x-www-form-urlencoded; charset=UTF-8", response.json_body["headers"]["Content-Type"]
70
+ assert_equal Hash("foo" => "bar"), response.json_body["form"]
71
+ end
72
+
73
+ test "POST request with json data" do
74
+ response = client.post("http://httpbin.org/post", :json => { :foo => "bar"})
75
+ assert_equal "application/json; charset=UTF-8", response.json_body["headers"]["Content-Type"]
76
+ assert_equal Hash("foo" => "bar"), response.json_body["json"]
77
+ end
78
+
79
+ test "POST request Content-Type in header takes precedence" do
80
+ response = client.post("http://httpbin.org/post",
81
+ :json => { "foo" => "bar" },
82
+ :headers => { "Content-Type" => "application/x-json" }
83
+ )
84
+ assert_equal "application/x-json", response.json_body["headers"]["Content-Type"]
85
+ end
86
+ end
87
+
88
+ # # Content-Type precedence
89
+ #
90
+ # client.post("http://httpbin.org/post",
91
+ # :headers => {
92
+ # :content_type => "application/xml"
93
+ # },
94
+ # :body => %Q'<?xml version="1.0" encoding="utf-8"?><foo>bar</foo>'
95
+ # )
96
+ # client.post("http://httpbin.org/post",
97
+ # :content_type => "application/xml+foo",
98
+ # :headers => {
99
+ # :content_type => "application/xml"
100
+ # },
101
+ # :body => %Q'<?xml version="1.0" encoding="utf-8"?><foo>bar</foo>'
102
+ # )
103
+ # client.post("http://httpbin.org/post",
104
+ # :content_type => "application/xml+foo",
105
+ # :headers => {
106
+ # :content_type => "application/xml"
107
+ # },
108
+ # :json => %Q'{"foo":"bar"}'
109
+ # )
Binary file
Binary file
metadata ADDED
@@ -0,0 +1,57 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: http_client
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.5.3
5
+ platform: java
6
+ authors:
7
+ - Lukas Rieder
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-11-08 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
+ - Gemfile
20
+ - Gemfile.lock
21
+ - http_client.gemspec
22
+ - LICENSE
23
+ - README.md
24
+ - lib/http_client.rb
25
+ - test/http_client_test.rb
26
+ - vendor/commons-logging-1.1.3.jar
27
+ - vendor/httpclient-4.3.1.jar
28
+ - vendor/httpcore-4.3.jar
29
+ - vendor/httpmime-4.3.1.jar
30
+ homepage: https://github.com/Overbryd/http_client
31
+ licenses:
32
+ - MIT
33
+ metadata: {}
34
+ post_install_message:
35
+ rdoc_options: []
36
+ require_paths:
37
+ - lib
38
+ required_ruby_version: !ruby/object:Gem::Requirement
39
+ requirements:
40
+ - - '>='
41
+ - !ruby/object:Gem::Version
42
+ version: '0'
43
+ required_rubygems_version: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - '>='
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ requirements:
49
+ - JRuby
50
+ - Java, since this library wraps a Java library
51
+ rubyforge_project:
52
+ rubygems_version: 2.1.9
53
+ signing_key:
54
+ specification_version: 4
55
+ summary: HTTP client for JRuby
56
+ test_files: []
57
+ has_rdoc: