http_request.rb 1.0.1 → 1.0.2

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.
Files changed (4) hide show
  1. data/Changelog +5 -1
  2. data/README.rdoc +22 -4
  3. data/lib/http_request.rb +227 -186
  4. metadata +3 -3
data/Changelog CHANGED
@@ -1,4 +1,8 @@
1
- v1.1
1
+ v1.0.2
2
+ * Proc.call (block) style supported and some small changes
3
+ * improve ftp protocol and add a new method get_as_string for the ftp calls
4
+
5
+ v1.0.1
2
6
  * new feature: upload or download files by ftp
3
7
 
4
8
  v1.0
data/README.rdoc CHANGED
@@ -22,7 +22,8 @@ The HttpRequest class is based on the 'net/http' and 'net/ftp' libraries, so the
22
22
  == Examples for your ruby program:
23
23
 
24
24
  include http_request.rb first
25
- require '/path/to/http_request.rb'
25
+ require '/path/to/http_request.rb'
26
+ or install it "gem install http_request.rb", then require 'http_request'
26
27
 
27
28
  get
28
29
  puts HttpRequest.get('http://www.github.com')
@@ -33,7 +34,7 @@ get with query string, 4 are same
33
34
  puts HttpRequest.get({:url => 'http://www.google.com/search', :parameters => 'hl=en&q=ruby&start=0&sa=N'})
34
35
  puts HttpRequest.get({:url => 'http://www.google.com/search', :parameters => {:hl => 'en', :q => 'ruby', :start => 0, :sa => 'N'}})
35
36
 
36
- post with some paramet
37
+ post with some parameters
37
38
  puts HttpRequest.get(:url => 'http://localhost/test.php', :parameters => 'from=http_request.rb').body
38
39
  puts HttpRequest.get(:url => 'http://localhost/test.php', :parameters => {:name => 'Ruby', :time => 'Now'}).body
39
40
 
@@ -133,11 +134,14 @@ post
133
134
 
134
135
  such as "get_only_header" and "get_with_header", post and other http methods also can do such as "post_only_header", "put_with_header" etc.
135
136
 
136
- == Examples for FTP (since 1.0.1):
137
+ == Examples for FTP (since v1.0.1):
137
138
 
138
139
  download and save to
139
140
  ftp = HttpRequest.ftp(:get, :url => 'ftp://user:pass@my.domain.name/path/to/hello.mp3', :to => '/tmp/hello.mp3')
140
141
 
142
+ # get as string (since v1.0.2)
143
+ puts HttpRequest.ftp(:get_as_string, 'ftp://user:pass@localhost/path/to/file.txt')
144
+
141
145
  upload from local
142
146
  ftp = HttpRequest.ftp(:put, :url => 'ftp://user:pass@my.domain.name/path/to/hello.mp3', :from => '/tmp/hello.mp3')
143
147
 
@@ -177,12 +181,26 @@ download multiple files from a directory
177
181
  }
178
182
  ftp.close
179
183
 
184
+ == Proc.call style (since v1.0.2)
185
+
186
+ HttpRequest.get('http://www.example.com/') {|http|
187
+ puts http.body
188
+ http.each_header {|k, v| puts "#{k} => #{v}" }
189
+ http.cookies.each {|k, v| puts "#{k} => #{v}" }
190
+ }
191
+
192
+ HttpRequest.ftp(:get, {"ftp://user:pass@localhost", :to => }) {|ftp|
193
+ puts ftp.ls
194
+ ftp.chdir('soft')
195
+ ftp.getbinaryfile('ruby.exe', '/path/to/local/ruby.exe');
196
+ }
197
+
180
198
  == TODO
181
199
 
182
200
  ......
183
201
 
184
202
  == LATEST VERSION
185
- 1.0.1
203
+ 1.0.2
186
204
 
187
205
  == Author
188
206
 
data/lib/http_request.rb CHANGED
@@ -3,7 +3,7 @@
3
3
  # == Description
4
4
  #
5
5
  # This is a small, lightweight, powerful HttpRequest class based on the 'net/http' and 'net/ftp' libraries,
