def test_runtime_instance_dies_immediately(self):
    """Runtime instance dies without sending a port."""
    # start()
    # As the lock is mocked out, this provides a mox expectation.
    with self.proxy._process_lock:
      safe_subprocess.start_process_file(
          args=['/runtime'],
          input_string=self.runtime_config.SerializeToString(),
          env={'foo': 'bar'},
          cwd=self.tmpdir,
          stderr=subprocess.PIPE).AndReturn(self.process)
    self.process.poll().AndReturn(1)
    self.process.child_out.close().AndReturn(None)
    self.process.child_out.name = '/tmp/c-out.ABC'
    os.remove('/tmp/c-out.ABC').AndReturn(None)
    header = "bad runtime process port ['']\n\n"
    stderr0 = 'Go away..\n'
    self.proxy._stderr_tee = FakeTee(stderr0)
    time.sleep(.1).AndReturn(None)

    self.mox.ReplayAll()
    self.proxy.start()
    expected_headers = {
        'Content-Type': 'text/plain',
        'Content-Length': str(len(header) + len(stderr0)),
    }
    self.assertResponse('500 Internal Server Error', expected_headers,
                        header + stderr0,
                        self.proxy.handle, {},
                        url_map=self.url_map,
                        match=re.match(self.url_map.url, '/get%20request'),
                        request_id='request id',
                        request_type=instance.NORMAL_REQUEST)
    self.mox.VerifyAll()
Beispiel #2
0
    def test_slow_shattered(self):
        """The port number is received slowly in chunks."""
        # start()
        # As the lock is mocked out, this provides a mox expectation.
        with self.proxy._process_lock:
            safe_subprocess.start_process_file(
                args=['/runtime'],
                input_string=self.runtime_config.SerializeToString(),
                env={
                    'foo': 'bar'
                },
                cwd=self.tmpdir,
                stderr=subprocess.PIPE).AndReturn(self.process)
        for response, sleeptime in [('', .125), ('43', .25), ('4321', .5),
                                    ('4321\n', None)]:
            self.process.poll().AndReturn(None)
            self.process.child_out.seek(0).AndReturn(None)
            self.process.child_out.read().AndReturn(response)
            if sleeptime is not None:
                time.sleep(sleeptime).AndReturn(None)
        self.process.child_out.close().AndReturn(None)
        self.process.child_out.name = '/tmp/c-out.ABC'
        os.remove('/tmp/c-out.ABC').AndReturn(None)
        self.proxy._stderr_tee = FakeTee('')

        self.mox.ReplayAll()
        self.proxy.start()
        self.assertEquals(4321, self.proxy._proxy._port)
        self.mox.VerifyAll()
  def test_slow_shattered(self):
    """The port number is received slowly in chunks."""
    # start()
    # As the lock is mocked out, this provides a mox expectation.
    with self.proxy._process_lock:
      safe_subprocess.start_process_file(
          args=['/runtime'],
          input_string=self.runtime_config.SerializeToString(),
          env={'foo': 'bar'},
          cwd=self.tmpdir,
          stderr=subprocess.PIPE).AndReturn(self.process)
    for response, sleeptime in [
        ('', .125), ('43', .25), ('4321', .5), ('4321\n', None)]:
      self.process.poll().AndReturn(None)
      self.process.child_out.seek(0).AndReturn(None)
      self.process.child_out.read().AndReturn(response)
      if sleeptime is not None:
        time.sleep(sleeptime).AndReturn(None)
    self.process.child_out.close().AndReturn(None)
    self.process.child_out.name = '/tmp/c-out.ABC'
    os.remove('/tmp/c-out.ABC').AndReturn(None)
    self.proxy._stderr_tee = FakeTee('')

    self.mox.ReplayAll()
    self.proxy.start()
    self.assertEquals(4321, self.proxy._proxy._port)
    self.mox.VerifyAll()
