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

[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

[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.

[Source]

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

[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

[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

[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

[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

[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.

[Source]

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

[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

[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

[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

[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

[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]

     # 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.

[Source]

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

[Source]

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

[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

[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

[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

[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

[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

[Validate]