class HTTP::Message::Body
Represents HTTP message body.
Constants
- DEFAULT_CHUNK_SIZE
Default value for #chunk_size
Attributes
maxbytes of IO#read for streaming request. See DEFAULT_CHUNK_SIZE.
Hash that keeps IO positions
Size of body. nil when size is unknown (e.g. chunked response).
Public Class Methods
Creates a Message::Body. Use #init_request or #init_response for acutual initialize.
# File lib/httpclient/http.rb, line 455 def initialize @body = nil @size = nil @positions = nil @chunk_size = nil end
Public Instance Methods
Returns a message body itself.
# File lib/httpclient/http.rb, line 544 def content @body 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
# File lib/httpclient/http.rb, line 493 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, @body.sizes[part]) else dev << part end end elsif Message.file?(@body) dev << header reset_pos(@body) dump_file(@body, dev, @size) elsif @body dev << header + @body else dev << header end dev end
Dumps message body with chunked encoding 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.
# File lib/httpclient/http.rb, line 523 def dump_chunked(header = '', dev = '') dev << header if @body.is_a?(Parts) @body.parts.each do |part| if Message.file?(part) reset_pos(part) dump_chunks(part, dev) else dev << dump_chunk(part) end end dev << (dump_last_chunk + CRLF) elsif @body reset_pos(@body) dump_chunks(@body, dev) dev << (dump_last_chunk + CRLF) end dev end
Initialize this instance as a request.
# File lib/httpclient/http.rb, line 463 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.
# File lib/httpclient/http.rb, line 472 def init_response(body = nil) @body = body if @body.respond_to?(:bytesize) @size = @body.bytesize elsif @body.respond_to?(:size) @size = @body.size else @size = nil end self end
Private Instance Methods
# File lib/httpclient/http.rb, line 662 def build_query_multipart_str(query, boundary) parts = Parts.new query.each do |attr, value| headers = ["--#{boundary}"] if Message.file?(value) remember_pos(value) param_str = params_from_file(value).collect { |k, v| "#{k}=\"#{v}\"" }.join("; ") if value.respond_to?(:mime_type) content_type = value.mime_type elsif value.respond_to?(:content_type) content_type = value.content_type else path = value.respond_to?(:path) ? value.path : nil content_type = Message.mime_type(path) end headers << %Q{Content-Disposition: form-data; name="#{attr}"; #{param_str}} headers << %Q{Content-Type: #{content_type}} elsif attr.is_a?(Hash) h = attr value = h[:content] h.each do |h_key, h_val| headers << %Q{#{h_key}: #{h_val}} if h_key != :content end remember_pos(value) if Message.file?(value) else headers << %Q{Content-Disposition: form-data; name="#{attr}"} value = value.to_s end parts.add(headers.join(CRLF) + CRLF + CRLF) parts.add(value) parts.add(CRLF) end parts.add("--#{boundary}--" + CRLF + CRLF) # empty epilogue parts end
# File lib/httpclient/http.rb, line 593 def dump_chunk(str) dump_chunk_size(str.bytesize) + (str + CRLF) end
# File lib/httpclient/http.rb, line 601 def dump_chunk_size(size) sprintf("%x", size) + CRLF end
# File lib/httpclient/http.rb, line 586 def dump_chunks(io, dev) buf = '' while !io.read(@chunk_size, buf).nil? dev << dump_chunk(buf) end end
# File lib/httpclient/http.rb, line 575 def dump_file(io, dev, sz) buf = '' 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
# File lib/httpclient/http.rb, line 597 def dump_last_chunk dump_chunk_size(0) end
# File lib/httpclient/http.rb, line 700 def params_from_file(value) params = {} path = value.respond_to?(:path) ? value.path : nil params['filename'] = File.basename(path || '') # Creation time is not available from File::Stat if value.respond_to?(:mtime) params['modification-date'] = value.mtime.rfc822 end if value.respond_to?(:atime) params['read-date'] = value.atime.rfc822 end params end
# File lib/httpclient/http.rb, line 566 def remember_pos(io) # IO may not support it (ex. IO.pipe) @positions[io] = io.pos if io.respond_to?(:pos) end
# File lib/httpclient/http.rb, line 571 def reset_pos(io) io.pos = @positions[io] if @positions.key?(io) end
# File lib/httpclient/http.rb, line 550 def set_content(body, boundary = nil) if Message.file?(body) # uses Transfer-Encoding: chunked if body does not respond to :size. # bear in mind that server may not support it. at least ruby's CGI doesn't. @body = body remember_pos(@body) @size = body.respond_to?(:size) ? body.size - body.pos : nil elsif boundary and Message.multiparam_query?(body) @body = build_query_multipart_str(body, boundary) @size = @body.size else @body = Message.create_query_part_str(body) @size = @body.bytesize end end