Beispiel #4
0
  def test_basic(self):
    """Basic functionality test of START_PROCESS_FILE flavor."""
    # start()
    # As the lock is mocked out, this provides a mox expectation.
    with self.proxy._process_lock:
      safe_subprocess.start_process_file(
          args=['/runtime'],
          input_string=self.runtime_config.SerializeToString(),
          env={'foo': 'bar'},
          cwd=self.tmpdir,
          stderr=subprocess.PIPE).AndReturn(self.process)
    self.process.poll().AndReturn(None)
    self.process.child_out.seek(0).AndReturn(None)
    self.process.child_out.read().AndReturn('1234\n')
    self.process.child_out.close().AndReturn(None)
    self.process.child_out.name = '/tmp/c-out.ABC'
    os.remove('/tmp/c-out.ABC').AndReturn(None)
    self.proxy._stderr_tee = FakeTee('')

    # _can_connect() via start().
    httplib.HTTPConnection.connect()
    httplib.HTTPConnection.close()

    self.mox.ReplayAll()
    self.proxy.start()
    self.assertEquals(1234, self.proxy._port)
    self.mox.VerifyAll()
Beispiel #5
0
    def test_basic(self):
        """Basic functionality test of START_PROCESS_FILE flavor."""
        # start()
        # As the lock is mocked out, this provides a mox expectation.
        with self.proxy._process_lock:
            safe_subprocess.start_process_file(
                args=['/runtime'],
                input_string=self.runtime_config.SerializeToString(),
                env={
                    'foo': 'bar'
                },
                cwd=self.tmpdir,
                stderr=subprocess.PIPE).AndReturn(self.process)
        self.process.poll().AndReturn(None)
        self.process.child_out.seek(0).AndReturn(None)
        self.process.child_out.read().AndReturn('1234\n')
        self.process.child_out.close().AndReturn(None)
        self.process.child_out.name = '/tmp/c-out.ABC'
        os.remove('/tmp/c-out.ABC').AndReturn(None)
        self.proxy._stderr_tee = FakeTee('')

        self.mox.ReplayAll()
        self.proxy.start()
        self.assertEquals(1234, self.proxy._proxy._port)
        self.mox.VerifyAll()
Beispiel #6
0
  def test_runtime_instance_dies_immediately(self):
    """Runtime instance dies without sending a port."""
    # start()
    # As the lock is mocked out, this provides a mox expectation.
    with self.proxy._process_lock:
      safe_subprocess.start_process_file(
          args=['/runtime'],
          input_string=self.runtime_config.SerializeToString(),
          env={'foo': 'bar'},
          cwd=self.tmpdir,
          stderr=subprocess.PIPE).AndReturn(self.process)
    self.process.poll().AndReturn(1)
    self.process.child_out.close().AndReturn(None)
    self.process.child_out.name = '/tmp/c-out.ABC'
    os.remove('/tmp/c-out.ABC').AndReturn(None)
    header = "bad runtime process port ['']\n\n"
    stderr0 = 'Go away..\n'
    self.proxy._stderr_tee = FakeTee(stderr0)
    time.sleep(.1).AndReturn(None)

    self.mox.ReplayAll()
    self.proxy.start()
    expected_headers = {
        'Content-Type': 'text/plain',
        'Content-Length': str(len(header) + len(stderr0)),
    }
    self.assertResponse('500 Internal Server Error', expected_headers,
                        header + stderr0,
                        self.proxy.handle, {},
                        url_map=self.url_map,
                        match=re.match(self.url_map.url, '/get%20request'),
                        request_id='request id',
                        request_type=instance.NORMAL_REQUEST)
    self.mox.VerifyAll()
