Class: PhusionPassenger::Utils::UnseekableSocket
- Defined in:
- lib/phusion_passenger/utils/unseekable_socket.rb
Overview
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?
Class Method Summary collapse
Instance Method Summary collapse
- #addr ⇒ Object
-
#binmode ⇒ Object
Already set to binary mode.
- #close ⇒ Object
- #close_read ⇒ Object
- #close_write ⇒ Object
- #closed? ⇒ Boolean
- #each(&block) ⇒ Object
-
#flush ⇒ Object
Socket is sync’ed so flushing shouldn’t do anything.
- #gets ⇒ Object
- #puts(*args) ⇒ Object
- #read(*args) ⇒ Object
- #readline ⇒ Object
- #readpartial(*args) ⇒ Object
- #source_of_exception?(exception) ⇒ Boolean
-
#sync=(value) ⇒ Object
Don’t allow disabling of sync.
- #to_io ⇒ Object
- #wrap(socket) ⇒ Object
- #write(string) ⇒ Object
- #writev(components) ⇒ Object
- #writev2(components, components2) ⇒ Object
- #writev3(components, components2, components3) ⇒ Object
Class Method Details
.wrap(socket) ⇒ Object
44 45 46 |
# File 'lib/phusion_passenger/utils/unseekable_socket.rb', line 44 def self.wrap(socket) return new.wrap(socket) end |
Instance Method Details
#addr ⇒ Object
94 95 96 97 98 |
# File 'lib/phusion_passenger/utils/unseekable_socket.rb', line 94 def addr @socket.addr rescue => e raise annotate(e) end |
#binmode ⇒ Object
Already set to binary mode.
87 88 |
# File 'lib/phusion_passenger/utils/unseekable_socket.rb', line 87 def binmode end |
#close ⇒ Object
166 167 168 169 170 |
# File 'lib/phusion_passenger/utils/unseekable_socket.rb', line 166 def close @socket.close rescue => e raise annotate(e) end |
#close_read ⇒ Object
172 173 174 175 176 |
# File 'lib/phusion_passenger/utils/unseekable_socket.rb', line 172 def close_read @socket.close_read rescue => e raise annotate(e) end |
#close_write ⇒ Object
178 179 180 181 182 |
# File 'lib/phusion_passenger/utils/unseekable_socket.rb', line 178 def close_write @socket.close_write rescue => e raise annotate(e) end |
#closed? ⇒ Boolean
160 161 162 163 164 |
# File 'lib/phusion_passenger/utils/unseekable_socket.rb', line 160 def closed? @socket.closed? rescue => e raise annotate(e) end |
#each(&block) ⇒ Object
154 155 156 157 158 |
# File 'lib/phusion_passenger/utils/unseekable_socket.rb', line 154 def each(&block) @socket.each(&block) rescue => e raise annotate(e) end |
#flush ⇒ Object
Socket is sync’ed so flushing shouldn’t do anything.
83 84 |
# File 'lib/phusion_passenger/utils/unseekable_socket.rb', line 83 def flush end |
#gets ⇒ Object
130 131 132 133 134 |
# File 'lib/phusion_passenger/utils/unseekable_socket.rb', line 130 def gets @socket.gets rescue => e raise annotate(e) end |
#puts(*args) ⇒ Object
124 125 126 127 128 |
# File 'lib/phusion_passenger/utils/unseekable_socket.rb', line 124 def puts(*args) @socket.puts(*args) rescue => e raise annotate(e) end |
#read(*args) ⇒ Object
136 137 138 139 140 |
# File 'lib/phusion_passenger/utils/unseekable_socket.rb', line 136 def read(*args) @socket.read(*args) rescue => e raise annotate(e) end |
#readline ⇒ Object
148 149 150 151 152 |
# File 'lib/phusion_passenger/utils/unseekable_socket.rb', line 148 def readline @socket.readline rescue => e raise annotate(e) end |
#readpartial(*args) ⇒ Object
142 143 144 145 146 |
# File 'lib/phusion_passenger/utils/unseekable_socket.rb', line 142 def readpartial(*args) @socket.readpartial(*args) rescue => e raise annotate(e) end |
#source_of_exception?(exception) ⇒ Boolean
184 185 186 |
# File 'lib/phusion_passenger/utils/unseekable_socket.rb', line 184 def source_of_exception?(exception) return exception.instance_variable_get(:"@from_unseekable_socket") == @socket.object_id end |
#sync=(value) ⇒ Object
Don’t allow disabling of sync.
79 80 |
# File 'lib/phusion_passenger/utils/unseekable_socket.rb', line 79 def sync=(value) end |
#to_io ⇒ Object
90 91 92 |
# File 'lib/phusion_passenger/utils/unseekable_socket.rb', line 90 def to_io self end |
#wrap(socket) ⇒ Object
48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 |
# File 'lib/phusion_passenger/utils/unseekable_socket.rb', line 48 def wrap(socket) # Some people report that sometimes their Ruby (MRI/REE) # processes get stuck with 100% CPU usage. Upon further # inspection with strace, it turns out that these Ruby # processes are continuously calling lseek() on a socket, # which of course returns ESPIPE as error. gdb reveals # lseek() is called by fwrite(), which in turn is called # by rb_fwrite(). The affected socket is the # AbstractRequestHandler client socket. # # I inspected the MRI source code and didn't find # anything that would explain this behavior. This makes # me think that it's a glibc bug, but that's very # unlikely. # # The rb_fwrite() implementation takes an entirely # different code path if I set 'sync' to true: it will # skip fwrite() and use write() instead. So here we set # 'sync' to true in the hope that this will work around # the problem. socket.sync = true # There's no need to set the encoding for Ruby 1.9 because # abstract_request_handler.rb is tagged with 'encoding: binary'. @socket = socket return self end |
#write(string) ⇒ Object
100 101 102 103 104 |
# File 'lib/phusion_passenger/utils/unseekable_socket.rb', line 100 def write(string) @socket.write(string) rescue => e raise annotate(e) end |
#writev(components) ⇒ Object
106 107 108 109 110 |
# File 'lib/phusion_passenger/utils/unseekable_socket.rb', line 106 def writev(components) @socket.writev(components) rescue => e raise annotate(e) end |
#writev2(components, components2) ⇒ Object
112 113 114 115 116 |
# File 'lib/phusion_passenger/utils/unseekable_socket.rb', line 112 def writev2(components, components2) @socket.writev2(components, components2) rescue => e raise annotate(e) end |
#writev3(components, components2, components3) ⇒ Object
118 119 120 121 122 |
# File 'lib/phusion_passenger/utils/unseekable_socket.rb', line 118 def writev3(components, components2, components3) @socket.writev3(components, components2, components3) rescue => e raise annotate(e) end |