fresh_simplehttp 0.1.7
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
|