Beispiel #7
0
    def test_runtime_instance_invalid_response(self):
        """Runtime instance does not terminate port with a newline."""
        # start()
        # As the lock is mocked out, this provides a mox expectation.
        with self.proxy._process_lock:
            safe_subprocess.start_process_file(
                args=['/runtime'],
                input_string=self.runtime_config.SerializeToString(),
                env={
                    'foo': 'bar'
                },
                cwd=self.tmpdir,
                stderr=subprocess.PIPE).AndReturn(self.process)
        for response, sleeptime in [('30000', .125), ('30000', .25),
                                    ('30000', .5), ('30000', 1.0),
                                    ('30000', 2.0), ('30000', 4.0),
                                    ('30000', 8.0), ('30000', 16.0),
                                    ('30000', 32.0), ('30000', None)]:
            self.process.poll().AndReturn(None)
            self.process.child_out.seek(0).AndReturn(None)
            self.process.child_out.read().AndReturn(response)
            if sleeptime is not None:
                time.sleep(sleeptime).AndReturn(None)
        self.process.child_out.close().AndReturn(None)
        self.process.child_out.name = '/tmp/c-out.ABC'
        os.remove('/tmp/c-out.ABC').AndReturn(None)
        header = "bad runtime process port ['']\n\n"
        stderr0 = 'Go away..\n'
        self.proxy._stderr_tee = FakeTee(stderr0)
        time.sleep(.1)

        self.mox.ReplayAll()
        self.proxy.start()
        expected_headers = {
            'Content-Type': 'text/plain',
            'Content-Length': str(len(header) + len(stderr0)),
        }
        self.assertResponse('500 Internal Server Error',
                            expected_headers,
                            header + stderr0,
                            self.proxy.handle, {},
                            url_map=self.url_map,
                            match=re.match(self.url_map.url, '/get%20request'),
                            request_id='request id',
                            request_type=instance.NORMAL_REQUEST)
        self.mox.VerifyAll()
  def test_basic(self):
    """Basic functionality test of START_PROCESS_REVERSE flavor."""
    portpicker.pick_unused_port().AndReturn(2345)
    # As the lock is mocked out, this provides a mox expectation.
    with self.proxy._process_lock:
      safe_subprocess.start_process_file(
          args=['/runtime'],
          input_string=self.runtime_config.SerializeToString(),
          env={'foo': 'bar',
               'PORT': '2345'},
          cwd=self.tmpdir,
          stderr=subprocess.PIPE).AndReturn(self.process)
    self.proxy._stderr_tee = FakeTee('')

    self.mox.ReplayAll()
    self.proxy.start()
    self.assertEquals(2345, self.proxy._proxy._port)
    self.mox.VerifyAll()
Beispiel #9
0
  def test_basic(self):
    """Basic functionality test of START_PROCESS_REVERSE flavor."""
    portpicker.PickUnusedPort().AndReturn(2345)
    # As the lock is mocked out, this provides a mox expectation.
    with self.proxy._process_lock:
      safe_subprocess.start_process_file(
          args=['/runtime'],
          input_string=self.runtime_config.SerializeToString(),
          env={'foo': 'bar',
               'PORT': '2345'},
          cwd=self.tmpdir,
          stderr=subprocess.PIPE).AndReturn(self.process)
    self.proxy._stderr_tee = FakeTee('')

    self.mox.ReplayAll()
    self.proxy.start()
    self.assertEquals(2345, self.proxy._proxy._port)
    self.mox.VerifyAll()
Beispiel #10
0
    def start(self):
        """Starts the runtime process and waits until it is ready to serve."""
        runtime_config = self._runtime_config_getter()
        # TODO: Use a different process group to isolate the child process
        # from signals sent to the parent. Only available in subprocess in
        # Python 2.7.
        assert self._start_process_flavor in self._VALID_START_PROCESS_FLAVORS
        if self._start_process_flavor == START_PROCESS:
            serialized_config = base64.b64encode(
                runtime_config.SerializeToString())
            with self._process_lock:
                assert not self._process, 'start() can only be called once'
                self._process = safe_subprocess.start_process(
                    self._args,
                    serialized_config,
                    stdout=subprocess.PIPE,
                    stderr=subprocess.PIPE,
                    env=self._env,
                    cwd=self._module_configuration.application_root)
            line = self._process.stdout.readline()
        elif self._start_process_flavor == START_PROCESS_FILE:
            serialized_config = runtime_config.SerializeToString()
            with self._process_lock:
                assert not self._process, 'start() can only be called once'
                self._process = safe_subprocess.start_process_file(
                    args=self._args,
                    input_string=serialized_config,
                    env=self._env,
                    cwd=self._module_configuration.application_root,
                    stderr=subprocess.PIPE)
            line = self._read_start_process_file()
            _remove_retry_sharing_violation(self._process.child_out.name)

        # _stderr_tee may be pre-set by unit tests.
        if self._stderr_tee is None:
            self._stderr_tee = tee.Tee(self._process.stderr, sys.stderr)
            self._stderr_tee.start()

        port = None
        error = None
        try:
            port = int(line)
        except ValueError:
            error = 'bad runtime process port [%r]' % line
            logging.error(error)
        finally:
            self._proxy = http_proxy.HttpProxy(
                host='localhost',
                port=port,
                instance_died_unexpectedly=self._instance_died_unexpectedly,
                instance_logs_getter=self._get_instance_logs,
                error_handler_file=application_configuration.
                get_app_error_file(self._module_configuration),
                prior_error=error)
            self._proxy.wait_for_connection()
