lib/httpclient/http.rb in httpclient-2.5.3.3 vs lib/httpclient/http.rb in httpclient-2.6.0
- old
+ new
@@ -196,10 +196,11 @@
@is_request = true
@request_method = method
@request_uri = uri || NIL_URI
@request_query = query
@request_absolute_uri = false
+ self
end
# Initialize this instance as a response.
def init_response(status_code, req = nil)
@is_request = false
@@ -207,10 +208,11 @@
if req
@request_method = req.request_method
@request_uri = req.request_uri
@request_query = req.request_query
end
+ self
end
# Sets status code and reason phrase.
def status_code=(status_code)
@status_code = status_code
@@ -439,10 +441,12 @@
class Body
# Size of body. nil when size is unknown (e.g. chunked response).
attr_reader :size
# maxbytes of IO#read for streaming request. See DEFAULT_CHUNK_SIZE.
attr_accessor :chunk_size
+ # Hash that keeps IO positions
+ attr_accessor :positions
# Default value for chunk_size
DEFAULT_CHUNK_SIZE = 1024 * 16
# Creates a Message::Body. Use init_request or init_response
@@ -458,10 +462,11 @@
def init_request(body = nil, boundary = nil)
@boundary = boundary
@positions = {}
set_content(body, boundary)
@chunk_size = DEFAULT_CHUNK_SIZE
+ self
end
# Initialize this instance as a response.
def init_response(body = nil)
@body = body
@@ -470,34 +475,37 @@
elsif @body.respond_to?(:size)
@size = @body.size
else
@size = nil
end
+ self
end
# Dumps message body to given dev.
# dev needs to respond to <<.
#
# Message header must be given as the first argument for performance
# reason. (header is dumped to dev, too)
# If no dev (the second argument) given, this method returns a dumped
# String.
+ #
+ # assert: @size is not nil
def dump(header = '', dev = '')
if @body.is_a?(Parts)
dev << header
@body.parts.each do |part|
if Message.file?(part)
reset_pos(part)
- dump_file(part, dev)
+ dump_file(part, dev, @body.sizes[part])
else
dev << part
end
end
elsif Message.file?(@body)
dev << header
reset_pos(@body)
- dump_file(@body, dev)
+ dump_file(@body, dev, @size)
elsif @body
dev << header + @body
else
dev << header
end
@@ -561,14 +569,18 @@
def reset_pos(io)
io.pos = @positions[io] if @positions.key?(io)
end
- def dump_file(io, dev)
+ def dump_file(io, dev, sz)
buf = ''
- while !io.read(@chunk_size, buf).nil?
+ rest = sz
+ while rest > 0
+ n = io.read([rest, @chunk_size].min, buf)
+ raise ArgumentError.new("Illegal size value: #size returns #{sz} but cannot read") if n.nil?
dev << buf
+ rest -= n.bytesize
end
end
def dump_chunks(io, dev)
buf = ''
@@ -589,31 +601,36 @@
sprintf("%x", size) + CRLF
end
class Parts
attr_reader :size
+ attr_reader :sizes
def initialize
@body = []
- @size = 0
+ @sizes = {}
+ @size = 0 # total
@as_stream = false
end
def add(part)
if Message.file?(part)
@as_stream = true
@body << part
if part.respond_to?(:lstat)
- @size += part.lstat.size
+ sz = part.lstat.size
+ add_size(part, sz)
elsif part.respond_to?(:size)
if sz = part.size
- @size += sz
+ add_size(part, sz)
else
+ @sizes.clear
@size = nil
end
else
# use chunked upload
+ @sizes.clear
@size = nil
end
elsif @body[-1].is_a?(String)
@body[-1] += part.to_s
@size += part.to_s.bytesize if @size
@@ -628,10 +645,19 @@
@body
else
[@body.join]
end
end
+
+ private
+
+ def add_size(part, sz)
+ if @size
+ @sizes[part] = sz
+ @size += sz
+ end
+ end
end
def build_query_multipart_str(query, boundary)
parts = Parts.new
query.each do |attr, value|
@@ -906,16 +932,21 @@
# OpenSSL::X509::Certificate:: response only. server certificate which is
# used for retrieving the response.
attr_accessor :peer_cert
+ # The other Message object when this Message is generated instead of
+ # the Message because of redirection, negotiation, or format conversion.
+ attr_accessor :previous
+
# Creates a Message. This method should be used internally.
# Use Message.new_connect_request, Message.new_request or
# Message.new_response instead.
def initialize # :nodoc:
@http_header = Headers.new
@http_body = @peer_cert = nil
+ @previous = nil
end
# Dumps message (header and body) to given dev.
# dev needs to respond to <<.
def dump(dev = '')
@@ -1021,13 +1052,11 @@
def cookies
set_cookies = http_header['set-cookie']
unless set_cookies.empty?
uri = http_header.request_uri
set_cookies.map { |str|
- cookie = WebAgent::Cookie.new
- cookie.parse(str, uri)
- cookie
- }
+ WebAgent::Cookie.parse(str, uri)
+ }.flatten
end
end
# Convenience method to return boolean of whether we had a successful request
def ok?