Class: HTTP::Request::Writer

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

Overview

Streams HTTP requests to a socket

Constant Summary collapse

CRLF =

CRLF is the universal HTTP delimiter

Returns:

  • (String)
"\r\n"
ZERO =

Chunked data terminator

Returns:

  • (String)
"0"
CHUNKED_END =

End of a chunked transfer

Returns:

  • (String)
"#{ZERO}#{CRLF}#{CRLF}".freeze

Instance Method Summary collapse

Constructor Details

#initialize(socket, body, headers, headline) ⇒ HTTP::Request::Writer

Initialize a new request writer

Examples:

Writer.new(socket, body, headers, "GET / HTTP/1.1")

Parameters:



25
26
27
28
29
30
# File 'lib/http/request/writer.rb', line 25

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

Instance Method Details

#add_body_type_headersvoid

This method returns an undefined value.

Adds content length or transfer encoding headers

Examples:

writer.add_body_type_headers


77
78
79
80
81
82
83
84
85
# File 'lib/http/request/writer.rb', line 77

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.fetch(0).start_with?("#{method} ")
    end
  )

  @request_header << "#{Headers::CONTENT_LENGTH}: #{@body.size}"
end

#add_headersvoid

This method returns an undefined value.

Adds headers to the request header array

Examples:

writer.add_headers


39
40
41
42
43
# File 'lib/http/request/writer.rb', line 39

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

#chunked?Boolean

Returns true if using chunked transfer encoding

Examples:

writer.chunked?

Returns:

  • (Boolean)


162
163
164
# File 'lib/http/request/writer.rb', line 162

def chunked?
  @headers[Headers::TRANSFER_ENCODING].eql?(Headers::CHUNKED)
end

#connect_through_proxyvoid

This method returns an undefined value.

Send headers needed to connect through proxy

Examples:

writer.connect_through_proxy


65
66
67
68
# File 'lib/http/request/writer.rb', line 65

def connect_through_proxy
  add_headers
  write(join_headers)
end

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

This method returns an undefined value.

Yields chunks of request data for 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.

Examples:

writer.each_chunk { |chunk| socket.write(chunk) }

Yields:

  • (data)

Yield Parameters:

  • arg0 (String)

Yield Returns:

  • (void)


126
127
128
129
130
131
132
133
134
135
136
137
138
# File 'lib/http/request/writer.rb', line 126

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) ⇒ String

Returns chunk encoded per Transfer-Encoding header

Examples:

writer.encode_chunk("hello")

Parameters:

  • chunk (String)

Returns:

  • (String)


147
148
149
150
151
152
153
# File 'lib/http/request/writer.rb', line 147

def encode_chunk(chunk)
  if chunked?
    chunk.bytesize.to_s(16) << CRLF << chunk << CRLF
  else
    chunk
  end
end

#join_headersString

Joins headers into an HTTP request header string

Examples:

writer.join_headers

Returns:

  • (String)


94
95
96
97
98
# File 'lib/http/request/writer.rb', line 94

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_requestvoid

This method returns an undefined value.

Writes HTTP request data into the socket

Examples:

writer.send_request


107
108
109
110
111
112
# File 'lib/http/request/writer.rb', line 107

def send_request
  each_chunk { |chunk| write chunk }
rescue Errno::EPIPE
  # server doesn't need any more data
  nil
end

#streamvoid

This method returns an undefined value.

Stream the request to a socket

Examples:

writer.stream


52
53
54
55
56
# File 'lib/http/request/writer.rb', line 52

def stream
  add_headers
  add_body_type_headers
  send_request
end

#write(data) ⇒ void

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

This method returns an undefined value.

Write data to the underlying socket

Parameters:

  • data (String)

Raises:



172
173
174
175
176
177
178
179
180
181
182
183
# File 'lib/http/request/writer.rb', line 172

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 SocketWriteError, "error writing to socket: #{e}", e.backtrace
end