Module: LongBody::HijackHandler

Extended by:
HijackHandler
Included in:
HijackHandler
Defined in:
lib/long_body/hijack_handler.rb

Overview

The problem with fast each() bodies is that the Ruby process will enter a busy wait if the write socket for the webserver is saturated.

We can bypass that by releasing the CPU using a select(), but for that we have to use “rack.hijack” in combination with a nonblocking write.

For more on this: apidock.com/ruby/IO/write_nonblock old.blog.phusion.nl/2013/01/23/the-new-rack-socket-hijacking-api/

Constant Summary collapse

HIJACK_HEADER =
'rack.hijack'.freeze

Instance Method Summary collapse

Instance Method Details

#create_socket_writer_lambda_with_body(rack_response_body) ⇒ Object



20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
# File 'lib/long_body/hijack_handler.rb', line 20

def create_socket_writer_lambda_with_body(rack_response_body)
  lambda do |socket|
    begin
      rack_response_body.each do | chunk |
        begin
          num_bytes_written = socket.write_nonblock(chunk)
          # If we could write only partially, make sure we do a retry on the next
          # iteration with the remaining part
          if num_bytes_written < chunk.bytesize
            chunk = chunk[num_bytes_written..-1]
            raise Errno::EINTR
          end
        rescue IO::WaitWritable, Errno::EINTR # The output socket is saturated.
          # If we are running within a threaded server, 
          # let another thread preempt here. We are waiting for IO
          # and some other thread might have things to do here.
          IO.select(nil, [socket]) # ...then wait on the socket to be writable again
          retry # and off we go...
        rescue Errno::EPIPE, Errno::EPROTOTYPE # Happens when the client aborts the connection
          return
        end
      end
    ensure
      rack_response_body.close if rack_response_body.respond_to?(:close)
      socket.close if socket.respond_to?(:closed?) && socket.respond_to?(:close) && !socket.closed?
    end
  end
end

#perform(env, s, h, b) ⇒ Object



14
15
16
17
18
# File 'lib/long_body/hijack_handler.rb', line 14

def perform(env, s, h, b)
  # Replace the output with our socket moderating technology 2.0
  h[HIJACK_HEADER] = create_socket_writer_lambda_with_body(b)
  [s, h, []] # Recommended response body for partial hijack is an empty Array
end