def perform(async=false, &block)

    @results = {}

    @clients.each do |client|
      @threads << Thread.new do

        loop do
          break if @reqs.empty?
          req = @reqs.shift
          break if req.nil? # can sometimes reach here due to a race condition. saw it a lot on travis
          client.url = req.uri

          args = ["http_#{req.method}"]
          if [:put, :post].include? req.method
            # add body to args for these methods
            if req.body then
              if req.body.kind_of? Array then
                args += req.body
              else
                args << req.body
              end
            else
              args << ""
            end
          end

          client.send(*args)
          if block then
            yield(req, client.body_str)
          else
            @results[req.key] = client.body_str
          end
        end

      end
    end

    if async then
      # don't wait for threads to join, just return
      return true
    end

    join()
    return true if block

    return @results
  end
def perform(async=false, &block)

    @results = {}

    @clients.each do |client|
      @threads << Thread.new do

        loop do
          break if @reqs.empty?
          req = @reqs.shift
          break if req.nil? # can sometimes reach here due to a race condition. saw it a lot on travis
          client.url = req.uri

          args = ["http_#{req.method}"]
          if [:put, :post].include? req.method
            # add body to args for these methods
            if req.body then
              if req.body.kind_of? Array then
                args += req.body
              else
                args << req.body
              end
            else
              args << ""
            end
          end

          client.send(*args)
          if block then
            yield(req, client.body_str)
          else
            @results[req.key] = client.body_str
          end
        end

      end
    end

    if async then
      # don't wait for threads to join, just return
      return true
    end

    join()
    return true if block

    return @results
  end
Execute requests. By default, will block until complete and return results.

 @param [Boolean] async      If true, will not wait for requests to finish.
                             (Default=false)

 @param [Block] block        If passed, responses will be passed into the callback
                             instead of being returned directly

 @yield [Request, String]    Passes to the block the request and the response body

 @return [Hash<Key, String>] Hash of responses, if no block given. Returns true otherwise