6
- # it's easy to use to send http request and get response, also can use it as a shell script in command line.
6
+ # it's easy to use to send http request and get response, also can use it as a shell script on command line in some cases.
7
7
  #
8
8
  # == Example
9
9
  #
@@ -11,9 +11,9 @@
11
11
  #
12
12
  # == Version
13
13
  #
14
- # v1.0.1
14
+ # v1.0.2
15
15
  #
16
- # Last Change: 11 Apail, 2009
16
+ # Last Change: 2 May, 2009
17
17
  #
18
18
  # == Author
19
19
  #
@@ -29,226 +29,267 @@ require 'singleton'
29
29
  class HttpRequest
30
30
  include Singleton
31
31
 
32
+ # version
33
+ VERSION = '1.0.2'.freeze
34
+ def self.version;VERSION;end
35
+
36
+ # avaiabled http methods
32
37
  def self.http_methods
33
- %w{get head post put proppatch lock unlock options propfind delete move copy mkcol trace}
38
+ %w{get head post put proppatch lock unlock options propfind delete move copy mkcol trace}
34
39
  end
35
40
 
36
- # initialize
37
- def init_args(method, options)
38
- options = {:url => options.to_s} if [String, Array].include? options.class
39
- @options = {
40
- :ssl_port => 443,
41
- :redirect_limits => 5,
42
- :redirect => true,
43
- :url => nil
44
- }
45
- @options.merge!(options)
46
- @uri = URI(@options[:url])
47
- @uri.path = '/' if @uri.path.empty?
48
- @headers = {
49
- 'Host' => @uri.host,
50
- 'Referer' => @options[:url],
51
- 'Accept-Language' => 'en-us,zh-cn;q=0.7,en;q=0.3',
52
- 'Accept-Charset' => 'zh-cn,zh;q=0.5',
53
- 'Accept' => 'text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5',
54
- 'Cache-Control' => 'max-age=0',
55
- 'User-Agent' => 'Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.0.7) Gecko/2009030423 Ubuntu/8.04 (hardy) Firefox/3.0.7',
56
- 'Connection' => 'keep-alive'
57
- }
58
-
59
- # Basic Authenication
60
- @headers['Authorization'] = "Basic " + [@uri.userinfo].pack('m').delete!("\r\n") if @uri.userinfo
61
-
62
- # headers
63
- @options[:headers].each {|k, v| @headers[k] = v} if @options[:headers].is_a? Hash
64
-
65
- # add cookies if have
66
- if @options[:cookies]
67
- if @options[:cookies].is_a? Hash
68
- cookies = []
69
- @options[:cookies].each {|k, v|
70
- cookies << "#{CGI.escape(k.to_s)}=#{CGI.escape(v.to_s)}"
71
- }
72
- cookies = cookies.join('; ')
73
- else
74
- cookies = @options[:cookies].to_s
75
- end
76
- @headers['Cookie'] = cookies unless cookies.empty?
77
- end
78
-
79
- @redirect_times = 0 if @options[:redirect]
41
+ # return data with or without block
42
+ def self.data(response, &block)
43
+ block_given? ? block.call(response) : response
80
44
  end
81
45
 
82
- # send request
83
- def request(method, opt)
46
+ # send request by some given parameters
47
+ def request(method, opt, &block)
84
48
  init_args(method, opt)
85
49
  @options[:method] = method
86
50
 
