summaryrefslogtreecommitdiffstats
path: root/python/mozbuild/mozbuild/html_build_viewer.py
blob: 0582e6f1beb30f49fe1d9f9b6dd2744e66a96e77 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

# This module contains code for running an HTTP server to view build info.
import http.server
import json
import os

import requests


class HTTPHandler(http.server.BaseHTTPRequestHandler):
    def do_GET(self):
        s = self.server.wrapper
        p = self.path

        if p == "/build_resources.json":
            self.send_response(200)
            self.send_header("Content-Type", "application/json; charset=utf-8")
            self.end_headers()

            keys = sorted(s.json_files.keys())
            s = json.dumps({"files": ["resources/%s" % k for k in keys]})
            self.wfile.write(s.encode("utf-8"))
            return

        if p.startswith("/resources/"):
            key = p[len("/resources/") :]

            if key not in s.json_files:
                self.send_error(404)
                return

            self.send_response(200)
            self.send_header("Content-Type", "application/json; charset=utf-8")
            self.end_headers()

            self.wfile.write(s.json_files[key])
            return

        if p == "/":
            p = "/build_resources.html"

        self.serve_docroot(s.doc_root, p[1:])

    def do_POST(self):
        if self.path == "/shutdown":
            self.server.wrapper.do_shutdown = True
            self.send_response(200)
            return

        self.send_error(404)

    def serve_docroot(self, root, path):
        local_path = os.path.normpath(os.path.join(root, path))

        # Cheap security. This doesn't resolve symlinks, etc. But, it should be
        # acceptable since this server only runs locally.
        if not local_path.startswith(root):
            self.send_error(404)

        if not os.path.exists(local_path):
            self.send_error(404)
            return

        if os.path.isdir(local_path):
            self.send_error(500)
            return

        self.send_response(200)
        ct = "text/plain"
        if path.endswith(".html"):
            ct = "text/html"

        self.send_header("Content-Type", ct)
        self.end_headers()

        with open(local_path, "rb") as fh:
            self.wfile.write(fh.read())


class BuildViewerServer(object):
    def __init__(self, address="localhost", port=0):
        # TODO use pkg_resources to obtain HTML resources.
        pkg_dir = os.path.dirname(os.path.abspath(__file__))
        doc_root = os.path.join(pkg_dir, "resources", "html-build-viewer")
        assert os.path.isdir(doc_root)

        self.doc_root = doc_root
        self.json_files = {}

        self.server = http.server.HTTPServer((address, port), HTTPHandler)
        self.server.wrapper = self
        self.do_shutdown = False

    @property
    def url(self):
        hostname, port = self.server.server_address
        return "http://%s:%d/" % (hostname, port)

    def add_resource_json_file(self, key, path):
        """Register a resource JSON file with the server.

        The file will be made available under the name/key specified."""
        with open(path, "rb") as fh:
            self.json_files[key] = fh.read()

    def add_resource_json_url(self, key, url):
        """Register a resource JSON file at a URL."""
        r = requests.get(url)
        if r.status_code != 200:
            raise Exception("Non-200 HTTP response code")
        self.json_files[key] = r.text

    def run(self):
        while not self.do_shutdown:
            self.server.handle_request()