/hyde/server.py

http://github.com/hyde/hyde · Python · 211 lines · 180 code · 11 blank · 20 comment · 17 complexity · 6d8e9de26fd88b8edb50f09d98522f7e MD5 · raw file

  1. # -*- coding: utf-8 -*-
  2. """
  3. Contains classes and utilities for serving a site
  4. generated from hyde.
  5. """
  6. import threading
  7. import urllib
  8. import traceback
  9. from datetime import datetime
  10. from hyde._compat import (HTTPServer, iteritems, parse, PY3,
  11. SimpleHTTPRequestHandler, unquote)
  12. from hyde.generator import Generator
  13. from fswrap import File, Folder
  14. from commando.util import getLoggerWithNullHandler
  15. logger = getLoggerWithNullHandler('hyde.server')
  16. class HydeRequestHandler(SimpleHTTPRequestHandler):
  17. """
  18. Serves files by regenerating the resource (or)
  19. everything when a request is issued.
  20. """
  21. def do_GET(self):
  22. """
  23. Identify the requested path. If the query string
  24. contains `refresh`, regenerat the entire site.
  25. Otherwise, regenerate only the requested resource
  26. and serve.
  27. """
  28. self.server.request_time = datetime.now()
  29. logger.debug("Processing request: [%s]" % self.path)
  30. result = parse.urlparse(self.path)
  31. query = parse.parse_qs(result.query)
  32. if 'refresh' in query or result.query == 'refresh':
  33. self.server.regenerate()
  34. if 'refresh' in query:
  35. del query['refresh']
  36. parts = list(tuple(result))
  37. parts[4] = urllib.urlencode(query)
  38. parts = tuple(parts)
  39. new_url = parse.urlunparse(parts)
  40. logger.info('Redirecting... [%s]' % new_url)
  41. self.redirect(new_url)
  42. else:
  43. SimpleHTTPRequestHandler.do_GET(self)
  44. def translate_path(self, path):
  45. """
  46. Finds the absolute path of the requested file by
  47. referring to the `site` variable in the server.
  48. """
  49. site = self.server.site
  50. path = unquote(self.path)
  51. if not PY3:
  52. path = path.decode('utf-8')
  53. result = parse.urlparse(path)
  54. logger.debug(
  55. "Trying to load file based on request: [%s]" % result.path)
  56. path = result.path.lstrip('/')
  57. res = None
  58. if path.strip() == "" or File(path).kind.strip() == "":
  59. deployed = site.config.deploy_root_path.child(path)
  60. deployed = Folder.file_or_folder(deployed)
  61. if isinstance(deployed, Folder):
  62. node = site.content.node_from_relative_path(path)
  63. res = node.get_resource('index.html')
  64. elif hasattr(site.config, 'urlcleaner') and hasattr(
  65. site.config.urlcleaner, 'strip_extensions'):
  66. for ext in site.config.urlcleaner.strip_extensions:
  67. res = site.content.resource_from_relative_deploy_path(
  68. path + '.' + ext)
  69. if res:
  70. break
  71. for ext in site.config.urlcleaner.strip_extensions:
  72. new_path = site.config.deploy_root_path.child(
  73. path + '.' + ext)
  74. if File(new_path).exists:
  75. return new_path
  76. else:
  77. res = site.content.resource_from_relative_deploy_path(path)
  78. if not res:
  79. logger.error("Cannot load file: [%s]" % path)
  80. return site.config.deploy_root_path.child(path)
  81. else:
  82. self.server.generate_resource(res)
  83. new_path = site.config.deploy_root_path.child(
  84. res.relative_deploy_path)
  85. return new_path
  86. def do_404(self):
  87. """
  88. Sends a 'not found' response.
  89. """
  90. site = self.server.site
  91. if self.path != site.config.not_found:
  92. self.redirect(site.config.not_found)
  93. else:
  94. res = site.content.resource_from_relative_deploy_path(
  95. site.config.not_found)
  96. message = "Requested resource not found"
  97. if not res:
  98. logger.error(
  99. "Cannot find the 404 template [%s]."
  100. % site.config.not_found)
  101. else:
  102. f404 = File(self.translate_path(site.config.not_found))
  103. if f404.exists:
  104. message = f404.read_all()
  105. self.send_response(200, message)
  106. def redirect(self, path, temporary=True):
  107. """
  108. Sends a redirect header with the new location.
  109. """
  110. self.send_response(302 if temporary else 301)
  111. self.send_header('Location', path)
  112. self.end_headers()
  113. class HydeWebServer(HTTPServer):
  114. """
  115. The hyde web server that regenerates the resource, node or site when
  116. a request is issued.
  117. """
  118. def __init__(self, site, address, port):
  119. self.site = site
  120. self.site.load()
  121. self.generator = Generator(self.site)
  122. self.request_time = datetime.strptime('1-1-1999', '%m-%d-%Y')
  123. self.regeneration_time = datetime.strptime('1-1-1998', '%m-%d-%Y')
  124. self.__is_shut_down = threading.Event()
  125. self.__shutdown_request = False
  126. self.map_extensions()
  127. HTTPServer.__init__(self, (address, port),
  128. HydeRequestHandler)
  129. def map_extensions(self):
  130. """
  131. Maps extensions specified in the configuration.
  132. """
  133. try:
  134. extensions = self.site.config.server.extensions.to_dict()
  135. except AttributeError:
  136. extensions = {}
  137. for extension, type in iteritems(extensions):
  138. ext = "." + extension if not extension == 'default' else ''
  139. HydeRequestHandler.extensions_map[ext] = type
  140. def regenerate(self):
  141. """
  142. Regenerates the entire site.
  143. """
  144. try:
  145. logger.info('Regenerating the entire site')
  146. self.regeneration_time = datetime.now()
  147. if self.site.config.needs_refresh():
  148. self.site.config.reload()
  149. self.site.load()
  150. self.generator.generate_all(incremental=False)
  151. except Exception as exception:
  152. logger.error('Error occured when regenerating the site [%s]'
  153. % exception.message)
  154. logger.debug(traceback.format_exc())
  155. def generate_node(self, node):
  156. """
  157. Generates the given node.
  158. """
  159. deploy = self.site.config.deploy_root_path
  160. if not deploy.exists:
  161. return self.regenerate()
  162. try:
  163. logger.debug('Serving node [%s]' % node)
  164. self.generator.generate_node(node, incremental=True)
  165. except Exception as exception:
  166. logger.error(
  167. 'Error [%s] occured when generating the node [%s]'
  168. % (repr(exception), node))
  169. logger.debug(traceback.format_exc())
  170. def generate_resource(self, resource):
  171. """
  172. Regenerates the given resource.
  173. """
  174. deploy = self.site.config.deploy_root_path
  175. if not deploy.exists:
  176. return self.regenerate()
  177. dest = deploy.child_folder(resource.node.relative_path)
  178. if not dest.exists:
  179. return self.generate_node(resource.node)
  180. try:
  181. logger.debug('Serving resource [%s]' % resource)
  182. self.generator.generate_resource(resource, incremental=True)
  183. except Exception as exception:
  184. logger.error(
  185. 'Error [%s] occured when serving the resource [%s]'
  186. % (repr(exception), resource))
  187. logger.debug(traceback.format_exc())