diff options
author | aamine <aamine@b2dd03c8-39d4-4d8f-98ff-823fe69b080e> | 2000-03-26 08:48:15 +0000 |
---|---|---|
committer | aamine <aamine@b2dd03c8-39d4-4d8f-98ff-823fe69b080e> | 2000-03-26 08:48:15 +0000 |
commit | d8d148d81653d7be02ee2d4237b5e75856e02ee3 (patch) | |
tree | fb8b3458ccde7a9923c0ab9b33ef77f1bdf0f899 /lib/net/http.rb | |
parent | 688169fd83b24564b653c03977c168cea50ccd35 (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.rb | 359 |
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 |