tests/dumbhttp.py
author Ian Moody <moz-ian@perix.co.uk>
Tue, 16 Jul 2019 19:18:16 +0100
changeset 42618 c17e6a3e7356
parent 38594 e46c3b6a47b5
child 43076 2372284d9457
permissions -rwxr-xr-x
phabricator: handle local:commits time being string or int When setting local:commits arcanist has different behaviour depending on whether the repo is git or hg. With hg it sets the time as a number, since it calls PHP's strtotime on the value, but with git it sets it as a string. Normally this wouldn't be an issue since phabread wouldn't be interacting with Phabricator Revisions for git repos, but Mozilla has a secondary workflow for git users that uses the git-cinnabar tool to interact with their hg repos. When a git-cinnabar user uses the moz-phab tool to submit patches for mozilla-central it makes use of Mozilla's fork of arcanist, which works with their local git version of m-c, and thus sets the local:commit time as a string, and then translates the commit hashes. Currently when encountering such DREVS phabread dies with "TypeError: %d format: a number is required, not str". phabsend also used to set it as a string but wouldn't have encountered the issue with its own DREVs since it would read hg:meta first. Differential Revision: https://phab.mercurial-scm.org/D6650

#!/usr/bin/env python

from __future__ import absolute_import

"""
Small and dumb HTTP server for use in tests.
"""

import optparse
import os
import signal
import socket
import sys

from mercurial import (
    encoding,
    pycompat,
    server,
    util,
)

httpserver = util.httpserver
OptionParser = optparse.OptionParser

if os.environ.get('HGIPV6', '0') == '1':
    class simplehttpserver(httpserver.httpserver):
        address_family = socket.AF_INET6
else:
    simplehttpserver = httpserver.httpserver

class _httprequesthandler(httpserver.simplehttprequesthandler):
    def log_message(self, format, *args):
        httpserver.simplehttprequesthandler.log_message(self, format, *args)
        sys.stderr.flush()

class simplehttpservice(object):
    def __init__(self, host, port):
        self.address = (host, port)
    def init(self):
        self.httpd = simplehttpserver(self.address, _httprequesthandler)
    def run(self):
        self.httpd.serve_forever()

if __name__ == '__main__':
    parser = OptionParser()
    parser.add_option('-p', '--port', dest='port', type='int', default=8000,
        help='TCP port to listen on', metavar='PORT')
    parser.add_option('-H', '--host', dest='host', default='localhost',
        help='hostname or IP to listen on', metavar='HOST')
    parser.add_option('--logfile', help='file name of access/error log')
    parser.add_option('--pid', dest='pid',
        help='file name where the PID of the server is stored')
    parser.add_option('-f', '--foreground', dest='foreground',
        action='store_true',
        help='do not start the HTTP server in the background')
    parser.add_option('--daemon-postexec', action='append')

    (options, args) = parser.parse_args()

    signal.signal(signal.SIGTERM, lambda x, y: sys.exit(0))

    if options.foreground and options.logfile:
        parser.error("options --logfile and --foreground are mutually "
                     "exclusive")
    if options.foreground and options.pid:
        parser.error("options --pid and --foreground are mutually exclusive")

    opts = {b'pid_file': options.pid,
            b'daemon': not options.foreground,
            b'daemon_postexec': pycompat.rapply(encoding.strtolocal,
                                                options.daemon_postexec)}
    service = simplehttpservice(options.host, options.port)
    runargs = [sys.executable, __file__] + sys.argv[1:]
    runargs = [pycompat.fsencode(a) for a in runargs]
    server.runservice(opts, initfn=service.init, runfn=service.run,
                      logfile=options.logfile,
                      runargs=runargs)