A fork of hyde, the static site generation. Some patches will be pushed upstream.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

214 lines
7.3 KiB

  1. # -*- coding: utf-8 -*-
  2. """
  3. Contains classes and utilities for serving a site
  4. generated from hyde.
  5. """
  6. import os
  7. import select
  8. import threading
  9. import urlparse
  10. import urllib
  11. from SimpleHTTPServer import SimpleHTTPRequestHandler
  12. from BaseHTTPServer import HTTPServer
  13. from hyde.fs import File, Folder
  14. from hyde.site import Site
  15. from hyde.generator import Generator
  16. from hyde.exceptions import HydeException
  17. from hyde.util import getLoggerWithNullHandler
  18. logger = getLoggerWithNullHandler('hyde.server')
  19. from datetime import datetime
  20. class HydeRequestHandler(SimpleHTTPRequestHandler):
  21. """
  22. Serves files by regenerating the resource (or)
  23. everything when a request is issued.
  24. """
  25. def do_GET(self):
  26. """
  27. Identify the requested path. If the query string
  28. contains `refresh`, regenerat the entire site.
  29. Otherwise, regenerate only the requested resource
  30. and serve.
  31. """
  32. self.server.request_time = datetime.now()
  33. logger.debug("Processing request: [%s]" % self.path)
  34. result = urlparse.urlparse(self.path)
  35. query = urlparse.parse_qs(result.query)
  36. if 'refresh' in query or result.query=='refresh':
  37. self.server.regenerate()
  38. if 'refresh' in query:
  39. del query['refresh']
  40. parts = list(tuple(result))
  41. parts[4] = urllib.urlencode(query)
  42. parts = tuple(parts)
  43. new_url = urlparse.urlunparse(parts)
  44. logger.info('Redirecting... [%s]' % new_url)
  45. self.redirect(new_url)
  46. else:
  47. SimpleHTTPRequestHandler.do_GET(self)
  48. def translate_path(self, path):
  49. """
  50. Finds the absolute path of the requested file by
  51. referring to the `site` variable in the server.
  52. """
  53. path = SimpleHTTPRequestHandler.translate_path(self, path)
  54. site = self.server.site
  55. result = urlparse.urlparse(self.path)
  56. logger.debug("Trying to load file based on request: [%s]" % result.path)
  57. path = result.path.lstrip('/')
  58. res = None
  59. if path.strip() == "" or File(path).kind.strip() == "":
  60. deployed = site.config.deploy_root_path.child(path)
  61. deployed = Folder.file_or_folder(deployed)
  62. if isinstance(deployed, Folder):
  63. node = site.content.node_from_relative_path(path)
  64. res = node.get_resource('index.html')
  65. else:
  66. res = site.content.resource_from_relative_deploy_path(path)
  67. if not res:
  68. logger.error("Cannot load file: [%s]" % path)
  69. return site.config.deploy_root_path.child(path)
  70. else:
  71. self.server.generate_resource(res)
  72. new_path = site.config.deploy_root_path.child(
  73. res.relative_deploy_path)
  74. return new_path
  75. def do_404(self):
  76. """
  77. Sends a 'not found' response.
  78. """
  79. site = self.server.site
  80. if self.path != site.config.not_found:
  81. self.redirect(site.config.not_found)
  82. else:
  83. res = site.content.resource_from_relative_deploy_path(
  84. site.config.not_found)
  85. message = "Requested resource not found"
  86. if not res:
  87. logger.error(
  88. "Cannot find the 404 template [%s]."
  89. % site.config.not_found)
  90. else:
  91. f404 = File(self.translate_path(site.config.not_found))
  92. if f404.exists:
  93. message = f404.read_all()
  94. self.send_response(200, message)
  95. def redirect(self, path, temporary=True):
  96. """
  97. Sends a redirect header with the new location.
  98. """
  99. self.send_response(302 if temporary else 301)
  100. self.send_header('Location', path)
  101. self.end_headers()
  102. class HydeWebServer(HTTPServer):
  103. """
  104. The hyde web server that regenerates the resource, node or site when
  105. a request is issued.
  106. """
  107. def __init__(self, site, address, port):
  108. self.site = site
  109. self.site.load()
  110. self.generator = Generator(self.site)
  111. self.request_time = datetime.strptime('1-1-1999', '%m-%d-%Y')
  112. self.regeneration_time = datetime.strptime('1-1-1998', '%m-%d-%Y')
  113. self.__is_shut_down = threading.Event()
  114. self.__shutdown_request = False
  115. HTTPServer.__init__(self, (address, port),
  116. HydeRequestHandler)
  117. ####### Code from python 2.7.1: Socket server
  118. ####### Duplicated to make sure shutdown works in Python v > 2.6
  119. #######
  120. def serve_forever(self, poll_interval=0.5):
  121. """Handle one request at a time until shutdown.
  122. Polls for shutdown every poll_interval seconds. Ignores
  123. self.timeout. If you need to do periodic tasks, do them in
  124. another thread.
  125. """
  126. self.__is_shut_down.clear()
  127. try:
  128. while not self.__shutdown_request:
  129. # XXX: Consider using another file descriptor or
  130. # connecting to the socket to wake this up instead of
  131. # polling. Polling reduces our responsiveness to a
  132. # shutdown request and wastes cpu at all other times.
  133. r, w, e = select.select([self], [], [], poll_interval)
  134. if self in r:
  135. self._handle_request_noblock()
  136. finally:
  137. self.__shutdown_request = False
  138. self.__is_shut_down.set()
  139. def shutdown(self):
  140. """Stops the serve_forever loop.
  141. Blocks until the loop has finished. This must be called while
  142. serve_forever() is running in another thread, or it will
  143. deadlock.
  144. """
  145. self.__shutdown_request = True
  146. self.__is_shut_down.wait()
  147. ############## Duplication End.
  148. def regenerate(self):
  149. """
  150. Regenerates the entire site.
  151. """
  152. try:
  153. logger.info('Regenerating the entire site')
  154. self.regeneration_time = datetime.now()
  155. self.site.load()
  156. self.generator.generate_all(incremental=False)
  157. except Exception, exception:
  158. logger.error('Error occured when regenerating the site [%s]'
  159. % exception.message)
  160. def generate_node(self, node):
  161. """
  162. Generates the given node.
  163. """
  164. deploy = self.site.config.deploy_root_path
  165. if not deploy.exists:
  166. return self.regenerate()
  167. try:
  168. logger.debug('Serving node [%s]' % node)
  169. self.generator.generate_node(node, incremental=True)
  170. except Exception, exception:
  171. logger.error(
  172. 'Error [%s] occured when generating the node [%s]'
  173. % (repr(exception), node))
  174. def generate_resource(self, resource):
  175. """
  176. Regenerates the given resource.
  177. """
  178. deploy = self.site.config.deploy_root_path
  179. if not deploy.exists:
  180. return self.regenerate()
  181. dest = deploy.child_folder(resource.node.relative_path)
  182. if not dest.exists:
  183. return self.generate_node(resource.node)
  184. try:
  185. logger.debug('Serving resource [%s]' % resource)
  186. self.generator.generate_resource(resource, incremental=True)
  187. except Exception, exception:
  188. logger.error(
  189. 'Error [%s] occured when serving the resource [%s]'
  190. % (repr(exception), resource))