sevenwire-http_client 0.1.0

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/README.rdoc ADDED
@@ -0,0 +1,159 @@
1
+ = HTTP Client -- simple DSL for accessing HTTP resources
2
+
3
+ A simple HTTP client for Ruby, inspired by the Sinatra's microframework style
4
+ of specifying actions: get, put, post, delete.
5
+
6
+ == Usage: Raw URL
7
+
8
+ require 'http_client'
9
+
10
+ HttpClient.get 'http://example.com/resource'
11
+ HttpClient.get 'https://user:password@example.com/private/resource'
12
+
13
+ HttpClient.post 'http://example.com/resource', :param1 => 'one', :nested => { :param2 => 'two' }
14
+
15
+ HttpClient.delete 'http://example.com/resource'
16
+
17
+ See HttpClient module docs for details.
18
+
19
+ == Usage: ActiveResource-Style
20
+
21
+ resource = HttpClient::Resource.new 'http://example.com/resource'
22
+ resource.get
23
+
24
+ private_resource = HttpClient::Resource.new 'https://example.com/private/resource', :user => 'adam', :password => 'secret', :timeout => 20, :open_timeout => 5
25
+ private_resource.put File.read('pic.jpg'), :content_type => 'image/jpg'
26
+
27
+ See HttpClient::Resource module docs for details.
28
+
29
+ == Usage: Resource Nesting
30
+
31
+ site = HttpClient::Resource.new('http://example.com')
32
+ site['posts/1/comments'].post 'Good article.', :content_type => 'text/plain'
33
+
34
+ See HttpClient::Resource docs for details.
35
+
36
+ == Shell
37
+
38
+ The http_client shell command gives an IRB session with HttpClient already loaded:
39
+
40
+ $ http_client
41
+ >> HttpClient.get 'http://example.com'
42
+
43
+ Specify a URL argument for get/post/put/delete on that resource:
44
+
45
+ $ http_client http://example.com
46
+ >> put '/resource', 'data'
47
+
48
+ Add a user and password for authenticated resources:
49
+
50
+ $ http_client https://example.com user pass
51
+ >> delete '/private/resource'
52
+
53
+ Create ~/.http_client for named sessions:
54
+
55
+ sinatra:
56
+ url: http://localhost:4567
57
+ rack:
58
+ url: http://localhost:9292
59
+ private_site:
60
+ url: http://example.com
61
+ username: user
62
+ password: pass
63
+
64
+ Then invoke:
65
+
66
+ $ http_client private_site
67
+
68
+ Use as a one-off, curl-style:
69
+
70
+ $ http_client get http://example.com/resource > output_body
71
+
72
+ $ http_client put http://example.com/resource < input_body
73
+
74
+ == Logging
75
+
76
+ Write calls to a log filename (can also be "stdout" or "stderr"):
77
+
78
+ HttpClient.log = '/tmp/http_client.log'
79
+
80
+ Or set an environment variable to avoid modifying the code:
81
+
82
+ $ HTTPCLIENT_LOG=stdout path/to/my/program
83
+
84
+ Either produces logs like this:
85
+
86
+ HttpClient.get "http://some/resource"
87
+ # => 200 OK | text/html 250 bytes
88
+ HttpClient.put "http://some/resource", "payload"
89
+ # => 401 Unauthorized | application/xml 340 bytes
90
+
91
+ Note that these logs are valid Ruby, so you can paste them into the http_client
92
+ shell or a script to replay your sequence of http calls.
93
+
94
+ == Proxy
95
+
96
+ All calls to HttpClient, including Resources, will use the proxy specified by
97
+ HttpClient.proxy:
98
+
99
+ HttpClient.proxy = "http://proxy.example.com/"
100
+ HttpClient.get "http://some/resource"
101
+ # => response from some/resource as proxied through proxy.example.com
102
+
103
+ Often the proxy url is set in an environment variable, so you can do this to
104
+ use whatever proxy the system is configured to use:
105
+
106
+ HttpClient.proxy = ENV['http_proxy']
107
+
108
+ == Cookies
109
+
110
+ Request and Response objects know about HTTP cookies, and will automatically
111
+ extract and set headers for them as needed:
112
+
113
+ response = HttpClient.get 'http://example.com/action_which_sets_session_id'
114
+ response.cookies
115
+ # => {"_applicatioN_session_id" => "1234"}
116
+
117
+ response2 = HttpClient.post(
118
+ 'http://localhost:3000/',
119
+ {:param1 => "foo"},
120
+ {:cookies => {:session_id => "1234"}}
121
+ )
122
+ # ...response body
123
+
124
+ == SSL Client Certificates
125
+
126
+ HttpClient::Resource.new(
127
+ 'https://example.com',
128
+ :ssl_client_cert => OpenSSL::X509::Certificate.new(File.read("cert.pem")),
129
+ :ssl_client_key => OpenSSL::PKey::RSA.new(File.read("key.pem"), "passphrase, if any"),
130
+ :ssl_ca_file => "ca_certificate.pem",
131
+ :verify_ssl => OpenSSL::SSL::VERIFY_PEER
132
+ ).get
133
+
134
+ Self-signed certificates can be generated with the openssl command-line tool.
135
+
136
+ == Meta
137
+
138
+ This library began its life as RestClient. Due to some changes and the desire
139
+ to not conflict with the original library, Sevenwire forked and renamed it.
140
+
141
+ RestClient was written by Adam Wiggins (adam at heroku dot com)
142
+
143
+ RestClient Patches contributed by: Chris Anderson, Greg Borenstein, Ardekantur, Pedro
144
+ Belo, Rafael Souza, Rick Olson, Aman Gupta, Blake Mizerany, Brian Donovan, Ivan
145
+ Makfinsky, Marc-André Cournoyer, Coda Hale, Tetsuo Watanabe, Dusty Doris,
146
+ Lennon Day-Reynolds, James Edward Gray II, Cyril Rohr, Juan Alvarez, and Adam
147
+ Jacob
148
+
149
+ Released under the MIT License: http://www.opensource.org/licenses/mit-license.php
150
+
151
+ RestClient can be found at:
152
+
153
+ http://rest-client.heroku.com
154
+
155
+ http://github.com/adamwiggins/rest-client
156
+
157
+ HttpClient can be found at:
158
+
159
+ http://github.com/sevenwire/http-client
data/Rakefile ADDED
@@ -0,0 +1,48 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "http_client"
8
+ gem.summary = %Q{Simple HTTP/REST client for Ruby, inspired by microframework syntax for specifying actions. Forked from RestClient http://rest-client.heroku.com see README for reasons}
9
+ gem.email = "nate@sevenwire.com"
10
+ gem.homepage = "http://github.com/sevenwire/http_client"
11
+ gem.authors = ["Adam Wiggins", "Nate Sutton"]
12
+
13
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
14
+ end
15
+ rescue LoadError
16
+ puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
17
+ end
18
+
19
+ require 'spec/rake/spectask'
20
+ Spec::Rake::SpecTask.new(:spec) do |spec|
21
+ spec.libs << 'lib' << 'spec'
22
+ spec.spec_files = FileList['spec/**/*_spec.rb']
23
+ end
24
+
25
+ Spec::Rake::SpecTask.new(:rcov) do |spec|
26
+ spec.libs << 'lib' << 'spec'
27
+ spec.pattern = 'spec/**/*_spec.rb'
28
+ spec.rcov = true
29
+ end
30
+
31
+
32
+ task :default => :spec
33
+
34
+ require 'rake/rdoctask'
35
+ Rake::RDocTask.new do |rdoc|
36
+ if File.exist?('VERSION.yml')
37
+ config = YAML.load(File.read('VERSION.yml'))
38
+ version = "#{config[:major]}.#{config[:minor]}.#{config[:patch]}"
39
+ else
40
+ version = ""
41
+ end
42
+
43
+ rdoc.rdoc_dir = 'rdoc'
44
+ rdoc.title = "http-client #{version}"
45
+ rdoc.rdoc_files.include('README*')
46
+ rdoc.rdoc_files.include('lib/**/*.rb')
47
+ end
48
+
data/VERSION.yml ADDED
@@ -0,0 +1,4 @@
1
+ ---
2
+ :major: 0
3
+ :minor: 1
4
+ :patch: 0
data/bin/http_client ADDED
@@ -0,0 +1,82 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $:.unshift File.dirname(__FILE__) + "/../lib"
4
+ require 'http_client'
5
+
6
+ require "yaml"
7
+
8
+ def usage(why = nil)
9
+ puts "failed for reason: #{why}" if why
10
+ puts "usage: http_client [get|put|post|delete] url|name [username] [password]"
11
+ puts " The verb is optional, if you leave it off you'll get an interactive shell."
12
+ puts " put and post both take the input body on stdin."
13
+ exit(1)
14
+ end
15
+
16
+ if %w(get put post delete).include? ARGV.first
17
+ @verb = ARGV.shift
18
+ else
19
+ @verb = nil
20
+ end
21
+
22
+ @url = ARGV.shift || 'http://localhost:4567'
23
+
24
+ config = YAML.load(File.read(ENV['HOME'] + "/.http_client")) rescue {}
25
+
26
+ @url, @username, @password = if c = config[@url]
27
+ [c['url'], c['username'], c['password']]
28
+ else
29
+ [@url, *ARGV]
30
+ end
31
+
32
+ usage("invalid url '#{@url}") unless @url =~ /^https?/
33
+ usage("too few args") unless ARGV.size < 3
34
+
35
+ def r
36
+ @r ||= HttpClient::Resource.new(@url, @username, @password)
37
+ end
38
+
39
+ r # force rc to load
40
+
41
+ if @verb
42
+ begin
43
+ if %w(put post).include? @verb
44
+ puts r.send(@verb, STDIN.read)
45
+ else
46
+ puts r.send(@verb)
47
+ end
48
+ exit 0
49
+ rescue HttpClient::Exception => e
50
+ puts e.response.body if e.respond_to? :response
51
+ raise
52
+ end
53
+ end
54
+
55
+ %w(get post put delete).each do |m|
56
+ eval <<-end_eval
57
+ def #{m}(path, *args, &b)
58
+ r[path].#{m}(*args, &b)
59
+ end
60
+ end_eval
61
+ end
62
+
63
+ def method_missing(s, *args, &b)
64
+ super unless r.respond_to?(s)
65
+ r.send(s, *args, &b)
66
+ end
67
+
68
+ require 'irb'
69
+ require 'irb/completion'
70
+
71
+ if File.exists? ".irbrc"
72
+ ENV['IRBRC'] = ".irbrc"
73
+ end
74
+
75
+ if File.exists?(rcfile = "~/.http_clientrc")
76
+ load(rcfile)
77
+ end
78
+
79
+ ARGV.clear
80
+
81
+ IRB.start
82
+ exit!
@@ -0,0 +1,93 @@
1
+ require 'uri'
2
+ require 'net/https'
3
+ require 'zlib'
4
+ require 'stringio'
5
+
6
+ require File.dirname(__FILE__) + '/http_client/request'
7
+ require File.dirname(__FILE__) + '/http_client/mixin/response'
8
+ require File.dirname(__FILE__) + '/http_client/response'
9
+ require File.dirname(__FILE__) + '/http_client/raw_response'
10
+ require File.dirname(__FILE__) + '/http_client/resource'
11
+ require File.dirname(__FILE__) + '/http_client/exceptions'
12
+
13
+ # This module's static methods are the entry point for using the HTTP client.
14
+ #
15
+ # # GET
16
+ # xml = HttpClient.get 'http://example.com/resource'
17
+ # jpg = HttpClient.get 'http://example.com/resource', :accept => 'image/jpg'
18
+ #
19
+ # # authentication and SSL
20
+ # HttpClient.get 'https://user:password@example.com/private/resource'
21
+ #
22
+ # # POST or PUT with a hash sends parameters as a urlencoded form body
23
+ # HttpClient.post 'http://example.com/resource', :param1 => 'one'
24
+ #
25
+ # # nest hash parameters
26
+ # HttpClient.post 'http://example.com/resource', :nested => { :param1 => 'one' }
27
+ #
28
+ # # POST and PUT with raw payloads
29
+ # HttpClient.post 'http://example.com/resource', 'the post body', :content_type => 'text/plain'
30
+ # HttpClient.post 'http://example.com/resource.xml', xml_doc
31
+ # HttpClient.put 'http://example.com/resource.pdf', File.read('my.pdf'), :content_type => 'application/pdf'
32
+ #
33
+ # # DELETE
34
+ # HttpClient.delete 'http://example.com/resource'
35
+ #
36
+ # # retreive the response http code and headers
37
+ # res = HttpClient.get 'http://example.com/some.jpg'
38
+ # res.code # => 200
39
+ # res.headers[:content_type] # => 'image/jpg'
40
+ #
41
+ # # HEAD
42
+ # HttpClient.head('http://example.com').headers
43
+ #
44
+ # To use with a proxy, just set HttpClient.proxy to the proper http proxy:
45
+ #
46
+ # HttpClient.proxy = "http://proxy.example.com/"
47
+ #
48
+ # Or inherit the proxy from the environment:
49
+ #
50
+ # HttpClient.proxy = ENV['http_proxy']
51
+ #
52
+ # For live tests of HttpClient, try using http://http-test.heroku.com, which echoes back information about the http call:
53
+ #
54
+ # >> HttpClient.put 'http://http-test.heroku.com/resource', :foo => 'baz'
55
+ # => "PUT http://http-test.heroku.com/resource with a 7 byte payload, content type application/x-www-form-urlencoded {\"foo\"=>\"baz\"}"
56
+ #
57
+ module HttpClient
58
+ def self.get(url, headers={})
59
+ Request.execute(:method => :get, :url => url, :headers => headers)
60
+ end
61
+
62
+ def self.post(url, payload, headers={})
63
+ Request.execute(:method => :post, :url => url, :payload => payload, :headers => headers)
64
+ end
65
+
66
+ def self.put(url, payload, headers={})
67
+ Request.execute(:method => :put, :url => url, :payload => payload, :headers => headers)
68
+ end
69
+
70
+ def self.delete(url, headers={})
71
+ Request.execute(:method => :delete, :url => url, :headers => headers)
72
+ end
73
+
74
+ def self.head(url, headers={})
75
+ Request.execute(:method => :head, :url => url, :headers => headers)
76
+ end
77
+
78
+ class << self
79
+ attr_accessor :proxy
80
+ end
81
+
82
+ # Print log of HttpClient calls. Value can be stdout, stderr, or a filename.
83
+ # You can also configure logging by the environment variable HTTPCLIENT_LOG.
84
+ def self.log=(log)
85
+ @@log = log
86
+ end
87
+
88
+ def self.log # :nodoc:
89
+ return ENV['HTTPCLIENT_LOG'] if ENV['HTTPCLIENT_LOG']
90
+ return @@log if defined? @@log
91
+ nil
92
+ end
93
+ end
@@ -0,0 +1,39 @@
1
+ module HttpClient
2
+ # This is the base HttpClient exception class. Rescue it if you want to
3
+ # catch any exception that your request might raise
4
+ class Exception < StandardError
5
+ def message(default=nil)
6
+ self.class::ErrorMessage
7
+ end
8
+ end
9
+
10
+ # Base HttpClient exception when there's a response available
11
+ class ExceptionWithResponse < Exception
12
+ attr_accessor :response
13
+
14
+ def initialize(response=nil)
15
+ @response = response
16
+ end
17
+
18
+ def http_code
19
+ @response.code.to_i if @response
20
+ end
21
+ end
22
+
23
+ # The server broke the connection prior to the request completing. Usually
24
+ # this means it crashed, or sometimes that your network connection was
25
+ # severed before it could complete.
26
+ class ServerBrokeConnection < Exception
27
+ ErrorMessage = 'Server broke connection'
28
+ end
29
+
30
+ # The server took too long to respond.
31
+ class RequestTimeout < Exception
32
+ ErrorMessage = 'Request timed out'
33
+ end
34
+
35
+ # The server refused the connection
36
+ class ConnectionRefused < Exception
37
+ ErrorMessage = 'Server refused connection'
38
+ end
39
+ end
@@ -0,0 +1,43 @@
1
+ module HttpClient
2
+ module Mixin
3
+ module Response
4
+ attr_reader :net_http_res
5
+
6
+ # HTTP status code, always 200 since HttpClient throws exceptions for
7
+ # other codes.
8
+ def code
9
+ @code ||= @net_http_res.code.to_i
10
+ end
11
+
12
+ # A hash of the headers, beautified with symbols and underscores.
13
+ # e.g. "Content-type" will become :content_type.
14
+ def headers
15
+ @headers ||= self.class.beautify_headers(@net_http_res.to_hash)
16
+ end
17
+
18
+ # Hash of cookies extracted from response headers
19
+ def cookies
20
+ @cookies ||= (self.headers[:set_cookie] || "").split('; ').inject({}) do |out, raw_c|
21
+ key, val = raw_c.split('=')
22
+ unless %w(expires domain path secure).member?(key)
23
+ out[key] = val
24
+ end
25
+ out
26
+ end
27
+ end
28
+
29
+ def self.included(receiver)
30
+ receiver.extend(HttpClient::Mixin::Response::ClassMethods)
31
+ end
32
+
33
+ module ClassMethods
34
+ def beautify_headers(headers)
35
+ headers.inject({}) do |out, (key, value)|
36
+ out[key.gsub(/-/, '_').to_sym] = value.first
37
+ out
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end