Beispiel #11
0
  def test_runtime_instance_invalid_response(self):
    """Runtime instance does not terminate port with a newline."""
    # start()
    # As the lock is mocked out, this provides a mox expectation.
    with self.proxy._process_lock:
      safe_subprocess.start_process_file(
          args=['/runtime'],
          input_string=self.runtime_config.SerializeToString(),
          env={'foo': 'bar'},
          cwd=self.tmpdir,
          stderr=subprocess.PIPE).AndReturn(self.process)
    for response, sleeptime in [
        ('30000', .125), ('30000', .25), ('30000', .5), ('30000', 1.0),
        ('30000', 2.0), ('30000', 4.0), ('30000', 8.0), ('30000', 16.0),
        ('30000', 32.0), ('30000', None)]:
      self.process.poll().AndReturn(None)
      self.process.child_out.seek(0).AndReturn(None)
      self.process.child_out.read().AndReturn(response)
      if sleeptime is not None:
        time.sleep(sleeptime).AndReturn(None)
    self.process.child_out.close().AndReturn(None)
    self.process.child_out.name = '/tmp/c-out.ABC'
    os.remove('/tmp/c-out.ABC').AndReturn(None)
    header = "bad runtime process port ['']\n\n"
    stderr0 = 'Go away..\n'
    self.proxy._stderr_tee = FakeTee(stderr0)
    time.sleep(.1)

    self.mox.ReplayAll()
    self.proxy.start()
    expected_headers = {
        'Content-Type': 'text/plain',
        'Content-Length': str(len(header) + len(stderr0)),
    }
    self.assertResponse('500 Internal Server Error', expected_headers,
                        header + stderr0,
                        self.proxy.handle, {},
                        url_map=self.url_map,
                        match=re.match(self.url_map.url, '/get%20request'),
                        request_id='request id',
                        request_type=instance.NORMAL_REQUEST)
    self.mox.VerifyAll()
  def start(self):
    """Starts the runtime process and waits until it is ready to serve."""
    runtime_config = self._runtime_config_getter()
    # TODO: Use a different process group to isolate the child process
    # from signals sent to the parent. Only available in subprocess in
    # Python 2.7.
    assert self._start_process_flavor in self._VALID_START_PROCESS_FLAVORS
    if self._start_process_flavor == START_PROCESS:
      serialized_config = base64.b64encode(runtime_config.SerializeToString())
      with self._process_lock:
        assert not self._process, 'start() can only be called once'
        self._process = safe_subprocess.start_process(
            self._args,
            serialized_config,
            stdout=subprocess.PIPE,
            stderr=subprocess.PIPE,
            env=self._env,
            cwd=self._module_configuration.application_root)
      line = self._process.stdout.readline()
    elif self._start_process_flavor == START_PROCESS_FILE:
      serialized_config = runtime_config.SerializeToString()
      with self._process_lock:
        assert not self._process, 'start() can only be called once'
        self._process = safe_subprocess.start_process_file(
            args=self._args,
            input_string=serialized_config,
            env=self._env,
            cwd=self._module_configuration.application_root,
            stderr=subprocess.PIPE)
      line = self._read_start_process_file()
      _remove_retry_sharing_violation(self._process.child_out.name)

    # _stderr_tee may be pre-set by unit tests.
    if self._stderr_tee is None:
      self._stderr_tee = tee.Tee(self._process.stderr, sys.stderr)
      self._stderr_tee.start()

    port = None
    error = None
    try:
      port = int(line)
    except ValueError:
      error = 'bad runtime process port [%r]' % line
      logging.error(error)
    finally:
      self._proxy = http_proxy.HttpProxy(
          host='localhost', port=port,
          instance_died_unexpectedly=self._instance_died_unexpectedly,
          instance_logs_getter=self._get_instance_logs,
          error_handler_file=application_configuration.get_app_error_file(
              self._module_configuration),
          prior_error=error)
      self._proxy.wait_for_connection()
