PageRenderTime 35ms CodeModel.GetById 13ms RepoModel.GetById 0ms app.codeStats 0ms

/vendor/bundle/gems/excon-0.37.0/lib/excon/socket.rb

https://github.com/trmchale1/personal_website
Ruby | 251 lines | 212 code | 28 blank | 11 comment | 39 complexity | 091b560429410b7c577430e7d5fc2dab MD5 | raw file
Possible License(s): Apache-2.0, 0BSD, GPL-2.0
  1. module Excon
  2. class Socket
  3. include Utils
  4. extend Forwardable
  5. attr_accessor :data
  6. def params
  7. Excon.display_warning('Excon::Socket#params is deprecated use Excon::Socket#data instead.')
  8. @data
  9. end
  10. def params=(new_params)
  11. Excon.display_warning('Excon::Socket#params= is deprecated use Excon::Socket#data= instead.')
  12. @data = new_params
  13. end
  14. attr_reader :remote_ip
  15. def_delegators(:@socket, :close, :close)
  16. def initialize(data = {})
  17. @data = data
  18. @nonblock = data[:nonblock]
  19. @read_buffer = ''
  20. @eof = false
  21. connect
  22. end
  23. def read(max_length=nil)
  24. if @eof
  25. return max_length ? nil : ''
  26. elsif @nonblock
  27. begin
  28. if max_length
  29. until @read_buffer.length >= max_length
  30. @read_buffer << @socket.read_nonblock(max_length - @read_buffer.length)
  31. end
  32. else
  33. while true
  34. @read_buffer << @socket.read_nonblock(@data[:chunk_size])
  35. end
  36. end
  37. rescue OpenSSL::SSL::SSLError => error
  38. if error.message == 'read would block'
  39. if IO.select([@socket], nil, nil, @data[:read_timeout])
  40. retry
  41. else
  42. raise(Excon::Errors::Timeout.new("read timeout reached"))
  43. end
  44. else
  45. raise(error)
  46. end
  47. rescue Errno::EAGAIN, Errno::EWOULDBLOCK, IO::WaitReadable
  48. if @read_buffer.empty?
  49. # if we didn't read anything, try again...
  50. if IO.select([@socket], nil, nil, @data[:read_timeout])
  51. retry
  52. else
  53. raise(Excon::Errors::Timeout.new("read timeout reached"))
  54. end
  55. end
  56. rescue EOFError
  57. @eof = true
  58. end
  59. if max_length
  60. if @read_buffer.empty?
  61. nil # EOF met at beginning
  62. else
  63. @read_buffer.slice!(0, max_length)
  64. end
  65. else
  66. # read until EOFError, so return everything
  67. @read_buffer.slice!(0, @read_buffer.length)
  68. end
  69. else
  70. begin
  71. Timeout.timeout(@data[:read_timeout]) do
  72. @socket.read(max_length)
  73. end
  74. rescue Timeout::Error
  75. raise Excon::Errors::Timeout.new('read timeout reached')
  76. end
  77. end
  78. end
  79. def readline
  80. if @eof
  81. raise EOFError, 'end of file reached'
  82. else
  83. line = ''
  84. if @nonblock
  85. while char = read(1)
  86. line << char
  87. break if char == $/
  88. end
  89. raise EOFError, 'end of file reached' if line.empty?
  90. else
  91. begin
  92. Timeout.timeout(@data[:read_timeout]) do
  93. line = @socket.readline
  94. end
  95. rescue Timeout::Error
  96. raise Excon::Errors::Timeout.new('read timeout reached')
  97. end
  98. end
  99. line
  100. end
  101. end
  102. def write(data)
  103. if @nonblock
  104. if FORCE_ENC
  105. data.force_encoding('BINARY')
  106. end
  107. while true
  108. written = nil
  109. begin
  110. # I wish that this API accepted a start position, then we wouldn't
  111. # have to slice data when there is a short write.
  112. written = @socket.write_nonblock(data)
  113. rescue OpenSSL::SSL::SSLError, Errno::EAGAIN, Errno::EWOULDBLOCK, IO::WaitWritable => error
  114. if error.is_a?(OpenSSL::SSL::SSLError) && error.message != 'write would block'
  115. raise error
  116. else
  117. if IO.select(nil, [@socket], nil, @data[:write_timeout])
  118. retry
  119. else
  120. raise Excon::Errors::Timeout.new('write timeout reached')
  121. end
  122. end
  123. end
  124. # Fast, common case.
  125. break if written == data.size
  126. # This takes advantage of the fact that most ruby implementations
  127. # have Copy-On-Write strings. Thusly why requesting a subrange
  128. # of data, we actually don't copy data because the new string
  129. # simply references a subrange of the original.
  130. data = data[written, data.size]
  131. end
  132. else
  133. begin
  134. Timeout.timeout(@data[:write_timeout]) do
  135. @socket.write(data)
  136. end
  137. rescue Timeout::Error
  138. raise(Excon::Errors::Timeout.new('write timeout reached'))
  139. end
  140. end
  141. end
  142. def local_port
  143. ::Socket.unpack_sockaddr_in(@socket.to_io.getsockname)[0]
  144. rescue ArgumentError => e
  145. raise unless e.message == 'not an AF_INET/AF_INET6 sockaddr'
  146. end
  147. def local_address
  148. ::Socket.unpack_sockaddr_in(@socket.to_io.getsockname)[1]
  149. rescue ArgumentError => e
  150. raise unless e.message == 'not an AF_INET/AF_INET6 sockaddr'
  151. end
  152. private
  153. def connect
  154. @socket = nil
  155. exception = nil
  156. if @data[:proxy]
  157. family = @data[:proxy][:family] || ::Socket::Constants::AF_UNSPEC
  158. args = [@data[:proxy][:host], @data[:proxy][:port], family, ::Socket::Constants::SOCK_STREAM]
  159. else
  160. family = @data[:family] || ::Socket::Constants::AF_UNSPEC
  161. args = [@data[:host], @data[:port], family, ::Socket::Constants::SOCK_STREAM]
  162. end
  163. if RUBY_VERSION >= '1.9.2' && defined?(RUBY_ENGINE) && RUBY_ENGINE == 'ruby'
  164. args << nil << nil << false # no reverse lookup
  165. end
  166. addrinfo = ::Socket.getaddrinfo(*args)
  167. addrinfo.each do |_, port, _, ip, a_family, s_type|
  168. @remote_ip = ip
  169. # nonblocking connect
  170. begin
  171. sockaddr = ::Socket.sockaddr_in(port, ip)
  172. socket = ::Socket.new(a_family, s_type, 0)
  173. if @data[:reuseaddr]
  174. socket.setsockopt(::Socket::Constants::SOL_SOCKET, ::Socket::Constants::SO_REUSEADDR, true)
  175. if defined?(::Socket::Constants::SO_REUSEPORT)
  176. socket.setsockopt(::Socket::Constants::SOL_SOCKET, ::Socket::Constants::SO_REUSEPORT, true)
  177. end
  178. end
  179. begin
  180. Timeout.timeout(@data[:connect_timeout]) do
  181. if @nonblock
  182. socket.connect_nonblock(sockaddr)
  183. else
  184. socket.connect(sockaddr)
  185. end
  186. end
  187. rescue Timeout::Error
  188. raise Excon::Errors::Timeout.new('connect timeout reached')
  189. end
  190. @socket = socket
  191. break
  192. rescue Errno::EINPROGRESS
  193. unless IO.select(nil, [socket], nil, @data[:connect_timeout])
  194. raise(Excon::Errors::Timeout.new("connect timeout reached"))
  195. end
  196. begin
  197. socket.connect_nonblock(sockaddr)
  198. @socket = socket
  199. break
  200. rescue Errno::EISCONN
  201. @socket = socket
  202. break
  203. rescue SystemCallError => exception
  204. socket.close rescue nil
  205. next
  206. end
  207. rescue SystemCallError => exception
  208. socket.close rescue nil if socket
  209. next
  210. end
  211. end
  212. unless @socket
  213. # this will be our last encountered exception
  214. raise exception
  215. end
  216. if @data[:tcp_nodelay]
  217. @socket.setsockopt(::Socket::IPPROTO_TCP,
  218. ::Socket::TCP_NODELAY,
  219. true)
  220. end
  221. end
  222. end
  223. end