{ "info": { "author": "Evan Borgstrom", "author_email": "evan@borgstrom.ca", "bugtrack_url": null, "classifiers": [ "Development Status :: 4 - Beta", "Intended Audience :: Developers", "Intended Audience :: System Administrators", "License :: OSI Approved :: Apache Software License", "Natural Language :: English", "Topic :: Software Development :: Libraries", "Topic :: Software Development :: Libraries :: Python Modules" ], "description": "HTTPBenchmark\n=============\nHTTPBenchmark aims to be inbetween Apache Bench (ab) and a more full blown\nHTTP test suite. In its simplest form it provides some basic compatibility with\nab in that it supports a -n switch to specify the number of requests to make\nand a -c switch to specify how many concurrent users. \n\nHowever, the true intended use is to subclass HTTPBenchmark and to override the\n``worker`` method to implement your own test logic. In this regard you can\nthink of HTTPBenchmark as a framework that allows you to build unit tests for\nyour HTTP application that aim to stress a server as well. See the Worker Code\nsection below.\n\n\nInstallation\n------------\nInstallation is easily accomplished using ``pip`` or ``easy_install``.\n\n.. code-block:: python\n\n pip install httpbenchmark\n\n\nBasic Usage\n-----------\nA script named ``pb`` is provided and is intended to be a basic analog to the\nApache ``ab`` script.\n\n.. code-block:: shell\n\n pb -n 1000 -c 25 http://my-host.tld/endpoint/\n\n\nWorker code\n-----------\nBy default HTTPBenchmark does nothing more than send a ``GET`` request and\nexpect a 200 OK return code. To fully utilize the system you should create\nyour own script that subclasses ``HTTPBenchmark``.\n\nHere's an incomplete and untested example that is load testing some application\nthat deals with users and friends. It aims to illustrate the fundamentals of\nusing HTTPBenchmark\n\n.. code-block:: python\n\n from httpbenchmark import HTTPBenchmark\n from tornado import gen\n import random\n\n USER_IDS = [1111, 2222, 3333, 4444]\n\n _url = lambda x: ''.join(['http://my-host.tld/', x])\n\n class MyBenchmark(HTTPBenchmark):\n @gen.coroutine\n def worker(self):\n '''\n get_worker should return a callable that will be called by the async http client\n '''\n if random.choice([True, False]):\n yield self.new_user()\n else:\n yield self.returning_user()\n\n @gen.coroutine\n def new_user(self):\n user_id = random.choice(USER_IDS)\n self.log.debug(\"New user: %s\" % user_id)\n\n friends = yield self.open_json(_url(\"register?uid=%d\" % user_id))\n # ... handle registration response ...\n if failure:\n self.log.error(\"Indicate reason for failure\")\n self.log.debug(\"Show debugging info if you want\")\n self.finish_request(False)\n else:\n yield self.next_step(user_id, friends['friendList'][0])\n\n @gen.coroutine\n def returning_user(self):\n user_id = random.choice(USER_IDS)\n self.log.debug(\"Returning user: %s\" % user_id)\n\n def handle_login(response, friends):\n friends = yield self.open_json(_url(\"login?uid=%d\" % user_id))\n # ... handle login response ...\n if failure:\n self.log.error(\"Indicate reason for failure\")\n self.log.debug(\"Show debugging info if you want\")\n self.finish_request(False)\n else:\n yield self.next_step(user_id, friends['friendList'][0])\n\n @gen.coroutine\n def next_step(self, user_id, friend_id):\n # ... do something else ...\n if failure:\n self.log.error(\"Indicate reason for failure\")\n self.log.debug(\"Show debugging info if you want\")\n self.finish_request(False)\n else:\n # success!\n self.finish_request()\n\n if __name__ == '__main__':\n MyBenchmark().main()\n\n\nEssentials\n^^^^^^^^^^\n\n* This uses `Tornado's async generator interface`_ to achive concurrency, your\n functions need to be wrapped in ``@gen.coroutine`` and you should ``yield``\n between them.\n\n* ``worker`` is where your main code lives. It will be called whenever there\n is a free slot based on concurrency.\n\n* ``yield self.get(url, code=200)`` is used to make a GET request. You will\n get the response object back when the operation completes.\n\n* ``self.post(url, params={}, callback)`` is used to POST data. ``params``\n should be a dictionary and will be sent as the POST data. It functions\n the same as ``get`` otherwise.\n\n* If you're posting to a PHP backend and need to use PHP's neseted array\n syntax for parameters you pass ``php_urlencode`` to the ``self.post`` method\n with a value of ``True`` and it will encode the params accordingly.\n\n* ``self.get_json(url, callback)`` is a shortcut for getting and parsing json\n data that is returned. Your callback should accept two arguments, the first\n is the response object and the second is the decoded json.\n\n* ``self.finish_request(True/False)`` should be called to signal the end of a\n request. If everything worked as you expected pass it ``True``, otherwise\n pass it ``False``\n\n* ``self.debug_response(response)`` is a handy function to use while you're\n developing your test cases. If you pass it a response object it will print\n out a summary of the object as well as the headers and body so you can debug\n the live data.\n\nTODO\n----\n\n* Add some working examples\n\n.. _Tornado's async generator interface: http://www.tornadoweb.org/en/stable/gen.html", "description_content_type": null, "docs_url": null, "download_url": "UNKNOWN", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "https://github.com/borgstrom/httpbenchmark", "keywords": null, "license": "Apache License, Version 2.0", "maintainer": null, "maintainer_email": null, "name": "httpbenchmark", "package_url": "https://pypi.org/project/httpbenchmark/", "platform": "UNKNOWN", "project_url": "https://pypi.org/project/httpbenchmark/", "project_urls": { "Download": "UNKNOWN", "Homepage": "https://github.com/borgstrom/httpbenchmark" }, "release_url": "https://pypi.org/project/httpbenchmark/0.5/", "requires_dist": null, "requires_python": null, "summary": "Python HTTP Benchmarking Tool", "version": "0.5" }, "last_serial": 1560798, "releases": { "0.3": [], "0.3.1": [ { "comment_text": "", "digests": { "md5": "d8b2d9acde68df1a5f0b3c87674267e2", "sha256": "9300f737fb8610e0212135cf23f31eba09ccd473ffcd4d225c1648e784bb3ed7" }, "downloads": -1, "filename": "httpbenchmark-0.3.1.tar.gz", "has_sig": false, "md5_digest": "d8b2d9acde68df1a5f0b3c87674267e2", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 7279, "upload_time": "2014-03-06T19:42:23", "url": "https://files.pythonhosted.org/packages/60/28/06439622c88839df3b2d8b42b552acf8282dbd9c5af98375dadd18d0f1ff/httpbenchmark-0.3.1.tar.gz" } ], "0.3.2": [ { "comment_text": "", "digests": { "md5": "962c54b04cf35b72039a6ee9612d2426", "sha256": "ae9f51f668939470a841d4af4f284191f711f48bf33665359f00fd063721d470" }, "downloads": -1, "filename": "httpbenchmark-0.3.2.tar.gz", "has_sig": false, "md5_digest": "962c54b04cf35b72039a6ee9612d2426", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 7812, "upload_time": "2014-03-26T18:08:55", "url": "https://files.pythonhosted.org/packages/52/d6/293d2df2cc0b311b69b02ff71131575a1db78f723bf0451a81743e53854d/httpbenchmark-0.3.2.tar.gz" } ], "0.3.3": [ { "comment_text": "", "digests": { "md5": "8e1cdb5c4aebf38150ce8c7f7e8a81eb", "sha256": "f59bb91f42ed4119c191a192006b58961639353817bf5066503a1303cbf8c832" }, "downloads": -1, "filename": "httpbenchmark-0.3.3.tar.gz", "has_sig": false, "md5_digest": "8e1cdb5c4aebf38150ce8c7f7e8a81eb", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 7802, "upload_time": "2014-03-26T18:31:48", "url": "https://files.pythonhosted.org/packages/fa/5d/d541a5ffc40f1f16deda9dbe2a8ab39e745789b86d388c07e96a383f023a/httpbenchmark-0.3.3.tar.gz" } ], "0.4": [ { "comment_text": "", "digests": { "md5": "9dae1fff7a0e35c4072972bf5d973053", "sha256": "3a942b11ac4a5554be004f9eeae606a1a0c113fddd3f400c16a1a1844704b2ad" }, "downloads": -1, "filename": "httpbenchmark-0.4.tar.gz", "has_sig": false, "md5_digest": "9dae1fff7a0e35c4072972bf5d973053", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 8146, "upload_time": "2014-04-01T14:54:55", "url": "https://files.pythonhosted.org/packages/72/6d/c95d1425b1d1630014aa9dfdb80f5c14dd634fc6cf19718b2fba5095ec6f/httpbenchmark-0.4.tar.gz" } ], "0.5": [ { "comment_text": "", "digests": { "md5": "873d82b30e66dd6d38fb424671fd1c06", "sha256": "b0a63aa116e5768c66cb1f83f2a249f7aa04c8fb153a9153f0e89b10307a562e" }, "downloads": -1, "filename": "httpbenchmark-0.5.tar.gz", "has_sig": false, "md5_digest": "873d82b30e66dd6d38fb424671fd1c06", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 8169, "upload_time": "2014-04-03T02:26:50", "url": "https://files.pythonhosted.org/packages/32/6a/73f34c52f4bb765da866e95368766d25060a4394503efed55776bf689986/httpbenchmark-0.5.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "873d82b30e66dd6d38fb424671fd1c06", "sha256": "b0a63aa116e5768c66cb1f83f2a249f7aa04c8fb153a9153f0e89b10307a562e" }, "downloads": -1, "filename": "httpbenchmark-0.5.tar.gz", "has_sig": false, "md5_digest": "873d82b30e66dd6d38fb424671fd1c06", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 8169, "upload_time": "2014-04-03T02:26:50", "url": "https://files.pythonhosted.org/packages/32/6a/73f34c52f4bb765da866e95368766d25060a4394503efed55776bf689986/httpbenchmark-0.5.tar.gz" } ] }