зеркало из https://github.com/github/ruby.git
protocol.rb smtp.rb pop.rb http.rb version 1.1.12
o protocol.rb: update Net::Protocol::Proxy#connect o protocol.rb: ReplyCode is not a class o http.rb: header value does not include header name o http.rb: header is not a Hash, but HTTPResponse git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@659 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
Родитель
9fd5174ef3
Коммит
be72d9a5da
368
lib/net/http.rb
368
lib/net/http.rb
|
@ -15,10 +15,7 @@ require 'net/protocol'
|
||||||
|
|
||||||
module Net
|
module Net
|
||||||
|
|
||||||
|
class HTTPBadResponse < StandardError; end
|
||||||
class HTTPError < ProtocolError; end
|
|
||||||
class HTTPBadResponse < HTTPError; end
|
|
||||||
|
|
||||||
|
|
||||||
=begin
|
=begin
|
||||||
|
|
||||||
|
@ -43,18 +40,20 @@ class HTTPBadResponse < HTTPError; end
|
||||||
get data from "path" on connecting host.
|
get data from "path" on connecting host.
|
||||||
"header" must be a Hash like { 'Accept' => '*/*', ... }.
|
"header" must be a Hash like { 'Accept' => '*/*', ... }.
|
||||||
Data is written to "dest" by using "<<" method.
|
Data is written to "dest" by using "<<" method.
|
||||||
This method returns response header (Hash) and "dest".
|
This method returns Net::HTTPResponse object and "dest".
|
||||||
|
|
||||||
If called as iterator, give a part String of entity body.
|
If called as iterator, give a part String of entity body.
|
||||||
|
|
||||||
: head( path, header = nil )
|
: head( path, header = nil )
|
||||||
get only header from "path" on connecting host.
|
get only header from "path" on connecting host.
|
||||||
"header" is a Hash like { 'Accept' => '*/*', ... }.
|
"header" is a Hash like { 'Accept' => '*/*', ... }.
|
||||||
This method returns header as a Hash like
|
This method returns Net::HTTPResponse object.
|
||||||
|
You can http header from this object like:
|
||||||
|
|
||||||
{ 'content-length' => 'Content-Length: 2554',
|
response['content-length'] #-> '2554'
|
||||||
'content-type' => 'Content-Type: text/html',
|
response['content-type'] #-> 'text/html'
|
||||||
... }
|
response['Content-Type'] #-> 'text/html'
|
||||||
|
response['CoNtEnT-tYpe'] #-> 'text/html'
|
||||||
|
|
||||||
: post( path, data, header = nil, dest = '' )
|
: post( path, data, header = nil, dest = '' )
|
||||||
: post( path, data, header = nil ) {|str| .... }
|
: post( path, data, header = nil ) {|str| .... }
|
||||||
|
@ -62,42 +61,58 @@ class HTTPBadResponse < HTTPError; end
|
||||||
If body exists, also get entity body.
|
If body exists, also get entity body.
|
||||||
It is written to "dest" by using "<<" method.
|
It is written to "dest" by using "<<" method.
|
||||||
"header" must be a Hash like { 'Accept' => '*/*', ... }.
|
"header" must be a Hash like { 'Accept' => '*/*', ... }.
|
||||||
This method returns response header (Hash) and "dest".
|
This method returns Net::HTTPResponse object and "dest".
|
||||||
|
|
||||||
If called as iterator, gives a part String of entity body.
|
If called as iterator, gives a part String of entity body.
|
||||||
|
|
||||||
: get2( path, header = nil ) {|writer| .... }
|
: get2( path, header = nil ) {|writer| .... }
|
||||||
send GET request for "path".
|
send GET request for "path".
|
||||||
"header" must be a Hash like { 'Accept' => '*/*', ... }.
|
"header" must be a Hash like { 'Accept' => '*/*', ... }.
|
||||||
This method gives HTTPWriter object to block.
|
This method gives HTTPReadAdapter object to block.
|
||||||
|
|
||||||
: 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, gives a part String of entity body.
|
|
||||||
|
|
||||||
: post2( path, data, header = nil ) {|writer| .... }
|
: post2( path, data, header = nil ) {|writer| .... }
|
||||||
post "data"(must be String now) to "path".
|
post "data"(must be String now) to "path".
|
||||||
"header" must be a Hash like { 'Accept' => '*/*', ... }.
|
"header" must be a Hash like { 'Accept' => '*/*', ... }.
|
||||||
This method gives HTTPWriter object to block.
|
This method gives HTTPReadAdapter object to block.
|
||||||
|
|
||||||
|
|
||||||
= class HTTPWriter
|
= class HTTPResponse
|
||||||
|
|
||||||
|
== Methods
|
||||||
|
|
||||||
|
HTTP response object.
|
||||||
|
All "key" is case-insensitive.
|
||||||
|
|
||||||
|
: code
|
||||||
|
HTTP result code. ex. '302'
|
||||||
|
|
||||||
|
: message
|
||||||
|
HTTP result message. ex. 'Not Found'
|
||||||
|
|
||||||
|
: self[ key ]
|
||||||
|
returns header field for "key".
|
||||||
|
for HTTP, value is a string like 'text/plain'(for Content-Type),
|
||||||
|
'2045'(for Content-Length), 'bytes 0-1024/10024'(for Content-Range).
|
||||||
|
Multiple header had be joined by HTTP1.1 scheme.
|
||||||
|
|
||||||
|
: self[ key ] = val
|
||||||
|
set field value for "key".
|
||||||
|
|
||||||
|
: key?( key )
|
||||||
|
true if key is exist
|
||||||
|
|
||||||
|
|
||||||
|
= class HTTPReadAdapter
|
||||||
|
|
||||||
== Methods
|
== Methods
|
||||||
|
|
||||||
: header
|
: header
|
||||||
HTTP header.
|
|
||||||
|
|
||||||
: response
|
: response
|
||||||
ReplyCode object.
|
Net::HTTPResponse object
|
||||||
|
|
||||||
: entity( dest = '' )
|
: entity( dest = '' )
|
||||||
: body( dest = '' )
|
: body( dest = '' )
|
||||||
entity body.
|
entity body
|
||||||
|
|
||||||
: entity {|str| ... }
|
: entity {|str| ... }
|
||||||
get entity body by using iterator.
|
get entity body by using iterator.
|
||||||
|
@ -130,30 +145,19 @@ class HTTPBadResponse < HTTPError; end
|
||||||
@command.get_body( resp, dest )
|
@command.get_body( resp, dest )
|
||||||
}
|
}
|
||||||
|
|
||||||
return resp['http-header'], ret
|
return resp, ret
|
||||||
end
|
end
|
||||||
|
|
||||||
def get2( path, u_header = nil )
|
def get2( path, u_header = nil )
|
||||||
u_header = procheader( u_header )
|
u_header = procheader( u_header )
|
||||||
connecting( u_header ) {
|
connecting( u_header ) {
|
||||||
@command.get edit_path(path), u_header
|
@command.get edit_path(path), u_header
|
||||||
tmp = HTTPWriter.new( @command )
|
tmp = HTTPReadAdapter.new( @command )
|
||||||
yield tmp
|
yield tmp
|
||||||
tmp.off
|
tmp.off
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
=begin c
|
|
||||||
def get_body( dest = '', &block )
|
|
||||||
if block then
|
|
||||||
dest = ReadAdapter.new( block )
|
|
||||||
end
|
|
||||||
@command.get_body @response, dest
|
|
||||||
ensure_termination @u_header
|
|
||||||
|
|
||||||
dest
|
|
||||||
end
|
|
||||||
=end
|
|
||||||
|
|
||||||
def head( path, u_header = nil )
|
def head( path, u_header = nil )
|
||||||
u_header = procheader( u_header )
|
u_header = procheader( u_header )
|
||||||
|
@ -163,7 +167,7 @@ class HTTPBadResponse < HTTPError; end
|
||||||
resp = @command.get_response_no_body
|
resp = @command.get_response_no_body
|
||||||
}
|
}
|
||||||
|
|
||||||
resp['http-header']
|
resp
|
||||||
end
|
end
|
||||||
|
|
||||||
def post( path, data, u_header = nil, dest = nil, &block )
|
def post( path, data, u_header = nil, dest = nil, &block )
|
||||||
|
@ -176,14 +180,14 @@ class HTTPBadResponse < HTTPError; end
|
||||||
@command.get_body( resp, dest )
|
@command.get_body( resp, dest )
|
||||||
}
|
}
|
||||||
|
|
||||||
return resp['http-header'], ret
|
return resp, ret
|
||||||
end
|
end
|
||||||
|
|
||||||
def post2( path, data, u_header = nil )
|
def post2( path, data, u_header = nil )
|
||||||
u_header = procheader( u_header )
|
u_header = procheader( u_header )
|
||||||
connecting( u_header ) {
|
connecting( u_header ) {
|
||||||
@command.post edit_path(path), u_header, data
|
@command.post edit_path(path), u_header, data
|
||||||
tmp = HTTPWriter.new( @command )
|
tmp = HTTPReadAdapter.new( @command )
|
||||||
yield tmp
|
yield tmp
|
||||||
tmp.off
|
tmp.off
|
||||||
}
|
}
|
||||||
|
@ -193,35 +197,20 @@ class HTTPBadResponse < HTTPError; end
|
||||||
def put( path, src, u_header = nil )
|
def put( path, src, u_header = nil )
|
||||||
u_header = procheader( u_header )
|
u_header = procheader( u_header )
|
||||||
ret = ''
|
ret = ''
|
||||||
|
resp = nil
|
||||||
connecting( u_header ) {
|
connecting( u_header ) {
|
||||||
@command.put path, u_header, src, dest
|
@command.put path, u_header, src, dest
|
||||||
resp = @comman.get_response
|
resp = @comman.get_response
|
||||||
@command.get_body( resp, ret )
|
@command.get_body( resp, ret )
|
||||||
}
|
}
|
||||||
|
|
||||||
return header, ret
|
return resp, ret
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
|
|
||||||
=begin c
|
|
||||||
def only_header( mid, path, u_header, data = nil )
|
|
||||||
@u_header = u_header
|
|
||||||
@response = nil
|
|
||||||
connecting 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
|
|
||||||
=end
|
|
||||||
|
|
||||||
|
|
||||||
# called when connecting
|
# called when connecting
|
||||||
def do_finish
|
def do_finish
|
||||||
unless @socket.closed? then
|
unless @socket.closed? then
|
||||||
|
@ -255,8 +244,8 @@ class HTTPBadResponse < HTTPError; end
|
||||||
end
|
end
|
||||||
|
|
||||||
def keep_alive?( header )
|
def keep_alive?( header )
|
||||||
if str = header['Connection'] then
|
if header.key? 'connection' then
|
||||||
if /\A\s*keep-alive/i === str then
|
if /\A\s*keep-alive/i === header['connection'] then
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
|
@ -301,82 +290,126 @@ class HTTPBadResponse < HTTPError; end
|
||||||
HTTPSession = HTTP
|
HTTPSession = HTTP
|
||||||
|
|
||||||
|
|
||||||
class HTTPWriter
|
class HTTPReadAdapter
|
||||||
|
|
||||||
def initialize( command )
|
def initialize( command )
|
||||||
@command = command
|
@command = command
|
||||||
@response = @header = @entity = nil
|
@header = @body = nil
|
||||||
end
|
|
||||||
|
|
||||||
def response
|
|
||||||
unless @resp then
|
|
||||||
@resp = @command.get_response
|
|
||||||
end
|
|
||||||
@resp
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def header
|
def header
|
||||||
unless @header then
|
unless @header then
|
||||||
@header = response['http-header']
|
@header = @command.get_response
|
||||||
end
|
end
|
||||||
@header
|
@header
|
||||||
end
|
end
|
||||||
|
alias response header
|
||||||
|
|
||||||
def entity( dest = nil, &block )
|
def body( dest = nil, &block )
|
||||||
dest, ret = HTTP.procdest( dest, block )
|
dest, ret = HTTP.procdest( dest, block )
|
||||||
unless @entity then
|
unless @body then
|
||||||
@entity = @command.get_body( response, dest )
|
@body = @command.get_body( response, dest )
|
||||||
end
|
end
|
||||||
@entity
|
@body
|
||||||
end
|
end
|
||||||
alias body entity
|
alias entity body
|
||||||
|
|
||||||
def off
|
def off
|
||||||
entity
|
body
|
||||||
@command = nil
|
@command = nil
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
class HTTPSwitchProtocol < SuccessCode; end
|
class HTTPResponse < Response
|
||||||
|
|
||||||
class HTTPOK < SuccessCode; end
|
def initialize( code_type, code, msg )
|
||||||
class HTTPCreated < SuccessCode; end
|
super
|
||||||
class HTTPAccepted < SuccessCode; end
|
@data = {}
|
||||||
class HTTPNonAuthoritativeInformation < SuccessCode; end
|
@http_body_exist = true
|
||||||
class HTTPNoContent < SuccessCode; end
|
end
|
||||||
class HTTPResetContent < SuccessCode; end
|
|
||||||
class HTTPPartialContent < SuccessCode; end
|
|
||||||
|
|
||||||
class HTTPMultipleChoice < RetryCode; end
|
attr_accessor :http_body_exist
|
||||||
class HTTPMovedPermanently < RetryCode; end
|
|
||||||
class HTTPMovedTemporarily < RetryCode; end
|
def []( key )
|
||||||
class HTTPNotModified < RetryCode; end
|
@data[ key.downcase ]
|
||||||
class HTTPUseProxy < RetryCode; end
|
end
|
||||||
|
|
||||||
|
def []=( key, val )
|
||||||
|
@data[ key.downcase ] = val
|
||||||
|
end
|
||||||
|
|
||||||
|
def each( &block )
|
||||||
|
@data.each( &block )
|
||||||
|
end
|
||||||
|
|
||||||
|
def each_key( &block )
|
||||||
|
@data.each_key( &block )
|
||||||
|
end
|
||||||
|
|
||||||
|
def each_value( &block )
|
||||||
|
@data.each_value( &block )
|
||||||
|
end
|
||||||
|
|
||||||
|
def delete( key )
|
||||||
|
@data.delete key.downcase
|
||||||
|
end
|
||||||
|
|
||||||
|
def key?( key )
|
||||||
|
@data.key? key.downcase
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_hash
|
||||||
|
@data.dup
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
HTTPSuccessCode = SuccessCode.mkchild
|
||||||
|
HTTPRetriableCode = RetriableCode.mkchild
|
||||||
|
HTTPFatalErrorCode = FatalErrorCode.mkchild
|
||||||
|
|
||||||
|
|
||||||
|
HTTPSwitchProtocol = HTTPSuccessCode.mkchild
|
||||||
|
|
||||||
|
HTTPOK = HTTPSuccessCode.mkchild
|
||||||
|
HTTPCreated = HTTPSuccessCode.mkchild
|
||||||
|
HTTPAccepted = HTTPSuccessCode.mkchild
|
||||||
|
HTTPNonAuthoritativeInformation = HTTPSuccessCode.mkchild
|
||||||
|
HTTPNoContent = HTTPSuccessCode.mkchild
|
||||||
|
HTTPResetContent = HTTPSuccessCode.mkchild
|
||||||
|
HTTPPartialContent = HTTPSuccessCode.mkchild
|
||||||
|
|
||||||
|
HTTPMultipleChoice = HTTPRetriableCode.mkchild
|
||||||
|
HTTPMovedPermanently = HTTPRetriableCode.mkchild
|
||||||
|
HTTPMovedTemporarily = HTTPRetriableCode.mkchild
|
||||||
|
HTTPNotModified = HTTPRetriableCode.mkchild
|
||||||
|
HTTPUseProxy = HTTPRetriableCode.mkchild
|
||||||
|
|
||||||
class HTTPBadRequest < RetryCode; end
|
HTTPBadRequest = HTTPRetriableCode.mkchild
|
||||||
class HTTPUnauthorized < RetryCode; end
|
HTTPUnauthorized = HTTPRetriableCode.mkchild
|
||||||
class HTTPPaymentRequired < RetryCode; end
|
HTTPPaymentRequired = HTTPRetriableCode.mkchild
|
||||||
class HTTPForbidden < FatalErrorCode; end
|
HTTPForbidden = HTTPFatalErrorCode.mkchild
|
||||||
class HTTPNotFound < FatalErrorCode; end
|
HTTPNotFound = HTTPFatalErrorCode.mkchild
|
||||||
class HTTPMethodNotAllowed < FatalErrorCode; end
|
HTTPMethodNotAllowed = HTTPFatalErrorCode.mkchild
|
||||||
class HTTPNotAcceptable < FatalErrorCode; end
|
HTTPNotAcceptable = HTTPFatalErrorCode.mkchild
|
||||||
class HTTPProxyAuthenticationRequired < RetryCode; end
|
HTTPProxyAuthenticationRequired = HTTPRetriableCode.mkchild
|
||||||
class HTTPRequestTimeOut < FatalErrorCode; end
|
HTTPRequestTimeOut = HTTPFatalErrorCode.mkchild
|
||||||
class HTTPConflict < FatalErrorCode; end
|
HTTPConflict = HTTPFatalErrorCode.mkchild
|
||||||
class HTTPGone < FatalErrorCode; end
|
HTTPGone = HTTPFatalErrorCode.mkchild
|
||||||
class HTTPLengthRequired < FatalErrorCode; end
|
HTTPLengthRequired = HTTPFatalErrorCode.mkchild
|
||||||
class HTTPPreconditionFailed < FatalErrorCode; end
|
HTTPPreconditionFailed = HTTPFatalErrorCode.mkchild
|
||||||
class HTTPRequestEntityTooLarge < FatalErrorCode; end
|
HTTPRequestEntityTooLarge = HTTPFatalErrorCode.mkchild
|
||||||
class HTTPRequestURITooLarge < FatalErrorCode; end
|
HTTPRequestURITooLarge = HTTPFatalErrorCode.mkchild
|
||||||
class HTTPUnsupportedMediaType < FatalErrorCode; end
|
HTTPUnsupportedMediaType = HTTPFatalErrorCode.mkchild
|
||||||
|
|
||||||
class HTTPNotImplemented < FatalErrorCode; end
|
HTTPNotImplemented = HTTPFatalErrorCode.mkchild
|
||||||
class HTTPBadGateway < FatalErrorCode; end
|
HTTPBadGateway = HTTPFatalErrorCode.mkchild
|
||||||
class HTTPServiceUnavailable < FatalErrorCode; end
|
HTTPServiceUnavailable = HTTPFatalErrorCode.mkchild
|
||||||
class HTTPGatewayTimeOut < FatalErrorCode; end
|
HTTPGatewayTimeOut = HTTPFatalErrorCode.mkchild
|
||||||
class HTTPVersionNotSupported < FatalErrorCode; end
|
HTTPVersionNotSupported = HTTPFatalErrorCode.mkchild
|
||||||
|
|
||||||
|
|
||||||
class HTTPCommand < Command
|
class HTTPCommand < Command
|
||||||
|
@ -431,38 +464,48 @@ class HTTPBadResponse < HTTPError; end
|
||||||
|
|
||||||
|
|
||||||
def get_response
|
def get_response
|
||||||
rep = get_reply
|
resp = get_reply
|
||||||
rep = get_reply while ContinueCode === rep
|
resp = get_reply while ContinueCode === resp
|
||||||
header = {}
|
|
||||||
while true do
|
while true do
|
||||||
line = @socket.readline
|
line = @socket.readline
|
||||||
break if line.empty?
|
break if line.empty?
|
||||||
nm = /\A[^:]+/.match( line )[0].strip.downcase
|
|
||||||
header[nm] = line
|
|
||||||
end
|
|
||||||
rep['http-header'] = header
|
|
||||||
|
|
||||||
rep
|
m = /\A([^:]+):\s*(.*)/p.match( line )
|
||||||
|
unless m then
|
||||||
|
raise HTTPBadResponse, 'wrong header line format'
|
||||||
|
end
|
||||||
|
nm = m[1]
|
||||||
|
line = m[2]
|
||||||
|
if resp.key? nm then
|
||||||
|
resp[nm] << ', ' << line
|
||||||
|
else
|
||||||
|
resp[nm] = line
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
resp
|
||||||
end
|
end
|
||||||
|
|
||||||
def check_response( resp )
|
def check_response( resp )
|
||||||
reply_must resp, SuccessCode
|
reply_must resp, SuccessCode
|
||||||
end
|
end
|
||||||
|
|
||||||
def get_body( rep, dest )
|
def get_body( resp, dest )
|
||||||
header = rep['http-header']
|
if resp.http_body_exist then
|
||||||
|
if chunked? resp then
|
||||||
if rep['body-exist'] then
|
read_chunked( dest, resp )
|
||||||
if chunked? header then
|
|
||||||
read_chunked( dest, header )
|
|
||||||
else
|
else
|
||||||
if clen = content_length( header ) then
|
clen = content_length( resp )
|
||||||
|
if clen then
|
||||||
@socket.read clen, dest
|
@socket.read clen, dest
|
||||||
else
|
else
|
||||||
if false then # "multipart/byteranges" check should be done
|
clen = range_length( resp )
|
||||||
|
if clen then
|
||||||
|
@socket.read clen, dest
|
||||||
else
|
else
|
||||||
if header['Connection'] and
|
tmp = resp['connection']
|
||||||
/connection:\s*close/i === header['Connection'] then
|
if tmp and /close/i === tmp then
|
||||||
@socket.read_all dest
|
@socket.read_all dest
|
||||||
@socket.close
|
@socket.close
|
||||||
end
|
end
|
||||||
|
@ -471,7 +514,7 @@ class HTTPBadResponse < HTTPError; end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end_critical
|
end_critical
|
||||||
reply_must rep, SuccessCode
|
reply_must resp, SuccessCode
|
||||||
|
|
||||||
dest
|
dest
|
||||||
end
|
end
|
||||||
|
@ -501,7 +544,7 @@ class HTTPBadResponse < HTTPError; end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
CODE_TO_CLASS = {
|
HTTPCODE_TO_OBJ = {
|
||||||
'100' => [ContinueCode, false],
|
'100' => [ContinueCode, false],
|
||||||
'100' => [HTTPSwitchProtocol, false],
|
'100' => [HTTPSwitchProtocol, false],
|
||||||
|
|
||||||
|
@ -547,17 +590,18 @@ class HTTPBadResponse < HTTPError; end
|
||||||
|
|
||||||
def get_reply
|
def get_reply
|
||||||
str = @socket.readline
|
str = @socket.readline
|
||||||
unless /\AHTTP\/(\d+\.\d+)?\s+(\d\d\d)\s*(.*)\z/i === str then
|
m = /\AHTTP\/(\d+\.\d+)?\s+(\d\d\d)\s*(.*)\z/i.match( str )
|
||||||
|
unless m then
|
||||||
raise HTTPBadResponse, "wrong status line format: #{str}"
|
raise HTTPBadResponse, "wrong status line format: #{str}"
|
||||||
end
|
end
|
||||||
@http_version = $1
|
@http_version = m[1]
|
||||||
status = $2
|
status = m[2]
|
||||||
discrip = $3
|
discrip = m[3]
|
||||||
|
|
||||||
klass, bodyexist = CODE_TO_CLASS[status] || [UnknownCode, true]
|
klass, bodyexist = HTTPCODE_TO_OBJ[status] || [UnknownCode, true]
|
||||||
code = klass.new( status, discrip )
|
resp = HTTPResponse.new( klass, status, discrip )
|
||||||
code['body-exist'] = bodyexist
|
resp.http_body_exist = bodyexist
|
||||||
code
|
resp
|
||||||
end
|
end
|
||||||
|
|
||||||
def read_chunked( ret, header )
|
def read_chunked( ret, header )
|
||||||
|
@ -567,10 +611,11 @@ class HTTPBadResponse < HTTPError; end
|
||||||
|
|
||||||
while true do
|
while true do
|
||||||
line = @socket.readline
|
line = @socket.readline
|
||||||
unless /[0-9a-hA-H]+/ === line then
|
m = /[0-9a-hA-H]+/.match( line )
|
||||||
|
unless m then
|
||||||
raise HTTPBadResponse, "chunk size not given"
|
raise HTTPBadResponse, "chunk size not given"
|
||||||
end
|
end
|
||||||
len = $&.hex
|
len = m[0].hex
|
||||||
break if len == 0
|
break if len == 0
|
||||||
@socket.read( len, ret ); total += len
|
@socket.read( len, ret ); total += len
|
||||||
@socket.read 2 # \r\n
|
@socket.read 2 # \r\n
|
||||||
|
@ -581,28 +626,47 @@ class HTTPBadResponse < HTTPError; end
|
||||||
end
|
end
|
||||||
|
|
||||||
header.delete 'transfer-encoding'
|
header.delete 'transfer-encoding'
|
||||||
header[ 'content-length' ] = "Content-Length: #{total}"
|
header[ 'content-length' ] = total.to_s
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
def content_length( header )
|
def content_length( header )
|
||||||
unless str = header[ 'content-length' ] then
|
if header.key? 'content-length' then
|
||||||
return nil
|
m = /\d+/.match( header['content-length'] )
|
||||||
|
unless m then
|
||||||
|
raise HTTPBadResponse, 'wrong Content-Length format'
|
||||||
|
end
|
||||||
|
m[0].to_i
|
||||||
|
else
|
||||||
|
nil
|
||||||
end
|
end
|
||||||
unless /\Acontent-length:\s*(\d+)/i === str then
|
|
||||||
raise HTTPBadResponse, "content-length format error"
|
|
||||||
end
|
|
||||||
$1.to_i
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def chunked?( header )
|
def chunked?( header )
|
||||||
if str = header[ 'transfer-encoding' ] then
|
str = header[ 'transfer-encoding' ]
|
||||||
if /\Atransfer-encoding:\s*chunked/i === str then
|
if str and /(\A|\s+)chunked(?:\s+|\z)/i === str then
|
||||||
return true
|
true
|
||||||
end
|
else
|
||||||
|
false
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
false
|
def range_length( header )
|
||||||
|
if header.key? 'content-range' then
|
||||||
|
m = %r<bytes\s+(\d+)-(\d+)/\d+>.match( header['content-range'] )
|
||||||
|
unless m then
|
||||||
|
raise HTTPBadResponse, 'wrong Content-Range format'
|
||||||
|
end
|
||||||
|
l = m[2].to_i
|
||||||
|
u = m[1].to_i
|
||||||
|
if l > u then
|
||||||
|
nil
|
||||||
|
else
|
||||||
|
u - l
|
||||||
|
end
|
||||||
|
else
|
||||||
|
nil
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -314,9 +314,9 @@ Net::POP3
|
||||||
str = @socket.readline
|
str = @socket.readline
|
||||||
|
|
||||||
if /\A\+/ === str then
|
if /\A\+/ === str then
|
||||||
return SuccessCode.new( str[0,3], str[3, str.size - 3].strip )
|
return Response.new( SuccessCode, str[0,3], str[3, str.size - 3].strip )
|
||||||
else
|
else
|
||||||
return ErrorCode.new( str[0,4], str[4, str.size - 4].strip )
|
return Response.new( ErrorCode, str[0,4], str[4, str.size - 4].strip )
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,7 @@ require 'socket'
|
||||||
|
|
||||||
module Net
|
module Net
|
||||||
|
|
||||||
Version = '1.1.11'
|
Version = '1.1.12'
|
||||||
|
|
||||||
=begin
|
=begin
|
||||||
|
|
||||||
|
@ -95,7 +95,7 @@ Object
|
||||||
@port = port
|
@port = port
|
||||||
end
|
end
|
||||||
|
|
||||||
def connect( addr, port )
|
def connect( addr = nil, port = nil )
|
||||||
super @proxyaddr, @proxyport
|
super @proxyaddr, @proxyport
|
||||||
end
|
end
|
||||||
private :connect
|
private :connect
|
||||||
|
@ -286,55 +286,21 @@ Object
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
class ProtocolError < StandardError ; end
|
class Response
|
||||||
class ProtoSyntaxError < ProtocolError ; end
|
|
||||||
class ProtoFatalError < ProtocolError ; end
|
|
||||||
class ProtoUnknownError < ProtocolError ; end
|
|
||||||
class ProtoServerError < ProtocolError ; end
|
|
||||||
class ProtoAuthError < ProtocolError ; end
|
|
||||||
class ProtoCommandError < ProtocolError ; end
|
|
||||||
class ProtoRetryError < ProtocolError ; end
|
|
||||||
|
|
||||||
class ReplyCode
|
def initialize( ctype, cno, msg )
|
||||||
|
@code_type = ctype
|
||||||
class << self
|
@code = cno
|
||||||
|
@message = msg
|
||||||
def error_type( err )
|
super()
|
||||||
module_eval "def self.get_error_type() #{err.name} end"
|
|
||||||
end
|
|
||||||
|
|
||||||
def error!( mes )
|
|
||||||
raise get_error_type, mes
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
def initialize( cod, mes )
|
|
||||||
@code = cod
|
|
||||||
@msg = mes
|
|
||||||
@data = nil
|
|
||||||
end
|
|
||||||
|
|
||||||
attr_reader :code, :msg
|
|
||||||
|
|
||||||
def []( key )
|
|
||||||
if @data then
|
|
||||||
@data[key]
|
|
||||||
else
|
|
||||||
nil
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def []=( key, val )
|
|
||||||
unless h = @data then
|
|
||||||
@data = h = {}
|
|
||||||
end
|
|
||||||
h[key] = val
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
attr_reader :code_type, :code, :message
|
||||||
|
alias msg message
|
||||||
|
|
||||||
def error!( sending )
|
def error!( sending )
|
||||||
mes = <<MES
|
raise @code_type.error_type,
|
||||||
|
sprintf( <<MSG, @code, Net.quote(sending), Net.quote(@message) )
|
||||||
|
|
||||||
status %s
|
status %s
|
||||||
writing string is:
|
writing string is:
|
||||||
|
@ -342,43 +308,58 @@ writing string is:
|
||||||
|
|
||||||
error message from server is:
|
error message from server is:
|
||||||
%s
|
%s
|
||||||
MES
|
MSG
|
||||||
type.error! sprintf( mes, @code, Net.quote(sending), Net.quote(@msg) )
|
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
class SuccessCode < ReplyCode
|
|
||||||
error_type ProtoUnknownError
|
|
||||||
end
|
|
||||||
|
|
||||||
class ContinueCode < SuccessCode
|
class ProtocolError < StandardError; end
|
||||||
error_type ProtoUnknownError
|
class ProtoSyntaxError < ProtocolError; end
|
||||||
end
|
class ProtoFatalError < ProtocolError; end
|
||||||
|
class ProtoUnknownError < ProtocolError; end
|
||||||
|
class ProtoServerError < ProtocolError; end
|
||||||
|
class ProtoAuthError < ProtocolError; end
|
||||||
|
class ProtoCommandError < ProtocolError; end
|
||||||
|
class ProtoRetriableError < ProtocolError; end
|
||||||
|
ProtocRetryError = ProtoRetriableError
|
||||||
|
|
||||||
class ErrorCode < ReplyCode
|
|
||||||
error_type ProtocolError
|
|
||||||
end
|
|
||||||
|
|
||||||
class SyntaxErrorCode < ErrorCode
|
class Code
|
||||||
error_type ProtoSyntaxError
|
|
||||||
end
|
|
||||||
|
|
||||||
class FatalErrorCode < ErrorCode
|
def initialize( paren, err )
|
||||||
error_type ProtoFatalError
|
@parents = paren
|
||||||
end
|
@err = err
|
||||||
|
|
||||||
class ServerBusyCode < ErrorCode
|
@parents.push self
|
||||||
error_type ProtoServerError
|
end
|
||||||
end
|
|
||||||
|
|
||||||
class RetryCode < ReplyCode
|
attr_reader :parents
|
||||||
error_type ProtoRetryError
|
|
||||||
end
|
|
||||||
|
|
||||||
class UnknownCode < ReplyCode
|
def error_type
|
||||||
error_type ProtoUnknownError
|
@err
|
||||||
|
end
|
||||||
|
|
||||||
|
def ===( response )
|
||||||
|
response.code_type.parents.reverse_each {|i| return true if i == self }
|
||||||
|
false
|
||||||
|
end
|
||||||
|
|
||||||
|
def mkchild( err = nil )
|
||||||
|
type.new( @parents + [self], err || @err )
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
ReplyCode = Code.new( [], ProtoUnknownError )
|
||||||
|
SuccessCode = ReplyCode.mkchild( ProtoUnknownError )
|
||||||
|
ContinueCode = ReplyCode.mkchild( ProtoUnknownError )
|
||||||
|
ErrorCode = ReplyCode.mkchild( ProtocolError )
|
||||||
|
SyntaxErrorCode = ErrorCode.mkchild( ProtoSyntaxError )
|
||||||
|
FatalErrorCode = ErrorCode.mkchild( ProtoFatalError )
|
||||||
|
ServerErrorCode = ErrorCode.mkchild( ProtoServerError )
|
||||||
|
RetriableCode = ReplyCode.mkchild( ProtoRetriableError )
|
||||||
|
UnknownCode = ReplyCode.mkchild( ProtoUnknownError )
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -415,6 +396,7 @@ MES
|
||||||
@addr = addr
|
@addr = addr
|
||||||
@port = port
|
@port = port
|
||||||
@pipe = pipe
|
@pipe = pipe
|
||||||
|
@prepipe = nil
|
||||||
|
|
||||||
@closed = true
|
@closed = true
|
||||||
@ipaddr = ''
|
@ipaddr = ''
|
||||||
|
@ -471,47 +453,49 @@ MES
|
||||||
TERMEXP = /\n|\r\n|\r/o
|
TERMEXP = /\n|\r\n|\r/o
|
||||||
|
|
||||||
|
|
||||||
def read( len, ret = '' )
|
def read( len, dest = '' )
|
||||||
@pipe << "reading #{len} bytes...\n" if pre = @pipe ; @pipe = nil
|
@pipe << "reading #{len} bytes...\n" if @pipe; pipeoff
|
||||||
|
|
||||||
rsize = 0
|
rsize = 0
|
||||||
while rsize + @buffer.size < len do
|
while rsize + @buffer.size < len do
|
||||||
rsize += writeinto( ret, @buffer.size )
|
rsize += writeinto( dest, @buffer.size )
|
||||||
fill_rbuf
|
fill_rbuf
|
||||||
end
|
end
|
||||||
writeinto( ret, len - rsize )
|
writeinto( dest, len - rsize )
|
||||||
|
|
||||||
@pipe << "read #{len} bytes\n" if @pipe = pre
|
@pipe << "read #{len} bytes\n" if pipeon
|
||||||
ret
|
dest
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
def read_all( ret = '' )
|
def read_all( dest = '' )
|
||||||
@pipe << "reading all...\n" if pre = @pipe; @pipe = nil
|
@pipe << "reading all...\n" if @pipe; pipeoff
|
||||||
|
|
||||||
rsize = 0
|
rsize = 0
|
||||||
begin
|
begin
|
||||||
while true do
|
while true do
|
||||||
rsize += writeinto( ret, @buffer.size )
|
rsize += writeinto( dest, @buffer.size )
|
||||||
fill_rbuf
|
fill_rbuf
|
||||||
end
|
end
|
||||||
rescue EOFError
|
rescue EOFError
|
||||||
;
|
;
|
||||||
end
|
end
|
||||||
|
|
||||||
@pipe << "read #{rsize} bytes\n" if @pipe = pre
|
@pipe << "read #{rsize} bytes\n" if pipeon
|
||||||
ret
|
dest
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
def readuntil( target )
|
def readuntil( target )
|
||||||
until idx = @buffer.index( target ) do
|
while true do
|
||||||
|
idx = @buffer.index( target )
|
||||||
|
break if idx
|
||||||
fill_rbuf
|
fill_rbuf
|
||||||
end
|
end
|
||||||
|
|
||||||
ret = ''
|
dest = ''
|
||||||
writeinto( ret, idx + target.size )
|
writeinto( dest, idx + target.size )
|
||||||
ret
|
dest
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
@ -522,8 +506,8 @@ MES
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
def read_pendstr( dest = '' )
|
def read_pendstr( dest )
|
||||||
@pipe << "reading text...\n" if pre = @pipe ; @pipe = nil
|
@pipe << "reading text...\n" if @pipe; pipeoff
|
||||||
|
|
||||||
rsize = 0
|
rsize = 0
|
||||||
|
|
||||||
|
@ -533,17 +517,16 @@ MES
|
||||||
dest << str
|
dest << str
|
||||||
end
|
end
|
||||||
|
|
||||||
@pipe << "read #{rsize} bytes\n" if @pipe = pre
|
@pipe << "read #{rsize} bytes\n" if pipeon
|
||||||
dest
|
dest
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
def read_pendlist
|
def read_pendlist
|
||||||
@pipe << "reading list...\n" if pre = @pipe ; @pipe = nil
|
@pipe << "reading list...\n" if @pipe; pipeoff
|
||||||
|
|
||||||
arr = []
|
arr = []
|
||||||
str = nil
|
str = nil
|
||||||
call = iterator?
|
|
||||||
|
|
||||||
while (str = readuntil( CRLF )) != D_CRLF do
|
while (str = readuntil( CRLF )) != D_CRLF do
|
||||||
str.chop!
|
str.chop!
|
||||||
|
@ -551,7 +534,7 @@ MES
|
||||||
yield str if iterator?
|
yield str if iterator?
|
||||||
end
|
end
|
||||||
|
|
||||||
@pipe << "read #{arr.size} lines\n" if @pipe = pre
|
@pipe << "read #{arr.size} lines\n" if pipeon
|
||||||
arr
|
arr
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -565,12 +548,12 @@ MES
|
||||||
@buffer << @socket.sysread( READ_BLOCK )
|
@buffer << @socket.sysread( READ_BLOCK )
|
||||||
end
|
end
|
||||||
|
|
||||||
def writeinto( ret, len )
|
def writeinto( dest, len )
|
||||||
bsi = @buffer.size
|
bsi = @buffer.size
|
||||||
ret << @buffer[ 0, len ]
|
dest << @buffer[ 0, len ]
|
||||||
@buffer = @buffer[ len, bsi - len ]
|
@buffer = @buffer[ len, bsi - len ]
|
||||||
|
|
||||||
@pipe << %{read "#{Net.quote ret}"\n} if @pipe
|
@pipe << %{read "#{Net.quote dest}"\n} if @pipe
|
||||||
len
|
len
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -593,7 +576,7 @@ MES
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
def write_bin( src, block = nil )
|
def write_bin( src, block )
|
||||||
do_write_beg
|
do_write_beg
|
||||||
if block then
|
if block then
|
||||||
block.call WriteAdapter.new( self, :do_write_do )
|
block.call WriteAdapter.new( self, :do_write_do )
|
||||||
|
@ -606,12 +589,12 @@ MES
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
def write_pendstr( src )
|
def write_pendstr( src, block )
|
||||||
@pipe << "writing text from #{src.type}\n" if pre = @pipe ; @pipe = nil
|
@pipe << "writing text from #{src.type}\n" if @pipe; pipeoff
|
||||||
|
|
||||||
do_write_beg
|
do_write_beg
|
||||||
if iterator? then
|
if block then
|
||||||
yield WriteAdapter.new( self, :write_pendstr_inner )
|
block.call WriteAdapter.new( self, :write_pendstr_inner )
|
||||||
else
|
else
|
||||||
write_pendstr_inner src
|
write_pendstr_inner src
|
||||||
end
|
end
|
||||||
|
@ -619,7 +602,7 @@ MES
|
||||||
do_write_do D_CRLF
|
do_write_do D_CRLF
|
||||||
wsize = do_write_fin
|
wsize = do_write_fin
|
||||||
|
|
||||||
@pipe << "wrote #{wsize} bytes text" if @pipe = pre
|
@pipe << "wrote #{wsize} bytes text" if pipeon
|
||||||
wsize
|
wsize
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -649,11 +632,13 @@ MES
|
||||||
adding( src ) do
|
adding( src ) do
|
||||||
beg = 0
|
beg = 0
|
||||||
buf = @wbuf
|
buf = @wbuf
|
||||||
while pos = buf.index( TERMEXP, beg ) do
|
while true do
|
||||||
|
pos = buf.index( TERMEXP, beg )
|
||||||
|
break unless pos
|
||||||
s = $&.size
|
s = $&.size
|
||||||
break if pos + s == buf.size - 1 and buf[-1] == ?\r
|
break if pos + s == buf.size - 1 and buf[-1] == ?\r
|
||||||
|
|
||||||
send mid, buf[ beg, pos - beg ] << CRLF
|
__send__ mid, buf[ beg, pos - beg ] << CRLF
|
||||||
beg = pos + s
|
beg = pos + s
|
||||||
end
|
end
|
||||||
@wbuf = buf[ beg, buf.size - beg ] if beg != 0
|
@wbuf = buf[ beg, buf.size - beg ] if beg != 0
|
||||||
|
@ -671,7 +656,9 @@ MES
|
||||||
end
|
end
|
||||||
|
|
||||||
when File
|
when File
|
||||||
while i = src.read( 512 ) do
|
while true do
|
||||||
|
i = src.read( 512 )
|
||||||
|
break unless i
|
||||||
@wbuf << i
|
@wbuf << i
|
||||||
yield
|
yield
|
||||||
end
|
end
|
||||||
|
@ -691,8 +678,10 @@ MES
|
||||||
buf << "\n" unless /\n|\r/o === buf[-1,1]
|
buf << "\n" unless /\n|\r/o === buf[-1,1]
|
||||||
|
|
||||||
beg = 0
|
beg = 0
|
||||||
while pos = buf.index( TERMEXP, beg ) do
|
while true do
|
||||||
send mid, buf[ beg, pos - beg ] << CRLF
|
pos = buf.index( TERMEXP, beg )
|
||||||
|
break unless pos
|
||||||
|
__send__ mid, buf[ beg, pos - beg ] << CRLF
|
||||||
beg = pos + $&.size
|
beg = pos + $&.size
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -727,6 +716,19 @@ MES
|
||||||
@writtensize
|
@writtensize
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
def pipeoff
|
||||||
|
@prepipe = @pipe
|
||||||
|
@pipe = nil
|
||||||
|
@prepipe
|
||||||
|
end
|
||||||
|
|
||||||
|
def pipeon
|
||||||
|
@pipe = @prepipe
|
||||||
|
@prepipe = nil
|
||||||
|
@pipe
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -76,12 +76,12 @@ Net::Protocol
|
||||||
|
|
||||||
def sendmail( mailsrc, fromaddr, toaddrs )
|
def sendmail( mailsrc, fromaddr, toaddrs )
|
||||||
do_ready fromaddr, toaddrs
|
do_ready fromaddr, toaddrs
|
||||||
@command.write_mail mailsrc
|
@command.write_mail mailsrc, nil
|
||||||
end
|
end
|
||||||
|
|
||||||
def ready( fromaddr, toaddrs, &block )
|
def ready( fromaddr, toaddrs, &block )
|
||||||
do_ready fromaddr, toaddrs
|
do_ready fromaddr, toaddrs
|
||||||
@command.write_mail( &block )
|
@command.write_mail nil, block
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
@ -163,8 +163,8 @@ Net::Protocol
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
def write_mail( mailsrc = nil, &block )
|
def write_mail( mailsrc, block )
|
||||||
@socket.write_pendstr mailsrc, &block
|
@socket.write_pendstr mailsrc, block
|
||||||
check_reply SuccessCode
|
check_reply SuccessCode
|
||||||
end_critical
|
end_critical
|
||||||
end
|
end
|
||||||
|
@ -189,7 +189,7 @@ Net::Protocol
|
||||||
klass = case stat[0]
|
klass = case stat[0]
|
||||||
when ?2 then SuccessCode
|
when ?2 then SuccessCode
|
||||||
when ?3 then ContinueCode
|
when ?3 then ContinueCode
|
||||||
when ?4 then ServerBusyCode
|
when ?4 then ServerErrorCode
|
||||||
when ?5 then
|
when ?5 then
|
||||||
case stat[1]
|
case stat[1]
|
||||||
when ?0 then SyntaxErrorCode
|
when ?0 then SyntaxErrorCode
|
||||||
|
@ -197,7 +197,7 @@ Net::Protocol
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
klass.new( stat, arr.join('') )
|
Response.new( klass, stat, arr.join('') )
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
Загрузка…
Ссылка в новой задаче