http_request.rb 1.0.1 → 1.0.2

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