http_request.rb 1.1.8 → 1.1.9

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 +3 -0
  2. data/README.rdoc +1 -1
  3. data/lib/http_request.rb +522 -522
  4. metadata +2 -2
data/Changelog CHANGED
@@ -1,3 +1,6 @@
1
+ v1.1.9
2
+ * fixed parameters when uploading files (it's double escaped)
3
+
1
4
  v1.1.8
2
5
  * fixed parameters when uploading files (it's double escaped)
3
6
 
data/README.rdoc CHANGED
@@ -227,4 +227,4 @@ download multiple files from a directory
227
227
  bug fixing, testing and testing...
228
228
 
229
229
  == LATEST VERSION
230
- 1.1.8
230
+ 1.1.9
data/lib/http_request.rb CHANGED
@@ -11,9 +11,9 @@
11
11
  #
12
12
  # == Version
13
13
  #
14
- # v1.1.7
14
+ # v1.1.9
15
15
  #
16
- # Last Change: 18 Aug, 2010
16
+ # Last Change: 13 Sep, 2010
17
17
  #
18
18
  # == Author
19
19
  #
@@ -29,514 +29,514 @@ require 'digest/md5'
29
29
  require 'stringio'
30
30
 
31
31
  class HttpRequest
32
- include Singleton
33
- class << self
34
- # version
35
- VERSION = '1.1.7'.freeze
36
- def version;VERSION;end
37
-
38
- # avaiabled http methods
39
- def http_methods
40
- %w{get head post put proppatch lock unlock options propfind delete move copy mkcol trace}
41
- end
42
-
43
- # return data with or without block
44
- def data(response, &block)
45
- response.url = @@__url if defined? @@__url
46
- block_given? ? block.call(response) : response
47
- end
48
-
49
- # update cookies
50
- def update_cookies(response)
51
- return unless response.header['set-cookie']
52
- response.get_fields('set-cookie').each {|k|
53
- k, v = k.split(';')[0].split('=')
54
- @@__cookies[k] = v
55
- }
56
- end
57
-
58
- # return cookies
59
- def cookies
60
- @@__cookies
61
- end
62
-
63
- # check the http resource whether or not available
64
- def available?(url, timeout = 5)
65
- timeout(timeout) {
66
- u = URI(url)
67
- s = TCPSocket.new(u.host, u.port)
68
- s.close
69
- }
70
- return true
71
- rescue Exception => e
72
- return false
73
- end
74
- end
75
-
76
- def data(response, &block)
77
- self.class.data(response, &block)
78
- end
79
-
80
- # send request by some given parameters
81
- def request(method, options, &block)
82
-
83
- # parse the @options
84
- parse_options(method, options)
85
-
86
- # parse and merge for the options[:parameters]
87
- parse_parameters
88
-
89
- # send http request and get the response
90
- response = send_request_and_get_response
91
-
92
- return data(response, &block) unless @options[:redirect]
93
-
94
- # redirect?
95
- process_redirection response, &block
96
- end
97
-
98
- # catch all available http requests
99
- def self.method_missing(method_name, *options, &block)
100
- options = if options.size.eql? 2
101
- options.last.merge({:url => options.first})
102
- else
103
- options.first
104
- end
105
- @@redirect_times = 0
106
- # we need to retrieve the cookies from last http response before reset cookies if it's a Net::HTTPResponse
107
- options[:cookies] = options[:cookies].cookies if options[:cookies].is_a? Net::HTTPResponse
108
- @@__cookies = {}
109
- method_name = method_name.to_s.downcase
110
- raise NoHttpMethodException, "No such http method can be called: #{method_name}" unless self.http_methods.include?(method_name)
111
- self.instance.request(method_name, options, &block)
112
- end
113
-
114
- # for ftp, no plan to add new features to this method except bug fixing
115
- def self.ftp(method, options, &block)
116
- options = {:url => options} if options.is_a? String
117
- options = {:close => true}.merge(options)
118
- @@__url = options[:url] = "ftp://#{options[:url]}" unless options[:url] =~ /^ftp:\/\//
119
- uri = URI(options[:url])
120
- guest_name, guest_pass = 'anonymous', "guest@#{uri.host}"
121
- unless options[:username]
122
- options[:username], options[:password] =
123
- uri.userinfo ? uri.userinfo.split(':') : [guest_name, guest_pass]
124
- end
125
- options[:username] = guest_name unless options[:username]
126
- options[:password] = guest_pass if options[:password].nil?
127
- ftp = Net::FTP.open(uri.host, options[:username], options[:password])
128
- return data(ftp, &block) unless method
129
- stat = case method.to_sym
130
- when :get_as_string
131
- require 'tempfile'
132
- tmp = Tempfile.new('http_request_ftp')
133
- ftp.getbinaryfile(uri.path, tmp.path)
134
- ftp.response = tmp.read
135
- tmp.close
136
- unless block_given?
137
- ftp.close
138
- return ftp.response
139
- end
140
- when :get
141
- options[:to] = File.basename(uri.path) unless options[:to]
142
- ftp.getbinaryfile(uri.path, options[:to])
143
- when :put
144
- ftp.putbinaryfile(options[:from], uri.path)
145
- when :mkdir, :rmdir, :delete, :size, :mtime, :list, :nlst
146
- ftp.method(method).call(uri.path)
147
- when :rename
148
- ftp.rename(uri.path, options[:to]) if options[:to]
149
- when :status
150
- ftp.status
151
- else
152
- return ftp
153
- end
154
- if options[:close] and not block_given?
155
- ftp.close
156
- stat
157
- else
158
- ftp.response = stat unless ftp.response
159
- data(ftp, &block)
160
- end
161
- end
162
-
163
- private
164
-
165
- def md5(string)
166
- Digest::MD5.hexdigest string
167
- end
168
-
169
- # get params for the Digest auth
170
- # see: http://www.rooftopsolutions.nl/article/223
171
- def get_params_for_digest
172
- return '' unless @options[:auth_username] and @options[:auth_password]
173
- user, passwd = @options[:auth_username], @options[:auth_password]
174
- hr = self.class.send @options[:method],
175
- @uri.userinfo ?
176
- @@__url.sub(/:\/\/(.+?)@/, '://') :
177
- @@__url
178
-
179
- params = HttpRequestParams.parse hr['WWW-Authenticate'].split(' ', 2).last
180
- method = @options[:method].upcase
181
- cnonce = md5(rand.to_s + Time.new.to_s)
182
- nc = "00000001"
183
-
184
- data = []
185
- data << md5("#{user}:#{params['realm']}:#{passwd}") \
186
- << params['nonce'] \
187
- << ('%08x' % nc) \
188
- << cnonce \
189
- << params['qop'] \
190
- << md5("#{method}:#{@uri.path}")
191
-
192
- params = params.update({
193
- :username => user,
194
- :nc => nc,
195
- :cnonce => cnonce,
196
- :uri => @uri.path,
197
- :method => method,
198
- :response => md5(data.join(":"))
199
- })
200
-
201
- headers = []
202
- params.each {|k, v| headers << "#{k}=\"#{v}\"" }
203
- headers.join(", ")
204
- rescue
205
- ''
206
- end
207
-
208
- # for the Http Auth if need
209
- def parse_http_auth
210
- if @options[:auth] or
211
- @uri.userinfo or
212
- (@options[:auth_username] and @options[:auth_password])
213
-
214
- if @options[:auth].is_a? Hash
215
- @options[:auth_username] = @options[:auth][:username]
216
- @options[:auth_password] = @options[:auth][:password]
217
- @options[:auth] = @options[:auth][:type]
218
- elsif @uri.userinfo and (!@options[:auth_username] or !@options[:auth_password])
219
- @options[:auth_username], @options[:auth_password] = @uri.userinfo.split(/:/, 2)
220
- end
221
-
222
- if @options[:auth_username] and @options[:auth_password]
223
- # Digest Auth
224
- if @options[:auth].to_s == 'digest'
225
- digest = get_params_for_digest
226
- @headers['Authorization'] = "Digest " + digest unless digest.empty?
227
- else
228
- # Basic Auth
229
- @headers['Authorization'] = "Basic " +
230
- ["#{@options[:auth_username]}:#{@options[:auth_password]}"].pack('m').delete!("\r\n")
231
- end
232
- end
233
-
234
- end
235
- end
236
-
237
- # initialize for the http request
238
- def parse_options(method, options)
239
- options = {:url => options.to_s} if [String, Array].include? options.class
240
- @options = {
241
- :ssl_port => 443,
242
- :redirect_limits => 5,
243
- :redirect => true,
244
- :url => nil,
245
- :ajax => false,
246
- :xhr => false,
247
- :method => method
248
- }
249
- @options.merge!(options)
250
- @@__url = @options[:url]
251
- @uri = URI(@options[:url])
252
- @uri.path = '/' if @uri.path.empty?
253
- @headers = {
254
- 'Host' => @uri.host,
255
- 'Referer' => @options[:url],
256
- 'User-Agent' => 'HttpRequest.rb ' + self.class.version
257
- }
258
-
259
- # support gzip
260
- begin; require 'zlib'; rescue LoadError; end
261
- @headers['Accept-Encoding'] = 'gzip,deflate' if defined? ::Zlib
262
-
263
- # ajax calls?
264
- @headers['X-Requested-With'] = 'XMLHttpRequest' if @options[:ajax] or @options[:xhr]
265
-
266
- # Http Authenication
267
- parse_http_auth
268
-
269
- # headers
270
- @options[:headers].each {|k, v| @headers[k] = v} if @options[:headers].is_a? Hash
271
-
272
- # add cookies if have
273
- if @options[:cookies]
274
- if @options[:cookies].is_a? Hash
275
- cookies = []
276
- @options[:cookies].each {|k, v|
277
- cookies << "#{CGI.escape(k.to_s)}=#{CGI.escape(v.to_s)}"
278
- }
279
- cookies = cookies.join('; ')
280
- else
281
- cookies = @options[:cookies].to_s
282
- end
283
- @headers['Cookie'] = cookies unless cookies.empty?
284
- end
285
- end
286
-
287
- # parse parameters for the options[:parameters] and @uri.query
288
- def parse_parameters
289
- if @options[:parameters].is_a?(Hash)
290
- @options[:parameters] = @options[:parameters].collect{|k, v|
291
- "#{CGI.escape(k.to_s)}=#{CGI.escape(v.to_s)}"
292
- }.join('&')
293
- end
294
- @options[:parameters] = '' if @options[:parameters].nil?
295
- if not @uri.query.to_s.empty?
296
- @options[:parameters] << (@options[:parameters].empty? ? @uri.query : "&#{@uri.query}")
297
- end
298
-
299
- # for uploading files
300
- build_multipart if @options[:files] and 'post'.eql?(@options[:method])
301
- end
302
-
303
- # for uploading files
304
- def build_multipart
305
- boundary = md5(rand.to_s).to_s[0..5]
306
- @headers['Content-type'] = "multipart/form-data, boundary=#{boundary}"
307
- multipart = []
308
- if @options[:parameters].is_a?(String)
309
- @options[:parameters] = CGI.parse(@options[:parameters])
310
- if @options[:parameters].is_a? Hash
311
- @options[:parameters].each {|k, v|
312
- multipart << "--#{boundary}"
313
- multipart << "Content-disposition: form-data; name=\"#{k}\""
314
- multipart << "\r\n#{v.first}"
315
- }
316
- end
317
- end
32
+ include Singleton
33
+ class << self
34
+ # version
35
+ VERSION = '1.1.9'.freeze
36
+ def version;VERSION;end
37
+
38
+ # avaiabled http methods
39
+ def http_methods
40
+ %w{get head post put proppatch lock unlock options propfind delete move copy mkcol trace}
41
+ end
42
+
43
+ # return data with or without block
44
+ def data(response, &block)
45
+ response.url = @@__url if defined? @@__url
46
+ block_given? ? block.call(response) : response
47
+ end
48
+
49
+ # update cookies
50
+ def update_cookies(response)
51
+ return unless response.header['set-cookie']
52
+ response.get_fields('set-cookie').each {|k|
53
+ k, v = k.split(';')[0].split('=')
54
+ @@__cookies[k] = v
55
+ }
56
+ end
57
+
58
+ # return cookies
59
+ def cookies
60
+ @@__cookies
61
+ end
62
+
63
+ # check the http resource whether or not available
64
+ def available?(url, timeout = 5)
65
+ timeout(timeout) {
66
+ u = URI(url)
67
+ s = TCPSocket.new(u.host, u.port)
68
+ s.close
69
+ }
70
+ return true
71
+ rescue Exception => e
72
+ return false
73
+ end
74
+ end
75
+
76
+ def data(response, &block)
77
+ self.class.data(response, &block)
78
+ end
79
+
80
+ # send request by some given parameters
81
+ def request(method, options, &block)
82
+
83
+ # parse the @options
84
+ parse_options(method, options)
85
+
86
+ # parse and merge for the options[:parameters]
87
+ parse_parameters
88
+
89
+ # send http request and get the response
90
+ response = send_request_and_get_response
91
+
92
+ return data(response, &block) unless @options[:redirect]
93
+
94
+ # redirect?
95
+ process_redirection response, &block
96
+ end
97
+
98
+ # catch all available http requests
99
+ def self.method_missing(method_name, *options, &block)
100
+ options = if options.size.eql? 2
101
+ options.last.merge({:url => options.first})
102
+ else
103
+ options.first
104
+ end
105
+ @@redirect_times = 0
106
+ # we need to retrieve the cookies from last http response before reset cookies if it's a Net::HTTPResponse
107
+ options[:cookies] = options[:cookies].cookies if options[:cookies].is_a? Net::HTTPResponse
108
+ @@__cookies = {}
109
+ method_name = method_name.to_s.downcase
110
+ raise NoHttpMethodException, "No such http method can be called: #{method_name}" unless self.http_methods.include?(method_name)
111
+ self.instance.request(method_name, options, &block)
112
+ end
113
+
114
+ # for ftp, no plan to add new features to this method except bug fixing
115
+ def self.ftp(method, options, &block)
116
+ options = {:url => options} if options.is_a? String
117
+ options = {:close => true}.merge(options)
118
+ @@__url = options[:url] = "ftp://#{options[:url]}" unless options[:url] =~ /^ftp:\/\//
119
+ uri = URI(options[:url])
120
+ guest_name, guest_pass = 'anonymous', "guest@#{uri.host}"
121
+ unless options[:username]
122
+ options[:username], options[:password] =
123
+ uri.userinfo ? uri.userinfo.split(':') : [guest_name, guest_pass]
124
+ end
125
+ options[:username] = guest_name unless options[:username]
126
+ options[:password] = guest_pass if options[:password].nil?
127
+ ftp = Net::FTP.open(uri.host, options[:username], options[:password])
128
+ return data(ftp, &block) unless method
129
+ stat = case method.to_sym
130
+ when :get_as_string
131
+ require 'tempfile'
132
+ tmp = Tempfile.new('http_request_ftp')
133
+ ftp.getbinaryfile(uri.path, tmp.path)
134
+ ftp.response = tmp.read
135
+ tmp.close
136
+ unless block_given?
137
+ ftp.close
138
+ return ftp.response
139
+ end
140
+ when :get
141
+ options[:to] = File.basename(uri.path) unless options[:to]
142
+ ftp.getbinaryfile(uri.path, options[:to])
143
+ when :put
144
+ ftp.putbinaryfile(options[:from], uri.path)
145
+ when :mkdir, :rmdir, :delete, :size, :mtime, :list, :nlst
146
+ ftp.method(method).call(uri.path)
147
+ when :rename
148
+ ftp.rename(uri.path, options[:to]) if options[:to]
149
+ when :status
150
+ ftp.status
151
+ else
152
+ return ftp
153
+ end
154
+ if options[:close] and not block_given?
155
+ ftp.close
156
+ stat
157
+ else
158
+ ftp.response = stat unless ftp.response
159
+ data(ftp, &block)
160
+ end
161
+ end
162
+
163
+ private
164
+
165
+ def md5(string)
166
+ Digest::MD5.hexdigest string
167
+ end
168
+
169
+ # get params for the Digest auth
170
+ # see: http://www.rooftopsolutions.nl/article/223
171
+ def get_params_for_digest
172
+ return '' unless @options[:auth_username] and @options[:auth_password]
173
+ user, passwd = @options[:auth_username], @options[:auth_password]
174
+ hr = self.class.send @options[:method],
175
+ @uri.userinfo ?
176
+ @@__url.sub(/:\/\/(.+?)@/, '://') :
177
+ @@__url
178
+
179
+ params = HttpRequestParams.parse hr['WWW-Authenticate'].split(' ', 2).last
180
+ method = @options[:method].upcase
181
+ cnonce = md5(rand.to_s + Time.new.to_s)
182
+ nc = "00000001"
183
+
184
+ data = []
185
+ data << md5("#{user}:#{params['realm']}:#{passwd}") \
186
+ << params['nonce'] \
187
+ << ('%08x' % nc) \
188
+ << cnonce \
189
+ << params['qop'] \
190
+ << md5("#{method}:#{@uri.path}")
191
+
192
+ params = params.update({
193
+ :username => user,
194
+ :nc => nc,
195
+ :cnonce => cnonce,
196
+ :uri => @uri.path,
197
+ :method => method,
198
+ :response => md5(data.join(":"))
199
+ })
200
+
201
+ headers = []
202
+ params.each {|k, v| headers << "#{k}=\"#{v}\"" }
203
+ headers.join(", ")
204
+ rescue
205
+ ''
206
+ end
207
+
208
+ # for the Http Auth if need
209
+ def parse_http_auth
210
+ if @options[:auth] or
211
+ @uri.userinfo or
212
+ (@options[:auth_username] and @options[:auth_password])
213
+
214
+ if @options[:auth].is_a? Hash
215
+ @options[:auth_username] = @options[:auth][:username]
216
+ @options[:auth_password] = @options[:auth][:password]
217
+ @options[:auth] = @options[:auth][:type]
218
+ elsif @uri.userinfo and (!@options[:auth_username] or !@options[:auth_password])
219
+ @options[:auth_username], @options[:auth_password] = @uri.userinfo.split(/:/, 2)
220
+ end
221
+
222
+ if @options[:auth_username] and @options[:auth_password]
223
+ # Digest Auth
224
+ if @options[:auth].to_s == 'digest'
225
+ digest = get_params_for_digest
226
+ @headers['Authorization'] = "Digest " + digest unless digest.empty?
227
+ else
228
+ # Basic Auth
229
+ @headers['Authorization'] = "Basic " +
230
+ ["#{@options[:auth_username]}:#{@options[:auth_password]}"].pack('m').delete!("\r\n")
231
+ end
232
+ end
233
+
234
+ end
235
+ end
236
+
237
+ # initialize for the http request
238
+ def parse_options(method, options)
239
+ options = {:url => options.to_s} if [String, Array].include? options.class
240
+ @options = {
241
+ :ssl_port => 443,
242
+ :redirect_limits => 5,
243
+ :redirect => true,
244
+ :url => nil,
245
+ :ajax => false,
246
+ :xhr => false,
247
+ :method => method
248
+ }
249
+ @options.merge!(options)
250
+ @@__url = @options[:url]
251
+ @uri = URI(@options[:url])
252
+ @uri.path = '/' if @uri.path.empty?
253
+ @headers = {
254
+ 'Host' => @uri.host,
255
+ 'Referer' => @options[:url],
256
+ 'User-Agent' => 'HttpRequest.rb ' + self.class.version
257
+ }
258
+
259
+ # support gzip
260
+ begin; require 'zlib'; rescue LoadError; end
261
+ @headers['Accept-Encoding'] = 'gzip,deflate' if defined? ::Zlib
262
+
263
+ # ajax calls?
264
+ @headers['X-Requested-With'] = 'XMLHttpRequest' if @options[:ajax] or @options[:xhr]
265
+
266
+ # Http Authenication
267
+ parse_http_auth
268
+
269
+ # headers
270
+ @options[:headers].each {|k, v| @headers[k] = v} if @options[:headers].is_a? Hash
271
+
272
+ # add cookies if have
273
+ if @options[:cookies]
274
+ if @options[:cookies].is_a? Hash
275
+ cookies = []
276
+ @options[:cookies].each {|k, v|
277
+ cookies << "#{CGI.escape(k.to_s)}=#{CGI.escape(v.to_s)}"
278
+ }
279
+ cookies = cookies.join('; ')
280
+ else
281
+ cookies = @options[:cookies].to_s
282
+ end
283
+ @headers['Cookie'] = cookies unless cookies.empty?
284
+ end
285
+ end
286
+
287
+ # parse parameters for the options[:parameters] and @uri.query
288
+ def parse_parameters
289
+ if @options[:parameters].is_a?(Hash)
290
+ @options[:parameters] = @options[:parameters].collect{|k, v|
291
+ "#{CGI.escape(k.to_s)}=#{CGI.escape(v.to_s)}"
292
+ }.join('&')
293
+ end
294
+ @options[:parameters] = '' if @options[:parameters].nil?
295
+ if not @uri.query.to_s.empty?
296
+ @options[:parameters] << (@options[:parameters].empty? ? @uri.query : "&#{@uri.query}")
297
+ end
298
+
299
+ # for uploading files
300
+ build_multipart if @options[:files] and 'post'.eql?(@options[:method])
301
+ end
302
+
303
+ # for uploading files
304
+ def build_multipart
305
+ boundary = md5(rand.to_s).to_s[0..5]
306
+ @headers['Content-type'] = "multipart/form-data, boundary=#{boundary}"
307
+ multipart = []
308
+ if @options[:parameters].is_a?(String)
309
+ @options[:parameters] = CGI.parse(@options[:parameters])
310
+ if @options[:parameters].is_a? Hash
311
+ @options[:parameters].each {|k, v|
312
+ multipart << "--#{boundary}"
313
+ multipart << "Content-disposition: form-data; name=\"#{k}\""
314
+ multipart << "\r\n#{v.first}"
315
+ }
316
+ end
317
+ end
318
318
  @options[:files] = [@options[:files]] if @options[:files].is_a?(Hash)
319
- @options[:files].each_with_index {|f, index|
320
- f[:field_name] ||= "files[]"
321
- f[:file_name] ||= "#{boundary}_#{index}"
322
- f[:transfer_encoding] ||= "binary"
323
- f[:content_type] ||= 'application/octet-stream'
324
- multipart << "--#{boundary}"
325
- multipart << "Content-disposition: form-data; name=\"#{f[:field_name]}\"; filename=\"#{f[:file_name]}\""
326
- multipart << "Content-type: #{f[:content_type]}"
327
- multipart << "Content-Transfer-Encoding: #{f[:transfer_encoding]}"
328
- multipart << "\r\n#{f[:file_content]}"
329
- }
330
- multipart << "--#{boundary}--"
331
- multipart = multipart.join("\r\n")
332
- @headers['Content-length'] = "#{multipart.size}"
333
- @options[:parameters] = multipart
334
- end
335
-
336
- # send request and get the response by some options
337
- def send_request_and_get_response
338
- # for proxy
339
- http = if @options[:proxy_addr]
340
- if @options[:proxy_user] and @options[:proxy_pass]
341
- Net::HTTP::Proxy(
342
- @options[:proxy_addr],
343
- @options[:proxy_port],
344
- @options[:proxy_user],
345
- @options[:proxy_pass]
346
- ).new(@u.host, @u.port)
347
- else
348
- Net::HTTP::Proxy(
349
- @options[:proxy_addr],
350
- @options[:proxy_port]
351
- ).new(@uri.host, @uri.port)
352
- end
353
- else
354
- Net::HTTP.new(@uri.host, @uri.port)
355
- end
356
-
357
- # ssl support
358
- http.use_ssl = true if @uri.scheme =~ /^https$/i
359
-
360
- # sending request and get response
361
- send_request http
362
- end
363
-
364
- # send http request
365
- def send_request(http)
366
- # xml data?
367
- if @options[:parameters].to_s[0..4].eql?('<?xml') and @options[:method].eql? 'post'
368
- @headers['Content-Type'] = 'application/xml'
369
- @headers['Content-Length'] = @options[:parameters].size.to_s
370
- @headers['Content-MD5'] = md5(@options[:parameters]).to_s
371
- end
372
-
373
- # GO !!
374
- if @options[:method] =~ /^(get|head|options|delete|move|copy|trace|)$/
375
- @options[:parameters] = "?#{@options[:parameters]}" if @options[:parameters]
376
- path = if @options[:parameters] =~ /^\?+$/
377
- @uri.path
378
- else
379
- @uri.path + @options[:parameters]
380
- end
381
- h = http.method(@options[:method]).call(path, @headers)
382
- else
383
- h = http.method(@options[:method]).call(@uri.path, @options[:parameters], @headers)
384
- end
385
-
386
- self.class.update_cookies h
387
- h
388
- end
389
-
390
- # process the redirectation if need
391
- def process_redirection(response, &block)
392
- case response
393
- when Net::HTTPRedirection
394
- url = "#{@uri.scheme}://#{@uri.host}#{':' + @uri.port.to_s if @uri.port != 80}"
395
- last_url = @options[:url]
396
- @options[:url] = case response['location']
397
- when /^https?:\/\//i
398
- response['location']
399
- when /^\//
400
- url + response['location']
401
- when /^(\.\.\/|\.\/)/
402
- paths = (File.dirname(@uri.path) + '/' + response['location']).split('/')
403
- location = []
404
- paths.each {|path|
405
- next if path.empty? || path.eql?('.')
406
- path == '..' ? location.pop : location.push(path)
407
- }
408
- url + '/' + location.join('/')
409
- else
410
- url + File.dirname(@uri.path) + '/' + response['location']
411
- end
412
- return data(response, &block) if @@redirect_times > 2 and @options[:url].eql? last_url
413
- @@redirect_times += 1
414
- raise 'too many redirects...' if @@redirect_times > @options[:redirect_limits]
415
- if @options[:cookies].nil?
416
- @options[:cookies] = self.class.cookies
417
- else
418
- @options[:cookies] = @options[:cookies].update self.class.cookies
419
- end
420
- @options.delete :parameters
421
- @options.delete :method
422
- request('get', @options, &block)
423
- else
424
- data(response, &block)
425
- end
426
- end
319
+ @options[:files].each_with_index {|f, index|
320
+ f[:field_name] ||= "files[]"
321
+ f[:file_name] ||= "#{boundary}_#{index}"
322
+ f[:transfer_encoding] ||= "binary"
323
+ f[:content_type] ||= 'application/octet-stream'
324
+ multipart << "--#{boundary}"
325
+ multipart << "Content-disposition: form-data; name=\"#{f[:field_name]}\"; filename=\"#{f[:file_name]}\""
326
+ multipart << "Content-type: #{f[:content_type]}"
327
+ multipart << "Content-Transfer-Encoding: #{f[:transfer_encoding]}"
328
+ multipart << "\r\n#{f[:file_content]}"
329
+ }
330
+ multipart << "--#{boundary}--"
331
+ multipart = multipart.join("\r\n")
332
+ @headers['Content-length'] = "#{multipart.size}"
333
+ @options[:parameters] = multipart
334
+ end
335
+
336
+ # send request and get the response by some options
337
+ def send_request_and_get_response
338
+ # for proxy
339
+ http = if @options[:proxy_addr]
340
+ if @options[:proxy_user] and @options[:proxy_pass]
341
+ Net::HTTP::Proxy(
342
+ @options[:proxy_addr],
343
+ @options[:proxy_port],
344
+ @options[:proxy_user],
345
+ @options[:proxy_pass]
346
+ ).new(@u.host, @u.port)
347
+ else
348
+ Net::HTTP::Proxy(
349
+ @options[:proxy_addr],
350
+ @options[:proxy_port]
351
+ ).new(@uri.host, @uri.port)
352
+ end
353
+ else
354
+ Net::HTTP.new(@uri.host, @uri.port)
355
+ end
356
+
357
+ # ssl support
358
+ http.use_ssl = true if @uri.scheme =~ /^https$/i
359
+
360
+ # sending request and get response
361
+ send_request http
362
+ end
363
+
364
+ # send http request
365
+ def send_request(http)
366
+ # xml data?
367
+ if @options[:parameters].to_s[0..4].eql?('<?xml') and @options[:method].eql? 'post'
368
+ @headers['Content-Type'] = 'application/xml'
369
+ @headers['Content-Length'] = @options[:parameters].size.to_s
370
+ @headers['Content-MD5'] = md5(@options[:parameters]).to_s
371
+ end
372
+
373
+ # GO !!
374
+ if @options[:method] =~ /^(get|head|options|delete|move|copy|trace|)$/
375
+ @options[:parameters] = "?#{@options[:parameters]}" if @options[:parameters]
376
+ path = if @options[:parameters] =~ /^\?+$/
377
+ @uri.path
378
+ else
379
+ @uri.path + @options[:parameters]
380
+ end
381
+ h = http.method(@options[:method]).call(path, @headers)
382
+ else
383
+ h = http.method(@options[:method]).call(@uri.path, @options[:parameters], @headers)
384
+ end
385
+
386
+ self.class.update_cookies h
387
+ h
388
+ end
389
+
390
+ # process the redirectation if need
391
+ def process_redirection(response, &block)
392
+ case response
393
+ when Net::HTTPRedirection
394
+ url = "#{@uri.scheme}://#{@uri.host}#{':' + @uri.port.to_s if @uri.port != 80}"
395
+ last_url = @options[:url]
396
+ @options[:url] = case response['location']
397
+ when /^https?:\/\//i
398
+ response['location']
399
+ when /^\//
400
+ url + response['location']
401
+ when /^(\.\.\/|\.\/)/
402
+ paths = (File.dirname(@uri.path) + '/' + response['location']).split('/')
403
+ location = []
404
+ paths.each {|path|
405
+ next if path.empty? || path.eql?('.')
406
+ path == '..' ? location.pop : location.push(path)
407
+ }
408
+ url + '/' + location.join('/')
409
+ else
410
+ url + File.dirname(@uri.path) + '/' + response['location']
411
+ end
412
+ return data(response, &block) if @@redirect_times > 2 and @options[:url].eql? last_url
413
+ @@redirect_times += 1
414
+ raise 'too many redirects...' if @@redirect_times > @options[:redirect_limits]
415
+ if @options[:cookies].nil?
416
+ @options[:cookies] = self.class.cookies
417
+ else
418
+ @options[:cookies] = @options[:cookies].update self.class.cookies
419
+ end
420
+ @options.delete :parameters
421
+ @options.delete :method
422
+ request('get', @options, &block)
423
+ else
424
+ data(response, &block)
425
+ end
426
+ end
427
427
 
428
428
  end
429
429
 
430
430
  module Net
431
- class HTTPResponse
432
-
433
- attr_accessor :url
434
-
435
- # get cookies as a hash
436
- def cookies
437
- HttpRequest.cookies
438
- end
439
-
440
- # for gzipped body
441
- def body
442
- bd = read_body()
443
- return bd unless bd
444
- if (self['content-encoding'] == 'gzip') and defined?(::Zlib)
445
- ::Zlib::GzipReader.new(StringIO.new(bd)).read
446
- else
447
- bd
448
- end
449
- end
450
-
451
- # body
452
- def raw_body
453
- read_body()
454
- end
455
-
456
- # detect the response code
457
- #
458
- # Example:
459
- # puts HttpRequest.get('http://www.example.com').code_200?
460
- # puts HttpRequest.get('http://www.example.com').code_2xx?
461
- # HttpRequest.get('http://www.example.com/404.html') {|http|
462
- # puts "IS 4xx" if http.code_4xx?
463
- # puts "IS 404" if http.code_404?
464
- # }
465
- #
466
- # supported methods
467
- # code_1xx? code_2xx? code_3xx? code_4xx? code_5xx?
468
- # code_100? code_101? code_200? code_201? ... code_505?
469
- def method_missing(method_name)
470
- case method_name.to_s
471
- when /^(code|status)_([0-9])xx\?$/
472
- not CODE_CLASS_TO_OBJ[$2].nil? and is_a? CODE_CLASS_TO_OBJ[$2]
473
- when /^(code|status)_([0-9]+)\?$/
474
- not CODE_TO_OBJ[$2].nil? and is_a? CODE_TO_OBJ[$2]
475
- else
476
- raise NoHttpMethodException, 'Unknown method of response code!'
477
- end
478
- end
479
-
480
- end
431
+ class HTTPResponse
432
+
433
+ attr_accessor :url
434
+
435
+ # get cookies as a hash
436
+ def cookies
437
+ HttpRequest.cookies
438
+ end
439
+
440
+ # for gzipped body
441
+ def body
442
+ bd = read_body()
443
+ return bd unless bd
444
+ if (self['content-encoding'] == 'gzip') and defined?(::Zlib)
445
+ ::Zlib::GzipReader.new(StringIO.new(bd)).read
446
+ else
447
+ bd
448
+ end
449
+ end
450
+
451
+ # body
452
+ def raw_body
453
+ read_body()
454
+ end
455
+
456
+ # detect the response code
457
+ #
458
+ # Example:
459
+ # puts HttpRequest.get('http://www.example.com').code_200?
460
+ # puts HttpRequest.get('http://www.example.com').code_2xx?
461
+ # HttpRequest.get('http://www.example.com/404.html') {|http|
462
+ # puts "IS 4xx" if http.code_4xx?
463
+ # puts "IS 404" if http.code_404?
464
+ # }
465
+ #
466
+ # supported methods
467
+ # code_1xx? code_2xx? code_3xx? code_4xx? code_5xx?
468
+ # code_100? code_101? code_200? code_201? ... code_505?
469
+ def method_missing(method_name)
470
+ case method_name.to_s
471
+ when /^(code|status)_([0-9])xx\?$/
472
+ not CODE_CLASS_TO_OBJ[$2].nil? and is_a? CODE_CLASS_TO_OBJ[$2]
473
+ when /^(code|status)_([0-9]+)\?$/
474
+ not CODE_TO_OBJ[$2].nil? and is_a? CODE_TO_OBJ[$2]
475
+ else
476
+ raise NoHttpMethodException, 'Unknown method of response code!'
477
+ end
478
+ end
479
+
480
+ end
481
481
  end
482
482
 
483
483
  # for ftp response
484
484
  class Net::FTP
485
- def response=(response)
486
- @_response = response
487
- end
485
+ def response=(response)
486
+ @_response = response
487
+ end
488
488
 
489
- def response
490
- @_response
491
- end
489
+ def response
490
+ @_response
491
+ end
492
492
  end
493
493
 
494
494
  # from Rack, parsing parameters for the Digest auth
495
495
  class HttpRequestParams < Hash
496
- def self.parse(str)
497
- split_header_value(str).inject(new) do |header, param|
498
- k, v = param.split('=', 2)
499
- header[k] = dequote(v)
500
- header
501
- end
502
- end
503
-
504
- def self.dequote(str) # From WEBrick::HTTPUtils
505
- ret = (/\A"(.*)"\Z/ =~ str) ? $1 : str.dup
506
- ret.gsub!(/\\(.)/, "\\1")
507
- ret
508
- end
509
-
510
- def self.split_header_value(str)
511
- str.scan( /(\w+\=(?:"[^\"]+"|[^,]+))/n ).collect{ |v| v[0] }
512
- end
513
-
514
- def initialize
515
- super
516
-
517
- yield self if block_given?
518
- end
519
-
520
- def [](k)
521
- super k.to_s
522
- end
523
-
524
- def []=(k, v)
525
- super k.to_s, v.to_s
526
- end
527
-
528
- UNQUOTED = ['qop', 'nc', 'stale']
529
-
530
- def to_s
531
- inject([]) do |parts, (k, v)|
532
- parts << "#{k}=" + (UNQUOTED.include?(k) ? v.to_s : quote(v))
533
- parts
534
- end.join(', ')
535
- end
536
-
537
- def quote(str) # From WEBrick::HTTPUtils
538
- '"' << str.gsub(/[\\\"]/o, "\\\1") << '"'
539
- end
496
+ def self.parse(str)
497
+ split_header_value(str).inject(new) do |header, param|
498
+ k, v = param.split('=', 2)
499
+ header[k] = dequote(v)
500
+ header
501
+ end
502
+ end
503
+
504
+ def self.dequote(str) # From WEBrick::HTTPUtils
505
+ ret = (/\A"(.*)"\Z/ =~ str) ? $1 : str.dup
506
+ ret.gsub!(/\\(.)/, "\\1")
507
+ ret
508
+ end
509
+
510
+ def self.split_header_value(str)
511
+ str.scan( /(\w+\=(?:"[^\"]+"|[^,]+))/n ).collect{ |v| v[0] }
512
+ end
513
+
514
+ def initialize
515
+ super
516
+
517
+ yield self if block_given?
518
+ end
519
+
520
+ def [](k)
521
+ super k.to_s
522
+ end
523
+
524
+ def []=(k, v)
525
+ super k.to_s, v.to_s
526
+ end
527
+
528
+ UNQUOTED = ['qop', 'nc', 'stale']
529
+
530
+ def to_s
531
+ inject([]) do |parts, (k, v)|
532
+ parts << "#{k}=" + (UNQUOTED.include?(k) ? v.to_s : quote(v))
533
+ parts
534
+ end.join(', ')
535
+ end
536
+
537
+ def quote(str) # From WEBrick::HTTPUtils
538
+ '"' << str.gsub(/[\\\"]/o, "\\\1") << '"'
539
+ end
540
540
  end
541
541
 
542
542
 
@@ -545,31 +545,31 @@ class NoHttpMethodException < Exception; end
545
545
 
546
546
  # for command line
547
547
  if __FILE__.eql? $0
548
- method, url, params = ARGV
549
- exit unless method
550
- source_method = method
551
- method = method.split('_')[0] if method.include? '_'
552
-
553
- # fix path of the url
554
- url = "http://#{url}" unless url =~ /^(https?:\/\/)/i
555
-
556
- params = if params
557
- "{:url => '#{url}', :parameters => '" + params + "'}"
558
- else
559
- "'#{url}'"
560
- end
561
-
562
- if HttpRequest.http_methods.include?(method) && url
563
- http = eval("HttpRequest.#{method}(#{params})")
564
- case source_method
565
- when /_only_header$/
566
- http.each{|k,v| puts "#{k}: #{v}"}
567
- when /_with_header$/
568
- http.each{|k,v| puts "#{k}: #{v}"}
569
- print http.body unless http.body.to_s.empty?
570
- else
571
- print http.body unless http.body.to_s.empty?
572
- end
573
- end
548
+ method, url, params = ARGV
549
+ exit unless method
550
+ source_method = method
551
+ method = method.split('_')[0] if method.include? '_'
552
+
553
+ # fix path of the url
554
+ url = "http://#{url}" unless url =~ /^(https?:\/\/)/i
555
+
556
+ params = if params
557
+ "{:url => '#{url}', :parameters => '" + params + "'}"
558
+ else
559
+ "'#{url}'"
560
+ end
561
+
562
+ if HttpRequest.http_methods.include?(method) && url
563
+ http = eval("HttpRequest.#{method}(#{params})")
564
+ case source_method
565
+ when /_only_header$/
566
+ http.each{|k,v| puts "#{k}: #{v}"}
567
+ when /_with_header$/
568
+ http.each{|k,v| puts "#{k}: #{v}"}
569
+ print http.body unless http.body.to_s.empty?
570
+ else
571
+ print http.body unless http.body.to_s.empty?
572
+ end
573
+ end
574
574
 
575
575
  end
metadata CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
5
5
  segments:
6
6
  - 1
7
7
  - 1
8
- - 8
9
- version: 1.1.8
8
+ - 9
9
+ version: 1.1.9
10
10
  platform: ruby
11
11
  authors:
12
12
  - xianhua.zhou