# Phusion Passenger - https://www.phusionpassenger.com/# Copyright (c) 2010 Phusion## "Phusion Passenger" is a trademark of Hongli Lai & Ninh Bui.## Permission is hereby granted, free of charge, to any person obtaining a copy# of this software and associated documentation files (the "Software"), to deal# in the Software without restriction, including without limitation the rights# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell# copies of the Software, and to permit persons to whom the Software is# furnished to do so, subject to the following conditions:## The above copyright notice and this permission notice shall be included in# all copies or substantial portions of the Software.## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN# THE SOFTWARE.PhusionPassenger.require_passenger_lib'utils'# So that we can know whether #writev is supported.modulePhusionPassengermoduleUtils# 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?classUnseekableSocketdefself.wrap(socket)returnnew.wrap(socket)enddefwrap(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=socketreturnselfend# Don't allow disabling of sync.defsync=(value)end# Socket is sync'ed so flushing shouldn't do anything.defflushend# Already set to binary mode.defbinmodeend# This makes select() work.defto_io@socketenddefsimulate_eof!@simulate_eof=trueenddefstop_simulating_eof!@simulate_eof=falseenddeffileno@socket.filenoenddefaddr@socket.addrrescue=>eraiseannotate(e)enddefwrite(string)@socket.write(string)rescue=>eraiseannotate(e)enddefwrite_nonblock(string)@socket.write_nonblock(string)rescue=>eraiseannotate(e)enddefwritev(components)@socket.writev(components)rescue=>eraiseannotate(e)endifIO.method_defined?(:writev)defwritev2(components,components2)@socket.writev2(components,components2)rescue=>eraiseannotate(e)endifIO.method_defined?(:writev2)defwritev3(components,components2,components3)@socket.writev3(components,components2,components3)rescue=>eraiseannotate(e)endifIO.method_defined?(:writev3)defsend(*args)@socket.send(*args)rescue=>eraiseannotate(e)enddefsendmsg(*args)@socket.sendmsg(*args)rescue=>eraiseannotate(e)enddefsendmsg_nonblock(*args)@socket.sendmsg_nonblock(*args)rescue=>eraiseannotate(e)enddefputs(*args)@socket.puts(*args)rescue=>eraiseannotate(e)enddefgetsreturnnilif@simulate_eof@socket.getsrescue=>eraiseannotate(e)enddefread(*args)if@simulate_eoflength,buffer=argsifbufferbuffer.replace(binary_string(""))elsebuffer=binary_string("")endiflengthreturnnilelsereturnbufferendend@socket.read(*args)rescue=>eraiseannotate(e)enddefread_nonblock(*args)raiseEOFError,"end of file reached"if@simulate_eof@socket.read_nonblock(*args)rescue=>eraiseannotate(e)enddefreadpartial(*args)raiseEOFError,"end of file reached"if@simulate_eof@socket.readpartial(*args)rescue=>eraiseannotate(e)enddefreadlineraiseEOFError,"end of file reached"if@simulate_eof@socket.readlinerescue=>eraiseannotate(e)enddefrecv(*args)raiseEOFError,"end of file reached"if@simulate_eof@socket.recv(*args)rescue=>eraiseannotate(e)enddefrecvfrom(*args)raiseEOFError,"end of file reached"if@simulate_eof@socket.recvfrom(*args)rescue=>eraiseannotate(e)enddefrecvfrom_nonblock(*args)raiseEOFError,"end of file reached"if@simulate_eof@socket.recvfrom_nonblock(*args)rescue=>eraiseannotate(e)enddefeach(&block)returnif@simulate_eof@socket.each(&block)rescue=>eraiseannotate(e)enddefeof?returntrueif@simulate_eof@socket.eof?rescue=>eraiseannotate(e)enddefclosed?@socket.closed?rescue=>eraiseannotate(e)enddefclose@socket.closerescue=>eraiseannotate(e)enddefclose_read@socket.close_readrescue=>eraiseannotate(e)enddefclose_write@socket.close_writerescue=>eraiseannotate(e)enddefsource_of_exception?(exception)returnexception.instance_variable_get(:"@from_unseekable_socket")==@socket.object_idendprivatedefannotate(exception)exception.instance_variable_set(:"@from_unseekable_socket",@socket.object_id)returnexceptionenddefraise_error_because_activity_disallowed!raiseIOError,"It is not possible to read or write from the client socket because the current."endif''.respond_to?(:force_encoding)defbinary_string(str)return''.force_encoding('binary')endelsedefbinary_string(str)return''endendendend# module Utilsend# module PhusionPassenger