From 9fd5174ef3aebe3d862289b4c646e65b2400b2f9 Mon Sep 17 00:00:00 2001 From: aamine Date: Mon, 27 Mar 2000 15:52:27 +0000 Subject: v1.1.11 o all: use "critical" to avoid duplicated command dispatch o http.rb: change get2, post2 usage (HTTPWriter) o http.rb: entity reading algorithm is better o http.rb: more reply code (4xx, 5xx) o protocol.rb: arguments of "connect" can be omitted o protocol.rb: "quit" is not template method (now do_quit is removed) o protocol.rb: ReplyCode.error_type was not work: using module_eval git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@657 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- lib/net/http.rb | 347 +++++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 242 insertions(+), 105 deletions(-) (limited to 'lib/net/http.rb') diff --git a/lib/net/http.rb b/lib/net/http.rb index 14f9da4003..009850abbc 100644 --- a/lib/net/http.rb +++ b/lib/net/http.rb @@ -58,19 +58,18 @@ class HTTPBadResponse < HTTPError; end : post( path, data, header = nil, dest = '' ) : post( path, data, header = nil ) {|str| .... } - post "data"(must be String now) to "path" (and get entity body). + post "data"(must be String now) to "path". + If body exists, also get entity body. + It is written to "dest" by using "<<" method. "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 + If called as iterator, gives a part String of entity body. -: get2( path, header = nil ) +: get2( path, header = nil ) {|writer| .... } send GET request for "path". "header" must be a Hash like { 'Accept' => '*/*', ... }. - This method returns response header (Hash). + This method gives HTTPWriter object to block. : get_body( dest = '' ) : get_body {|str| .... } @@ -78,7 +77,31 @@ class HTTPBadResponse < HTTPError; end Data is written in "dest" by using "<<" method. This method returns "dest". - If called as iterator, give a part String of entity body. + If called as iterator, gives a part String of entity body. + +: post2( path, data, header = nil ) {|writer| .... } + post "data"(must be String now) to "path". + "header" must be a Hash like { 'Accept' => '*/*', ... }. + This method gives HTTPWriter object to block. + + += class HTTPWriter + +== Methods + +: header + HTTP header. + +: response + ReplyCode object. + +: entity( dest = '' ) +: body( dest = '' ) + entity body. + +: entity {|str| ... } + get entity body by using iterator. + If this method is called twice, block is not called. =end @@ -87,20 +110,24 @@ class HTTPBadResponse < HTTPError; end protocol_param :port, '80' protocol_param :command_type, '::Net::HTTPCommand' - - def get( path, u_header = nil, dest = nil, &block ) - u_header = procheader( u_header ) + def HTTP.procdest( dest, block ) if block then - dest = ReadAdapter.new( block ) - ret = nil + return ReadAdapter.new( block ), nil else - dest = ret = '' + dest ||= '' + return dest, dest end + end + + + def get( path, u_header = nil, dest = nil, &block ) + u_header = procheader( u_header ) + dest, ret = HTTP.procdest( dest, block ) resp = nil connecting( u_header ) { @command.get edit_path(path), u_header resp = @command.get_response - @command.try_get_body( resp, dest ) + @command.get_body( resp, dest ) } return resp['http-header'], ret @@ -108,67 +135,82 @@ class HTTPBadResponse < HTTPError; end def get2( path, u_header = nil ) u_header = procheader( u_header ) - only_header( :get, path, u_header ) + connecting( u_header ) { + @command.get edit_path(path), u_header + tmp = HTTPWriter.new( @command ) + yield tmp + tmp.off + } end +=begin c def get_body( dest = '', &block ) if block then dest = ReadAdapter.new( block ) end - @command.try_get_body @response, dest + @command.get_body @response, dest ensure_termination @u_header dest end +=end def head( path, u_header = nil ) u_header = procheader( u_header ) - header = only_header( :head, path, u_header ) - ensure_termination u_header - header + resp = nil + connecting( u_header ) { + @command.head( edit_path(path), u_header ) + resp = @command.get_response_no_body + } + + resp['http-header'] end def post( path, data, u_header = nil, dest = nil, &block ) u_header = procheader( u_header ) - if block then - dest = ReadAdapter.new( block ) - ret = nil - else - dest = ret = '' - end + dest, ret = HTTP.procdest( dest, block ) resp = nil - connecting( u_header, true ) { - @command.post path, u_header, data + connecting( u_header ) { + @command.post edit_path(path), u_header, data resp = @command.get_response - @command.try_get_body( resp, dest ) + @command.get_body( resp, dest ) } return resp['http-header'], ret end - def post2( path, data, u_header = {} ) + def post2( path, data, u_header = nil ) u_header = procheader( u_header ) - only_header :post, path, u_header, data + connecting( u_header ) { + @command.post edit_path(path), u_header, data + tmp = HTTPWriter.new( @command ) + yield tmp + tmp.off + } end # not tested because I could not setup apache (__;;; - def put( path, src = nil, u_header = nil, &block ) + def put( path, src, u_header = nil ) u_header = procheader( u_header ) - connecting( u_header, true ) { + ret = '' + connecting( u_header ) { @command.put path, u_header, src, dest + resp = @comman.get_response + @command.get_body( resp, ret ) } - header + return header, ret end private +=begin c def only_header( mid, path, u_header, data = nil ) @u_header = u_header @response = nil - ensure_connection u_header + connecting u_header if data then @command.send mid, edit_path(path), u_header, data else @@ -177,6 +219,7 @@ class HTTPBadResponse < HTTPError; end @response = @command.get_response @response['http-header'] end +=end # called when connecting @@ -189,19 +232,19 @@ class HTTPBadResponse < HTTPError; end end end - def connecting( u_header, putp = false ) - ensure_connection u_header - yield - ensure_termination u_header - end - - def ensure_connection( u_header ) + def connecting( u_header ) if not @socket then u_header['Connection'] = 'Close' start elsif @socket.closed? then @socket.reopen end + + if iterator? then + ret = yield + ensure_termination u_header + ret + end end def ensure_termination( u_header ) @@ -258,18 +301,82 @@ 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 HTTPWriter + + def initialize( command ) + @command = command + @response = @header = @entity = nil + end + + def response + unless @resp then + @resp = @command.get_response + end + @resp + end + + def header + unless @header then + @header = response['http-header'] + end + @header + end + + def entity( dest = nil, &block ) + dest, ret = HTTP.procdest( dest, block ) + unless @entity then + @entity = @command.get_body( response, dest ) + end + @entity + end + alias body entity + + def off + entity + @command = nil + end + + end + - class HTTPMultipleChoiceCode < RetryCode; end - class HTTPMovedPermanentlyCode < RetryCode; end - class HTTPMovedTemporarilyCode < RetryCode; end - class HTTPNotModifiedCode < RetryCode; end - class HTTPUseProxyCode < RetryCode; end + class HTTPSwitchProtocol < SuccessCode; end + + class HTTPOK < SuccessCode; end + class HTTPCreated < SuccessCode; end + class HTTPAccepted < SuccessCode; end + class HTTPNonAuthoritativeInformation < SuccessCode; end + class HTTPNoContent < SuccessCode; end + class HTTPResetContent < SuccessCode; end + class HTTPPartialContent < SuccessCode; end + + class HTTPMultipleChoice < RetryCode; end + class HTTPMovedPermanently < RetryCode; end + class HTTPMovedTemporarily < RetryCode; end + class HTTPNotModified < RetryCode; end + class HTTPUseProxy < RetryCode; end + + class HTTPBadRequest < RetryCode; end + class HTTPUnauthorized < RetryCode; end + class HTTPPaymentRequired < RetryCode; end + class HTTPForbidden < FatalErrorCode; end + class HTTPNotFound < FatalErrorCode; end + class HTTPMethodNotAllowed < FatalErrorCode; end + class HTTPNotAcceptable < FatalErrorCode; end + class HTTPProxyAuthenticationRequired < RetryCode; end + class HTTPRequestTimeOut < FatalErrorCode; end + class HTTPConflict < FatalErrorCode; end + class HTTPGone < FatalErrorCode; end + class HTTPLengthRequired < FatalErrorCode; end + class HTTPPreconditionFailed < FatalErrorCode; end + class HTTPRequestEntityTooLarge < FatalErrorCode; end + class HTTPRequestURITooLarge < FatalErrorCode; end + class HTTPUnsupportedMediaType < FatalErrorCode; end + + class HTTPNotImplemented < FatalErrorCode; end + class HTTPBadGateway < FatalErrorCode; end + class HTTPServiceUnavailable < FatalErrorCode; end + class HTTPGatewayTimeOut < FatalErrorCode; end + class HTTPVersionNotSupported < FatalErrorCode; end class HTTPCommand < Command @@ -292,30 +399,36 @@ class HTTPBadResponse < HTTPError; end def get( path, u_header ) + return unless begin_critical request sprintf('GET %s HTTP/%s', path, HTTPVersion), u_header end def head( path, u_header ) + return unless begin_critical request sprintf('HEAD %s HTTP/%s', path, HTTPVersion), u_header end def post( path, u_header, data ) + return unless begin_critical request sprintf('POST %s HTTP/%s', path, HTTPVersion), u_header @socket.write data end def put( path, u_header, src ) + return unless begin_critical request sprintf('PUT %s HTTP/%s', path, HTTPVersion), u_header @socket.write_bin src end - # def delete # def trace # def options + def quit + end + def get_response rep = get_reply @@ -327,35 +440,47 @@ class HTTPBadResponse < HTTPError; end nm = /\A[^:]+/.match( line )[0].strip.downcase header[nm] = line end - rep['http-header'] = header - reply_must rep, SuccessCode rep end + def check_response( resp ) + reply_must resp, SuccessCode + 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/byteranges" check should be done here ... - # now, length is designated by closing socket - @socket.read_all dest - @socket.close + if rep['body-exist'] then + if chunked? header then + read_chunked( dest, header ) + else + if clen = content_length( header ) then + @socket.read clen, dest + else + if false then # "multipart/byteranges" check should be done + else + if header['Connection'] and + /connection:\s*close/i === header['Connection'] then + @socket.read_all dest + @socket.close + end + end + end end end - end + end_critical + reply_must rep, SuccessCode - def try_get_body( rep, dest ) - rep = get_reply while ContinueCode === rep - return nil unless rep['body-exist'] + dest + end - get_body rep, dest + def get_response_no_body + resp = get_response + end_critical + reply_must resp, SuccessCode + resp end @@ -376,6 +501,50 @@ class HTTPBadResponse < HTTPError; end end + CODE_TO_CLASS = { + '100' => [ContinueCode, false], + '100' => [HTTPSwitchProtocol, false], + + '200' => [HTTPOK, true], + '201' => [HTTPCreated, true], + '202' => [HTTPAccepted, true], + '203' => [HTTPNonAuthoritativeInformation, true], + '204' => [HTTPNoContent, false], + '205' => [HTTPResetContent, false], + '206' => [HTTPPartialContent, true], + + '300' => [HTTPMultipleChoice, true], + '301' => [HTTPMovedPermanently, true], + '302' => [HTTPMovedTemporarily, true], + '303' => [HTTPMovedPermanently, true], + '304' => [HTTPNotModified, false], + '305' => [HTTPUseProxy, false], + + '400' => [HTTPBadRequest, true], + '401' => [HTTPUnauthorized, true], + '402' => [HTTPPaymentRequired, true], + '403' => [HTTPForbidden, true], + '404' => [HTTPNotFound, true], + '405' => [HTTPMethodNotAllowed, true], + '406' => [HTTPNotAcceptable, true], + '407' => [HTTPProxyAuthenticationRequired, true], + '408' => [HTTPRequestTimeOut, true], + '409' => [HTTPConflict, true], + '410' => [HTTPGone, true], + '411' => [FatalErrorCode, true], + '412' => [HTTPPreconditionFailed, true], + '413' => [HTTPRequestEntityTooLarge, true], + '414' => [HTTPRequestURITooLarge, true], + '415' => [HTTPUnsupportedMediaType, true], + + '500' => [FatalErrorCode, true], + '501' => [HTTPNotImplemented, true], + '502' => [HTTPBadGateway, true], + '503' => [HTTPServiceUnavailable, true], + '504' => [HTTPGatewayTimeOut, true], + '505' => [HTTPVersionNotSupported, true] + } + def get_reply str = @socket.readline unless /\AHTTP\/(\d+\.\d+)?\s+(\d\d\d)\s*(.*)\z/i === str then @@ -385,41 +554,9 @@ 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 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 ?4 then ServerBusyCode - when ?5 then FatalErrorCode - else UnknownCode - end + klass, bodyexist = CODE_TO_CLASS[status] || [UnknownCode, true] code = klass.new( status, discrip ) - code['body-exist'] = be + code['body-exist'] = bodyexist code end -- cgit v1.2.3