1d3abcdfa30e476571e214d5d74fb12ac43d153baTed Kremenek# Copyright (C) 2011 Google Inc. All rights reserved.
2d3abcdfa30e476571e214d5d74fb12ac43d153baTed Kremenek#
3d3abcdfa30e476571e214d5d74fb12ac43d153baTed Kremenek# Redistribution and use in source and binary forms, with or without
4d3abcdfa30e476571e214d5d74fb12ac43d153baTed Kremenek# modification, are permitted provided that the following conditions are
5d3abcdfa30e476571e214d5d74fb12ac43d153baTed Kremenek# met:
6d3abcdfa30e476571e214d5d74fb12ac43d153baTed Kremenek#
7d3abcdfa30e476571e214d5d74fb12ac43d153baTed Kremenek# 1.  Redistributions of source code must retain the above copyright
8d3abcdfa30e476571e214d5d74fb12ac43d153baTed Kremenek#     notice, this list of conditions and the following disclaimer.
9d3abcdfa30e476571e214d5d74fb12ac43d153baTed Kremenek# 2.  Redistributions in binary form must reproduce the above copyright
10d3abcdfa30e476571e214d5d74fb12ac43d153baTed Kremenek#     notice, this list of conditions and the following disclaimer in the
11d3abcdfa30e476571e214d5d74fb12ac43d153baTed Kremenek#     documentation and/or other materials provided with the distribution.
12d3abcdfa30e476571e214d5d74fb12ac43d153baTed Kremenek#
13d3abcdfa30e476571e214d5d74fb12ac43d153baTed Kremenek# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
149b663716449b618ba0390b1dbebc54fa8e971124Ted Kremenek# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
15fea5f5a1e8fe558d716ddeddf1aecf63f2e9a54fTed Kremenek# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
16183ba8e19d49ab1ae25d3cdd0a19591369c5ab9fJordan Rose# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
17fea5f5a1e8fe558d716ddeddf1aecf63f2e9a54fTed Kremenek# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
1855fc873017f10f6f566b182b70f6fc22aefa3464Chandler Carruth# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
194d353eb8af7324c0ee3736c736668f6c9b162ee0Anna Zaks# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20d3a413d3b8eb39bcee5944bc545d9997c1abe492Sebastian Redl# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2155fc873017f10f6f566b182b70f6fc22aefa3464Chandler Carruth# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2255fc873017f10f6f566b182b70f6fc22aefa3464Chandler Carruth# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23f4c839657742b823cea1a95b18422f1ba74d3dddChris Lattner# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24615a092a511cd2dfe1a5364ebf5f80e55e33034dJordan Rose
25a93d0f280693b8418bc88cf7a8c93325f7fcf4c6Benjamin Kramerimport BaseHTTPServer
261140186abc9c6bd39f50d9866892e0b050e5f938Ted Kremenekimport SocketServer
27d3abcdfa30e476571e214d5d74fb12ac43d153baTed Kremenekimport logging
289ef6537a894c33003359b1f9b9676e9178e028b7Ted Kremenekimport json
290e5c8d4ca6c7e472ddde13495c8d3558983de85aTed Kremenekimport os
300e5c8d4ca6c7e472ddde13495c8d3558983de85aTed Kremenekimport re
31ed866e73bab7733f5226f84c52edefe23d694b2fTed Kremenekimport sys
32802e02463b880f53a6e645bde78cc412481ce9e0Ted Kremenekimport urllib
330e5c8d4ca6c7e472ddde13495c8d3558983de85aTed Kremenek
340e5c8d4ca6c7e472ddde13495c8d3558983de85aTed Kremenekfrom webkitpy.common.memoized import memoized
35ed866e73bab7733f5226f84c52edefe23d694b2fTed Kremenekfrom webkitpy.tool.servers.reflectionhandler import ReflectionHandler
360e5c8d4ca6c7e472ddde13495c8d3558983de85aTed Kremenekfrom webkitpy.layout_tests.port import builders
370e5c8d4ca6c7e472ddde13495c8d3558983de85aTed Kremenek
380e5c8d4ca6c7e472ddde13495c8d3558983de85aTed Kremenek
390e5c8d4ca6c7e472ddde13495c8d3558983de85aTed Kremenek_log = logging.getLogger(__name__)
400e5c8d4ca6c7e472ddde13495c8d3558983de85aTed Kremenek
418af2975d50270813ae6366d007e9e1f5b65ddc68Ted Kremenek
425f9e272e632e951b1efe824cd16acb4d96077930Chris Lattnerclass GardeningHTTPServer(SocketServer.ThreadingMixIn, BaseHTTPServer.HTTPServer):
435f9e272e632e951b1efe824cd16acb4d96077930Chris Lattner    def __init__(self, httpd_port, config):
44d8e494faa9f4bc13cd0a136b4d1ee1cfd52860eaBenjamin Kramer        server_name = ''
45d8e494faa9f4bc13cd0a136b4d1ee1cfd52860eaBenjamin Kramer        self.tool = config['tool']
46d8e494faa9f4bc13cd0a136b4d1ee1cfd52860eaBenjamin Kramer        self.options = config['options']
478af2975d50270813ae6366d007e9e1f5b65ddc68Ted Kremenek        BaseHTTPServer.HTTPServer.__init__(self, (server_name, httpd_port), GardeningHTTPRequestHandler)
488af2975d50270813ae6366d007e9e1f5b65ddc68Ted Kremenek
495f9e272e632e951b1efe824cd16acb4d96077930Chris Lattner    def url(self, args=None):
50e3ce2656790523fd8c70653ee1ec41e5898f82ebTed Kremenek        # We can't use urllib.encode() here because that encodes spaces as plus signs and the buildbots don't decode those properly.
51d95b70175646829c26344d5f0bda1ec3009f2a5bAnna Zaks        arg_string = ('?' + '&'.join("%s=%s" % (key, urllib.quote(value)) for (key, value) in args.items())) if args else ''
52d95b70175646829c26344d5f0bda1ec3009f2a5bAnna Zaks        return 'http://localhost:8127/garden-o-matic.html' + arg_string
534850451c8e93789c099d907dd469898ea9561ecbTed Kremenek
541f9bd0fbf5894b41bba5f2fdb0c6546a7a6ef3d8Ted Kremenek
55d95b70175646829c26344d5f0bda1ec3009f2a5bAnna Zaksclass GardeningHTTPRequestHandler(ReflectionHandler):
56082cb8d7bd9bdb3fe58e8e1a2897c79c4ebcc3a7Ted Kremenek    REVISION_LIMIT = 100
574e06387c2d6d00c7a30167d4c2206756992dde38Ted Kremenek    BLINK_SVN_URL = 'http://src.chromium.org/blink/trunk'
581fbfd5b9b8b82aea084773b76dd1ec6796a7000cTed Kremenek    CHROMIUM_SVN_DEPS_URL = 'http://src.chromium.org/chrome/trunk/src/DEPS'
592042fc1f36d471f437023e8899f0c4fadded2341Ted Kremenek    # "webkit_revision": "149598",
601fbfd5b9b8b82aea084773b76dd1ec6796a7000cTed Kremenek    BLINK_REVISION_REGEXP = re.compile(r'^  "webkit_revision": "(?P<revision>\d+)",$', re.MULTILINE);
61802e02463b880f53a6e645bde78cc412481ce9e0Ted Kremenek
6207189521a15d9c088216b943649cb9fe231cbb57Ted Kremenek    STATIC_FILE_NAMES = frozenset()
6307189521a15d9c088216b943649cb9fe231cbb57Ted Kremenek
64802e02463b880f53a6e645bde78cc412481ce9e0Ted Kremenek    STATIC_FILE_EXTENSIONS = ('.js', '.css', '.html', '.gif', '.png', '.ico')
65b0e1badc2a9b8275b48dfb15c6907a282b949b02Jordan Rose
66b0e1badc2a9b8275b48dfb15c6907a282b949b02Jordan Rose    STATIC_FILE_DIRECTORY = os.path.join(
67b0e1badc2a9b8275b48dfb15c6907a282b949b02Jordan Rose        os.path.dirname(__file__),
68ed866e73bab7733f5226f84c52edefe23d694b2fTed Kremenek        '..',
69ed866e73bab7733f5226f84c52edefe23d694b2fTed Kremenek        '..',
70b0e1badc2a9b8275b48dfb15c6907a282b949b02Jordan Rose        '..',
71b0e1badc2a9b8275b48dfb15c6907a282b949b02Jordan Rose        '..',
72b0e1badc2a9b8275b48dfb15c6907a282b949b02Jordan Rose        'GardeningServer')
73b0e1badc2a9b8275b48dfb15c6907a282b949b02Jordan Rose
74b0e1badc2a9b8275b48dfb15c6907a282b949b02Jordan Rose    allow_cross_origin_requests = True
75b0e1badc2a9b8275b48dfb15c6907a282b949b02Jordan Rose    debug_output = ''
76b0e1badc2a9b8275b48dfb15c6907a282b949b02Jordan Rose
77b0e1badc2a9b8275b48dfb15c6907a282b949b02Jordan Rose    def ping(self):
78b0e1badc2a9b8275b48dfb15c6907a282b949b02Jordan Rose        self._serve_text('pong')
79b0e1badc2a9b8275b48dfb15c6907a282b949b02Jordan Rose
80b0e1badc2a9b8275b48dfb15c6907a282b949b02Jordan Rose    def _run_webkit_patch(self, command, input_string):
81b0e1badc2a9b8275b48dfb15c6907a282b949b02Jordan Rose        PIPE = self.server.tool.executive.PIPE
82b0e1badc2a9b8275b48dfb15c6907a282b949b02Jordan Rose        process = self.server.tool.executive.popen([self.server.tool.path()] + command, cwd=self.server.tool.scm().checkout_root, stdin=PIPE, stdout=PIPE, stderr=PIPE)
83b0e1badc2a9b8275b48dfb15c6907a282b949b02Jordan Rose        process.stdin.write(input_string)
84b0e1badc2a9b8275b48dfb15c6907a282b949b02Jordan Rose        output, error = process.communicate()
85b0e1badc2a9b8275b48dfb15c6907a282b949b02Jordan Rose        return (process.returncode, output, error)
86b0e1badc2a9b8275b48dfb15c6907a282b949b02Jordan Rose
87b0e1badc2a9b8275b48dfb15c6907a282b949b02Jordan Rose    def svnlog(self):
88b0e1badc2a9b8275b48dfb15c6907a282b949b02Jordan Rose        self._serve_xml(self.server.tool.executive.run_command(['svn', 'log', '--xml', '--limit', self.REVISION_LIMIT, self.BLINK_SVN_URL]))
89b0e1badc2a9b8275b48dfb15c6907a282b949b02Jordan Rose
90b0e1badc2a9b8275b48dfb15c6907a282b949b02Jordan Rose    def lastroll(self):
91b0e1badc2a9b8275b48dfb15c6907a282b949b02Jordan Rose        deps_contents = self.server.tool.executive.run_command(['svn', 'cat', self.CHROMIUM_SVN_DEPS_URL])
92b0e1badc2a9b8275b48dfb15c6907a282b949b02Jordan Rose        match = re.search(self.BLINK_REVISION_REGEXP, deps_contents)
93b0e1badc2a9b8275b48dfb15c6907a282b949b02Jordan Rose        if not match:
94b0e1badc2a9b8275b48dfb15c6907a282b949b02Jordan Rose            _log.error("Unable to produce last Blink roll revision")
95b0e1badc2a9b8275b48dfb15c6907a282b949b02Jordan Rose            self._serve_text("0")
96b0e1badc2a9b8275b48dfb15c6907a282b949b02Jordan Rose            return
97b0e1badc2a9b8275b48dfb15c6907a282b949b02Jordan Rose
98b0e1badc2a9b8275b48dfb15c6907a282b949b02Jordan Rose        revision_line = match.group()
99b0e1badc2a9b8275b48dfb15c6907a282b949b02Jordan Rose        revision = match.group("revision")
100b0e1badc2a9b8275b48dfb15c6907a282b949b02Jordan Rose        self._serve_text(revision)
101b0e1badc2a9b8275b48dfb15c6907a282b949b02Jordan Rose
102b0e1badc2a9b8275b48dfb15c6907a282b949b02Jordan Rose    def rebaselineall(self):
103b0e1badc2a9b8275b48dfb15c6907a282b949b02Jordan Rose        command = ['rebaseline-json']
104b0e1badc2a9b8275b48dfb15c6907a282b949b02Jordan Rose        if self.server.options.results_directory:
105b0e1badc2a9b8275b48dfb15c6907a282b949b02Jordan Rose            command.extend(['--results-directory', self.server.options.results_directory])
106b0e1badc2a9b8275b48dfb15c6907a282b949b02Jordan Rose        if not self.server.options.optimize:
107eb2303c76971f3cc89bbb367ce77564ccb7042c1Ted Kremenek            command.append('--no-optimize')
108eb2303c76971f3cc89bbb367ce77564ccb7042c1Ted Kremenek        if self.server.options.verbose:
10907189521a15d9c088216b943649cb9fe231cbb57Ted Kremenek            command.append('--verbose')
1103a46f5fd1709f6df03bbb8b0abf84052dc0f39ffJordan Rose        json_input = self.read_entity_body()
11197bfb558f69c09b01a5c1510f08dc91eb62329a7Anna Zaks
11297bfb558f69c09b01a5c1510f08dc91eb62329a7Anna Zaks        _log.debug("calling %s, input='%s'", command, json_input)
11397bfb558f69c09b01a5c1510f08dc91eb62329a7Anna Zaks        return_code, output, error = self._run_webkit_patch(command, json_input)
11407189521a15d9c088216b943649cb9fe231cbb57Ted Kremenek        print >> sys.stderr, error
11507189521a15d9c088216b943649cb9fe231cbb57Ted Kremenek        if return_code:
1163a46f5fd1709f6df03bbb8b0abf84052dc0f39ffJordan Rose            _log.error("rebaseline-json failed: %d, output='%s'" % (return_code, output))
1173a46f5fd1709f6df03bbb8b0abf84052dc0f39ffJordan Rose        else:
1182042fc1f36d471f437023e8899f0c4fadded2341Ted Kremenek            _log.debug("rebaseline-json succeeded")
11997bfb558f69c09b01a5c1510f08dc91eb62329a7Anna Zaks
12097bfb558f69c09b01a5c1510f08dc91eb62329a7Anna Zaks        # FIXME: propagate error and/or log messages back to the UI.
1212042fc1f36d471f437023e8899f0c4fadded2341Ted Kremenek        self._serve_text('success')
122a127ccaa38a23dbd9bd3884a2627c091075a7227Ted Kremenek
123d95b70175646829c26344d5f0bda1ec3009f2a5bAnna Zaks    def localresult(self):
124d95b70175646829c26344d5f0bda1ec3009f2a5bAnna Zaks        path = self.query['path'][0]
125d95b70175646829c26344d5f0bda1ec3009f2a5bAnna Zaks        filesystem = self.server.tool.filesystem
12661dfd6f160f7501e140704990db9c449d29f8649Anna Zaks
12761dfd6f160f7501e140704990db9c449d29f8649Anna Zaks        # Ensure that we're only serving files from inside the results directory.
12861dfd6f160f7501e140704990db9c449d29f8649Anna Zaks        if not filesystem.isabs(path) and self.server.options.results_directory:
12961dfd6f160f7501e140704990db9c449d29f8649Anna Zaks            fullpath = filesystem.abspath(filesystem.join(self.server.options.results_directory, path))
13061dfd6f160f7501e140704990db9c449d29f8649Anna Zaks            if fullpath.startswith(filesystem.abspath(self.server.options.results_directory)):
13161dfd6f160f7501e140704990db9c449d29f8649Anna Zaks                self._serve_file(fullpath, headers_only=(self.command == 'HEAD'))
13261dfd6f160f7501e140704990db9c449d29f8649Anna Zaks                return
133d95b70175646829c26344d5f0bda1ec3009f2a5bAnna Zaks
134d95b70175646829c26344d5f0bda1ec3009f2a5bAnna Zaks        self.send_response(403)
135d95b70175646829c26344d5f0bda1ec3009f2a5bAnna Zaks