Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 22 additions & 10 deletions test/pyhttpd/env.py
Original file line number Diff line number Diff line change
Expand Up @@ -920,15 +920,31 @@ def _win_stop(self) -> int:
log.warning("port still in use after stop")
return 0

# Logged by every MPM once the new generation is serving.
RE_RESUMING = re.compile(r'.* configured -- resuming normal operations$')

def _apache_signal_restart(self, cmd: str) -> int:
"""Have the running parent restart, gracefully or not, and wait
for the new generation to be serving. "httpd -k restart" only
sends the signal; a request answered straight afterwards may have
been served by the old generation, with the old config, and about
to be killed."""
log_pos = self.httpd_error_log.current_pos()
r = self._run_apachectl(cmd)
if r.exit_code != 0:
return r.exit_code
timeout = timedelta(seconds=10)
if not self.httpd_error_log.wait_for(self.RE_RESUMING, log_pos,
timeout=timeout.total_seconds()):
log.warning(f"no restart logged after '{cmd}' within {timeout}")
return -1
return 0 if self.is_live(self._http_base, timeout=timeout) else -1

def apache_reload(self):
if self.isWindows:
self._win_stop()
return self._win_start()
r = self._run_apachectl("graceful")
if r.exit_code == 0:
timeout = timedelta(seconds=10)
return 0 if self.is_live(self._http_base, timeout=timeout) else -1
return r.exit_code
return self._apache_signal_restart("graceful")

def apache_restart(self):
if self.isWindows:
Expand Down Expand Up @@ -976,11 +992,7 @@ def apache_fail(self):

def apache_hard_restart(self) -> int:
"""Restart without the "graceful" flag, so the MPM starts over."""
r = self._run_apachectl("restart")
if r.exit_code == 0:
return 0 if self.is_live(self._http_base,
timeout=timedelta(seconds=10)) else -1
return r.exit_code
return self._apache_signal_restart("restart")

def read_pid_file(self, name: str = 'httpd.pid') -> Optional[int]:
# Where PidFile lands depends on how the httpd under test resolves
Expand Down
25 changes: 25 additions & 0 deletions test/pyhttpd/log.py
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,31 @@ def get_missed(self) -> Tuple[List[str], List[str]]:
self._caught_matches = set()
return errors, warnings

def current_pos(self) -> int:
"""The end of the log as it stands now, to wait_for() lines
logged after it.
"""
if not os.path.isfile(self._path):
return 0
with open(self._path) as fd:
return fd.seek(0, SEEK_END)

def wait_for(self, pattern: re.Pattern, start_pos: int, timeout=10) -> bool:
"""Wait for a line matching pattern to be logged after start_pos,
as returned by current_pos().
"""
end = datetime.now() + timedelta(seconds=timeout)
while True:
if os.path.isfile(self._path):
with open(self._path) as fd:
fd.seek(start_pos, os.SEEK_SET)
for line in fd:
if pattern.match(line):
return True
if datetime.now() > end:
return False
time.sleep(.1)

def scan_recent(self, pattern: re.Pattern, timeout=10):
if not os.path.isfile(self.path):
return False
Expand Down
Loading