summaryrefslogtreecommitdiff
path: root/lib/net/http.rb
diff options
context:
space:
mode:
authoraamine <aamine@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2000-03-26 08:48:15 +0000
committeraamine <aamine@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2000-03-26 08:48:15 +0000
commitd8d148d81653d7be02ee2d4237b5e75856e02ee3 (patch)
treefb8b3458ccde7a9923c0ab9b33ef77f1bdf0f899 /lib/net/http.rb
parent688169fd83b24564b653c03977c168cea50ccd35 (diff)
o protocol.rb: version 1.1.9
o smtp.rb: arguments discription of do_ready was wrongly void git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@654 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
Diffstat (limited to 'lib/net/http.rb')
-rw-r--r--lib/net/http.rb359
1 files changed, 246 insertions, 113 deletions
diff --git a/lib/net/http.rb b/lib/net/http.rb
index b908b8ae79..bdbcb82fd5 100644
--- a/lib/net/http.rb
+++ b/lib/net/http.rb
@@ -38,11 +38,14 @@ class HTTPBadResponse < HTTPError; end
== Methods
-: get( path, header = nil, ret = '' )
+: get( path, header = nil, dest = '' )
+: get( path, header = nil ) {|str| .... }
get data from "path" on connecting host.
- "header" is a Hash like { 'Accept' => '*/*', ... }.
- The data will be written to "ret" using "<<" method.
- This method returns response header (Hash) and "ret".
+ "header" must be a Hash like { 'Accept' => '*/*', ... }.
+ Data is written to "dest" by using "<<" method.
+ This method returns response header (Hash) and "dest".
+
+ If called as iterator, give a part String of entity body.
: head( path, header = nil )
get only header from "path" on connecting host.
@@ -53,6 +56,30 @@ class HTTPBadResponse < HTTPError; end
'content-type' => 'Content-Type: text/html',
... }
+: post( path, data, header = nil, dest = '' )
+: post( path, data, header = nil ) {|str| .... }
+ post "data"(must be String now) to "path" (and get entity body).
+ "header" must be a Hash like { 'Accept' => '*/*', ... }.
+ Data is written to "dest" by using "<<" method.
+ This method returns response header (Hash) and "dest".
+
+ If called as iterator, give a part String of entity body.
+
+ ATTENTION: entity body could be empty
+
+: get2( path, header = nil )
+ send GET request for "path".
+ "header" must be a Hash like { 'Accept' => '*/*', ... }.
+ This method returns response header (Hash).
+
+: get_body( dest = '' )
+: get_body {|str| .... }
+ gets entity body of forwarded 'get2' or 'post2' methods.
+ Data is written in "dest" by using "<<" method.
+ This method returns "dest".
+
+ If called as iterator, give a part String of entity body.
+
=end
class HTTP < Protocol
@@ -61,19 +88,71 @@ class HTTPBadResponse < HTTPError; end
protocol_param :command_type, '::Net::HTTPCommand'
- def get( path, u_header = nil, ret = '' )
+ def get( path, u_header = nil, dest = nil, &block )
u_header ||= {}
- header = connecting( u_header ) {
- @command.get ret, edit_path(path), u_header
+ if block then
+ dest = ReadAdapter.new( block )
+ ret = nil
+ else
+ dest = ret = ''
+ end
+ resp = nil
+ connecting( u_header ) {
+ @command.get edit_path(path), u_header
+ resp = @command.get_response
+ @command.try_get_body( resp, dest )
}
- return header, ret
+ return resp['http-header'], ret
end
- def head( path, u_header = nil )
+ def get2( path, u_header = {} )
+ only_header( :get, path, u_header )
+ end
+
+ def get_body( dest = '', &block )
+ if block then
+ dest = ReadAdapter.new( block )
+ end
+ @command.try_get_body @response, dest
+ ensure_termination @u_header
+
+ dest
+ end
+
+ def head( path, u_header = {} )
+ ret = only_header( :head, path, u_header )['http-header']
+ ensure_termination u_header
+ ret
+ end
+
+ def post( path, data, u_header = nil, dest = nil, &block )
u_header ||= {}
- header = connecting( u_header ) {
- @command.head edit_path(path), u_header
+ if block then
+ dest = ReadAdapter.new( block )
+ ret = nil
+ else
+ dest = ret = ''
+ end
+ resp = nil
+ connecting( u_header, true ) {
+ @command.post path, u_header, data
+ resp = @command.get_response
+ @command.try_get_body( resp, dest )
+ }
+
+ return resp['http-header'], ret
+ end
+
+ def post2( path, data, u_header = {} )
+ only_header :post, path, u_header, data
+ end
+
+ # not tested because I could not setup apache (__;;;
+ def put( path, src = nil, u_header = {}, &block )
+ u_header ||= u_header
+ connecting( u_header, true ) {
+ @command.put path, u_header, src, dest
}
header
@@ -83,30 +162,51 @@ class HTTPBadResponse < HTTPError; end
private
+ def only_header( mid, path, u_header, data = nil )
+ @u_header = u_header ? procheader(u_header) : {}
+ @response = nil
+ ensure_connection @u_header
+ if data then
+ @command.send mid, edit_path(path), @u_header, data
+ else
+ @command.send mid, edit_path(path), @u_header
+ end
+ @response = @command.get_response
+ @response['http-header']
+ end
+
+
# called when connecting
def do_finish
unless @socket.closed? then
- @command.head '/', { 'Connection' => 'Close' }
+ begin
+ @command.head '/', { 'Connection' => 'Close' }
+ rescue EOFError
+ end
end
end
- def connecting( u_header )
+ def connecting( u_header, putp = false )
u_header = procheader( u_header )
+ ensure_connection u_header
+ yield
+ ensure_termination u_header
+ end
+ def ensure_connection( u_header )
if not @socket then
u_header['Connection'] = 'Close'
start
elsif @socket.closed? then
@socket.reopen
end
+ end
- header = yield
-
+ def ensure_termination( u_header )
unless keep_alive? u_header then
@socket.close
end
-
- header
+ @u_header = @response = nil
end
def keep_alive?( header )
@@ -155,6 +255,20 @@ class HTTPBadResponse < HTTPError; end
HTTPSession = HTTP
+ class HTTPSuccessCode < SuccessCode; end
+ class HTTPCreatedCode < SuccessCode; end
+ class HTTPAcceptedCode < SuccessCode; end
+ class HTTPNoContentCode < SuccessCode; end
+ class HTTPResetContentCode < SuccessCode; end
+ class HTTPPartialContentCode < SuccessCode; end
+
+ class HTTPMultipleChoiceCode < RetryCode; end
+ class HTTPMovedPermanentlyCode < RetryCode; end
+ class HTTPMovedTemporarilyCode < RetryCode; end
+ class HTTPNotModifiedCode < RetryCode; end
+ class HTTPUseProxyCode < RetryCode; end
+
+
class HTTPCommand < Command
HTTPVersion = '1.1'
@@ -173,41 +287,26 @@ class HTTPBadResponse < HTTPError; end
attr_reader :http_version
- def get( ret, path, u_header = nil )
- header = get_response(
- sprintf( 'GET %s HTTP/%s', path, HTTPVersion ), u_header )
- if chunked? header then
- clen = read_chunked_body( ret )
- header.delete 'transfer-encoding'
- header[ 'content-length' ] = "Content-Length: #{clen}"
- else
- if clen = content_length( header ) then
- @socket.read clen, ret
- else
- @socket.read_all ret
- end
- end
-
- header
+ def get( path, u_header )
+ request sprintf('GET %s HTTP/%s', path, HTTPVersion), u_header
end
-
-
- def head( path, u_header = nil )
- get_response sprintf( 'HEAD %s HTTP/%s', path, HTTPVersion ), u_header
+
+ def head( path, u_header )
+ request sprintf('HEAD %s HTTP/%s', path, HTTPVersion), u_header
end
-
- # not work
- def post( path, u_header = nil )
- get_response sprintf( 'POST %s HTTP/%s', path, HTTPVersion ), u_header
+ def post( path, u_header, data )
+ request sprintf('POST %s HTTP/%s', path, HTTPVersion), u_header
+ @socket.write data
end
- # not work
- def put( path, u_header = nil )
- get_response sprintf( 'PUT %s HTTP/%s', path, HTTPVersion ), u_header
+ def put( path, u_header, src )
+ request sprintf('PUT %s HTTP/%s', path, HTTPVersion), u_header
+ @socket.write_bin src
end
+
# def delete
# def trace
@@ -215,19 +314,64 @@ class HTTPBadResponse < HTTPError; end
# def options
- private
-
-
- def get_response( line, u_header )
- @socket.writeline line
- write_header u_header
+ def get_response
rep = get_reply
- header = read_header
+ rep = get_reply while ContinueCode === rep
+ header = {}
+ while true do
+ line = @socket.readline
+ break if line.empty?
+ nm = /\A[^:]+/.match( line )[0].strip.downcase
+ header[nm] = line
+ end
+
+ rep['http-header'] = header
reply_must rep, SuccessCode
- header
+ rep
+ end
+
+ def get_body( rep, dest )
+ header = rep['http-header']
+ if chunked? header then
+ read_chunked( dest, header )
+ else
+ if clen = content_length( header ) then
+ @socket.read clen, dest
+ else
+ ###
+ ### "multipart/bytelenges" check should be done here ...
+ ###
+ @socket.read_all dest
+ end
+ end
+ end
+
+ def try_get_body( rep, dest )
+ rep = get_reply while ContinueCode === rep
+ return nil unless rep['body-exist']
+
+ get_body rep, dest
+ end
+
+
+ private
+
+
+ def request( req, u_header )
+ @socket.writeline req
+ if u_header then
+ header = @in_header.dup.update( u_header )
+ else
+ header = @in_header
+ end
+ header.each do |n,v|
+ @socket.writeline n + ': ' + v
+ end
+ @socket.writeline ''
end
+
def get_reply
str = @socket.readline
unless /\AHTTP\/(\d+\.\d+)?\s+(\d\d\d)\s*(.*)\z/i === str then
@@ -237,78 +381,45 @@ class HTTPBadResponse < HTTPError; end
status = $2
discrip = $3
+ be = false
klass = case status[0]
when ?1 then
case status[2]
when ?0 then ContinueCode
- when ?1 then SuccessCode
+ when ?1 then HTTPSuccessCode
+ else UnknownCode
+ end
+ when ?2 then
+ case status[2]
+ when ?0 then be = true; HTTPSuccessCode
+ when ?1 then be = false; HTTPSuccessCode
+ when ?2 then be = true; HTTPSuccessCode
+ when ?3 then be = true; HTTPSuccessCode
+ when ?4 then be = false; HTTPNoContentCode
+ when ?5 then be = false; HTTPResetContentCode
+ when ?6 then be = true; HTTPPartialContentCode
+ else UnknownCode
+ end
+ when ?3 then
+ case status[2]
+ when ?0 then be = true; HTTPMultipleChoiceCode
+ when ?1 then be = true; HTTPMovedPermanentryCode
+ when ?2 then be = true; HTTPMovedTemporarilyCode
+ when ?3 then be = true; HTTPMovedPermanentryCode
+ when ?4 then be = false; HTTPNotModifiedCode
+ when ?5 then be = false; HTTPUseProxyCode
else UnknownCode
end
- when ?2 then SuccessCode
- when ?3 then RetryCode
when ?4 then ServerBusyCode
when ?5 then FatalErrorCode
else UnknownCode
end
- klass.new( status, discrip )
- end
-
-
- def content_length( header )
- unless str = header[ 'content-length' ] then
- return nil
- end
- unless /\Acontent-length:\s*(\d+)/i === str then
- raise HTTPBadResponse, "content-length format error"
- end
- $1.to_i
- end
-
- def chunked?( header )
- if str = header[ 'transfer-encoding' ] then
- if /\Atransfer-encoding:\s*chunked/i === str then
- return true
- end
- end
-
- false
+ code = klass.new( status, discrip )
+ code['body-exist'] = be
+ code
end
-
- def read_header
- header = {}
- while true do
- line = @socket.readline
- break if line.empty?
- /\A[^:]+/ === line
- nm = $&
- nm.strip!
- nm.downcase!
- header[ nm ] = line
- end
-
- header
- end
-
- def write_header( user )
- if user then
- header = @in_header.dup.update user
- else
- header = @in_header
- end
- header.each do |n,v|
- @socket.writeline n + ': ' + v
- end
- @socket.writeline ''
-
- if tmp = header['Connection'] then
- /close/i === tmp
- else
- false
- end
- end
-
- def read_chunked_body( ret )
+ def read_chunked( ret, header )
line = nil
len = nil
total = 0
@@ -328,7 +439,29 @@ class HTTPBadResponse < HTTPError; end
break if line.empty?
end
- total
+ header.delete 'transfer-encoding'
+ header[ 'content-length' ] = "Content-Length: #{total}"
+ end
+
+
+ def content_length( header )
+ unless str = header[ 'content-length' ] then
+ return nil
+ end
+ unless /\Acontent-length:\s*(\d+)/i === str then
+ raise HTTPBadResponse, "content-length format error"
+ end
+ $1.to_i
+ end
+
+ def chunked?( header )
+ if str = header[ 'transfer-encoding' ] then
+ if /\Atransfer-encoding:\s*chunked/i === str then
+ return true
+ end
+ end
+
+ false
end
end