Class: HTTP::Request::Writer

Inherits:
Object
  • Object
show all
Defined in:
lib/http/request/writer.rb

Constant Summary collapse

CRLF =

CRLF is the universal HTTP delimiter

"\r\n"
ZERO =

Chunked data termintaor.

"0"
CHUNKED =

Chunked transfer encoding

"chunked"
CHUNKED_END =

End of a chunked transfer

"#{ZERO}#{CRLF}#{CRLF}"

Instance Method Summary collapse

Constructor Details

#initialize(socket, body, headers, headline) ⇒ Writer

Returns a new instance of Writer.



20
21
22
23
24
25
# File 'lib/http/request/writer.rb', line 20

def initialize(socket, body, headers, headline)
  @body           = body
  @socket         = socket
  @headers        = headers
  @request_header = [headline]
end

Instance Method Details

#add_body_type_headersObject

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



49
50
51
52
53
54
55
56
57
# 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_headersObject

Adds headers to the request header from the headers array



28
29
30
31
32
# File 'lib/http/request/writer.rb', line 28

def add_headers
  @headers.each do |field, value|
    @request_header << "#{field}: #{value}"
  end
end

#chunked?Boolean

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

Returns:

  • (Boolean)


105
106
107
# File 'lib/http/request/writer.rb', line 105

def chunked?
  @headers[Headers::TRANSFER_ENCODING] == CHUNKED
end

#connect_through_proxyObject

Send headers needed to connect through proxy



42
43
44
45
# File 'lib/http/request/writer.rb', line 42

def connect_through_proxy
  add_headers
  write(join_headers)
end

#each_chunk {|data| ... } ⇒ Object

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.

Yields:

  • (data)


81
82
83
84
85
86
87
88
89
90
91
92
93
# 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) ⇒ Object

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



96
97
98
99
100
101
102
# 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_headersObject

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



61
62
63
64
65
# 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_requestObject

Writes HTTP request data into the socket.



68
69
70
71
72
73
# 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

#streamObject

Stream the request to a socket



35
36
37
38
39
# File 'lib/http/request/writer.rb', line 35

def stream
  add_headers
  add_body_type_headers
  send_request
end