class HTTP::Request::Writer

Constants

CHUNKED

Chunked transfer encoding

CHUNKED_END

End of a chunked transfer

CRLF

CRLF is the universal HTTP delimiter

ZERO

Chunked data termintaor.

Public Class Methods

new(socket, body, headers, headline) click to toggle source
# File lib/http/request/writer.rb, line 20
def initialize(socket, body, headers, headline)
  @body           = body
  @socket         = socket
  @headers        = headers
  @request_header = [headline]
end

Public Instance Methods

add_body_type_headers() click to toggle source

Adds the headers to the header array for the given request body we are working with

# File lib/http/request/writer.rb, line 49
def add_body_type_headers
  return if @headers[Headers::CONTENT_LENGTH] || chunked? || (
    @body.source.nil? && %w[GET HEAD DELETE CONNECT].any? do |method|
      @request_header[0].start_with?("#{method} ")
    end
  )

  @request_header << "#{Headers::CONTENT_LENGTH}: #{@body.size}"
end
add_headers() click to toggle source

Adds headers to the request header from the headers array

# File lib/http/request/writer.rb, line 28
def add_headers
  @headers.each do |field, value|
    @request_header << "#{field}: #{value}"
  end
end
chunked?() click to toggle source

Returns true if the request should be sent in chunked encoding.

# File lib/http/request/writer.rb, line 105
def chunked?
  @headers[Headers::TRANSFER_ENCODING] == CHUNKED
end
connect_through_proxy() click to toggle source

Send headers needed to connect through proxy

# File lib/http/request/writer.rb, line 42
def connect_through_proxy
  add_headers
  write(join_headers)
end
each_chunk() { |data| ... } click to toggle source

Yields chunks of request data that should be sent to the socket.

It’s important to send the request in a single write call when possible in order to play nicely with Nagle’s algorithm. Making two writes in a row triggers a pathological case where Nagle is expecting a third write that never happens.

# File lib/http/request/writer.rb, line 81
def each_chunk
  data = join_headers

  @body.each do |chunk|
    data << encode_chunk(chunk)
    yield data
    data.clear
  end

  yield data unless data.empty?

  yield CHUNKED_END if chunked?
end
encode_chunk(chunk) click to toggle source

Returns the chunk encoded for to the specified “Transfer-Encoding” header.

# File lib/http/request/writer.rb, line 96
def encode_chunk(chunk)
  if chunked?
    chunk.bytesize.to_s(16) << CRLF << chunk << CRLF
  else
    chunk
  end
end
join_headers() click to toggle source

Joins the headers specified in the request into a correctly formatted http request header string

# File lib/http/request/writer.rb, line 61
def join_headers
  # join the headers array with crlfs, stick two on the end because
  # that ends the request header
  @request_header.join(CRLF) + (CRLF * 2)
end
send_request() click to toggle source

Writes HTTP request data into the socket.

# File lib/http/request/writer.rb, line 68
def send_request
  each_chunk { |chunk| write chunk }
rescue Errno::EPIPE
  # server doesn't need any more data
  nil
end
stream() click to toggle source

Stream the request to a socket

# File lib/http/request/writer.rb, line 35
def stream
  add_headers
  add_body_type_headers
  send_request
end

Private Instance Methods

write(data) click to toggle source
# File lib/http/request/writer.rb, line 111
def write(data)
  until data.empty?
    length = @socket.write(data)
    break unless data.bytesize > length

    data = data.byteslice(length..-1)
  end
rescue Errno::EPIPE
  raise
rescue IOError, SocketError, SystemCallError => e
  raise ConnectionError, "error writing to socket: #{e}", e.backtrace
end