Beispiel #13
0
    def start(self):
        """Starts the runtime process and waits until it is ready to serve."""
        runtime_config = self._runtime_config_getter()
        # TODO: Use a different process group to isolate the child process
        # from signals sent to the parent. Only available in subprocess in
        # Python 2.7.
        assert self._start_process_flavor in self._VALID_START_PROCESS_FLAVORS
        if self._start_process_flavor == START_PROCESS:
            serialized_config = base64.b64encode(
                runtime_config.SerializeToString())
            with self._process_lock:
                assert not self._process, 'start() can only be called once'
                self._process = safe_subprocess.start_process(
                    self._args,
                    serialized_config,
                    stdout=subprocess.PIPE,
                    stderr=subprocess.PIPE,
                    env=self._env,
                    cwd=self._module_configuration.application_root)
            line = self._process.stdout.readline()
        elif self._start_process_flavor == START_PROCESS_FILE:
            serialized_config = runtime_config.SerializeToString()
            with self._process_lock:
                assert not self._process, 'start() can only be called once'
                self._process = safe_subprocess.start_process_file(
                    args=self._args,
                    input_string=serialized_config,
                    env=self._env,
                    cwd=self._module_configuration.application_root,
                    stderr=subprocess.PIPE)
            line = self._read_start_process_file()
            _remove_retry_sharing_violation(self._process.child_out.name)

        # _stderr_tee may be pre-set by unit tests.
        if self._stderr_tee is None:
            self._stderr_tee = tee.Tee(self._process.stderr, sys.stderr)
            self._stderr_tee.start()
        self._prior_error = None
        self._port = None
        try:
            # Older runtimes output just the port, while newer ones prepend the host.
            self._port = int(line.split()[-1])
        except ValueError:
            self._prior_error = 'bad runtime process port [%r]' % line
            logging.error(self._prior_error)
        else:
            # Check if the runtime can serve requests.
            if not self._can_connect():
                self._prior_error = 'cannot connect to runtime on port %r' % self._port
                logging.error(self._prior_error)
Beispiel #14
0
  def start(self):
    """Starts the runtime process and waits until it is ready to serve."""
    runtime_config = self._runtime_config_getter()
    # TODO: Use a different process group to isolate the child process
    # from signals sent to the parent. Only available in subprocess in
    # Python 2.7.
    assert self._start_process_flavor in self._VALID_START_PROCESS_FLAVORS
    if self._start_process_flavor == START_PROCESS:
      serialized_config = base64.b64encode(runtime_config.SerializeToString())
      with self._process_lock:
        assert not self._process, 'start() can only be called once'
        self._process = safe_subprocess.start_process(
            self._args,
            serialized_config,
            stdout=subprocess.PIPE,
            stderr=subprocess.PIPE,
            env=self._env,
            cwd=self._module_configuration.application_root)
      line = self._process.stdout.readline()
    elif self._start_process_flavor == START_PROCESS_FILE:
      serialized_config = runtime_config.SerializeToString()
      with self._process_lock:
        assert not self._process, 'start() can only be called once'
        self._process = safe_subprocess.start_process_file(
            args=self._args,
            input_string=serialized_config,
            env=self._env,
            cwd=self._module_configuration.application_root,
            stderr=subprocess.PIPE)
      line = self._read_start_process_file()
      _remove_retry_sharing_violation(self._process.child_out.name)

    # _stderr_tee may be pre-set by unit tests.
    if self._stderr_tee is None:
      self._stderr_tee = tee.Tee(self._process.stderr, sys.stderr)
      self._stderr_tee.start()
    self._prior_error = None
    self._port = None
    try:
      # Older runtimes output just the port, while newer ones prepend the host.
      self._port = int(line.split()[-1])
    except ValueError:
      self._prior_error = 'bad runtime process port [%r]' % line
      logging.error(self._prior_error)
    else:
      # Check if the runtime can serve requests.
      if not self._can_connect():
        self._prior_error = 'cannot connect to runtime on port %r' % self._port
        logging.error(self._prior_error)
