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



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

#binmodeObject

Already set to binary mode.



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

def binmode
end

#closeObject



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_readObject



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_writeObject



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

Returns:

  • (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

#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



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

#readlineObject



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

Returns:

  • (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_ioObject



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