Home | History | Annotate | Download | only in servers
      1 # Copyright (C) 2011 Google Inc. All rights reserved.
      2 #
      3 # Redistribution and use in source and binary forms, with or without
      4 # modification, are permitted provided that the following conditions are
      5 # met:
      6 #
      7 # 1.  Redistributions of source code must retain the above copyright
      8 #     notice, this list of conditions and the following disclaimer.
      9 # 2.  Redistributions in binary form must reproduce the above copyright
     10 #     notice, this list of conditions and the following disclaimer in the
     11 #     documentation and/or other materials provided with the distribution.
     12 #
     13 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     14 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     15 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     16 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     17 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     18 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     19 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     20 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     21 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     22 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     23 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     24 
     25 import BaseHTTPServer
     26 import SocketServer
     27 import logging
     28 import json
     29 import os
     30 import re
     31 import sys
     32 import urllib
     33 
     34 from webkitpy.common.memoized import memoized
     35 from webkitpy.tool.servers.reflectionhandler import ReflectionHandler
     36 from webkitpy.layout_tests.port import builders
     37 
     38 
     39 _log = logging.getLogger(__name__)
     40 
     41 
     42 class GardeningHTTPServer(SocketServer.ThreadingMixIn, BaseHTTPServer.HTTPServer):
     43     def __init__(self, httpd_port, config):
     44         server_name = ''
     45         self.tool = config['tool']
     46         self.options = config['options']
     47         BaseHTTPServer.HTTPServer.__init__(self, (server_name, httpd_port), GardeningHTTPRequestHandler)
     48 
     49     def url(self, args=None):
     50         # We can't use urllib.encode() here because that encodes spaces as plus signs and the buildbots don't decode those properly.
     51         arg_string = ('?' + '&'.join("%s=%s" % (key, urllib.quote(value)) for (key, value) in args.items())) if args else ''
     52         return 'http://localhost:8127/garden-o-matic.html' + arg_string
     53 
     54 
     55 class GardeningHTTPRequestHandler(ReflectionHandler):
     56     REVISION_LIMIT = 100
     57     BLINK_SVN_URL = 'http://src.chromium.org/blink/trunk'
     58     CHROMIUM_SVN_DEPS_URL = 'http://src.chromium.org/chrome/trunk/src/DEPS'
     59     # "webkit_revision": "149598",
     60     BLINK_REVISION_REGEXP = re.compile(r'^  "webkit_revision": "(?P<revision>\d+)",$', re.MULTILINE);
     61 
     62     STATIC_FILE_NAMES = frozenset()
     63 
     64     STATIC_FILE_EXTENSIONS = ('.js', '.css', '.html', '.gif', '.png', '.ico')
     65 
     66     STATIC_FILE_DIRECTORY = os.path.join(
     67         os.path.dirname(__file__),
     68         '..',
     69         '..',
     70         '..',
     71         '..',
     72         'GardeningServer')
     73 
     74     allow_cross_origin_requests = True
     75     debug_output = ''
     76 
     77     def ping(self):
     78         self._serve_text('pong')
     79 
     80     def _run_webkit_patch(self, command, input_string):
     81         PIPE = self.server.tool.executive.PIPE
     82         process = self.server.tool.executive.popen([self.server.tool.path()] + command, cwd=self.server.tool.scm().checkout_root, stdin=PIPE, stdout=PIPE, stderr=PIPE)
     83         process.stdin.write(input_string)
     84         output, error = process.communicate()
     85         return (process.returncode, output, error)
     86 
     87     def svnlog(self):
     88         self._serve_xml(self.server.tool.executive.run_command(['svn', 'log', '--xml', '--limit', self.REVISION_LIMIT, self.BLINK_SVN_URL]))
     89 
     90     def lastroll(self):
     91         deps_contents = self.server.tool.executive.run_command(['svn', 'cat', self.CHROMIUM_SVN_DEPS_URL])
     92         match = re.search(self.BLINK_REVISION_REGEXP, deps_contents)
     93         if not match:
     94             _log.error("Unable to produce last Blink roll revision")
     95             self._serve_text("0")
     96             return
     97 
     98         revision_line = match.group()
     99         revision = match.group("revision")
    100         self._serve_text(revision)
    101 
    102     def rebaselineall(self):
    103         command = ['rebaseline-json']
    104         if self.server.options.results_directory:
    105             command.extend(['--results-directory', self.server.options.results_directory])
    106         if not self.server.options.optimize:
    107             command.append('--no-optimize')
    108         if self.server.options.verbose:
    109             command.append('--verbose')
    110         json_input = self.read_entity_body()
    111 
    112         _log.debug("calling %s, input='%s'", command, json_input)
    113         return_code, output, error = self._run_webkit_patch(command, json_input)
    114         print >> sys.stderr, error
    115         if return_code:
    116             _log.error("rebaseline-json failed: %d, output='%s'" % (return_code, output))
    117         else:
    118             _log.debug("rebaseline-json succeeded")
    119 
    120         # FIXME: propagate error and/or log messages back to the UI.
    121         self._serve_text('success')
    122 
    123     def localresult(self):
    124         path = self.query['path'][0]
    125         filesystem = self.server.tool.filesystem
    126 
    127         # Ensure that we're only serving files from inside the results directory.
    128         if not filesystem.isabs(path) and self.server.options.results_directory:
    129             fullpath = filesystem.abspath(filesystem.join(self.server.options.results_directory, path))
    130             if fullpath.startswith(filesystem.abspath(self.server.options.results_directory)):
    131                 self._serve_file(fullpath, headers_only=(self.command == 'HEAD'))
    132                 return
    133 
    134         self.send_response(403)
    135