87
- # for upload files
88
- if @options[:files].is_a?(Array) && 'post'.eql?(method)
89
- build_multipart
90
- else
91
- if @options[:parameters].is_a? Hash
92
- @options[:parameters] = @options[:parameters].collect{|k, v|
93
- CGI.escape(k.to_s) + '=' + CGI.escape(v.to_s)
94
- }.join('&')
95
- end
96
- end
97
-
98
- http = if @options[:proxy_addr]
99
- if @options[:proxy_user] and @options[:proxy_pass]
100
- Net::HTTP::Proxy(@options[:proxy_addr], @options[:proxy_port], @options[:proxy_user], @options[:proxy_pass]).new(@u.host, @u.port)
101
- else
102
- Net::HTTP::Proxy(@options[:proxy_addr], @options[:proxy_port]).new(@uri.host, @uri.port)
103
- end
104
- else
105
- Net::HTTP.new(@uri.host, @uri.port)
106
- end
51
+ # for upload files
52
+ if @options[:files].is_a?(Array) && 'post'.eql?(method)
53
+ build_multipart
54
+ else
55
+ if @options[:parameters].is_a? Hash
56
+ @options[:parameters] = @options[:parameters].collect{|k, v|
57
+ "#{CGI.escape(k.to_s)}=#{CGI.escape(v.to_s)}"
58
+ }.join('&')
59
+ end
60
+ end
61
+
62
+ # for proxy
63
+ http = if @options[:proxy_addr]
64
+ if @options[:proxy_user] && @options[:proxy_pass]
65
+ Net::HTTP::Proxy(@options[:proxy_addr], @options[:proxy_port], @options[:proxy_user], @options[:proxy_pass]).new(@u.host, @u.port)
66
+ else
67
+ Net::HTTP::Proxy(@options[:proxy_addr], @options[:proxy_port]).new(@uri.host, @uri.port)
68
+ end
69
+ else
70
+ Net::HTTP.new(@uri.host, @uri.port)
71
+ end
107
72
 
108
73
  # ssl support
109
74
  http.use_ssl = true if @uri.scheme =~ /^https$/i
110
75
 
111
- # get data by post or get method.
76
+ # sending request and get response
112
77
  response = send_request http
113
78
 
114
- return response unless @options[:redirect]
79
+ return HttpRequest.data(response, &block) unless @options[:redirect]
115
80
 
116
- # redirect....===>>>
81
+ # redirect?
117
82
  case response
118
83
  when Net::HTTPRedirection
119
- @options[:url] = if response['location'] =~ /^http[s]*:\/\//i
120
- response['location']
121
- else
122
- @uri.scheme + '://' + @uri.host + ':' + @uri.port.to_s + response['location']
123
- end
124
- @redirect_times = 0 unless @redirect_times
84
+ url = "#{@uri.scheme}://#{@uri.host}#{':' + @uri.port.to_s if @uri.port != 80}"
85
+ @options[:url] = case response['location']
86
+ when /^https?:\/\//i
87
+ response['location']
88
+ when /^\//
89
+ url + response['location']
90
+ when /^(\.\.\/|\.\/)/
91
+ paths = (File.dirname(@uri.path) + '/' + response['location']).split('/')
92
+ location = []
93
+ paths.each {|path|
94
+ next if path.empty? || path.eql?('.')
95
+ path == '..' ? location.pop : location.push(path)
96
+ }
97
+ url + '/' + location.join('/')
98
+ else
99
+ url + File.dirname(@uri.path) + '/' + response['location']
100
+ end
125
101
  @redirect_times = @redirect_times.succ
126
- raise 'too deep redirect...' if @redirect_times > @options[:redirect_limits]
127
- request('get', @options)
102
+ raise 'too many redirects...' if @redirect_times > @options[:redirect_limits]
103
+ request('get', @options, &block)
128
104
  else
129
- response
105
+ return HttpRequest.data(response, &block)
130
106
  end
131
107
  end
132
108
 
133
- # for ftp
134
- def self.ftp(method, options)
135
- require 'net/ftp'
136
- options = {:close => true}.merge(options)
137
- options[:url] = "ftp://#{options[:url]}" unless options[:url] =~ /^ftp:\/\//
138
- uri = URI(options[:url])
139
- guest_name, guest_pass = 'anonymous', 'guest@' + uri.host
140
- unless options[:username]
141
- options[:username], options[:password] = uri.userinfo ? uri.userinfo.split(':') : [guest_name, guest_pass]
142
- end
143
- options[:username] = guest_user unless options[:username]
144
- options[:password] = guest_pass unless options[:password]
145
- ftp = Net::FTP.open(uri.host, options[:username], options[:password])
146
- return ftp unless method
147
- stat = case method.to_sym
148
- when :get
149
- options[:to] = File.basename(uri.path) unless options[:to]
150
- ftp.getbinaryfile(uri.path, options[:to])
151
- when :put
152
- ftp.putbinaryfile(options[:from], uri.path)
153
- when :mkdir, :rmdir, :delete, :size, :mtime, :list, :nlst
154
- ftp.method(method).call(uri.path)
155
- when :rename
156
- ftp.rename(uri.path, options[:to]) if options[:to]
157
- when :status
158
- ftp.status
159
- else
160
- return ftp
161
- end
162
- if options[:close]
163
- ftp.close
164
- stat
165
- else
166
- ftp.response = stat
167
- ftp
168
- end
169
- end
170
-
171
- # catch all of http requests
172
- def self.method_missing(method_name, args)
109
+ # catch all of http requests
110
+ def self.method_missing(method_name, args, &block)
173
111
  method_name = method_name.to_s.downcase
