class HTTP::Message::Body

Represents HTTP message body.

def build_query_multipart_str(query, boundary)

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 << %{Content-Disposition: form-data; name="#{attr}"; #{param_str}}
      headers << %{Content-Type: #{content_type}}
    elsif attr.is_a?(Hash)
      h = attr
      value = h[:content]
      h.each do |h_key, h_val|
        headers << %{#{h_key}: #{h_val}} if h_key != :content
      end
      remember_pos(value) if Message.file?(value)
    else
      headers << %{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

def content

Returns a message body itself.
def content
  @body
end

def dump(header = '', dev = ''.dup)

assert: @size is not nil

String.
If no dev (the second argument) given, this method returns a dumped
reason. (header is dumped to dev, too)
Message header must be given as the first argument for performance

dev needs to respond to <<.
Dumps message body to given dev.
def dump(header = '', dev = ''.dup)
  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

def dump_chunk(str)

def dump_chunk(str)
  dump_chunk_size(str.bytesize) + (str + CRLF)
end

def dump_chunk_size(size)

def dump_chunk_size(size)
  sprintf("%x", size) + CRLF
end

def dump_chunked(header = '', dev = ''.dup)

String.
If no dev (the second argument) given, this method returns a dumped
reason. (header is dumped to dev, too)
Message header must be given as the first argument for performance

dev needs to respond to <<.
Dumps message body with chunked encoding to given dev.
def dump_chunked(header = '', dev = ''.dup)
  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

def dump_chunks(io, dev)

def dump_chunks(io, dev)
  buf = ''.dup
  while !io.read(@chunk_size, buf).nil?
    dev << dump_chunk(buf)
  end
end

def dump_file(io, dev, sz)

def dump_file(io, dev, sz)
  buf = ''.dup
  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_last_chunk

def dump_last_chunk
  dump_chunk_size(0)
end

def init_request(body = nil, boundary = nil)

Initialize this instance as a request.
def init_request(body = nil, boundary = nil)
  @boundary = boundary
  @positions = {}
  set_content(body, boundary)
  @chunk_size = DEFAULT_CHUNK_SIZE
  self
end

def init_response(body = nil)

Initialize this instance as a response.
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

def initialize

for acutual initialize.
Creates a Message::Body. Use init_request or init_response
def initialize
  @body = nil
  @size = nil
  @positions = nil
  @chunk_size = nil
end

def params_from_file(value)

def params_from_file(value)
  params = {}
  original_filename = value.respond_to?(:original_filename) ? value.original_filename : nil
  path = value.respond_to?(:path) ? value.path : nil
  params['filename'] = original_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

def remember_pos(io)

def remember_pos(io)
  # IO may not support it (ex. IO.pipe)
  @positions[io] = io.pos if io.respond_to?(:pos)
end

def reset_pos(io)

def reset_pos(io)
  io.pos = @positions[io] if @positions.key?(io)
end

def set_content(body, boundary = nil)

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