Skip to content

Commit 0ab7116

Browse files
authored
refs #10700 - donate-cpu-server.py: added query parameter pkgs to some reports to request a list of affected packages (cppcheck-opensource#3743)
1 parent 083efe6 commit 0ab7116

3 files changed

Lines changed: 86 additions & 38 deletions

File tree

.github/workflows/scriptcheck.yml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,13 @@ jobs:
148148
env:
149149
PYTHONPATH: ./tools
150150

151+
- name: test donate_cpu_server
152+
if: matrix.python-version != '2.7'
153+
run: |
154+
python -m pytest -v tools/test_donate_cpu_server.py
155+
env:
156+
PYTHONPATH: ./tools
157+
151158
- name: dmake
152159
if: matrix.python-version == '3.10'
153160
run: |

tools/donate-cpu-server.py

Lines changed: 62 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,12 @@
2121
import logging.handlers
2222
import operator
2323
import html as html_lib
24+
from urllib.parse import urlparse
2425

2526
# Version scheme (MAJOR.MINOR.PATCH) should orientate on "Semantic Versioning" https://semver.org/
2627
# Every change in this script should result in increasing the version number accordingly (exceptions may be cosmetic
2728
# changes)
28-
SERVER_VERSION = "1.3.29"
29+
SERVER_VERSION = "1.3.30"
2930

3031
OLD_VERSION = '2.9'
3132

@@ -42,12 +43,13 @@
4243
logfile += '/'
4344
logfile += 'donate-cpu-server.log'
4445
handler_file = logging.handlers.RotatingFileHandler(filename=logfile, maxBytes=100*1024, backupCount=1)
46+
handler_file.setFormatter(logging.Formatter('%(asctime)s %(message)s'))
4547
handler_file.setLevel(logging.ERROR)
4648
logger.addHandler(handler_file)
4749

4850

49-
def print_ts(msg):
50-
print('[' + strDateTime() + '] ' + msg)
51+
def print_ts(msg) -> None:
52+
print('[{}] {}'.format(strDateTime(), msg))
5153

5254

5355
# Set up an exception hook for all uncaught exceptions so they can be logged
@@ -179,7 +181,9 @@ def latestReport(latestResults: list) -> str:
179181
return html
180182

181183

182-
def crashReport(results_path: str) -> str:
184+
def crashReport(results_path: str, query_params: dict):
185+
pkgs = '' if query_params.get('pkgs') == '1' else None
186+
183187
html = '<html><head><title>Crash report</title></head><body>\n'
184188
html += '<h1>Crash report</h1>\n'
185189
html += '<pre>\n'
@@ -191,6 +195,7 @@ def crashReport(results_path: str) -> str:
191195
continue
192196
with open(filename, 'rt') as file_:
193197
datestr = None
198+
package_url = None
194199
for line in file_:
195200
line = line.strip()
196201
if line.startswith('cppcheck: '):
@@ -202,6 +207,8 @@ def crashReport(results_path: str) -> str:
202207
continue
203208
if datestr is None and line.startswith(str(current_year) + '-') or line.startswith(str(current_year - 1) + '-'):
204209
datestr = line
210+
elif pkgs is not None and package_url is None and line.startswith('ftp://'):
211+
package_url = line
205212
elif line.startswith('count:'):
206213
if line.find('Crash') < 0:
207214
break
@@ -214,6 +221,8 @@ def crashReport(results_path: str) -> str:
214221
if counts[1] == 'Crash!':
215222
c_head = 'Crash'
216223
html += fmt(package, datestr, c_version, c_head) + '\n'
224+
if package_url is not None:
225+
pkgs += '{}\n'.format(package_url)
217226
if c_head != 'Crash':
218227
break
219228
elif line.find(' received signal ') != -1:
@@ -271,7 +280,9 @@ def crashReport(results_path: str) -> str:
271280
html += '</pre>\n'
272281

273282
html += '</body></html>\n'
274-
return html
283+
if pkgs is not None:
284+
return pkgs, 'text/plain'
285+
return html, 'text/html'
275286

276287

277288
def timeoutReport(results_path: str) -> str:
@@ -609,7 +620,8 @@ def headReport(resultsPath: str) -> str:
609620
return html
610621

611622

