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