summaryrefslogtreecommitdiff
path: root/lib/net/http.rb
diff options
context:
space:
mode:
authoraamine <aamine@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2000-03-27 15:52:27 +0000
committeraamine <aamine@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2000-03-27 15:52:27 +0000
commit9fd5174ef3aebe3d862289b4c646e65b2400b2f9 (patch)
tree36ba0828cb3b1f2bb70a53e7661a6e9ca7162f78 /lib/net/http.rb
parent0dcf7498b10c7fd4739b34236502efd8e0f8353c (diff)
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
Diffstat (limited to 'lib/net/http.rb')
-rw-r--r--lib/net/http.rb347
1 files changed, 242 insertions, 105 deletions
diff --git a/lib/net/http.rb b/lib/net/http.rb
index 14f9da4..009850a 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