Class: PhusionPassenger::Utils::UnseekableSocket

Inherits:
Object
  • Object
show all
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

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

#addrObject



107
108
109
110
111
# File 'lib/phusion_passenger/utils/unseekable_socket.rb', line 107

def addr
	@socket.addr
rescue => e
	raise annotate(e)
end

#binmodeObject

Already set to binary mode.



87
88
# File 'lib/phusion_passenger/utils/unseekable_socket.rb', line 87

def binmode
end

#closeObject



255
256
257
258
259
# File 'lib/phusion_passenger/utils/unseekable_socket.rb', line 255

def close
	@socket.close
rescue => e
	raise annotate(e)
end

#close_readObject



261
262
263
264
265
# File 'lib/phusion_passenger/utils/unseekable_socket.rb', line 261

def close_read
	@socket.close_read
rescue => e
	raise annotate(e)
end

#close_writeObject



267
268
269
270
271
# File 'lib/phusion_passenger/utils/unseekable_socket.rb', line 267

def close_write
	@socket.close_write
rescue => e
	raise annotate(e)
end

#closed?Boolean

Returns:

  • (Boolean)


249
250
251
252
253
# File 'lib/phusion_passenger/utils/unseekable_socket.rb', line 249

def closed?
	@socket.closed?
rescue => e
	raise annotate(e)
end

#each(&block) ⇒ Object



235
236
237
238
239
240
# File 'lib/phusion_passenger/utils/unseekable_socket.rb', line 235

def each(&block)
	return if @simulate_eof
	@socket.each(&block)
rescue => e
	raise annotate(e)
end

#eof?Boolean

Returns:

  • (Boolean)


242
243
244
245
246
247
# File 'lib/phusion_passenger/utils/unseekable_socket.rb', line 242

def eof?
	return true if @simulate_eof
	@socket.eof?
rescue => e
	raise annotate(e)
end

#filenoObject



103
104
105
# File 'lib/phusion_passenger/utils/unseekable_socket.rb', line 103

def fileno
	@socket.fileno
end

#flushObject

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

#getsObject



167
168
169
170
171
172
# File 'lib/phusion_passenger/utils/unseekable_socket.rb', line 167

def gets
	return nil if @simulate_eof
	@socket.gets
rescue => e
	raise annotate(e)
end

#puts(*args) ⇒ Object



161
162
163
164
165
# File 'lib/phusion_passenger/utils/unseekable_socket.rb', line 161

def puts(*args)
	@socket.puts(*args)
rescue => e
	raise annotate(e)
end

#read(*args) ⇒ Object



174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
# File 'lib/phusion_passenger/utils/unseekable_socket.rb', line 174

def read(*args)
	if @simulate_eof
		length, buffer = args
		if buffer
			buffer.replace(binary_string(""))
		else
			buffer = binary_string("")
		end
		if length
			return nil
		else
			return buffer
		end
	end
	@socket.read(*args)
rescue => e
	raise annotate(e)
end

#read_nonblock(*args) ⇒ Object



193
194
195
196
197
198
# File 'lib/phusion_passenger/utils/unseekable_socket.rb', line 193

def read_nonblock(*args)
	raise EOFError, "end of file reached" if @simulate_eof
	@socket.read_nonblock(*args)
rescue => e
	raise annotate(e)
end

#readlineObject



207
208
209
210
211
212
# File 'lib/phusion_passenger/utils/unseekable_socket.rb', line 207

def readline
	raise EOFError, "end of file reached" if @simulate_eof
	@socket.readline
rescue => e
	raise annotate(e)
end

#readpartial(*args) ⇒ Object



200
201
202
203
204
205
# File 'lib/phusion_passenger/utils/unseekable_socket.rb', line 200

def readpartial(*args)
	raise EOFError, "end of file reached" if @simulate_eof
	@socket.readpartial(*args)
rescue => e
	raise annotate(e)
end