174
112
  raise NoHttpMethodException, "No such http method can be called: #{method_name}" unless self.http_methods.include?(method_name)
175
- self.instance.request(method_name, args)
113
+ self.instance.request(method_name, args, &block)
114
+ end
115
+
116
+ # for ftp
117
+ def self.ftp(method, options, &block)
118
+ require 'net/ftp'
119
+ options = {:url => options} if options.is_a? String
120
+ options = {:close => true}.merge(options)
121
+ options[:url] = "ftp://#{options[:url]}" unless options[:url] =~ /^ftp:\/\//
122
+ uri = URI(options[:url])
123
+ guest_name, guest_pass = 'anonymous', "guest@#{uri.host}"
124
+ unless options[:username]
125
+ options[:username], options[:password] = uri.userinfo ? uri.userinfo.split(':') : [guest_name, guest_pass]
126
+ end
127
+ options[:username] = guest_name unless options[:username]
128
+ options[:password] = guest_pass if options[:password].nil?
129
+ ftp = Net::FTP.open(uri.host, options[:username], options[:password])
130
+ return self.data(ftp, &block) unless method
131
+ stat = case method.to_sym
132
+ when :get_as_string
133
+ require 'tempfile'
134
+ tmp = Tempfile.new('http_request_ftp')
135
+ ftp.getbinaryfile(uri.path, tmp.path)
136
+ ftp.response = tmp.read
137
+ tmp.close
138
+ unless block_given?
139
+ ftp.close
140
+ return ftp.response
141
+ end
142
+ when :get
143
+ options[:to] = File.basename(uri.path) unless options[:to]
144
+ ftp.getbinaryfile(uri.path, options[:to])
145
+ when :put
146
+ ftp.putbinaryfile(options[:from], uri.path)
147
+ when :mkdir, :rmdir, :delete, :size, :mtime, :list, :nlst
148
+ ftp.method(method).call(uri.path)
149
+ when :rename
150
+ ftp.rename(uri.path, options[:to]) if options[:to]
151
+ when :status
152
+ ftp.status
153
+ else
154
+ return ftp
155
+ end
156
+ if options[:close] && !block_given?
157
+ ftp.close
158
+ stat
159
+ else
160
+ ftp.response = stat unless ftp.response
161
+ self.data(ftp, &block)
162
+ end
176
163
  end
177
164
 
178
165
  private
179
166
 
