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?
- 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
[ show source ]
# File lib/phusion_passenger/utils/unseekable_socket.rb, line 44 44: def self.wrap(socket) 45: return new.wrap(socket) 46: end
[ 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
Already set to binary mode.
[ show source ]
# File lib/phusion_passenger/utils/unseekable_socket.rb, line 87 87: def binmode 88: end
[ 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
[ 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
[ 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
[ 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
[ 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
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
[ 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
[ 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
[ 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
[ 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
[ 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
[ 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
Don‘t allow disabling of sync.
[ show source ]
# File lib/phusion_passenger/utils/unseekable_socket.rb, line 79 79: def sync=(value) 80: end
[ show source ]
# File lib/phusion_passenger/utils/unseekable_socket.rb, line 90 90: def to_io 91: self 92: end
[ 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
[ 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
[ 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
[ 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
[ 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