class Excon::Socket

def write(data)

def write(data)
  if @data[:nonblock]
    # We normally return from the return in the else block below, but
    # we guard that data is still something in case we get weird
    # values and String#[] returns nil. (This behavior has been observed
    # in the wild, so this is a simple defensive mechanism)
    while data
      begin
        # 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)
      rescue OpenSSL::SSL::SSLError => error
        if error.message == 'write would block'
          if IO.select(nil, [@socket], nil, @data[:write_timeout])
            retry
          else
            raise(Excon::Errors::Timeout.new("write timeout reached"))
          end
        else
          raise(error)
        end
      rescue Errno::EAGAIN, Errno::EWOULDBLOCK, IO::WaitWritable
        if IO.select(nil, [@socket], nil, @data[:write_timeout])
          retry
        else
          raise(Excon::Errors::Timeout.new("write timeout reached"))
        end
      else
        # Fast, common case.
        # The >= seems weird, why would it have written MORE than we
        # requested. But we're getting some weird behavior when @socket
        # is an OpenSSL socket, where it seems like it's saying it wrote
        # more (perhaps due to SSL packet overhead?).
        #
        # Pretty weird, but this is a simple defensive mechanism.
        return if written >= 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]
      end
    end
  else
    begin
      Timeout.timeout(@data[:write_timeout]) do
        @socket.write(data)
      end
    rescue Timeout::Error
      Excon::Errors::Timeout.new('write timeout reached')
    end
  end
end