summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/reporting/resources/report.py
diff options
context:
space:
mode:
Diffstat (limited to 'testing/web-platform/tests/reporting/resources/report.py')
-rw-r--r--testing/web-platform/tests/reporting/resources/report.py143
1 files changed, 143 insertions, 0 deletions
diff --git a/testing/web-platform/tests/reporting/resources/report.py b/testing/web-platform/tests/reporting/resources/report.py
new file mode 100644
index 0000000000..b5ee0c07d8
--- /dev/null
+++ b/testing/web-platform/tests/reporting/resources/report.py
@@ -0,0 +1,143 @@
+import time
+import json
+import re
+import uuid
+
+from wptserve.utils import isomorphic_decode
+
+
+def retrieve_from_stash(request, key, timeout, default_value, min_count=None, retain=False):
+ """Retrieve the set of reports for a given report ID.
+
+ This will extract either the set of reports, credentials, or request count
+ from the stash (depending on the key passed in) and return it encoded as JSON.
+
+ When retrieving reports, this will not return any reports until min_count
+ reports have been received.
+
+ If timeout seconds elapse before the requested data can be found in the stash,
+ or before at least min_count reports are received, default_value will be
+ returned instead."""
+ t0 = time.time()
+ while time.time() - t0 < timeout:
+ time.sleep(0.5)
+ with request.server.stash.lock:
+ value = request.server.stash.take(key=key)
+ if value is not None:
+ have_sufficient_reports = (
+ min_count is None or len(value) >= min_count)
+ if retain or not have_sufficient_reports:
+ request.server.stash.put(key=key, value=value)
+ if have_sufficient_reports:
+ return json.dumps(value)
+
+ return default_value
+
+
+def main(request, response):
+ # Handle CORS preflight requests
+ if request.method == u'OPTIONS':
+ # Always reject preflights for one subdomain
+ if b"www2" in request.headers[b"Origin"]:
+ return (400, [], u"CORS preflight rejected for www2")
+ return [
+ (b"Content-Type", b"text/plain"),
+ (b"Access-Control-Allow-Origin", b"*"),
+ (b"Access-Control-Allow-Methods", b"post"),
+ (b"Access-Control-Allow-Headers", b"Content-Type"),
+ ], u"CORS allowed"
+
+ # Delete reports as requested
+ if request.method == u'POST':
+ body = json.loads(request.body)
+ if (isinstance(body, dict) and "op" in body):
+ if body["op"] == "DELETE" and "reportIDs" in body:
+ with request.server.stash.lock:
+ for key in body["reportIDs"]:
+ request.server.stash.take(key=key)
+ return "reports cleared"
+ response.status = 400
+ return "op parameter value not recognized"
+
+ if b"reportID" in request.GET:
+ key = request.GET.first(b"reportID")
+ elif b"endpoint" in request.GET:
+ key = uuid.uuid5(uuid.NAMESPACE_OID, isomorphic_decode(
+ request.GET[b'endpoint'])).urn.encode('ascii')[9:]
+ else:
+ response.status = 400
+ return "Either reportID or endpoint parameter is required."
+
+ # Cookie and count keys are derived from the report ID.
+ cookie_key = re.sub(b'^....', b'cccc', key)
+ count_key = re.sub(b'^....', b'dddd', key)
+
+ if request.method == u'GET':
+ try:
+ timeout = float(request.GET.first(b"timeout"))
+ except:
+ timeout = 0.5
+ try:
+ min_count = int(request.GET.first(b"min_count"))
+ except:
+ min_count = 1
+ retain = (b"retain" in request.GET)
+
+ op = request.GET.first(b"op", b"")
+ if op in (b"retrieve_report", b""):
+ return [(b"Content-Type", b"application/json")], retrieve_from_stash(request, key, timeout, u'[]', min_count, retain)
+
+ if op == b"retrieve_cookies":
+ return [(b"Content-Type", b"application/json")], u"{ \"reportCookies\" : " + str(retrieve_from_stash(request, cookie_key, timeout, u"\"None\"")) + u"}"
+
+ if op == b"retrieve_count":
+ return [(b"Content-Type", b"application/json")], u"{ \"report_count\": %s }" % retrieve_from_stash(request, count_key, timeout, 0)
+
+ response.status = 400
+ return "op parameter value not recognized."
+
+ # Save cookies.
+ if len(request.cookies.keys()) > 0:
+ # Convert everything into strings and dump it into a dict.
+ temp_cookies_dict = {}
+ for dict_key in request.cookies.keys():
+ temp_cookies_dict[isomorphic_decode(dict_key)] = str(
+ request.cookies.get_list(dict_key))
+ with request.server.stash.lock:
+ # Clear any existing cookie data for this request before storing new data.
+ request.server.stash.take(key=cookie_key)
+ request.server.stash.put(key=cookie_key, value=temp_cookies_dict)
+
+ # Append new report(s).
+ new_reports = json.loads(request.body)
+
+ # If the incoming report is a CSP report-uri report, then it will be a single
+ # dictionary rather than a list of reports. To handle this case, ensure that
+ # any non-list request bodies are wrapped in a list.
+ if not isinstance(new_reports, list):
+ new_reports = [new_reports]
+
+ for report in new_reports:
+ report[u"metadata"] = {
+ u"content_type": isomorphic_decode(request.headers[b"Content-Type"]),
+ }
+
+ with request.server.stash.lock:
+ reports = request.server.stash.take(key=key)
+ if reports is None:
+ reports = []
+ reports.extend(new_reports)
+ request.server.stash.put(key=key, value=reports)
+
+ # Increment report submission count. This tracks the number of times this
+ # reporting endpoint was contacted, rather than the total number of reports
+ # submitted, which can be seen from the length of the report list.
+ with request.server.stash.lock:
+ count = request.server.stash.take(key=count_key)
+ if count is None:
+ count = 0
+ count += 1
+ request.server.stash.put(key=count_key, value=count)
+
+ # Return acknowledgement report.
+ return [(b"Content-Type", b"text/plain")], b"Recorded report " + request.body