moduleExconclassSocketextendForwardableattr_accessor:datadefparamsExcon.display_warning("Excon::Socket#params is deprecated use Excon::Socket#data instead (#{caller.first})")@dataenddefparams=(new_params)Excon.display_warning("Excon::Socket#params= is deprecated use Excon::Socket#data= instead (#{caller.first})")@data=new_paramsendattr_reader:remote_ipdef_delegators(:@socket,:close,:close)def_delegators(:@socket,:readline,:readline)definitialize(data={})@data=data@read_buffer=''@eof=false@data[:family]||=::Socket::Constants::AF_UNSPECif@data[:proxy]@data[:proxy][:family]||=::Socket::Constants::AF_UNSPECendconnectenddefread(max_length=nil)if@eofreturnnilelsif@data[:nonblock]beginifmax_lengthuntil@read_buffer.length>=max_length@read_buffer<<@socket.read_nonblock(max_length-@read_buffer.length)endelsewhiletrue@read_buffer<<@socket.read_nonblock(@data[:chunk_size])endendrescueOpenSSL::SSL::SSLError=>erroriferror.message=='read would block'ifIO.select([@socket],nil,nil,@data[:read_timeout])retryelseraise(Excon::Errors::Timeout.new("read timeout reached"))endelseraise(error)endrescueErrno::EAGAIN,Errno::EWOULDBLOCK,IO::WaitReadableifIO.select([@socket],nil,nil,@data[:read_timeout])retryelseraise(Excon::Errors::Timeout.new("read timeout reached"))endrescueEOFError@eof=trueendifmax_length@read_buffer.slice!(0,max_length)else# read until EOFError, so return everything@read_buffer.slice!(0,@read_buffer.length)endelsebeginTimeout.timeout(@data[:read_timeout])do@socket.read(max_length)endrescueTimeout::ErrorraiseExcon::Errors::Timeout.new('read timeout reached')endendenddefwrite(data)if@data[:nonblock]ifFORCE_ENCdata.force_encoding('BINARY')endwhiletruewritten=nilbegin# I wish that this API accepted a start position, then we wouldn't# have to slice data when there is a short write.written=@socket.write_nonblock(data)rescueOpenSSL::SSL::SSLError,Errno::EAGAIN,Errno::EWOULDBLOCK,IO::WaitWritable=>erroriferror.is_a?(OpenSSL::SSL::SSLError)&&error.message!='write would block'raiseerrorelseifIO.select(nil,[@socket],nil,@data[:write_timeout])retryelseraiseExcon::Errors::Timeout.new('write timeout reached')endendend# Fast, common case.breakifwritten==data.size# This takes advantage of the fact that most ruby implementations# have Copy-On-Write strings. Thusly why requesting a subrange# of data, we actually don't copy data because the new string# simply references a subrange of the original.data=data[written,data.size]endelsebeginTimeout.timeout(@data[:write_timeout])do@socket.write(data)endrescueTimeout::ErrorExcon::Errors::Timeout.new('write timeout reached')endendendprivatedefconnect@socket=nilexception=niladdrinfo=if@data[:proxy]::Socket.getaddrinfo(@data[:proxy][:host],@data[:proxy][:port],@data[:proxy][:family],::Socket::Constants::SOCK_STREAM)else::Socket.getaddrinfo(@data[:host],@data[:port],@data[:family],::Socket::Constants::SOCK_STREAM)endaddrinfo.eachdo|_,port,_,ip,a_family,s_type|@remote_ip=ip# nonblocking connectbeginsockaddr=::Socket.sockaddr_in(port,ip)socket=::Socket.new(a_family,s_type,0)if@data[:nonblock]socket.connect_nonblock(sockaddr)elsebeginTimeout.timeout(@data[:connect_timeout])dosocket.connect(sockaddr)endrescueTimeout::ErrorraiseExcon::Errors::Timeout.new('connect timeout reached')endend@socket=socketbreakrescueErrno::EINPROGRESSunlessIO.select(nil,[socket],nil,@data[:connect_timeout])raise(Excon::Errors::Timeout.new("connect timeout reached"))endbeginsocket.connect_nonblock(sockaddr)@socket=socketbreakrescueErrno::EISCONN@socket=socketbreakrescueSystemCallError=>exceptionsocket.closenextendrescueSystemCallError=>exceptionsocket.closeifsocketnextendendunless@socket# this will be our last encountered exceptionraiseexceptionendif@data[:tcp_nodelay]@socket.setsockopt(::Socket::IPPROTO_TCP,::Socket::TCP_NODELAY,true)endendendend