Class PhusionPassenger::Utils::UnseekableSocket |
In: | lib/phusion_passenger/utils/unseekable_socket.rb
Parent: | Object

Some frameworks (e.g. Merb) call seek and rewind on the input stream if it responds to these methods. In case of Phusion Passenger, the input stream is a socket, and altough socket objects respond to seek and rewind, calling these methods will raise an exception. We don‘t want this to happen so in AbstractRequestHandler we wrap the client socket into an UnseekableSocket wrapper, which doesn‘t respond to these methods.

We used to dynamically undef seek and rewind on sockets, but this blows the Ruby interpreter‘s method cache and made things slower. Wrapping a socket is faster despite extra method calls.

Furthermore, all exceptions originating from the wrapped socket will be annotated. One can check whether a certain exception originates from the wrapped socket by calling #source_of_exception?

Methods

- addr
- binmode
- close
- close_read
- close_write
- closed?
- each
- flush
- gets
- puts
- read
- readline
- readpartial
- source_of_exception?
- sync=
- to_io
- wrap
- wrap
- write
- writev
- writev2
- writev3

Public Class methods
wrap(socket)

[ show source ]

        # File lib/phusion_passenger/utils/unseekable_socket.rb, line 44
    44:         def self.wrap(socket)
    45:                 return new.wrap(socket)
    46:         end

Public Instance methods
addr()

[ show source ]

        # File lib/phusion_passenger/utils/unseekable_socket.rb, line 94
    94:         def addr
    95:                 @socket.addr
    96:         rescue => e
    97:                 raise annotate(e)
    98:         end

binmode()

Already set to binary mode.

[ show source ]

        # File lib/phusion_passenger/utils/unseekable_socket.rb, line 87
    87:         def binmode
    88:         end

close()

[ show source ]

         # File lib/phusion_passenger/utils/unseekable_socket.rb, line 166
    166:         def close
    167:                 @socket.close
    168:         rescue => e
    169:                 raise annotate(e)
    170:         end

close_read()

[ show source ]

         # File lib/phusion_passenger/utils/unseekable_socket.rb, line 172
    172:         def close_read
    173:                 @socket.close_read
    174:         rescue => e
    175:                 raise annotate(e)
    176:         end

close_write()

[ show source ]

         # File lib/phusion_passenger/utils/unseekable_socket.rb, line 178
    178:         def close_write
    179:                 @socket.close_write
    180:         rescue => e
    181:                 raise annotate(e)
    182:         end

closed?()

[ show source ]

         # File lib/phusion_passenger/utils/unseekable_socket.rb, line 160
    160:         def closed?
    161:                 @socket.closed?
    162:         rescue => e
    163:                 raise annotate(e)
    164:         end

each(&block)

[ show source ]

         # File lib/phusion_passenger/utils/unseekable_socket.rb, line 154
    154:         def each(&block)
    155:                 @socket.each(&block)
    156:         rescue => e
    157:                 raise annotate(e)
    158:         end

flush()

Socket is sync‘ed so flushing shouldn‘t do anything.

[ show source ]

        # File lib/phusion_passenger/utils/unseekable_socket.rb, line 83
    83:         def flush
    84:         end

gets()

[ show source ]

         # File lib/phusion_passenger/utils/unseekable_socket.rb, line 130
    130:         def gets
    131:                 @socket.gets
    132:         rescue => e
    133:                 raise annotate(e)
    134:         end

puts(*args)

[ show source ]

         # File lib/phusion_passenger/utils/unseekable_socket.rb, line 124
    124:         def puts(*args)
    125:                 @socket.puts(*args)
    126:         rescue => e
    127:                 raise annotate(e)
    128:         end

read(*args)

[ show source ]

         # File lib/phusion_passenger/utils/unseekable_socket.rb, line 136
    136:         def read(*args)
    137:                 @socket.read(*args)
    138:         rescue => e
    139:                 raise annotate(e)
    140:         end

readline()

[ show source ]

         # File lib/phusion_passenger/utils/unseekable_socket.rb, line 148
    148:         def readline
    149:                 @socket.readline
    150:         rescue => e
    151:                 raise annotate(e)
    152:         end

readpartial(*args)

[ show source ]

         # File lib/phusion_passenger/utils/unseekable_socket.rb, line 142
    142:         def readpartial(*args)
    143:                 @socket.readpartial(*args)
    144:         rescue => e
    145:                 raise annotate(e)
    146:         end

source_of_exception?(exception)

[ show source ]

         # File lib/phusion_passenger/utils/unseekable_socket.rb, line 184
    184:         def source_of_exception?(exception)
    185:                 return exception.instance_variable_get("@from_unseekable_socket""@from_unseekable_socket") == @socket.object_id
    186:         end

sync=(value)

Don‘t allow disabling of sync.

[ show source ]

        # File lib/phusion_passenger/utils/unseekable_socket.rb, line 79
    79:         def sync=(value)
    80:         end

to_io()

[ show source ]

        # File lib/phusion_passenger/utils/unseekable_socket.rb, line 90
    90:         def to_io
    91:                 self
    92:         end

wrap(socket)

[ show source ]

        # File lib/phusion_passenger/utils/unseekable_socket.rb, line 48
    48:         def wrap(socket)
    49:                 # Some people report that sometimes their Ruby (MRI/REE)
    50:                 # processes get stuck with 100% CPU usage. Upon further
    51:                 # inspection with strace, it turns out that these Ruby
    52:                 # processes are continuously calling lseek() on a socket,
    53:                 # which of course returns ESPIPE as error. gdb reveals
    54:                 # lseek() is called by fwrite(), which in turn is called
    55:                 # by rb_fwrite(). The affected socket is the
    56:                 # AbstractRequestHandler client socket.
    57:                 #
    58:                 # I inspected the MRI source code and didn't find
    59:                 # anything that would explain this behavior. This makes
    60:                 # me think that it's a glibc bug, but that's very
    61:                 # unlikely.
    62:                 #
    63:                 # The rb_fwrite() implementation takes an entirely
    64:                 # different code path if I set 'sync' to true: it will
    65:                 # skip fwrite() and use write() instead. So here we set
    66:                 # 'sync' to true in the hope that this will work around
    67:                 # the problem.
    68:                 socket.sync = true
    69:
    70:                 # There's no need to set the encoding for Ruby 1.9 because
    71:                 # abstract_request_handler.rb is tagged with 'encoding: binary'.
    72:
    73:                 @socket = socket
    74:
    75:                 return self
    76:         end

write(string)

[ show source ]

         # File lib/phusion_passenger/utils/unseekable_socket.rb, line 100
    100:         def write(string)
    101:                 @socket.write(string)
    102:         rescue => e
    103:                 raise annotate(e)
    104:         end

writev(components)

[ show source ]

         # File lib/phusion_passenger/utils/unseekable_socket.rb, line 106
    106:         def writev(components)
    107:                 @socket.writev(components)
    108:         rescue => e
    109:                 raise annotate(e)
    110:         end

writev2(components, components2)

[ show source ]

         # File lib/phusion_passenger/utils/unseekable_socket.rb, line 112
    112:         def writev2(components, components2)
    113:                 @socket.writev2(components, components2)
    114:         rescue => e
    115:                 raise annotate(e)
    116:         end

writev3(components, components2, components3)

[ show source ]

         # File lib/phusion_passenger/utils/unseekable_socket.rb, line 118
    118:         def writev3(components, components2, components3)
    119:                 @socket.writev3(components, components2, components3)
    120:         rescue => e
    121:                 raise annotate(e)
    122:         end