From 77b6d83e0beb9d633d0882d1be0aee2d7778d5fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Wed, 24 May 2017 16:53:54 +0200 Subject: [PATCH] Set X-Robots-Tag and remove Server header in responses Also set robots meta tag in index.jinja2 --- src/octoprint/server/__init__.py | 19 ++++++++-- src/octoprint/server/util/tornado.py | 56 ++++++++++++++++++++++++++-- src/octoprint/templates/index.jinja2 | 2 + 3 files changed, 71 insertions(+), 6 deletions(-) diff --git a/src/octoprint/server/__init__.py b/src/octoprint/server/__init__.py index 6b3c8cfc..64a4c0b6 100644 --- a/src/octoprint/server/__init__.py +++ b/src/octoprint/server/__init__.py @@ -164,7 +164,7 @@ class Server(object): global safe_mode from tornado.ioloop import IOLoop - from tornado.web import Application, RequestHandler + from tornado.web import Application debug = self._debug safe_mode = self._safe_mode @@ -437,9 +437,22 @@ class Server(object): self._logger.debug("Adding additional route {route} handled by handler {handler} and with additional arguments {kwargs!r}".format(**locals())) server_routes.append((route, handler, kwargs)) - server_routes.append((r".*", util.tornado.UploadStorageFallbackHandler, dict(fallback=util.tornado.WsgiInputContainer(app.wsgi_app), file_prefix="octoprint-file-upload-", file_suffix=".tmp", suffixes=upload_suffixes))) + headers = {"X-Robots-Tag": "noindex, nofollow, noimageindex"} + removed_headers = ["Server"] - self._tornado_app = Application(server_routes) + server_routes.append((r".*", util.tornado.UploadStorageFallbackHandler, dict(fallback=util.tornado.WsgiInputContainer(app.wsgi_app, + headers=headers, + removed_headers=removed_headers), + file_prefix="octoprint-file-upload-", + file_suffix=".tmp", + suffixes=upload_suffixes))) + + transforms = [util.tornado.GlobalHeaderTransform.for_headers("OctoPrintGlobalHeaderTransform", + headers=headers, + removed_headers=removed_headers)] + + self._tornado_app = Application(handlers=server_routes, + transforms=transforms) max_body_sizes = [ ("POST", r"/api/files/([^/]*)", self._settings.getInt(["server", "uploads", "maxSize"])), ("POST", r"/api/languages", 5 * 1024 * 1024) diff --git a/src/octoprint/server/util/tornado.py b/src/octoprint/server/util/tornado.py index 12ba3b9b..6373733e 100644 --- a/src/octoprint/server/util/tornado.py +++ b/src/octoprint/server/util/tornado.py @@ -531,9 +531,20 @@ class WsgiInputContainer(object): methods have been adjusted to allow for an optionally supplied ``body`` argument which is then used for ``wsgi.input``. """ - def __init__(self, wsgi_application): + def __init__(self, wsgi_application, headers=None, forced_headers=None, removed_headers=None): self.wsgi_application = wsgi_application + if headers is None: + headers = dict() + if forced_headers is None: + forced_headers = dict() + if removed_headers is None: + removed_headers = [] + + self.headers = headers + self.forced_headers = forced_headers + self.removed_headers = removed_headers + def __call__(self, request, body=None): """ Wraps the call against the WSGI app, deriving the WSGI environment from the supplied Tornado ``HTTPServerRequest``. @@ -569,8 +580,14 @@ class WsgiInputContainer(object): headers.append(("Content-Length", str(len(body)))) if "content-type" not in header_set: headers.append(("Content-Type", "text/html; charset=UTF-8")) - if "server" not in header_set: - headers.append(("Server", "TornadoServer/%s" % tornado.version)) + + header_set = set(k.lower() for (k, v) in headers) + for header, value in self.headers.items(): + if header.lower() not in header_set: + headers.append((header, value)) + for header, value in self.forced_headers.items(): + headers.append((header, value)) + headers = [(header, value) for header, value in headers if not header.lower() in self.removed_headers] parts = [tornado.escape.utf8("HTTP/1.1 " + data["status"] + "\r\n")] for key, value in headers: @@ -1025,6 +1042,39 @@ class StaticDataHandler(tornado.web.RequestHandler): self.finish() +class GlobalHeaderTransform(tornado.web.OutputTransform): + + HEADERS = dict() + FORCED_HEADERS = dict() + REMOVED_HEADERS = [] + + @classmethod + def for_headers(cls, name, headers=None, forced_headers=None, removed_headers=None): + if headers is None: + headers = dict() + if forced_headers is None: + forced_headers = dict() + if removed_headers is None: + removed_headers = [] + + return type(name, (GlobalHeaderTransform,), dict(HEADERS=headers, + FORCED_HEADERS=forced_headers, + REMOVED_HEADERS=removed_headers)) + + def __init__(self, request): + tornado.web.OutputTransform.__init__(self, request) + + def transform_first_chunk(self, status_code, headers, chunk, finishing): + for header, value in self.HEADERS.items(): + if not header in headers: + headers[header] = value + for header, value in self.FORCED_HEADERS.items(): + headers[header] = value + for header in self.REMOVED_HEADERS: + del headers[header] + return status_code, headers, chunk + + #~~ Factory method for creating Flask access validation wrappers from the Tornado request context diff --git a/src/octoprint/templates/index.jinja2 b/src/octoprint/templates/index.jinja2 index 826804a1..d2107598 100644 --- a/src/octoprint/templates/index.jinja2 +++ b/src/octoprint/templates/index.jinja2 @@ -8,6 +8,8 @@ + + {% include 'stylesheets.jinja2' %} {% include 'initscript.jinja2' %}