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

+ (Object) wrap(socket)



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

- (Object) addr



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

- (Object) binmode

Already set to binary mode.



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

def binmode
end

- (Object) close



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

- (Object) close_read



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

- (Object) close_write



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

- (Boolean) closed?



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

- (Object) each(&block)



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

- (Object) flush

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

- (Object) gets



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

- (Object) puts(*args)



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

- (Object) read(*args)



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

- (Object) readline



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

- (Object) readpartial(*args)



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

- (Boolean) source_of_exception?(exception)



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

- (Object) sync=(value)

Don't allow disabling of sync.



79
80
# File 'lib/phusion_passenger/utils/unseekable_socket.rb', line 79

def sync=(value)
end

- (Object) to_io



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

def to_io
	self
end

- (Object) wrap(socket)



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

- (Object) write(string)



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

- (Object) writev(components)



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

- (Object) writev2(components, components2)



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

- (Object) writev3(components, components2, components3)



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