Beispiel #15
0
  def start(self):
    """Starts the runtime process and waits until it is ready to serve."""
    runtime_config = self._runtime_config_getter()
    # TODO: Use a different process group to isolate the child process
    # from signals sent to the parent. Only available in subprocess in
    # Python 2.7.
    assert self._start_process_flavor in self._VALID_START_PROCESS_FLAVORS
    host = 'localhost'
    if self._start_process_flavor == START_PROCESS:
      serialized_config = base64.b64encode(runtime_config.SerializeToString())
      with self._process_lock:
        assert not self._process, 'start() can only be called once'
        self._process = safe_subprocess.start_process(
            self._args,
            serialized_config,
            stdout=subprocess.PIPE,
            stderr=subprocess.PIPE,
            env=self._env,
            cwd=self._module_configuration.application_root)
      port = self._process.stdout.readline()
      if '\t' in port:  # Split out the host if present.
        host, port = port.split('\t', 1)
    elif self._start_process_flavor == START_PROCESS_FILE:
      serialized_config = runtime_config.SerializeToString()
      with self._process_lock:
        assert not self._process, 'start() can only be called once'
        self._process = safe_subprocess.start_process_file(
            args=self._args,
            input_string=serialized_config,
            env=self._env,
            cwd=self._module_configuration.application_root,
            stderr=subprocess.PIPE)
      port = self._read_start_process_file()
      _remove_retry_sharing_violation(self._process.child_out.name)
    elif self._start_process_flavor == START_PROCESS_REVERSE:
      serialized_config = runtime_config.SerializeToString()
      with self._process_lock:
        assert not self._process, 'start() can only be called once'
        port = portpicker.PickUnusedPort()
        self._env['PORT'] = str(port)

        # If any of the strings in args contain {port}, replace that substring
        # with the selected port. This allows a user-specified runtime to
        # pass the port along to the subprocess as a command-line argument.
        args = [arg.replace('{port}', str(port)) for arg in self._args]

        self._process = safe_subprocess.start_process_file(
            args=args,
            input_string=serialized_config,
            env=self._env,
            cwd=self._module_configuration.application_root,
            stderr=subprocess.PIPE)
    elif self._start_process_flavor == START_PROCESS_REVERSE_NO_FILE:
      serialized_config = runtime_config.SerializeToString()
      with self._process_lock:
        assert not self._process, 'start() can only be called once'
        port = portpicker.PickUnusedPort()
        if self._extra_args_getter:
          self._args.append(self._extra_args_getter(port))

        # If any of the strings in _args contain {port}, {api_host}, {api_port},
        # replace that substring with the selected port. This allows
        # a user-specified runtime to pass the port along to the subprocess
        # as a command-line argument.
        args = [arg.replace('{port}', str(port))
                .replace('{api_port}', str(runtime_config.api_port))
                .replace('{api_host}', runtime_config.api_host)
                for arg in self._args]

        self._process = safe_subprocess.start_process(
            args=args,
            input_string=serialized_config,
            stdout=subprocess.PIPE,
            stderr=subprocess.PIPE,
            env=self._env,
            cwd=self._module_configuration.application_root)

    # _stderr_tee may be pre-set by unit tests.
    if self._stderr_tee is None:
      self._stderr_tee = tee.Tee(self._process.stderr, sys.stderr)
      self._stderr_tee.start()

    error = None
    try:
      port = int(port)
    except ValueError:
      error = 'bad runtime process port [%r]' % port
      logging.error(error)
    finally:
      self._proxy = http_proxy.HttpProxy(
          host=host, port=port,
          instance_died_unexpectedly=self._instance_died_unexpectedly,
          instance_logs_getter=self._get_instance_logs,
          error_handler_file=application_configuration.get_app_error_file(
              self._module_configuration),
          prior_error=error)
      self._proxy.wait_for_connection()
