fresh_simplehttp 0.1.7
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.
- data/AUTHORS +4 -0
- data/CHANGELOG +20 -0
- data/LICENSE +58 -0
- data/README +96 -0
- data/Rakefile +137 -0
- data/TODO +28 -0
- data/VERSION +1 -0
- data/fresh_simplehttp.gemspec +55 -0
- data/lib/fresh_simplehttp.rb +7 -0
- data/lib/simple_http.rb +7 -0
- data/lib/simplehttp.rb +482 -0
- data/setup.rb +1585 -0
- data/simplehttp.gemspec +53 -0
- data/test/http_test.rb +214 -0
- data/test/http_test_server.rb +102 -0
- metadata +77 -0
data/AUTHORS
ADDED
data/CHANGELOG
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
= Mon Sep 22 15:12:44 CEST 2008
|
2
|
+
* General code cleanup
|
3
|
+
* Additional Docs
|
4
|
+
* Reluctantly changes tabs to (ruby default) spaces (YUCK!)
|
5
|
+
= Mon Sep 22 13:25:22 CEST 2008
|
6
|
+
* bugfix: uri query parameter preserved on post req
|
7
|
+
|
8
|
+
= Sun Sep 21 18:29:20 CEST 2008
|
9
|
+
* changes to redirect handling
|
10
|
+
* bugfix make_query
|
11
|
+
|
12
|
+
= Wed Sep 26 16:25:55 2007
|
13
|
+
implemented +head+, +options+ and +trace+,
|
14
|
+
made filenames easier. 'simple_http' -> 'simplehttp'
|
15
|
+
|
16
|
+
= Fri Feb 9 12:23:20 CET 2007
|
17
|
+
* easier access to response headers
|
18
|
+
* pass through headers during redirects
|
19
|
+
* more tests and documentation
|
20
|
+
* included `setup.rb`
|
data/LICENSE
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
This package is copyrighted free software by Tim Becker <tim@kuriositaet.de>.
|
2
|
+
You can redistribute it and/or modify it under either the terms of the GPL
|
3
|
+
(see COPYING.txt file), or the conditions below:
|
4
|
+
|
5
|
+
1. You may make and give away verbatim copies of the source form of the
|
6
|
+
software without restriction, provided that you duplicate all of the
|
7
|
+
original copyright notices and associated disclaimers.
|
8
|
+
|
9
|
+
2. You may modify your copy of the software in any way, provided that
|
10
|
+
you do at least ONE of the following:
|
11
|
+
|
12
|
+
a) place your modifications in the Public Domain or otherwise
|
13
|
+
make them Freely Available, such as by posting said
|
14
|
+
modifications to Usenet or an equivalent medium, or by allowing
|
15
|
+
the author to include your modifications in the software.
|
16
|
+
|
17
|
+
b) use the modified software only within your corporation or
|
18
|
+
organization.
|
19
|
+
|
20
|
+
c) rename any non-standard executables so the names do not conflict
|
21
|
+
with standard executables, which must also be provided.
|
22
|
+
|
23
|
+
d) make other distribution arrangements with the author.
|
24
|
+
|
25
|
+
3. You may distribute the software in object code or executable
|
26
|
+
form, provided that you do at least ONE of the following:
|
27
|
+
|
28
|
+
a) distribute the executables and library files of the software,
|
29
|
+
together with instructions (in the manual page or equivalent)
|
30
|
+
on where to get the original distribution.
|
31
|
+
|
32
|
+
b) accompany the distribution with the machine-readable source of
|
33
|
+
the software.
|
34
|
+
|
35
|
+
c) give non-standard executables non-standard names, with
|
36
|
+
instructions on where to get the original software distribution.
|
37
|
+
|
38
|
+
d) make other distribution arrangements with the author.
|
39
|
+
|
40
|
+
4. You may modify and include the part of the software into any other
|
41
|
+
software (possibly commercial). But some files in the distribution
|
42
|
+
are not written by the author, so that they are not under this terms.
|
43
|
+
|
44
|
+
They are gc.c(partly), utils.c(partly), regex.[ch], st.[ch] and some
|
45
|
+
files under the ./missing directory. See each file for the copying
|
46
|
+
condition.
|
47
|
+
|
48
|
+
5. The scripts and library files supplied as input to or produced as
|
49
|
+
output from the software do not automatically fall under the
|
50
|
+
copyright of the software, but belong to whomever generated them,
|
51
|
+
and may be sold commercially, and may be aggregated with this
|
52
|
+
software.
|
53
|
+
|
54
|
+
6. THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR
|
55
|
+
IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
|
56
|
+
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
57
|
+
PURPOSE.
|
58
|
+
|
data/README
ADDED
@@ -0,0 +1,96 @@
|
|
1
|
+
= SimpleHttp - a simplified wrapper around Net::Http
|
2
|
+
|
3
|
+
SimpleHttp aims to reduce the complexity of Net::Http while providing
|
4
|
+
the most commonly used (by me) http functionality.
|
5
|
+
|
6
|
+
INSTALLATION
|
7
|
+
|
8
|
+
* Using +gem+
|
9
|
+
|
10
|
+
gem install simplehttp
|
11
|
+
|
12
|
+
* Using +setup.rb+
|
13
|
+
|
14
|
+
ruby setup.rb config
|
15
|
+
ruby setup.rb install
|
16
|
+
|
17
|
+
* tarball and zip packages are available from
|
18
|
+
RubyForge[http://rubyforge.org/projects/simplehttp/]
|
19
|
+
|
20
|
+
|
21
|
+
|
22
|
+
FEATURES / USAGE
|
23
|
+
|
24
|
+
* Require the lib:
|
25
|
+
|
26
|
+
require 'simplehttp'
|
27
|
+
|
28
|
+
* No fuss one line GET and POST requests:
|
29
|
+
|
30
|
+
str = SimpleHttp.get "http://www.example.com"
|
31
|
+
str = SimpleHttp.get "www.example.com"
|
32
|
+
|
33
|
+
* Can use URI or String url interchangibly
|
34
|
+
|
35
|
+
str = SimpleHttp.get URI.parse "http://www.example.com/"
|
36
|
+
|
37
|
+
* Transparent Proxy Handling. Uses the 'http_proxy' environment
|
38
|
+
variable if set, also provides a +set_proxy+ method.
|
39
|
+
|
40
|
+
http = SimpleHttp.new "http://www.example.com"
|
41
|
+
http.set_proxy "http://proxy.example.com:8000"
|
42
|
+
http.post "query" => "example_query"
|
43
|
+
|
44
|
+
* POST sends ruby Hashes as 'application/x-www-form/urlencoded' per
|
45
|
+
default, but can send any data.
|
46
|
+
|
47
|
+
http = SimpleHttp.new "http://www.example.com/image_upload"
|
48
|
+
http.post imageData, "img/png"
|
49
|
+
|
50
|
+
* Automatically handles SSL
|
51
|
+
|
52
|
+
str = SimpleHttp.get "https://secure.example.com"
|
53
|
+
|
54
|
+
* Easy HTTP Basic Authentication
|
55
|
+
str = SimpleHttp.get URI.parse("http://usr:pwd@www.example.com")
|
56
|
+
#or
|
57
|
+
http = SimpleHttp.new "http://www.example.com"
|
58
|
+
http.basic_authentication "user", "password"
|
59
|
+
http.post "query" => "example_query"
|
60
|
+
|
61
|
+
* Access headers of the request or response
|
62
|
+
http = SimpleHttp.new "www.example.com"
|
63
|
+
http.request_headers["X-Custom-Header"]="useful header"
|
64
|
+
http.get
|
65
|
+
puts "server set cookie: #{http.response_headers['set-cookie']}"
|
66
|
+
|
67
|
+
* Automatically follows Http Redirects.
|
68
|
+
|
69
|
+
|
70
|
+
The +get+ and +post+ methods return a +String+ containing the
|
71
|
+
body of the request if the request was successful (HTTP 200). In case of
|
72
|
+
a redirect, the redirect is followed and the ultimate response is
|
73
|
+
returned. Per Default, up to three redirects are followed, this
|
74
|
+
behaviour can be modified by setting +follow_num_redirects+.
|
75
|
+
|
76
|
+
In case of any other type of response, an exception is raised.
|
77
|
+
|
78
|
+
The default behaviour can be modified by registering handlers
|
79
|
+
using the +register_response_handler+ method. E.g. if you'd like to
|
80
|
+
retrieve the +Date+ header instead of the body for successful
|
81
|
+
transactions:
|
82
|
+
|
83
|
+
http = SimpleHttp.new ...
|
84
|
+
http.register_response_handler(Net::HTTPSuccess) {|req,res,http|
|
85
|
+
res['date']
|
86
|
+
}
|
87
|
+
|
88
|
+
Or you'd like to print the +Location+ and then raise an exception in
|
89
|
+
case of a redirect:
|
90
|
+
|
91
|
+
|
92
|
+
http = SimpleHttp.new ...
|
93
|
+
http.register_response_handler(Net::HTTPRedirect) {|req,res,http|
|
94
|
+
puts res['location']
|
95
|
+
raise "REDIRECT not allowed!"
|
96
|
+
}
|
data/Rakefile
ADDED
@@ -0,0 +1,137 @@
|
|
1
|
+
require "rake/rdoctask"
|
2
|
+
require "rake/gempackagetask"
|
3
|
+
require "rake/testtask"
|
4
|
+
require "rake/clean"
|
5
|
+
require "rubygems"
|
6
|
+
|
7
|
+
require "lib/simple_http"
|
8
|
+
|
9
|
+
# Specifies the default task to execute. This is often the "test" task
|
10
|
+
# and we'll change things around as soon as we have some tests.
|
11
|
+
|
12
|
+
task :default => [:rdoc]
|
13
|
+
|
14
|
+
# The directory to generate +rdoc+ in.
|
15
|
+
RDOC_DIR="doc/html"
|
16
|
+
|
17
|
+
# This global variable contains files that will be erased by the `clean` task.
|
18
|
+
# The `clean` task itself is automatically generated by requiring `rake/clean`.
|
19
|
+
|
20
|
+
CLEAN << RDOC_DIR
|
21
|
+
|
22
|
+
begin
|
23
|
+
require 'jeweler'
|
24
|
+
Jeweler::Tasks.new do |gemspec|
|
25
|
+
gemspec.name = "fresh_simplehttp"
|
26
|
+
gemspec.summary = "simple_http: Simple Http client lib."
|
27
|
+
gemspec.description = "simple_http: Simple Http client lib. Use to do http requests without noise."
|
28
|
+
gemspec.email = "wilkerlucio@gmail.com"
|
29
|
+
gemspec.homepage = "http://github.com/wilkerlucio/simplehttp"
|
30
|
+
gemspec.authors = ["Wilker Lucio"]
|
31
|
+
|
32
|
+
gemspec.files.exclude "pkg/**/*"
|
33
|
+
gemspec.files.exclude "test/**/*"
|
34
|
+
end
|
35
|
+
Jeweler::GemcutterTasks.new
|
36
|
+
rescue LoadError
|
37
|
+
puts "Jeweler not available. Install it with: gem install jeweler"
|
38
|
+
end
|
39
|
+
|
40
|
+
# This is the task that generates the +rdoc+ documentation from the
|
41
|
+
# source files. Instantiating Rake::RDocTask automatically generates a
|
42
|
+
# task called `rdoc`.
|
43
|
+
|
44
|
+
Rake::RDocTask.new do |rd|
|
45
|
+
# Options for documenation generation are specified inside of
|
46
|
+
# this block. For example the following line specifies that the
|
47
|
+
# content of the README file should be the main page of the
|
48
|
+
# documenation.
|
49
|
+
rd.main = "README"
|
50
|
+
|
51
|
+
# The following line specifies all the files to extract
|
52
|
+
# documenation from.
|
53
|
+
rd.rdoc_files.include( "README", "AUTHORS", "LICENSE", "TODO",
|
54
|
+
"CHANGELOG", "bin/**/*", "lib/**/*.rb",
|
55
|
+
"examples/**/*rb","test/**/*.rb", "doc/*.rdoc")
|
56
|
+
# This one specifies the output directory ...
|
57
|
+
rd.rdoc_dir = "doc/html"
|
58
|
+
|
59
|
+
# Or the HTML title of the generated documentation set.
|
60
|
+
rd.title = "simple_http: Simple Http client lib."
|
61
|
+
|
62
|
+
# These are options specifiying how source code inlined in the
|
63
|
+
# documentation should be formatted.
|
64
|
+
|
65
|
+
rd.options = ["--line-numbers", "--inline-source"]
|
66
|
+
|
67
|
+
# Check:
|
68
|
+
# `rdoc --help` for more rdoc options
|
69
|
+
# the {rdoc documenation home}[http://www.ruby-doc.org/stdlib/libdoc/rdoc/rdoc/index.html]
|
70
|
+
# or the documentation for the +Rake::RDocTask+ task[http://rake.rubyforge.org/classes/Rake/RDocTask.html]
|
71
|
+
end
|
72
|
+
|
73
|
+
# The GemPackageTask facilitates getting all your files collected
|
74
|
+
# together into gem archives. You can also use it to generate tarball
|
75
|
+
# and zip archives.
|
76
|
+
|
77
|
+
# First you'll need to assemble a gemspec
|
78
|
+
|
79
|
+
PROJECT_NAME = "simplehttp"
|
80
|
+
PKG_VERSION = SimpleHttp::VERSION
|
81
|
+
PKG_FILES = FileList['lib/**/*.rb', 'bin/**/*', 'examples/**/*', '[A-Z]*', 'test/**/*'].to_a
|
82
|
+
|
83
|
+
spec = Gem::Specification.new do |s|
|
84
|
+
s.platform = Gem::Platform::RUBY
|
85
|
+
s.summary = "simple_http: Simple Http client lib."
|
86
|
+
s.name = PROJECT_NAME
|
87
|
+
s.version = PKG_VERSION
|
88
|
+
s.files = PKG_FILES
|
89
|
+
s.requirements << "none"
|
90
|
+
s.require_path = 'lib'
|
91
|
+
s.description = <<END_DESC
|
92
|
+
Wrapper around net/http to provide quick and dirty http access.
|
93
|
+
END_DESC
|
94
|
+
end
|
95
|
+
|
96
|
+
# Adding a new GemPackageTask adds a task named `package`, which generates
|
97
|
+
# packages as gems, tarball and zip archives.
|
98
|
+
Rake::GemPackageTask.new(spec) do |pkg|
|
99
|
+
pkg.need_zip = true
|
100
|
+
pkg.need_tar_gz = true
|
101
|
+
end
|
102
|
+
|
103
|
+
|
104
|
+
# This task is used to demonstrate how to upload files to Rubyforge.
|
105
|
+
# Calling `upload_page` creates a current version of the +rdoc+
|
106
|
+
# documentation and uploads it to the Rubyforge homepage of the project,
|
107
|
+
# assuming it's hosted there and naming conventions haven't changed.
|
108
|
+
#
|
109
|
+
# This task uses `sh` to call the `scp` binary, which is plattform
|
110
|
+
# dependant and may not be installed on your computer if you're using
|
111
|
+
# Windows. I'm currently not aware of any pure ruby way to do scp
|
112
|
+
# transfers.
|
113
|
+
|
114
|
+
RubyForgeUser="a2800276"
|
115
|
+
RubyForgeProject=PROJECT_NAME
|
116
|
+
|
117
|
+
desc "Upload the web pages to the web."
|
118
|
+
task :upload_pages => ["clean", :rdoc] do
|
119
|
+
if RubyForgeProject then
|
120
|
+
path = "/var/www/gforge-projects/#{RubyForgeProject}"
|
121
|
+
sh "scp -r doc/html/* #{RubyForgeUser}@rubyforge.org:#{path}"
|
122
|
+
sh "scp doc/images/*.png #{RubyForgeUser}@rubyforge.org:#{path}/images"
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
|
127
|
+
# This task will run the unit tests provided in files called
|
128
|
+
# `test/test*.rb`. The task itself can be run with a call to `rake test`
|
129
|
+
|
130
|
+
Rake::TestTask.new do |t|
|
131
|
+
t.libs << "test"
|
132
|
+
t.libs << "lib"
|
133
|
+
t.test_files = FileList['test/*.rb']
|
134
|
+
t.verbose = true
|
135
|
+
end
|
136
|
+
|
137
|
+
|
data/TODO
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
* Provide tests and documentation for +head+, +options+ and +trace+
|
2
|
+
|
3
|
+
* There seems to be a bug in webrick that it doesn't recognize the
|
4
|
+
Content-Length header if using basic authentication. The POST test
|
5
|
+
cases lead to a:
|
6
|
+
ERROR HTTPRequest#fixup: WEBrick::HTTPStatus::LengthRequired occured.
|
7
|
+
even though the Content-Length header is set correctly in the http
|
8
|
+
packet...
|
9
|
+
|
10
|
+
* I've not figured out how to set_up and teardown webbrick properly.
|
11
|
+
Since webbrick blocks after calling `start`, it needs to started in a
|
12
|
+
seperate thread. I didn't handle communication with it properly
|
13
|
+
though. Test::Unit calls set_up and teardown before
|
14
|
+
and after executing each test... method, and not as I expected, before
|
15
|
+
and after each test file. This leads to a new TestServer being
|
16
|
+
instantiated which complains "bind-adress already in use", because the
|
17
|
+
other instance wasn't properly shut down. I tried to dix this by setting
|
18
|
+
up the server in the constructor, but that has the same effect.
|
19
|
+
It seems like a new instance of TestCase is instantiated to run every
|
20
|
+
single test... method.
|
21
|
+
|
22
|
+
* There also seems to be something screwy about how rake calls the
|
23
|
+
tests. Some ruby files seem to be included twice. This leads to
|
24
|
+
"already initialized constant" error in the output of the unit tests.
|
25
|
+
|
26
|
+
* The two previous bugs aren't really problematic, because they only
|
27
|
+
make the output of the tests ugly. They should be easy to fix... (But
|
28
|
+
they're not high on my list of priorities.)
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.1.7
|
@@ -0,0 +1,55 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{fresh_simplehttp}
|
8
|
+
s.version = "0.1.7"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Wilker Lucio"]
|
12
|
+
s.date = %q{2010-04-22}
|
13
|
+
s.description = %q{simple_http: Simple Http client lib. Use to do http requests without noise.}
|
14
|
+
s.email = %q{wilkerlucio@gmail.com}
|
15
|
+
s.extra_rdoc_files = [
|
16
|
+
"LICENSE",
|
17
|
+
"README",
|
18
|
+
"TODO"
|
19
|
+
]
|
20
|
+
s.files = [
|
21
|
+
"AUTHORS",
|
22
|
+
"CHANGELOG",
|
23
|
+
"LICENSE",
|
24
|
+
"README",
|
25
|
+
"Rakefile",
|
26
|
+
"TODO",
|
27
|
+
"VERSION",
|
28
|
+
"fresh_simplehttp.gemspec",
|
29
|
+
"lib/fresh_simplehttp.rb",
|
30
|
+
"lib/simple_http.rb",
|
31
|
+
"lib/simplehttp.rb",
|
32
|
+
"setup.rb",
|
33
|
+
"simplehttp.gemspec"
|
34
|
+
]
|
35
|
+
s.homepage = %q{http://github.com/wilkerlucio/simplehttp}
|
36
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
37
|
+
s.require_paths = ["lib"]
|
38
|
+
s.rubygems_version = %q{1.3.6}
|
39
|
+
s.summary = %q{simple_http: Simple Http client lib.}
|
40
|
+
s.test_files = [
|
41
|
+
"test/http_test.rb",
|
42
|
+
"test/http_test_server.rb"
|
43
|
+
]
|
44
|
+
|
45
|
+
if s.respond_to? :specification_version then
|
46
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
47
|
+
s.specification_version = 3
|
48
|
+
|
49
|
+
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
50
|
+
else
|
51
|
+
end
|
52
|
+
else
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
data/lib/simple_http.rb
ADDED
data/lib/simplehttp.rb
ADDED
@@ -0,0 +1,482 @@
|
|
1
|
+
$:.unshift(File.dirname(__FILE__)) unless
|
2
|
+
$:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
|
3
|
+
|
4
|
+
|
5
|
+
require 'net/http'
|
6
|
+
require 'net/https'
|
7
|
+
require 'uri'
|
8
|
+
require 'cgi'
|
9
|
+
require 'base64'
|
10
|
+
|
11
|
+
|
12
|
+
# Wrapper around ruby's standard net/http classes. Currently, only GET
|
13
|
+
# and POST https methods are supported. `SimpleHttp` provides class
|
14
|
+
# methods `get` and `post` to handle basic functionality. In case more
|
15
|
+
# complicated requests need to be made or default settings need to be
|
16
|
+
# overriden, it's possible to instantiate `SimpleHttp` and use instance
|
17
|
+
# methods `get` and `put`.
|
18
|
+
#
|
19
|
+
#
|
20
|
+
# Features:
|
21
|
+
#
|
22
|
+
# * Handles Redirects automatically
|
23
|
+
# * Proxy used transparently if http_proxy environment variable is
|
24
|
+
# set.
|
25
|
+
# * SSL handled automatically
|
26
|
+
# * fault tolerant uri, e.g. all of these would work:
|
27
|
+
# "www.example.com", "www.example.com/", "http://www.example.com"
|
28
|
+
#
|
29
|
+
# Some usage examples:
|
30
|
+
# # plain GET (using class methods)
|
31
|
+
# SimpleHttp.get "www.example.com"
|
32
|
+
#
|
33
|
+
# # POST using the instance methods
|
34
|
+
# uri = URI.parse "https://www.example.com/index.html"
|
35
|
+
# sh = SimpleHttp uri
|
36
|
+
# sh.set_proxy "my.proxy", "8080"
|
37
|
+
# sh.post {"query" => "query_data"}
|
38
|
+
#
|
39
|
+
# # POST using class methods.
|
40
|
+
# binaryData = getImage
|
41
|
+
# SimpleData.post binaryData, "image/png"
|
42
|
+
#
|
43
|
+
# # GET requst with a custom request_header
|
44
|
+
# sh = SimpleHttp.new "http://www.example.com"
|
45
|
+
# sh.request_headers= {'X-Special-Http-Header'=>'my-value'}
|
46
|
+
# sh.get
|
47
|
+
class SimpleHttp
|
48
|
+
|
49
|
+
VERSION='0.1.5'
|
50
|
+
|
51
|
+
# Host component of proxy uri
|
52
|
+
attr_accessor :proxy_host
|
53
|
+
# Port component of proxy uri
|
54
|
+
attr_accessor :proxy_port
|
55
|
+
# Proxy User
|
56
|
+
attr_accessor :proxy_user
|
57
|
+
# Proxy Password
|
58
|
+
attr_accessor :proxy_pwd
|
59
|
+
# The +URI+ object to connect to
|
60
|
+
attr_reader :uri
|
61
|
+
# +Hash+ of headers that will be sent in the request.
|
62
|
+
attr_accessor :request_headers
|
63
|
+
# +Hash+ of headers that were set in the response.
|
64
|
+
attr_accessor :response_headers
|
65
|
+
# A +Hash+ of handlers for each class of HTTPResponse.
|
66
|
+
attr_accessor :response_handlers
|
67
|
+
# The number of redirects we should follow. Default 5.
|
68
|
+
# An exception gets raised after the fifth redirect.
|
69
|
+
attr_accessor :follow_num_redirects
|
70
|
+
|
71
|
+
RESPONSE_HANDLERS = {
|
72
|
+
Net::HTTPResponse => lambda { |request, response, http|
|
73
|
+
http._update_response_headers(response)
|
74
|
+
raise "#{response.to_s} : #{response.code} : #{http.uri}"
|
75
|
+
},
|
76
|
+
Net::HTTPSuccess => lambda { |request, response, http|
|
77
|
+
http._update_response_headers(response)
|
78
|
+
#http.cookies += response.cookies
|
79
|
+
case request
|
80
|
+
when Net::HTTP::Head, Net::HTTP::Options
|
81
|
+
http.response_headers
|
82
|
+
else
|
83
|
+
response.body
|
84
|
+
end
|
85
|
+
},
|
86
|
+
Net::HTTPRedirection => lambda { |request, response, http|
|
87
|
+
raise "too many redirects!" unless http.follow_num_redirects > 0
|
88
|
+
# create a new SimpleHttp for the location
|
89
|
+
# refered to decreasing the remaining redirects
|
90
|
+
# by one.
|
91
|
+
|
92
|
+
if (location = response['location']) !~ /^https?:\/\//
|
93
|
+
new_location = "#{http.uri.scheme}://#{http.uri.host}"
|
94
|
+
if location =~ /^\//
|
95
|
+
new_location += location
|
96
|
+
else
|
97
|
+
new_location += "/#{http.uri.path}/#{location}"
|
98
|
+
end
|
99
|
+
location = new_location
|
100
|
+
end
|
101
|
+
|
102
|
+
sh = SimpleHttp.new location
|
103
|
+
#STDERR.puts location
|
104
|
+
sh.follow_num_redirects = http.follow_num_redirects-1
|
105
|
+
|
106
|
+
# copy the response handlers used in the current
|
107
|
+
# request in case they were non standard.
|
108
|
+
sh.response_handlers = http.response_handlers
|
109
|
+
|
110
|
+
# copy the request headers
|
111
|
+
sh.request_headers=http.request_headers
|
112
|
+
sh.response_headers=http.response_headers
|
113
|
+
#sh.cookies+=http.cookies
|
114
|
+
|
115
|
+
# copy host and port
|
116
|
+
sh.uri.host = http.uri.host
|
117
|
+
sh.uri.port = http.uri.port
|
118
|
+
|
119
|
+
# HTTP doesn't permit redirects for methods other than
|
120
|
+
# GET or HEAD. The exception is 303 redirects, which
|
121
|
+
# should automatically follow the redirect URI using a
|
122
|
+
# GET method regardless of the initial method. For
|
123
|
+
# other classes of redirection, the client is required
|
124
|
+
# to prompt the user before redirection occurs. Because
|
125
|
+
# that's not a feasible action for this library, all
|
126
|
+
# 3xx redirect URIs are followed using a GET method.
|
127
|
+
#
|
128
|
+
# http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
|
129
|
+
|
130
|
+
case request
|
131
|
+
when Net::HTTP::Get,
|
132
|
+
Net::HTTP::Head,
|
133
|
+
Net::HTTP::Options
|
134
|
+
sh.get
|
135
|
+
when Net::HTTP::Post
|
136
|
+
sh.request_headers['content-length']=nil
|
137
|
+
sh.get
|
138
|
+
else
|
139
|
+
raise "Not a valid HTTP method for redirection: #{request.class}"
|
140
|
+
end
|
141
|
+
|
142
|
+
}
|
143
|
+
|
144
|
+
}
|
145
|
+
|
146
|
+
# SimpleHttp can either be used directly through the +get+ and
|
147
|
+
# +post+ class methods or be instantiated, in case you need to
|
148
|
+
# to add custom behaviour to the requests.
|
149
|
+
#
|
150
|
+
# @param may be a URI or a String.
|
151
|
+
#
|
152
|
+
# Example:
|
153
|
+
# http = SimpleHttp.new(URI.parse("http://www.example.com"))
|
154
|
+
# http = SimpleHttp.new "www.example.com"
|
155
|
+
# http = SimpleHttp.new "http://usr:pwd@www.example.com:1234"
|
156
|
+
def initialize uri
|
157
|
+
set_proxy ENV['http_proxy'] if ENV['http_proxy']
|
158
|
+
|
159
|
+
if uri.class == String
|
160
|
+
unless uri =~ /^https?:\/\//
|
161
|
+
uri = "http://#{uri}"
|
162
|
+
end
|
163
|
+
uri = URI.parse uri
|
164
|
+
end
|
165
|
+
@uri = uri
|
166
|
+
|
167
|
+
if !@uri.path || "" == @uri.path.strip
|
168
|
+
@uri.path="/"
|
169
|
+
end
|
170
|
+
|
171
|
+
@request_headers={}
|
172
|
+
@response_headers={}
|
173
|
+
@cookies=[]
|
174
|
+
@response_handlers=RESPONSE_HANDLERS.clone
|
175
|
+
@follow_num_redirects=5
|
176
|
+
|
177
|
+
if @uri.user
|
178
|
+
basic_authentication @uri.user, @uri.password
|
179
|
+
end
|
180
|
+
|
181
|
+
end
|
182
|
+
|
183
|
+
#
|
184
|
+
# Provides facilities to perform http basic authentication. You
|
185
|
+
# don't need to provide +usr+ and +pwd+ if they are already included
|
186
|
+
# in the uri, i.e. http://user:password@www.example.com/
|
187
|
+
#
|
188
|
+
# Usage:
|
189
|
+
# sh = SimpleHttp.new "www.example.com/password_protected_resource"
|
190
|
+
# sh.basic_authentication "user_name", "secret_password"
|
191
|
+
# sh.get
|
192
|
+
#
|
193
|
+
|
194
|
+
def basic_authentication usr, pwd
|
195
|
+
@basic_auth = [usr, pwd]
|
196
|
+
end
|
197
|
+
|
198
|
+
#
|
199
|
+
# This method can be used to register response handlers for specific
|
200
|
+
# http responses in case you need to override the default behaviour.
|
201
|
+
# Defaults are:
|
202
|
+
#
|
203
|
+
# <b>HTTPSuccess (200-206)</b> :: return the body of the response
|
204
|
+
# <b>HTTPRedirection (300-307)</b> :: follow the redirection until success
|
205
|
+
# *Others* :: raise an exception
|
206
|
+
#
|
207
|
+
# ===Parameters:
|
208
|
+
#
|
209
|
+
# +clazz+ is the subclass of <code>Net::HTTPResponse</code> (or +HTTPResponse+ in case you
|
210
|
+
# want to define "default" behaviour) that you are registering the
|
211
|
+
# handler for. E.g. to register a handler for a HTTP 303 response, +clazz+
|
212
|
+
# needs to be +HTTPSeeOther+.
|
213
|
+
#
|
214
|
+
# +block+ is the handler itself. When a response of the appropriate class
|
215
|
+
# is received by the library, +block+ is called with three parameters: the
|
216
|
+
# 'raw' <code>Net::HTTPRequest</code>, the actual +HTTPResponse+ object that was received
|
217
|
+
# and a reference to the instance of +SimpleHttp+ that is executing the
|
218
|
+
# call.
|
219
|
+
#
|
220
|
+
# ===Example:
|
221
|
+
#
|
222
|
+
# # to override the default action of following a HTTP
|
223
|
+
# # redirect, you could register the folllowing handler:
|
224
|
+
#
|
225
|
+
# sh = SimpleHttp "www.example.com"
|
226
|
+
# sh.register_response_handler Net::HTTPRedirection {|request, response, shttp|
|
227
|
+
# response['location']
|
228
|
+
# }
|
229
|
+
#
|
230
|
+
|
231
|
+
def register_response_handler clazz, &block
|
232
|
+
# completely unnecessary sanity check to make sure parameter
|
233
|
+
# `clazz` is in fact a HTTPResponse ...
|
234
|
+
unless clazz.ancestors.include? Net::HTTPResponse
|
235
|
+
raise "Trying to register a response handler for non-response class: #{clazz}"
|
236
|
+
end
|
237
|
+
@response_handlers[clazz]=block
|
238
|
+
|
239
|
+
end
|
240
|
+
|
241
|
+
#
|
242
|
+
# Set the proxy to use for the http request.
|
243
|
+
#
|
244
|
+
# Note: you don't need to set the proxy in case the
|
245
|
+
# +http_proxy+ environment variable is set.
|
246
|
+
#
|
247
|
+
# To override previous proxy settings and connect directly,
|
248
|
+
# call +set_proxy+ +nil+.
|
249
|
+
#
|
250
|
+
# ===Usage:
|
251
|
+
#
|
252
|
+
# http = SimpleHttp.new "www.example.com"
|
253
|
+
#
|
254
|
+
# http.set_proxy "http://proxy:8000"
|
255
|
+
# or:
|
256
|
+
# http.set_proxy(URI.parse("http://proxy:8000"))
|
257
|
+
# or:
|
258
|
+
# http.set_proxy 'proxy', '8000', 'my_user', 'secret'
|
259
|
+
# or:
|
260
|
+
# http.set_proxy nil # to override previous proxy
|
261
|
+
# settings and make the request directly.
|
262
|
+
#
|
263
|
+
|
264
|
+
|
265
|
+
def set_proxy proxy, port=nil, user=nil, pwd=nil
|
266
|
+
|
267
|
+
|
268
|
+
if !proxy
|
269
|
+
@proxy_host=@proxy_port=@proxy_user=@proxy_pwd=nil
|
270
|
+
return
|
271
|
+
end
|
272
|
+
|
273
|
+
if String === proxy
|
274
|
+
if !port && !user && !pwd
|
275
|
+
proxy = URI.parse(proxy)
|
276
|
+
else
|
277
|
+
@proxy_host= host
|
278
|
+
@proxy_port= port
|
279
|
+
@proxy_user= user
|
280
|
+
@proxy_pwd = pwd
|
281
|
+
end
|
282
|
+
end
|
283
|
+
|
284
|
+
if URI::HTTP === proxy
|
285
|
+
@proxy_host= proxy.host
|
286
|
+
@proxy_port= proxy.port
|
287
|
+
@proxy_user= proxy.user
|
288
|
+
@proxy_pwd = proxy.password
|
289
|
+
end
|
290
|
+
end
|
291
|
+
|
292
|
+
# ===Internal
|
293
|
+
# Takes a HTTPResponse (or subclass) and determines how to
|
294
|
+
# handle the response. Default behaviour is:
|
295
|
+
#
|
296
|
+
# HTTPSuccess : return the body of the response
|
297
|
+
# HTTPRedirection : follow the redirect until success.
|
298
|
+
# default : raise the HTTPResponse.
|
299
|
+
#
|
300
|
+
# the default behaviour can be overidden by registering a
|
301
|
+
# response handler using the `register_response_handler` method.
|
302
|
+
#
|
303
|
+
|
304
|
+
def _handle_response http_request, http_response
|
305
|
+
raise "Not a Net::HTTPResponse" unless http_response.is_a? Net::HTTPResponse
|
306
|
+
|
307
|
+
c = http_response.class
|
308
|
+
# Go up the inheritance chain to find the most specific handler
|
309
|
+
# for the class of response we received.
|
310
|
+
while c!=Object
|
311
|
+
# the response_handlers hash contains a handler
|
312
|
+
# for the specific response class.
|
313
|
+
if @response_handlers[c]
|
314
|
+
return @response_handlers[c].call(http_request, http_response, self)
|
315
|
+
end
|
316
|
+
c=c.superclass
|
317
|
+
end
|
318
|
+
|
319
|
+
# if we reached this place, no handler was registered
|
320
|
+
# for this response. default is to return the response.
|
321
|
+
|
322
|
+
return http_response
|
323
|
+
end
|
324
|
+
|
325
|
+
# ===Internal
|
326
|
+
#
|
327
|
+
# ===Parameter
|
328
|
+
# +request+ the <code>Net::HTTPRequest</code> to process.
|
329
|
+
def _do_http request
|
330
|
+
http = Net::HTTP.new(@uri.host, @uri.port,
|
331
|
+
@proxy_host, @proxy_port, @proxy_user, @proxy_pwd)
|
332
|
+
http.use_ssl = @uri.scheme == 'https'
|
333
|
+
request.basic_auth(@basic_auth[0], @basic_auth[1]) if @basic_auth
|
334
|
+
|
335
|
+
# add custom request headers.
|
336
|
+
@request_headers.each {|key,value|
|
337
|
+
request[key]=value;
|
338
|
+
}
|
339
|
+
|
340
|
+
response = http.request(request)
|
341
|
+
_handle_response(request, response);
|
342
|
+
end
|
343
|
+
|
344
|
+
# ===Internal
|
345
|
+
def _make_query query
|
346
|
+
return query unless query && query.class == Hash
|
347
|
+
query.inject([]) do |s, (key, value)|
|
348
|
+
s << CGI::escape(key) + "=" + CGI::escape(value)
|
349
|
+
end.join('&')
|
350
|
+
end
|
351
|
+
|
352
|
+
# Make a simple GET request to the provided URI.
|
353
|
+
#
|
354
|
+
# ===Parameter
|
355
|
+
#
|
356
|
+
# +uri+ :: the uri to connect to, may be a +URI+ or a +String+
|
357
|
+
# +query+ :: the query part of the +get+, may be a +String+ or +Hash+
|
358
|
+
#
|
359
|
+
# ===Usage:
|
360
|
+
#
|
361
|
+
# puts(SimpleHttp.get("www.example.com"))
|
362
|
+
# puts(SimpleHttp.get("www.example.com", "param"=>"value")
|
363
|
+
def self.get uri, query=nil
|
364
|
+
http = SimpleHttp.new uri
|
365
|
+
http.get query
|
366
|
+
end
|
367
|
+
|
368
|
+
# Make a simple +HEAD+ request
|
369
|
+
#
|
370
|
+
# ===Parameter
|
371
|
+
# see +get+
|
372
|
+
def self.head uri, query=nil
|
373
|
+
http = SimpleHttp.new uri
|
374
|
+
http.head query
|
375
|
+
end
|
376
|
+
|
377
|
+
# Make a simple +OPTIONS+ request
|
378
|
+
def self.options uri
|
379
|
+
http = SimpleHttp.new uri
|
380
|
+
http.options
|
381
|
+
end
|
382
|
+
|
383
|
+
# Make a simple +TRACE+ request
|
384
|
+
def self.trace uri
|
385
|
+
http = SimpleHttp.new uri
|
386
|
+
http.trace
|
387
|
+
end
|
388
|
+
|
389
|
+
# Make a POST request to the provided URI.
|
390
|
+
#
|
391
|
+
# ===Example:
|
392
|
+
# puts(SimpleHttp.post("www.example.com", "query"=>"my_query"))
|
393
|
+
#
|
394
|
+
# Alternatively, to post arbitrary data, all you need to do is
|
395
|
+
# set the appriate +content_type+:
|
396
|
+
#
|
397
|
+
# SimpleHttp.post("http://www.example.com/", binary_data, "img/png")
|
398
|
+
|
399
|
+
def self.post uri, query=nil, content_type='application/x-www-form-urlencoded'
|
400
|
+
http = SimpleHttp.new uri
|
401
|
+
http.post query, content_type
|
402
|
+
end
|
403
|
+
|
404
|
+
# Call the +get+ method as an instance method if you need to
|
405
|
+
# modify the default behaviour of the library, or set special
|
406
|
+
# headers:
|
407
|
+
#
|
408
|
+
# http = SimpleHttp.new "www.example.com"
|
409
|
+
# http.request_headers["X-Special"]="whatever"
|
410
|
+
# str = http.get
|
411
|
+
def get query = nil
|
412
|
+
req = Net::HTTP::Get.new( _handle_path(query) )
|
413
|
+
# puts Net::HTTP::Proxy(@proxy_host, @proxy_port, @proxy_user, @proxy_pwd).get(@uri)
|
414
|
+
_do_http req
|
415
|
+
end
|
416
|
+
|
417
|
+
#
|
418
|
+
# Call the +head+ method as an instance method.
|
419
|
+
# see +head+
|
420
|
+
def head query = nil
|
421
|
+
req = Net::HTTP::Head.new( _handle_path(query) )
|
422
|
+
# puts Net::HTTP::Proxy(@proxy_host, @proxy_port, @proxy_user, @proxy_pwd).get(@uri)
|
423
|
+
_do_http req
|
424
|
+
end
|
425
|
+
|
426
|
+
# Call http +options+ method. Returns the response
|
427
|
+
# see +options+
|
428
|
+
def options
|
429
|
+
# we don't support sending a payload in options' body.
|
430
|
+
req = Net::HTTP::Options.new(@uri.path)
|
431
|
+
_do_http req
|
432
|
+
end
|
433
|
+
|
434
|
+
# Call http +trace+ method. Returns the response
|
435
|
+
# see +trace+
|
436
|
+
def trace
|
437
|
+
# payload?
|
438
|
+
req = Net::HTTP::Trace.new(@uri.path)
|
439
|
+
_do_http req
|
440
|
+
end
|
441
|
+
|
442
|
+
#
|
443
|
+
# Post the query data to the url.
|
444
|
+
#
|
445
|
+
# The body of the request remains empty if query=nil.
|
446
|
+
#
|
447
|
+
# In case +query+ is a +Hash+, it's assumed that we are
|
448
|
+
# sending a form.
|
449
|
+
#
|
450
|
+
# In case +query+ is a +String+, it's also assumed that a
|
451
|
+
# form is being sent, UNLESS the +content_type+ parameter
|
452
|
+
# is set.
|
453
|
+
#
|
454
|
+
def post query=nil, content_type='application/x-www-form-urlencoded'
|
455
|
+
req = Net::HTTP::Post.new( _handle_path() )
|
456
|
+
req.body= _make_query query if query
|
457
|
+
req.content_type=content_type if query
|
458
|
+
req.content_length=query ? req.body.length : 0
|
459
|
+
|
460
|
+
_do_http req
|
461
|
+
end
|
462
|
+
|
463
|
+
def _handle_path query=nil
|
464
|
+
if (query = _make_query query)
|
465
|
+
@uri.query = @uri.query ? @uri.query+"&"+query : query
|
466
|
+
end
|
467
|
+
path = @uri.query ? "#{uri.path}?#{@uri.query}" : @uri.path
|
468
|
+
end
|
469
|
+
|
470
|
+
# ===Internal
|
471
|
+
# Used in the response handler to set the value of the
|
472
|
+
# response header fields.
|
473
|
+
def _update_response_headers http_response
|
474
|
+
http_response.each_header {|key, value|
|
475
|
+
self.response_headers[key]=value
|
476
|
+
}
|
477
|
+
end
|
478
|
+
|
479
|
+
end
|
480
|
+
|
481
|
+
|
482
|
+
|