/circuits/web/main.py

https://bitbucket.org/prologic/circuits/ · Python · 245 lines · 170 code · 69 blank · 6 comment · 36 complexity · c8b07e6c286e26914dd3c161a3a6ca53 MD5 · raw file

  1. #!/usr/bin/env python
  2. # -*- coding: utf-8 -*-
  3. """Main
  4. circutis.web Web Server and Testing Tool.
  5. """
  6. import os
  7. from sys import stderr
  8. from hashlib import md5
  9. from optparse import OptionParser
  10. from wsgiref.validate import validator
  11. from wsgiref.simple_server import make_server
  12. try:
  13. import hotshot
  14. import hotshot.stats
  15. except ImportError:
  16. hostshot = None
  17. import circuits
  18. from circuits import handler, Component, Manager, Debugger
  19. from circuits.core.pollers import Select
  20. from circuits.tools import inspect, graph
  21. from circuits.web.wsgi import Application
  22. from circuits.web.tools import check_auth, digest_auth
  23. from circuits.web import BaseServer, Controller, Logger, Server, Static
  24. try:
  25. from circuits.core.pollers import Poll
  26. except ImportError:
  27. Poll = None # NOQA
  28. try:
  29. from circuits.core.pollers import EPoll
  30. except ImportError:
  31. EPoll = None # NOQA
  32. USAGE = "%prog [options] [docroot]"
  33. VERSION = "%prog v" + circuits.__version__
  34. def parse_options():
  35. parser = OptionParser(usage=USAGE, version=VERSION)
  36. parser.add_option(
  37. "-b", "--bind",
  38. action="store", type="string", default="0.0.0.0:8000", dest="bind",
  39. help="Bind to address:[port]"
  40. )
  41. parser.add_option(
  42. "-l", "--logging",
  43. action="store_true", default=False, dest="logging",
  44. help="Enable logging of requests"
  45. )
  46. parser.add_option(
  47. "-p", "--passwd",
  48. action="store", default=None, dest="passwd",
  49. help="Location to passwd file for Digest Auth"
  50. )
  51. parser.add_option(
  52. "-j", "--jobs",
  53. action="store", type="int", default=0, dest="jobs",
  54. help="Specify no. of jobs/processes to start"
  55. )
  56. parser.add_option(
  57. "", "--poller",
  58. action="store", type="string", default="select", dest="poller",
  59. help="Specify type of poller to use"
  60. )
  61. parser.add_option(
  62. "", "--server",
  63. action="store", type="string", default="server", dest="server",
  64. help="Specify server to use"
  65. )
  66. parser.add_option(
  67. "", "--profile",
  68. action="store_true", default=False, dest="profile",
  69. help="Enable execution profiling support"
  70. )
  71. parser.add_option(
  72. "", "--debug",
  73. action="store_true", default=False, dest="debug",
  74. help="Enable debug mode"
  75. )
  76. parser.add_option(
  77. "", "--validate",
  78. action="store_true", default=False, dest="validate",
  79. help="Enable WSGI validation mode"
  80. )
  81. opts, args = parser.parse_args()
  82. return opts, args
  83. class Authentication(Component):
  84. channel = "web"
  85. realm = "Secure Area"
  86. users = {"admin": md5("admin").hexdigest()}
  87. def __init__(self, channel=channel, realm=None, passwd=None):
  88. super(Authentication, self).__init__(self, channel=channel)
  89. if realm is not None:
  90. self.realm = realm
  91. if passwd is not None:
  92. with open(passwd, "r") as f:
  93. lines = (line.strip() for line in f)
  94. self.users = dict((line.split(":", 1) for line in lines))
  95. @handler("request", priority=10)
  96. def request(self, event, request, response):
  97. if not check_auth(request, response, self.realm, self.users):
  98. event.stop()
  99. return digest_auth(request, response, self.realm, self.users)
  100. class HelloWorld(Component):
  101. channel = "web"
  102. def request(self, request, response):
  103. return "Hello World!"
  104. class Root(Controller):
  105. def hello(self):
  106. return "Hello World!"
  107. def select_poller(poller):
  108. if poller == "poll":
  109. if Poll is None:
  110. stderr.write(
  111. "No poll support available - defaulting to Select..."
  112. )
  113. Poller = Select
  114. else:
  115. Poller = Poll
  116. elif poller == "epoll":
  117. if EPoll is None:
  118. stderr.write(
  119. "No epoll support available - defaulting to Select..."
  120. )
  121. Poller = Select
  122. else:
  123. Poller = EPoll
  124. else:
  125. Poller = Select
  126. return Poller
  127. def parse_bind(bind):
  128. if ":" in bind:
  129. address, port = bind.split(":")
  130. port = int(port)
  131. else:
  132. address, port = bind, 8000
  133. return (address, port)
  134. def main():
  135. opts, args = parse_options()
  136. bind = parse_bind(opts.bind)
  137. if opts.validate:
  138. application = (Application() + Root())
  139. app = validator(application)
  140. httpd = make_server(bind[0], bind[1], app)
  141. httpd.serve_forever()
  142. raise SystemExit(0)
  143. manager = Manager()
  144. opts.debug and Debugger().register(manager)
  145. Poller = select_poller(opts.poller.lower())
  146. Poller().register(manager)
  147. if opts.server.lower() == "base":
  148. BaseServer(bind).register(manager)
  149. HelloWorld().register(manager)
  150. else:
  151. Server(bind).register(manager)
  152. Root().register(manager)
  153. docroot = os.getcwd() if not args else args[0]
  154. Static(docroot=docroot, dirlisting=True).register(manager)
  155. opts.passwd and Authentication(passwd=opts.passwd).register(manager)
  156. opts.logging and Logger().register(manager)
  157. if opts.profile and hotshot:
  158. profiler = hotshot.Profile(".profile")
  159. profiler.start()
  160. if opts.debug:
  161. print(graph(manager, name="circuits.web"))
  162. print()
  163. print(inspect(manager))
  164. for i in range(opts.jobs):
  165. manager.start(process=True)
  166. manager.run()
  167. if opts.profile and hotshot:
  168. profiler.stop()
  169. profiler.close()
  170. stats = hotshot.stats.load(".profile")
  171. stats.strip_dirs()
  172. stats.sort_stats("time", "calls")
  173. stats.print_stats(20)
  174. if __name__ == "__main__":
  175. main()