612-
def headMessageIdReport(resultPath: str, messageId: str) -> str:
623+
def headMessageIdReport(resultPath: str, messageId: str, query_params: dict) -> str:
624+
pkgs = '' if query_params.get('pkgs') == '1' else None
613625
text = messageId + '\n'
614626
e = '[' + messageId + ']\n'
615627
for filename in sorted(glob.glob(resultPath + '/*')):
@@ -629,8 +641,12 @@ def headMessageIdReport(resultPath: str, messageId: str) -> str:
629641
elif line.endswith(e):
630642
if url:
631643
text += url
644+
if pkgs is not None:
645+
pkgs += url
632646
url = None
633647
text += line
648+
if pkgs is not None:
649+
return pkgs
634650
return text
635651

636652

@@ -938,83 +954,91 @@ class HttpClientThread(Thread):
938954
def __init__(self, connection: socket.socket, cmd: str, resultPath: str, latestResults: list) -> None:
939955
Thread.__init__(self)
940956
self.connection = connection
941-
self.cmd = cmd[:cmd.find('\n')]
957+
self.cmd = cmd[:cmd.find('\r\n')]
942958
self.resultPath = resultPath
943959
self.latestResults = latestResults
944960

961+
# TODO: use a proper parser
962+
def parse_req(cmd):
963+
req_parts = cmd.split(' ')
964+
if len(req_parts) != 3 or req_parts[0] != 'GET' or not req_parts[2].startswith('HTTP'):
965+
return None, None
966+
url_obj = urlparse(req_parts[1])
967+
return url_obj.path, dict(urllib.parse.parse_qsl(url_obj.query))
968+
945969
def run(self):
946970
try:
947971
cmd = self.cmd
948972
print_ts(cmd)
949-
res = re.match(r'GET /([a-zA-Z0-9_\-\.\+%]*) HTTP', cmd)
950-
if res is None:
973+
url, queryParams = HttpClientThread.parse_req(cmd)
974+
if url is None:
975+
print_ts('invalid request: {}'.format(cmd))
951976
self.connection.close()
952977
return
953-
url = res.group(1)
954-
if url == '':
978+
if url == '/':
955979
html = overviewReport()
956980
httpGetResponse(self.connection, html, 'text/html')
957-
elif url == 'latest.html':
981+
elif url == '/latest.html':
958982
html = latestReport(self.latestResults)
959983
httpGetResponse(self.connection, html, 'text/html')
960-
elif url == 'crash.html':
961-
html = crashReport(self.resultPath)
962-
httpGetResponse(self.connection, html, 'text/html')
963-
elif url == 'timeout.html':
984+
elif url == '/crash.html':
985+
text, mime = crashReport(self.resultPath, queryParams)
986+
httpGetResponse(self.connection, text, mime)
987+
elif url == '/timeout.html':
964988
html = timeoutReport(self.resultPath)
965989
httpGetResponse(self.connection, html, 'text/html')
966-
elif url == 'stale.html':
990+
elif url == '/stale.html':
967991
html = staleReport(self.resultPath)
968992
httpGetResponse(self.connection, html, 'text/html')
969-
elif url == 'diff.html':
993+
elif url == '/diff.html':
970994
html = diffReport(self.resultPath)
971995
httpGetResponse(self.connection, html, 'text/html')
972-
elif url.startswith('difftoday-'):
973-
messageId = url[10:]
996+
elif url.startswith('/difftoday-'):
997+
messageId = url[len('/difftoday-'):]
974998
text = diffMessageIdTodayReport(self.resultPath, messageId)
975999
httpGetResponse(self.connection, text, 'text/plain')
976-
elif url.startswith('diff-'):
977-
messageId = url[5:]
1000+
elif url.startswith('/diff-'):
1001+
messageId = url[len('/diff-'):]
9781002
text = diffMessageIdReport(self.resultPath, messageId)
9791003
httpGetResponse(self.connection, text, 'text/plain')
980-
elif url == 'head.html':
1004+
elif url == '/head.html':
9811005
html = headReport(self.resultPath)
9821006
httpGetResponse(self.connection, html, 'text/html')
983-
elif url.startswith('headtoday-'):
984-
messageId = url[10:]
1007+
elif url.startswith('/headtoday-'):
1008+
messageId = url[len('/headtoday-'):]
9851009
text = headMessageIdTodayReport(self.resultPath, messageId)
9861010
httpGetResponse(self.connection, text, 'text/plain')
987-
elif url.startswith('head-'):
988-
messageId = url[5:]
989-
text = headMessageIdReport(self.resultPath, messageId)
1011+
elif url.startswith('/head-'):
1012+
messageId = url[len('/head-'):]
1013+
text = headMessageIdReport(self.resultPath, messageId, queryParams)
9901014
httpGetResponse(self.connection, text, 'text/plain')
991-
elif url == 'time_lt.html':
1015+
elif url == '/time_lt.html':
9921016
text = timeReport(self.resultPath, False)
9931017
httpGetResponse(self.connection, text, 'text/html')
994-
elif url == 'time_gt.html':
1018+
elif url == '/time_gt.html':
9951019
text = timeReport(self.resultPath, True)
9961020
httpGetResponse(self.connection, text, 'text/html')
997-
elif url == 'time_slow.html':
1021+
elif url == '/time_slow.html':
9981022
text = timeReportSlow(self.resultPath)
9991023
httpGetResponse(self.connection, text, 'text/html')
1000-
elif url == 'check_library_function_report.html':
1024+
elif url == '/check_library_function_report.html':
10011025
text = check_library_report(self.resultPath + '/' + 'info_output', message_id='checkLibraryFunction')
10021026
httpGetResponse(self.connection, text, 'text/html')
1003-
elif url == 'check_library_noreturn_report.html':
1027+
elif url == '/check_library_noreturn_report.html':
10041028
text = check_library_report(self.resultPath + '/' + 'info_output', message_id='checkLibraryNoReturn')
10051029
httpGetResponse(self.connection, text, 'text/html')
1006-
elif url == 'check_library_use_ignore_report.html':
1030+
elif url == '/check_library_use_ignore_report.html':
10071031
text = check_library_report(self.resultPath + '/' + 'info_output', message_id='checkLibraryUseIgnore')
10081032
httpGetResponse(self.connection, text, 'text/html')
1009-
elif url == 'check_library_check_type_report.html':
1033+
elif url == '/check_library_check_type_report.html':
10101034
text = check_library_report(self.resultPath + '/' + 'info_output', message_id='checkLibraryCheckType')
10111035
httpGetResponse(self.connection, text, 'text/html')
1012-
elif url.startswith('check_library-'):
1013-
function_name = url[len('check_library-'):]
1036+
elif url.startswith('/check_library-'):
1037+
function_name = url[len('/check_library-'):]
10141038
text = check_library_function_name(self.resultPath + '/' + 'info_output', function_name)
10151039
httpGetResponse(self.connection, text, 'text/plain')
10161040
else:
1017-
filename = resultPath + '/' + url
1041+
filename = resultPath + url
10181042
if not os.path.isfile(filename):
10191043
print_ts('HTTP/1.1 404 Not Found')
10201044
self.connection.send(b'HTTP/1.1 404 Not Found\r\n\r\n')