180
- # for upload files by post method
181
- def build_multipart
182
- require 'md5'
183
- boundary = MD5.md5(rand.to_s).to_s[0..5]
184
- @headers['Content-type'] = "multipart/form-data, boundary=#{boundary}"
185
- multipart = []
186
- if @options[:parameters]
187
- @options[:parameters] = CGI.parse(@options[:parameters]) if @options[:parameters].is_a? String
188
- if @options[:parameters].is_a? Hash
189
- @options[:parameters].each {|k, v|
190
- multipart << "--#{boundary}"
191
- multipart << "Content-disposition: form-data; name=\"#{CGI.escape(k.to_s)}\""
192
- multipart << "\r\n#{CGI.escape(v.to_s)}"
193
- }
194
- end
195
- end
196
- @options[:files].each_with_index {|f, index|
197
- f[:field_name] ||= "files[]"
198
- f[:file_name] ||= "#{boundary}_#{index}"
199
- f[:transfer_encoding] ||= "binary"
200
- f[:content_type] ||= 'application/octet-stream'
201
- multipart << "--#{boundary}"
202
- multipart << "Content-disposition: form-data; name=\"#{f[:field_name]}\"; filename=\"#{f[:file_name]}\""
203
- multipart << "Content-type: #{f[:content_type]}"
204
- multipart << "Content-Transfer-Encoding: #{f[:transfer_encoding]}"
205
- multipart << "\r\n#{f[:file_content]}"
206
- }
207
- multipart << "--#{boundary}--"
208
- multipart = multipart.join("\r\n")
209
- @headers['Content-length'] = "#{multipart.size}"
210
- @options[:parameters] = multipart
211
- end
212
-
213
- # send request
167
+ # initialize for the http request
168
+ def init_args(method, options)
169
+ options = {:url => options.to_s} if [String, Array].include? options.class
170
+ @options = {
171
+ :ssl_port => 443,
172
+ :redirect_limits => 5,
173
+ :redirect => true,
174
+ :url => nil
175
+ }
176
+ @options.merge!(options)
177
+ @uri = URI(@options[:url])
178
+ @uri.path = '/' if @uri.path.empty?
179
+ @headers = {
180
+ 'Host' => @uri.host,
181
+ 'Referer' => @options[:url],
182
+ 'User-Agent' => 'HttpRequest.rb ' + VERSION
183
+ }
184
+
185
+ # Basic Authenication
186
+ @headers['Authorization'] = "Basic " + [@uri.userinfo].pack('m').delete!("\r\n") if @uri.userinfo
187
+
188
+ # headers
189
+ @options[:headers].each {|k, v| @headers[k] = v} if @options[:headers].is_a? Hash
190
+
191
+ # add cookies if have
192
+ if @options[:cookies]
193
+ if @options[:cookies].is_a? Hash
194
+ cookies = []
195
+ @options[:cookies].each {|k, v|
196
+ cookies << "#{CGI.escape(k.to_s)}=#{CGI.escape(v.to_s)}"
197
+ }
198
+ cookies = cookies.join('; ')
199
+ else
200
+ cookies = @options[:cookies].to_s
201
+ end
202
+ @headers['Cookie'] = cookies unless cookies.empty?
203
+ end
204
+
205
+ @redirect_times = 0 if @options[:redirect]
206
+ end
207
+
208
+ # for upload files by post method
209
+ def build_multipart
210
+ require 'md5'
211
+ boundary = MD5.md5(rand.to_s).to_s[0..5]
212
+ @headers['Content-type'] = "multipart/form-data, boundary=#{boundary}"
213
+ multipart = []
214
+ if @options[:parameters]
215
+ @options[:parameters] = CGI.parse(@options[:parameters]) if @options[:parameters].is_a? String
216
+ if @options[:parameters].is_a? Hash
217
+ @options[:parameters].each {|k, v|
218
+ multipart << "--#{boundary}"
219
+ multipart << "Content-disposition: form-data; name=\"#{CGI.escape(k.to_s)}\""
220
+ multipart << "\r\n#{CGI.escape(v.to_s)}"
221
+ }
222
+ end
223
+ end
224
+ @options[:files].each_with_index {|f, index|
225
+ f[:field_name] ||= "files[]"
226
+ f[:file_name] ||= "#{boundary}_#{index}"
227
+ f[:transfer_encoding] ||= "binary"
228
+ f[:content_type] ||= 'application/octet-stream'
229
+ multipart << "--#{boundary}"
230
+ multipart << "Content-disposition: form-data; name=\"#{f[:field_name]}\"; filename=\"#{f[:file_name]}\""
231
+ multipart << "Content-type: #{f[:content_type]}"
232
+ multipart << "Content-Transfer-Encoding: #{f[:transfer_encoding]}"
233
+ multipart << "\r\n#{f[:file_content]}"
234
+ }
235
+ multipart << "--#{boundary}--"
236
+ multipart = multipart.join("\r\n")
237
+ @headers['Content-length'] = "#{multipart.size}"
238
+ @options[:parameters] = multipart
239
+ end
240
+
241
+ # send http request
214
242
  def send_request(http)
