simple-http 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/README.md +34 -0
- data/lib/simple/http/errors.rb +33 -0
- data/lib/simple/http/expires_in.rb +34 -0
- data/lib/simple/http/result.rb +41 -0
- data/lib/simple/http/version.rb +9 -0
- data/lib/simple/http.rb +166 -0
- data/test/simple_http_test.rb +14 -0
- data/test/test_helper.rb +11 -0
- metadata +50 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: b4588e9e8c027217ca27a55496429b8c17b1bb49
|
4
|
+
data.tar.gz: be034b0620f7358c4e3d806ca4a0ab8b7c16e77d
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 25d97c4098cdd837a45c7cf4b6377d943cd29fde1a51db79b7f52f9457658127a411f910b618d23bb42c368f1dc3ef49c47219f929b72ddd96df552a3639ac27
|
7
|
+
data.tar.gz: f8044c467a6f30109a21cfa2d86d5b4c9fadeec52f930375502a11a7dfa0f2a74ae932a669417e7c205e7610feb77a01cbf091e7674296dfe6fba64bf8788df9
|
data/README.md
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
# simple-http
|
2
|
+
|
3
|
+
A really simple HTTP client
|
4
|
+
|
5
|
+
- GET, POST, PUT, DELETE
|
6
|
+
|
7
|
+
require "simple/http"
|
8
|
+
http_client = Simple::HTTP.new
|
9
|
+
http_client.get "http://google.com" # returns a string
|
10
|
+
|
11
|
+
- Exceptions on errors: because, after all, when you consume a HTTP endpoint and don't
|
12
|
+
get a success (20x), then this is an error. Handle it!
|
13
|
+
|
14
|
+
require "simple/http"
|
15
|
+
http_client = Simple::HTTP.new
|
16
|
+
begin
|
17
|
+
http_client.get "http://google.com" # returns a string
|
18
|
+
rescue Simple::HTTP::Error
|
19
|
+
STDERR.puts "Ooops! #{$!}"
|
20
|
+
end
|
21
|
+
|
22
|
+
- Caching
|
23
|
+
|
24
|
+
require "simple/http"
|
25
|
+
http_client = Simple::HTTP.new
|
26
|
+
|
27
|
+
require "active_support/cache"
|
28
|
+
require "active_support/cache/file_store"
|
29
|
+
http_client.cache = ActiveSupport::Cache::FileStore.new("var/cache")
|
30
|
+
http_client.get "http://google.com" # returns a, potentially, cached string
|
31
|
+
|
32
|
+
- Automatic de/encoding of JSON payloads
|
33
|
+
|
34
|
+
- Does not requires anything except core ruby classes.
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# This file is part of the simple-http ruby gem.
|
2
|
+
#
|
3
|
+
# Copyright (c) 2011 - 2015 @radiospiel
|
4
|
+
# Distributed under the terms of the modified BSD license, see LICENSE.BSD
|
5
|
+
|
6
|
+
module Simple; end
|
7
|
+
class Simple::HTTP; end
|
8
|
+
|
9
|
+
class Simple::HTTP::Error < RuntimeError
|
10
|
+
attr :method, :request, :response
|
11
|
+
|
12
|
+
def initialize(method, request, response)
|
13
|
+
@method, @request, @response = method, request, response
|
14
|
+
end
|
15
|
+
|
16
|
+
def code
|
17
|
+
response.code.to_i
|
18
|
+
end
|
19
|
+
|
20
|
+
def message
|
21
|
+
message = "#{method} #{response.uri} ##{response.code} #{response.message}"
|
22
|
+
if response.is_a?(Net::HTTPRedirection)
|
23
|
+
message += " To #{response["Location"]}"
|
24
|
+
end
|
25
|
+
message
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
class Simple::HTTP::TooManyRedirections < Simple::HTTP::Error
|
30
|
+
def message
|
31
|
+
"Too many redirections; after\n#{super}"
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# This file is part of the simple-http ruby gem.
|
2
|
+
#
|
3
|
+
# Copyright (c) 2011 - 2015 @radiospiel
|
4
|
+
# Distributed under the terms of the modified BSD license, see LICENSE.BSD
|
5
|
+
|
6
|
+
require "net/http"
|
7
|
+
|
8
|
+
class Net::HTTPResponse
|
9
|
+
private
|
10
|
+
|
11
|
+
#
|
12
|
+
# The "max-age" value, in seconds, from the Cache-Control header.
|
13
|
+
def max_age
|
14
|
+
return unless cache_control = header["Cache-Control"]
|
15
|
+
cache_control.split(/, /).each do |part|
|
16
|
+
next unless part =~ /max-age=(\d+)/
|
17
|
+
return Integer($1)
|
18
|
+
end
|
19
|
+
nil
|
20
|
+
end
|
21
|
+
|
22
|
+
public
|
23
|
+
|
24
|
+
#
|
25
|
+
# returns expiration information, in seconds from now.
|
26
|
+
def expires_in
|
27
|
+
expires_in = max_age
|
28
|
+
if !expires_in && expires = header["Expires"]
|
29
|
+
expires_in = Time.parse(expires) - Time.now
|
30
|
+
end
|
31
|
+
|
32
|
+
return expires_in if expires_in && expires_in > 0
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# This file is part of the simple-http ruby gem.
|
2
|
+
#
|
3
|
+
# Copyright (c) 2011 - 2015 @radiospiel
|
4
|
+
# Distributed under the terms of the modified BSD license, see LICENSE.BSD
|
5
|
+
|
6
|
+
require "net/http"
|
7
|
+
|
8
|
+
class Net::HTTPResponse
|
9
|
+
#
|
10
|
+
# evaluate and potentially parse response
|
11
|
+
def result
|
12
|
+
case response.content_type
|
13
|
+
when "application/json"
|
14
|
+
JSON.parse(body) unless body.empty?
|
15
|
+
else
|
16
|
+
body_string
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
#
|
23
|
+
# returns a body. The resulting string is encoded in ASCII-8BIT, if the
|
24
|
+
# content type is binary, and encoded in UTF8 otherwise.
|
25
|
+
def body_string
|
26
|
+
default_encoding = content_is_binary? ? "ASCII-8BIT" : "UTF-8"
|
27
|
+
|
28
|
+
body = self.body
|
29
|
+
if charset = type_params["charset"]
|
30
|
+
body = body.force_encoding(charset)
|
31
|
+
end
|
32
|
+
body.encode(default_encoding)
|
33
|
+
end
|
34
|
+
|
35
|
+
def content_is_binary?
|
36
|
+
case content_type
|
37
|
+
when /^image/ then true
|
38
|
+
else false
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
data/lib/simple/http.rb
ADDED
@@ -0,0 +1,166 @@
|
|
1
|
+
# Author:: radiospiel (mailto:eno@radiospiel.org)
|
2
|
+
# Copyright:: Copyright (c) 2011-2015 radiospiel
|
3
|
+
# License:: Distributes under the terms of the Modified BSD License, see LICENSE.BSD for details.
|
4
|
+
|
5
|
+
require "net/http"
|
6
|
+
require "json"
|
7
|
+
|
8
|
+
module Simple; end
|
9
|
+
class Simple::HTTP; end
|
10
|
+
|
11
|
+
require_relative "http/version"
|
12
|
+
require_relative "http/result"
|
13
|
+
require_relative "http/expires_in"
|
14
|
+
require_relative "http/errors"
|
15
|
+
|
16
|
+
#
|
17
|
+
# A very simple, Net::HTTP-based HTTP client.
|
18
|
+
#
|
19
|
+
# Has some support for transferring JSON data: all data in PUT and POST
|
20
|
+
# requests are jsonized, and all data in responses are parsed as JSON if
|
21
|
+
# the Content-Type header is set to "application/json".
|
22
|
+
class Simple::HTTP
|
23
|
+
|
24
|
+
#
|
25
|
+
# The base URL: when set, all requests that do not start with http: or
|
26
|
+
# https: are done relative to this base URL.
|
27
|
+
attr :base_url, true
|
28
|
+
|
29
|
+
#
|
30
|
+
# When set (default), redirections are followed. Note: When
|
31
|
+
# follows_redirections is not set, a HTTP redirection would raise an
|
32
|
+
# error - which is probably only useful when testing an interface.
|
33
|
+
attr :follows_redirections, true
|
34
|
+
|
35
|
+
#
|
36
|
+
# When set, appends this to all request URLs
|
37
|
+
attr :default_params, true
|
38
|
+
|
39
|
+
def initialize
|
40
|
+
self.follows_redirections = true
|
41
|
+
end
|
42
|
+
|
43
|
+
def get(url, headers = {}); http :GET, url, nil, headers; end
|
44
|
+
def post(url, body = nil, headers = {}); http :POST, url, body, headers; end
|
45
|
+
def put(url, body = nil, headers = {}); http :PUT, url, body, headers; end
|
46
|
+
def delete(url, headers = {}); http :DELETE, url, nil, headers; end
|
47
|
+
|
48
|
+
#
|
49
|
+
# -- Caching ----------------------------------------------------------------
|
50
|
+
|
51
|
+
# When set, and a response is cacheable (as it returns a valid expires_in
|
52
|
+
# value), the cache object is used to cache responses.
|
53
|
+
attr :cache, true
|
54
|
+
|
55
|
+
#
|
56
|
+
# when does the response expire? By default, calculates expiration based
|
57
|
+
# on response headers. Override as needed.
|
58
|
+
def expires_in(response)
|
59
|
+
response.expires_in
|
60
|
+
end
|
61
|
+
|
62
|
+
private
|
63
|
+
|
64
|
+
# -- HTTP request -----------------------------------------------------------
|
65
|
+
|
66
|
+
def http(method, url, body = nil, headers)
|
67
|
+
#
|
68
|
+
# normalize url; i.e. prepend base_url if the url itself is incomplete.
|
69
|
+
unless url =~ /^(http|https):/
|
70
|
+
url = File.join(base_url, url)
|
71
|
+
end
|
72
|
+
|
73
|
+
# append default_params, if set
|
74
|
+
if default_params
|
75
|
+
url.concat(url.include?("?") ? "&" : "?")
|
76
|
+
url.concat default_params
|
77
|
+
end
|
78
|
+
|
79
|
+
# "raw" execute request
|
80
|
+
http_ method, url, body, headers
|
81
|
+
end
|
82
|
+
|
83
|
+
#
|
84
|
+
# do a HTTP request, return its response or, when not successful,
|
85
|
+
# raise an error.
|
86
|
+
def http_(method, url, body, headers, max_redirections = 10)
|
87
|
+
if method == :GET && cache && result = cache.read(url)
|
88
|
+
return result
|
89
|
+
end
|
90
|
+
|
91
|
+
uri = URI.parse(url)
|
92
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
93
|
+
|
94
|
+
#
|
95
|
+
# build request
|
96
|
+
request = build_request method, uri, body, headers
|
97
|
+
|
98
|
+
#
|
99
|
+
# execute request
|
100
|
+
response = http.request(request)
|
101
|
+
|
102
|
+
#
|
103
|
+
# Most of the times Net::HTTP#request returns a response with the :uri
|
104
|
+
# attribute set, but sometimes not. We make sure that the uri is set
|
105
|
+
# everytime.
|
106
|
+
response.uri = uri
|
107
|
+
|
108
|
+
#
|
109
|
+
# handle successful responses.
|
110
|
+
if response.is_a?(Net::HTTPSuccess)
|
111
|
+
result = response.result
|
112
|
+
if cache && method == :GET && expires_in = self.expires_in(response)
|
113
|
+
cache.write(url, result, expires_in: expires_in)
|
114
|
+
end
|
115
|
+
|
116
|
+
return result
|
117
|
+
end
|
118
|
+
|
119
|
+
#
|
120
|
+
# handle redirections.
|
121
|
+
if response.is_a?(Net::HTTPRedirection) && self.follows_redirections
|
122
|
+
if max_redirections <= 0
|
123
|
+
raise TooManyRedirections.new(method, request, respons)
|
124
|
+
end
|
125
|
+
|
126
|
+
return http_(:GET, response["Location"], nil, {}, max_redirections - 1)
|
127
|
+
end
|
128
|
+
|
129
|
+
#
|
130
|
+
# raise an error in any other case.
|
131
|
+
raise Error.new(method, request, response)
|
132
|
+
end
|
133
|
+
|
134
|
+
private
|
135
|
+
|
136
|
+
REQUEST_CLASSES = {
|
137
|
+
:GET => Net::HTTP::Get,
|
138
|
+
:POST => Net::HTTP::Post,
|
139
|
+
:PUT => Net::HTTP::Put,
|
140
|
+
:DELETE => Net::HTTP::Delete
|
141
|
+
}.freeze #:nodoc:
|
142
|
+
|
143
|
+
#
|
144
|
+
# build a HTTP request object.
|
145
|
+
def build_request(method, uri, body, headers)
|
146
|
+
klass = REQUEST_CLASSES.fetch(method)
|
147
|
+
request = klass.new(uri.request_uri)
|
148
|
+
|
149
|
+
# set request headers
|
150
|
+
# unless headers && !headers.empty?
|
151
|
+
# # TODO: set headers
|
152
|
+
# # set_request_headers request, headers
|
153
|
+
# end
|
154
|
+
|
155
|
+
# set request body
|
156
|
+
if request.request_body_permitted? && body
|
157
|
+
request.content_type = "application/json"
|
158
|
+
if body.is_a?(Hash) || body.is_a?(Array)
|
159
|
+
body = JSON.generate(body)
|
160
|
+
end
|
161
|
+
request.body = body
|
162
|
+
end
|
163
|
+
|
164
|
+
request
|
165
|
+
end
|
166
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# Author:: radiospiel (mailto:eno@radiospiel.org)
|
2
|
+
# Copyright:: Copyright (c) 2011-2015 radiospiel
|
3
|
+
# License:: Distributes under the terms of the Modified BSD License, see LICENSE.BSD for details.
|
4
|
+
|
5
|
+
require_relative 'test_helper'
|
6
|
+
|
7
|
+
class SimpleHttpTest < Test::Unit::TestCase
|
8
|
+
HTTP = Simple::HTTP.new
|
9
|
+
|
10
|
+
def test_loaded
|
11
|
+
google = HTTP.get "http://google.com"
|
12
|
+
assert_match(/doctype/, google)
|
13
|
+
end
|
14
|
+
end
|
data/test/test_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: simple-http
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- radiospiel
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-09-16 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description: Simple code for simple HTTP requests
|
14
|
+
email: eno@radiospiel.org
|
15
|
+
executables: []
|
16
|
+
extensions: []
|
17
|
+
extra_rdoc_files: []
|
18
|
+
files:
|
19
|
+
- README.md
|
20
|
+
- lib/simple/http.rb
|
21
|
+
- lib/simple/http/errors.rb
|
22
|
+
- lib/simple/http/expires_in.rb
|
23
|
+
- lib/simple/http/result.rb
|
24
|
+
- lib/simple/http/version.rb
|
25
|
+
- test/simple_http_test.rb
|
26
|
+
- test/test_helper.rb
|
27
|
+
homepage: http://github.com/radiospiel/simple-http
|
28
|
+
licenses: []
|
29
|
+
metadata: {}
|
30
|
+
post_install_message:
|
31
|
+
rdoc_options: []
|
32
|
+
require_paths:
|
33
|
+
- lib
|
34
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
35
|
+
requirements:
|
36
|
+
- - ">="
|
37
|
+
- !ruby/object:Gem::Version
|
38
|
+
version: '0'
|
39
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
40
|
+
requirements:
|
41
|
+
- - ">="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: '0'
|
44
|
+
requirements: []
|
45
|
+
rubyforge_project:
|
46
|
+
rubygems_version: 2.4.6
|
47
|
+
signing_key:
|
48
|
+
specification_version: 4
|
49
|
+
summary: Simple code for simple HTTP requests
|
50
|
+
test_files: []
|