tools/test_donate_cpu_server.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
from importlib import import_module
2+
3+
donate_cpu_server = import_module('donate-cpu-server')
4+
5+
def _test_parse_req(req_str, url_exp, queryParams_exp):
6+
url, queryParams = donate_cpu_server.HttpClientThread.parse_req(req_str)
7+
assert url == url_exp and queryParams == queryParams_exp
8+
9+
def test_parse_req():
10+
_test_parse_req("", None, None)
11+
_test_parse_req("GET / HTTP/1.1", '/', {})
12+
_test_parse_req("GET /crash.html HTTP/1.1", '/crash.html', {})
13+
_test_parse_req("GET /head-uninitvar HTTP/1.1", '/head-uninitvar', {})
14+
_test_parse_req("GET /check_library-std%3A%3Aunordered_set%3A%3Ainsert%28%29 HTTP/1.1", '/check_library-std%3A%3Aunordered_set%3A%3Ainsert%28%29', {})
15+
_test_parse_req("GET /head-uninitvar?pkgs=1 HTTP/1.1", '/head-uninitvar', {'pkgs': '1'})
16+
_test_parse_req("GET /crash.html?pkgs=1 HTTP/1.1", '/crash.html', {'pkgs': '1'})
17+
_test_parse_req("GET /head-uninitvar?pkgs=1&pkgs2=2 HTTP/1.1", '/head-uninitvar', {'pkgs': '1', 'pkgs2': '2'})

0 commit comments

Comments
 (0)