#recv(*args) ⇒ Object



214
215
216
217
218
219
# File 'lib/phusion_passenger/utils/unseekable_socket.rb', line 214

def recv(*args)
	raise EOFError, "end of file reached" if @simulate_eof
	@socket.recv(*args)
rescue => e
	raise annotate(e)
end

#recvfrom(*args) ⇒ Object



221
222
223
224
225
226
# File 'lib/phusion_passenger/utils/unseekable_socket.rb', line 221

def recvfrom(*args)
	raise EOFError, "end of file reached" if @simulate_eof
	@socket.recvfrom(*args)
rescue => e
	raise annotate(e)
end

#recvfrom_nonblock(*args) ⇒ Object



228
229
230
231
232
233
# File 'lib/phusion_passenger/utils/unseekable_socket.rb', line 228

def recvfrom_nonblock(*args)
	raise EOFError, "end of file reached" if @simulate_eof
	@socket.recvfrom_nonblock(*args)
rescue => e
	raise annotate(e)
end

#send(*args) ⇒ Object



143
144
145
146
147
# File 'lib/phusion_passenger/utils/unseekable_socket.rb', line 143

def send(*args)
	@socket.send(*args)
rescue => e
	raise annotate(e)
end

#sendmsg(*args) ⇒ Object



149
150
151
152
153
# File 'lib/phusion_passenger/utils/unseekable_socket.rb', line 149

def sendmsg(*args)
	@socket.sendmsg(*args)
rescue => e
	raise annotate(e)
end

#sendmsg_nonblock(*args) ⇒ Object



155
156
157
158
159
# File 'lib/phusion_passenger/utils/unseekable_socket.rb', line 155

def sendmsg_nonblock(*args)
	@socket.sendmsg_nonblock(*args)
rescue => e
	raise annotate(e)
end

#simulate_eof!Object



95
96
97
# File 'lib/phusion_passenger/utils/unseekable_socket.rb', line 95

def simulate_eof!
	@simulate_eof = true
end

#source_of_exception?(exception) ⇒ Boolean

Returns:

  • (Boolean)


273
274
275
# File 'lib/phusion_passenger/utils/unseekable_socket.rb', line 273

def source_of_exception?(exception)
	return exception.instance_variable_get(:"@from_unseekable_socket") == @socket.object_id
end

#stop_simulating_eof!Object



99
100
101
# File 'lib/phusion_passenger/utils/unseekable_socket.rb', line 99

def stop_simulating_eof!
	@simulate_eof = false
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_ioObject

This makes select() work.



91
92
93
# File 'lib/phusion_passenger/utils/unseekable_socket.rb', line 91

def to_io
	@socket
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



113
114
115
116
117
# File 'lib/phusion_passenger/utils/unseekable_socket.rb', line 113

def write(string)
	@socket.write(string)
rescue => e
	raise annotate(e)
end

#write_nonblock(string) ⇒ Object



119
120
121
122
123
# File 'lib/phusion_passenger/utils/unseekable_socket.rb', line 119

def write_nonblock(string)
	@socket.write_nonblock(string)
rescue => e
	raise annotate(e)
end

#writev(components) ⇒ Object



125
126
127
128
129
# File 'lib/phusion_passenger/utils/unseekable_socket.rb', line 125

def writev(components)
	@socket.writev(components)
rescue => e
	raise annotate(e)
end

#writev2(components, components2) ⇒ Object



131
132
133
134
135
# File 'lib/phusion_passenger/utils/unseekable_socket.rb', line 131

def writev2(components, components2)
	@socket.writev2(components, components2)
rescue => e
	raise annotate(e)
end

#writev3(components, components2, components3) ⇒ Object



137
138
139
140
141
# File 'lib/phusion_passenger/utils/unseekable_socket.rb', line 137

def writev3(components, components2, components3)
	@socket.writev3(components, components2, components3)
rescue => e
	raise annotate(e)
end