Class: HTTP::Request::Writer
- Inherits:
-
Object
- Object
- HTTP::Request::Writer
- 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
"\r\n"- ZERO =
Chunked data terminator
"0"- CHUNKED_END =
End of a chunked transfer
"#{ZERO}#{CRLF}#{CRLF}".freeze
Instance Method Summary collapse
-
#add_body_type_headers ⇒ void
Adds content length or transfer encoding headers.
-
#add_headers ⇒ void
Adds headers to the request header array.
-
#chunked? ⇒ Boolean
Returns true if using chunked transfer encoding.
-
#connect_through_proxy ⇒ void
Send headers needed to connect through proxy.
-
#each_chunk {|data| ... } ⇒ void
Yields chunks of request data for the socket.
-
#encode_chunk(chunk) ⇒ String
Returns chunk encoded per Transfer-Encoding header.
-
#initialize(socket, body, headers, headline) ⇒ HTTP::Request::Writer
constructor
Initialize a new request writer.
-
#join_headers ⇒ String
Joins headers into an HTTP request header string.
-
#send_request ⇒ void
Writes HTTP request data into the socket.
-
#stream ⇒ void
Stream the request to a socket.
-
#write(data) ⇒ void
private
Write data to the underlying socket.
Constructor Details
#initialize(socket, body, headers, headline) ⇒ HTTP::Request::Writer
Initialize a new request writer
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_headers ⇒ void
This method returns an undefined value.
Adds content length or transfer encoding 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_headers ⇒ void
This method returns an undefined value.
Adds headers to the request header array
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
162 163 164 |
# File 'lib/http/request/writer.rb', line 162 def chunked? @headers[Headers::TRANSFER_ENCODING].eql?(Headers::CHUNKED) end |
#connect_through_proxy ⇒ void
This method returns an undefined value.
Send headers needed to 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.
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
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_headers ⇒ String
Joins headers into an HTTP request header 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_request ⇒ void
This method returns an undefined value.
Writes HTTP request data into the socket
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 |
#stream ⇒ void
This method returns an undefined value.
Stream the request to a socket
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
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 |