Beispiel #16
0
  def start(self):
    """Starts the runtime process and waits until it is ready to serve."""
    runtime_config = self._runtime_config_getter()
    # TODO: Use a different process group to isolate the child process
    # from signals sent to the parent. Only available in subprocess in
    # Python 2.7.
    assert self._start_process_flavor in self._VALID_START_PROCESS_FLAVORS
    if self._start_process_flavor == START_PROCESS:
      serialized_config = base64.b64encode(runtime_config.SerializeToString())
      with self._process_lock:
        assert not self._process, 'start() can only be called once'
        self._process = safe_subprocess.start_process(
            self._args,
            serialized_config,
            stdout=subprocess.PIPE,
            stderr=subprocess.PIPE,
            env=self._env,
            cwd=self._module_configuration.application_root)
      port = self._process.stdout.readline()
      if '\t' in port:  # Split out the host if present.
        host, port = port.split('\t', 1)
    elif self._start_process_flavor == START_PROCESS_FILE:
      serialized_config = runtime_config.SerializeToString()
      with self._process_lock:
        assert not self._process, 'start() can only be called once'
        self._process = safe_subprocess.start_process_file(
            args=self._args,
            input_string=serialized_config,
            env=self._env,
            cwd=self._module_configuration.application_root,
            stderr=subprocess.PIPE)
      port = self._read_start_process_file()
      _remove_retry_sharing_violation(self._process.child_out.name)
    elif self._start_process_flavor == START_PROCESS_REVERSE:
      serialized_config = runtime_config.SerializeToString()
      with self._process_lock:
        assert not self._process, 'start() can only be called once'
        port = portpicker.PickUnusedPort()
        self._env['PORT'] = str(port)

        # If any of the strings in args contain {port}, replace that substring
        # with the selected port. This allows a user-specified runtime to
        # pass the port along to the subprocess as a command-line argument.
        args = [arg.replace('{port}', str(port)) for arg in self._args]

        self._process = safe_subprocess.start_process_file(
            args=args,
            input_string=serialized_config,
            env=self._env,
            cwd=self._module_configuration.application_root,
            stderr=subprocess.PIPE)
    elif self._start_process_flavor == START_PROCESS_REVERSE_NO_FILE:
      serialized_config = runtime_config.SerializeToString()
      with self._process_lock:
        assert not self._process, 'start() can only be called once'
        port = portpicker.PickUnusedPort()
        if self._extra_args_getter:
          self._args.append(self._extra_args_getter(port))

        # If any of the strings in _args contain {port}, {api_host}, {api_port},
        # replace that substring with the selected port. This allows
        # a user-specified runtime to pass the port along to the subprocess
        # as a command-line argument.
        args = [arg.replace('{port}', str(port))
                .replace('{api_port}', str(runtime_config.api_port))
                .replace('{api_host}', runtime_config.api_host)
                for arg in self._args]

        self._process = safe_subprocess.start_process(
            args=args,
            input_string=serialized_config,
            stdout=subprocess.PIPE,
            stderr=subprocess.PIPE,
            env=self._env,
            cwd=self._module_configuration.application_root)

    # _stderr_tee may be pre-set by unit tests.
    if self._stderr_tee is None:
      self._stderr_tee = tee.Tee(self._process.stderr, sys.stderr)
      self._stderr_tee.start()

    error = None
    try:
      port = int(port)
    except ValueError:
      error = 'bad runtime process port [%r]' % port
      logging.error(error)
    finally:
      self._proxy = http_proxy.HttpProxy(
          host='localhost', port=port,
          instance_died_unexpectedly=self._instance_died_unexpectedly,
          instance_logs_getter=self._get_instance_logs,
          error_handler_file=application_configuration.get_app_error_file(
              self._module_configuration),
          prior_error=error)
      self._proxy.wait_for_connection()