Skip to content

Commit

Permalink
Merge pull request #486 from tempesta-tech/am-1716-tls-conn-burst
Browse files Browse the repository at this point in the history
Fix and enable test accoring to #1716
  • Loading branch information
voodam authored Oct 6, 2023
2 parents f7843a4 + 16752be commit ea3ee60
Show file tree
Hide file tree
Showing 50 changed files with 905 additions and 794 deletions.
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,9 @@ failed, `u` — unexpected success, `x` — expected failure. `s` — skipped;
- `2` — Show tests names and performance counters;
- `3` — Full debug output.

The current state: be sure that verbose is set at least to 2 level, otherwise
you may find it difficult to detect errors, because some of exceptions are silently suppressed.

`duration` option controls duration in seconds of each workload test. Use small
values to obtain results quickly add large for more heavy stress tests. Default
is `10` seconds.
Expand Down
8 changes: 4 additions & 4 deletions access_log/test_access_log.py
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ def start_all(self):
class AccessLogTest(CheckedResponses):
def test_success_path_http1x(self):
self.start_all()
klog = dmesg.DmesgFinder(ratelimited=False)
klog = dmesg.DmesgFinder(disable_ratelimit=True)
for status, body in [
(200, "body http ok"),
(204, None),
Expand All @@ -158,7 +158,7 @@ def test_success_path_http1x(self):

def test_uri_truncate(self):
self.start_all()
klog = dmesg.DmesgFinder(ratelimited=False)
klog = dmesg.DmesgFinder(disable_ratelimit=True)
req = self.make_request(
"/too-long-uri_" + "1" * 4000, user_agent="user-agent", referer="referer"
)
Expand All @@ -176,7 +176,7 @@ def test_uri_truncate(self):

def test_bad_user_agent(self):
self.start_all()
klog = dmesg.DmesgFinder(ratelimited=False)
klog = dmesg.DmesgFinder(disable_ratelimit=True)
req = self.make_request("/some-uri", user_agent="bad\nagent", referer="Ok-Referer")
msg = self.send_request_and_get_dmesg(klog, req)
self.assertTrue(msg is not None, "No access_log message in dmesg")
Expand Down Expand Up @@ -206,7 +206,7 @@ class AccessLogFrang(CheckedResponses):

def test_frang(self):
self.start_all()
klog = dmesg.DmesgFinder(ratelimited=False)
klog = dmesg.DmesgFinder(disable_ratelimit=True)
req = self.make_request(
"/longer-than-10-symbols-uri", user_agent="user-agent", referer="referer"
)
Expand Down
2 changes: 1 addition & 1 deletion access_log/test_access_log_h2.py
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ def run_curl(self, curl):
curl.stop()

def run_test(self, status_code=200, is_frang=False):
klog = dmesg.DmesgFinder(ratelimited=False)
klog = dmesg.DmesgFinder(disable_ratelimit=True)
curl = self.get_client("curl")
referer = "http2-referer-%d" % status_code
user_agent = "http2-user-agent-%d" % status_code
Expand Down
6 changes: 2 additions & 4 deletions cache/test_purge.py
Original file line number Diff line number Diff line change
Expand Up @@ -570,8 +570,6 @@ def test_purge_without_get_completed_with_no_warnings(self):
response = client.last_response

self.assertEqual(response.status, 200, response)
self.assertEqual(
self.oops.warn_count(dmesg.WARN_GENERIC),
0,
f"Warnings: {self.oops.warn_match('Warning: .*')}",
self.assertTrue(
self.oops.find(dmesg.WARN_GENERIC, cond=dmesg.amount_zero), f"Some warnings were found"
)
1 change: 1 addition & 0 deletions error
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Root privileges are required: need access to module loading on localhost.
9 changes: 4 additions & 5 deletions framework/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import multiprocessing
import os

from helpers import remote, stateful, tf_cfg
from helpers import remote, stateful, tf_cfg, util


def _run_client(client, resq: multiprocessing.Queue):
Expand Down Expand Up @@ -83,7 +83,7 @@ def cleanup(self):
self.node.remove_file(f)

def copy_files(self):
for (name, content) in self.files:
for name, content in self.files:
self.node.copy_file(name, content)

def is_busy(self, verbose=True):
Expand Down Expand Up @@ -163,6 +163,5 @@ def add_option_file(self, option, filename, content):
def set_user_agent(self, ua):
self.options.append("-H 'User-Agent: %s'" % ua)

def wait_for_finish(self):
while self.is_busy(verbose=False):
pass
def wait_for_finish(self, timeout=5):
return util.wait_until(lambda: self.is_busy(verbose=False), timeout)
73 changes: 59 additions & 14 deletions framework/curl_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,11 +95,12 @@ class CurlArguments:
load_cookies: bool = False
ssl: bool = False
http2: bool = False
curl_iface: str = None
insecure: bool = True
parallel: int = None

@classmethod
def get_kwargs(cls) -> List[str]:
def get_arg_names(cls) -> List[str]:
"""Returns list of `CurlClient` supported argument names."""
return list(cls.__dataclass_fields__.keys())

Expand All @@ -115,15 +116,21 @@ class CurlClient(CurlArguments, client.Client):
cmd_args (str): additional curl options
data (str): data to POST
headers (dict[str, str]): headers to include in the request
dump_headers (bool): dump headers to the workdir, and enable response parsing.
Enabled by default.
dump_headers (bool): dump headers to the workdir, and enable response parsing
Enabled by default. In combination with parallel
can provide inaccurate results #488.
disable_output (bool): do not output results but only errors
save_cookies (bool): save cookies to the workdir
save_cookies (bool): Save cookies to the workdir.
Be aware: cookies are shared among all clients.
load_cookies (bool): load and send cookies from the workdir
ssl (bool): use SSL/TLS for the connection
http2 (bool): use HTTP version 2
curl_iface (str): Interface name, IP address or host name using for performing request.
Don't mess with "interface" argument, used by framework
for creating alias interfaces.
insecure (bool): Ignore SSL certificate errors. Enabled by default.
parallel (int): Enable parallel mode, with maximum <int> simultaneous transfers
parallel (int): Enable parallel mode, with maximum <int> simultaneous transfers.
In combination with dump_headers can provide inaccurate results #488.
"""

def __init__(self, **kwargs):
Expand Down Expand Up @@ -152,38 +159,72 @@ def requests(self, value):

@property
def responses(self) -> List[CurlResponse]:
"""List of all received responses."""
"""List of all received responses.
Can provide inaccurate results in some cases, (see dump_headers comment).
If dump_headers disabled, will be empty.
"""
return list(self._responses)

@property
def last_response(self) -> Optional[CurlResponse]:
"""Last parsed response if any."""
"""Last parsed response if any.
Can provide inaccurate results in some cases, (see dump_headers comment).
If dump_headers disabled, will be empty.
"""
return (self.responses or [None])[-1]

@property
def statuses(self) -> Dict[int, int]:
"""Received statuses counters, like:
"""Received statuses counters from headers, like:
{200: 1, 400: 2}
Can provide inaccurate results in some cases, (see dump_headers comment).
If dump_headers disabled, will be empty.
"""
return dict(self._statuses)

@statuses.setter
def statuses(self, value):
self._statuses = defaultdict(lambda: 0, value)

def statuses_from_stats(self) -> Dict[int, int]:
"""Received statuses counters from curl output, like:
{200: 1, 400: 2}
statuses property can provide inaccurate results in some cases,
(see dump_headers comment) - the function will be useful so.
"""
r = defaultdict(lambda: 0)
for s in self.stats:
r[int(s["response_code"])] += 1
return dict(r)

@property
def last_stats(self) -> Dict[str, Any]:
"""Information about last completed transfer.
See https://curl.se/docs/manpage.html#-w
for the list of available variables.
If disable_output enabled, will be empty.
"""
return (self._stats or [None])[-1]

@property
def stats(self) -> List[Dict[int, int]]:
"""List of stats of all transfers"""
def stats(self) -> List[Dict[str, Any]]:
"""List of stats of all transfers.
See https://curl.se/docs/manpage.html#-w
for the list of available variables.
If disable_output enabled, will be empty.
"""
return list(self._stats)

@property
def reset_conn_n(self) -> int:
"""
Amount of connections reseted by server (by RST).
"""
return sum(
"Connection reset by peer" in (s["errormsg"] or "") and s["response_code"] == 0
for s in self.stats
)

@property
def binary_version(self) -> Optional[str]:
"""curl binary version, parsed from the latest transfer."""
Expand All @@ -198,12 +239,14 @@ def cookie_jar_path(self):
@property
def output_path(self):
"""Path to write stdout (response)."""
return Path(self.workdir) / "curl-output"
# id(self) for the case of several client instances
return Path(self.workdir) / f"curl-output-{id(self)}"

@property
def headers_dump_path(self):
"""Path do dump received headers."""
return Path(self.workdir) / "curl-default.hdr"
# id(self) for the case of several client instances
return Path(self.workdir) / f"curl-{id(self)}.hdr"

@property
def cookie_string(self) -> str:
Expand Down Expand Up @@ -249,6 +292,9 @@ def form_command(self):
else:
options.append("--http1.1")

if self.curl_iface:
options.append(f"--interface {self.curl_iface}")

if self.ssl and self.insecure:
options.append("--insecure")

Expand All @@ -258,13 +304,12 @@ def form_command(self):
options.append(f"--parallel-max {self.parallel}")

cmd = " ".join([self.bin] + options + self.options + [f"'{self.uri}'"])
tf_cfg.dbg(3, f"Curl command formatted: {cmd}")
tf_cfg.dbg(2, f"Curl command formatted: {cmd}")
return cmd

def parse_out(self, stdout, stderr):
if self.dump_headers:
for dump in filter(None, self._read_headers_dump().split(b"\r\n\r\n")):

response = CurlResponse(
headers_dump=dump,
stdout_raw=self._read_output() if not self.disable_output else b"",
Expand Down
Loading

0 comments on commit ea3ee60

Please sign in to comment.