aboutsummaryrefslogtreecommitdiffstats
path: root/nikola/plugins/command/serve.py
diff options
context:
space:
mode:
authorLibravatarAgustin Henze <tin@sluc.org.ar>2015-08-26 07:57:04 -0300
committerLibravatarAgustin Henze <tin@sluc.org.ar>2015-08-26 07:57:04 -0300
commit8041475aab2b8efad7d2857027331bd626d26312 (patch)
tree3980e1763c067079a4cebb425d642ca9eac834bf /nikola/plugins/command/serve.py
parentb0b24795b24ee6809397fbbadf42f31f310a219f (diff)
Imported Upstream version 7.6.4
Diffstat (limited to 'nikola/plugins/command/serve.py')
-rw-r--r--nikola/plugins/command/serve.py83
1 files changed, 73 insertions, 10 deletions
diff --git a/nikola/plugins/command/serve.py b/nikola/plugins/command/serve.py
index 0e4d01f..0441c93 100644
--- a/nikola/plugins/command/serve.py
+++ b/nikola/plugins/command/serve.py
@@ -24,8 +24,11 @@
# OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+"""Start test server."""
+
from __future__ import print_function
import os
+import re
import socket
import webbrowser
try:
@@ -35,16 +38,25 @@ except ImportError:
from http.server import HTTPServer # NOQA
from http.server import SimpleHTTPRequestHandler # NOQA
+try:
+ from StringIO import StringIO
+except ImportError:
+ from io import BytesIO as StringIO # NOQA
+
+
from nikola.plugin_categories import Command
-from nikola.utils import get_logger
+from nikola.utils import get_logger, STDERR_HANDLER
class IPv6Server(HTTPServer):
+
"""An IPv6 HTTPServer."""
+
address_family = socket.AF_INET6
class CommandServe(Command):
+
"""Start test server."""
name = "serve"
@@ -70,6 +82,14 @@ class CommandServe(Command):
'help': 'Address to bind (default: 0.0.0.0 – all local IPv4 interfaces)',
},
{
+ 'name': 'detach',
+ 'short': 'd',
+ 'long': 'detach',
+ 'type': bool,
+ 'default': False,
+ 'help': 'Detach from TTY (work in the background)',
+ },
+ {
'name': 'browser',
'short': 'b',
'long': 'browser',
@@ -89,7 +109,7 @@ class CommandServe(Command):
def _execute(self, options, args):
"""Start test server."""
- self.logger = get_logger('serve', self.site.loghandlers)
+ self.logger = get_logger('serve', STDERR_HANDLER)
out_dir = self.site.config['OUTPUT_FOLDER']
if not os.path.isdir(out_dir):
self.logger.error("Missing '{0}' folder?".format(out_dir))
@@ -117,16 +137,42 @@ class CommandServe(Command):
server_url = "http://{0}:{1}/".format(*sa)
self.logger.info("Opening {0} in the default web browser...".format(server_url))
webbrowser.open(server_url)
- try:
- httpd.serve_forever()
- except KeyboardInterrupt:
- self.logger.info("Server is shutting down.")
- return 130
+ if options['detach']:
+ OurHTTPRequestHandler.quiet = True
+ try:
+ pid = os.fork()
+ if pid == 0:
+ httpd.serve_forever()
+ else:
+ self.logger.info("Detached with PID {0}. Run `kill {0}` to stop the server.".format(pid))
+ except AttributeError as e:
+ if os.name == 'nt':
+ self.logger.warning("Detaching is not available on Windows, server is running in the foreground.")
+ else:
+ raise e
+ else:
+ try:
+ httpd.serve_forever()
+ except KeyboardInterrupt:
+ self.logger.info("Server is shutting down.")
+ return 130
class OurHTTPRequestHandler(SimpleHTTPRequestHandler):
+
+ """A request handler, modified for Nikola."""
+
extensions_map = dict(SimpleHTTPRequestHandler.extensions_map)
extensions_map[""] = "text/plain"
+ quiet = False
+
+ def log_message(self, *args):
+ """Log messages. Or not, depending on a setting."""
+ if self.quiet:
+ return
+ else:
+ # Old-style class in Python 2.7, cannot use super()
+ return SimpleHTTPRequestHandler.log_message(self, *args)
# NOTICE: this is a patched version of send_head() to disable all sorts of
# caching. `nikola serve` is a development server, hence caching should
@@ -182,14 +228,31 @@ class OurHTTPRequestHandler(SimpleHTTPRequestHandler):
except IOError:
self.send_error(404, "File not found")
return None
+
+ filtered_bytes = None
+ if ctype == 'text/html':
+ # Comment out any <base> to allow local resolution of relative URLs.
+ data = f.read().decode('utf8')
+ f.close()
+ data = re.sub(r'<base\s([^>]*)>', '<!--base \g<1>-->', data, re.IGNORECASE)
+ data = data.encode('utf8')
+ f = StringIO()
+ f.write(data)
+ filtered_bytes = len(data)
+ f.seek(0)
+
self.send_response(200)
self.send_header("Content-type", ctype)
if os.path.splitext(path)[1] == '.svgz':
# Special handling for svgz to make it work nice with browsers.
self.send_header("Content-Encoding", 'gzip')
- fs = os.fstat(f.fileno())
- self.send_header("Content-Length", str(fs[6]))
- self.send_header("Last-Modified", self.date_time_string(fs.st_mtime))
+
+ if filtered_bytes is None:
+ fs = os.fstat(f.fileno())
+ self.send_header('Content-Length', str(fs[6]))
+ else:
+ self.send_header('Content-Length', filtered_bytes)
+
# begin no-cache patch
# For standard requests.
self.send_header("Cache-Control", "no-cache, no-store, "