text: # -*- coding: utf-8 -*-
  
  import argparse
  import multiprocessing
  import random
  import signal
  import sys
  import time
  import urlparse
  
  import pycurl
  
  URL_SETS = {
      "projector": [
          dict(
              path = "/projector/",
              headers = [
                  "X-Requested-With: XMLHttpRequest",
              ]
          ),
      ],
      "home": [
          "/",
          "/jsi18n/",
          "/static/styles/base.css",
          "/static/javascript/utils.js",
          "/static/javascript/jquery.min.js",
          "/static/img/logo.png",
      ],
      "agenda": [
          "/agenda/",
          "/static/styles/base.css",
          "/static/javascript/utils.js",
          "/static/styles/agenda.css",
          "/static/javascript/jquery.min.js",
          "/jsi18n/",
      ],
      "application": [
          "/application/",
          "/static/styles/base.css",
          "/static/javascript/utils.js",
          "/static/javascript/jquery.min.js",
          "/jsi18n/",
      ]
  }
  
  
  def nop_write(data):
      return len(data)
  
  class Client(object):
      def __init__(self):
          self._c = pycurl.Curl()
          self._c.setopt(pycurl.FAILONERROR, 1)
          self._c.setopt(pycurl.FOLLOWLOCATION, 1)
          self._c.setopt(pycurl.TIMEOUT, 10)
          self._c.setopt(pycurl.WRITEFUNCTION, nop_write)
          self._c.setopt(pycurl.AUTOREFERER, 1)
  
  
      def request(self, r):
          if isinstance(r, basestring):
              self._c.setopt(pycurl.URL, r)
          else:
              self._c.setopt(pycurl.URL, r["url"])
              self._c.setopt(pycurl.HTTPHEADER, r["headers"])
  
          try:
              self._c.perform()
          except pycurl.error as e:
              return False
          return True
  
  
  def request_loop(pause, repeat, urls, should_quit):
      c = Client()
  
      requests, errors = 0, 0
      max_time = 0
      sum_time = 0
  
      urls = list(urls)
      random.shuffle(urls)
  
      for x in xrange(repeat):
          if should_quit.value:
              break
          if pause:
              time.sleep(pause)
          for url in urls:
              if should_quit.value:
                  break
  
              requests += 1
              t0 = time.time()
              if not c.request(url):
                  errors += 1
              t1 = time.time()
  
              dt = t1 - t0
              sum_time += dt
              if dt > max_time:
                  max_time = dt
  
      return requests, errors, max_time, sum_time
  
  
  def worker(params, should_quit, lock):
      signal.signal(signal.SIGINT, signal.SIG_IGN)
  
      opts = params["opts"]
  
      pause = opts.pause / 1000.0
      res = request_loop(pause, opts.repeat, params["urls"], should_quit)
      with lock:
          params["requests"].value += res[0]
          params["errors"].value += res[1]
          params["max_request_time"].value = max((res[2], params["max_request_time"].value))
          params["sum_request_time"].value += res[3]
  
  
  def main():
      parser = argparse.ArgumentParser()
      parser.add_argument("-d", "--delay", type = int, default = 100)
      parser.add_argument("-j", "--jobs", type = int, default = 10)
      parser.add_argument("-p", "--pause", type = int, default = 500)
      parser.add_argument("-r", "--repeat", type = int, default = 100)
      parser.add_argument("-s", "--url-set", choices = list(URL_SETS),
          default = "projector")
      parser.add_argument("base_url")
  
      opts = parser.parse_args()
  
      base_url = opts.base_url
      urls = []
      for u in URL_SETS[opts.url_set]:
          if isinstance(u, basestring):
              u = urlparse.urljoin(base_url, u)
          else:
              u["url"] = urlparse.urljoin(base_url, u["path"])
          urls.append(u)
  
      data = dict(
          opts = opts,
          urls = urls,
  
          requests = multiprocessing.Value("i", 0),
          errors = multiprocessing.Value("i", 0),
          max_request_time = multiprocessing.Value("d", 0),
          sum_request_time = multiprocessing.Value("d", 0),
      )
  
      lock = multiprocessing.Lock()
      quit = multiprocessing.Value("i", 0)
  
      t0 = time.time()
  
      workers = []
      for job in xrange(opts.jobs):
          p = multiprocessing.Process(target = worker,
              args = (data, quit, lock))
          p.daemon = True
          p.start()
          workers.append(p)
  
          # spread out the start of each worker a bit
          delay = opts.delay
          if delay != 0:
              if delay < 0:
                  time.sleep(random.randint(0, -delay) / 1000.0)
              else:
                  time.sleep(delay / 1000.0)
  
      try:
          for p in workers:
              p.join()
      except KeyboardInterrupt:
          quit.value = 1
          for p in workers:
              p.join()
  
      t1 = time.time()
  
      data["total_time"] = t1 - t0
      data["avg_request_time"] = data["sum_request_time"].value / data["requests"].value
      print("Total Requests: {requests.value}\n"
          "Errors: {errors.value}\n"
          "Jobs: {opts.jobs}\n"
          "Time: {total_time:.1f}s\n"
          "Max time per request: {max_request_time.value:.4f}s\n"
          "Avg time per request: {avg_request_time:.4f}s\n".format(**data))
  
  
  
  if __name__ == "__main__":
      main()
  