215
- case @options[:method]
216
- when /^(get|head|options|delete|move|copy|trace|)$/
217
- @options[:parameters] = @uri.query.to_s if @options[:parameters].to_s.empty?
218
- @options[:parameters] = "?#{@options[:parameters]}" unless @options[:parameters].empty?
243
+
244
+ # merge parameters
245
+ parameters = @options[:parameters].to_s
246
+ @options[:parameters] = "#{@uri.query}" if @uri.query
247
+ if parameters
248
+ if @options[:parameters]
249
+ @options[:parameters] << "&#{parameters}"
250
+ else
251
+ @options[:parameters] = "#{parameters}"
252
+ end
253
+ end
254
+
255
+ # GO !!
256
+ if @options[:method] =~ /^(get|head|options|delete|move|copy|trace|)$/
257
+ @options[:parameters] = "?#{@options[:parameters]}" if @options[:parameters]
219
258
  http.method(@options[:method]).call("#{@uri.path}#{@options[:parameters]}", @headers)
220
259
  else
221
260
  http.method(@options[:method]).call(@uri.path, @options[:parameters], @headers)
222
261
  end
262
+
223
263
  end
264
+
224
265
  end
225
266
 
226
267
  # get cookies as hash
227
268
  class Net::HTTPResponse
228
- def cookies
229
- cookies = {}
230
- ignored_cookie_names = %w{expires domain path secure httponly}
231
- self['set-cookie'].split(/[;,]/).each {|it|
232
- next unless it.include? '='
233
- eq = it.index('=')
234
- key = it[0...eq].strip
235
- value = it[eq.succ..-1]
236
- next if ignored_cookie_names.include? key.downcase
237
- cookies[key] = value
238
- }
239
- cookies
240
- end
269
+ def cookies
270
+ cookies = {}
271
+ ignored_cookie_names = %w{expires domain path secure httponly}
272
+ self['set-cookie'].split(/[;,]/).each {|it|
273
+ next unless it.include? '='
274
+ eq = it.index('=')
275
+ key = it[0...eq].strip
276
+ value = it[eq.succ..-1]
277
+ next if ignored_cookie_names.include? key.downcase
278
+ cookies[key] = value
279
+ }
280
+ cookies
281
+ end
241
282
  end
242
283
 
243
284
  # for ftp response
244
285
  class Net::FTP
245
- def response=(response)
246
- @_response = response
247
- end
286
+ def response=(response)
287
+ @_response = response
288
+ end
248
289
 
249
- def response
250
- @_response
251
- end
290
+ def response
291
+ @_response
292
+ end
252
293
  end
253
294
 
254
295
  # exception
@@ -257,18 +298,18 @@ class NoHttpMethodException < Exception; end
257
298
  # for command line
258
299
  if __FILE__.eql? $0
259
300
  method, url, params = ARGV
260
- exit unless method
301
+ exit unless method
261
302
  source_method = method
262
303
  method = method.split('_')[0] if method.include? '_'
263
304
 
264
305
  # fix path of the url
265
- url = "http://#{url}" unless url =~ /^(http:\/\/)/i
306
+ url = "http://#{url}" unless url =~ /^(https?:\/\/)/i
266
307
 
267
308
  params = if params
268
- "{:url => '#{url}', :parameters => '" + params + "'}"
269
- else
270
- "'#{url}'"
271
- end
309
+ "{:url => '#{url}', :parameters => '" + params + "'}"
310
+ else
311
+ "'#{url}'"
312
+ end
272
313
 
273
314
  if HttpRequest.http_methods.include?(method) && url
274
315
  http = eval("HttpRequest.#{method}(#{params})")
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: http_request.rb
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.1
4
+ version: 1.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - xianhua.zhou
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-04-11 00:00:00 +08:00
12
+ date: 2009-05-02 00:00:00 +08:00
13
13
  default_executable:
14
14
  dependencies: []
15
15
 
@@ -26,7 +26,7 @@ files:
26
26
  - README.rdoc
27
27
  - lib/http_request.rb
28
28
  has_rdoc: true
29
- homepage: http://httprequest.rubyforge.org
29
+ homepage: http://my.cnzxh.net
30
30
  post_install_message:
31
31
  rdoc_options: []
32
32