"""Methods for the report-shared-storage and report-protected-audience endpoints (including debug endpoints)"""
import json
from typing import List, Optional, Tuple, Union
import urllib.parse
from wptserve.request import Request
from wptserve.stash import Stash
from wptserve.utils import isomorphic_decode, isomorphic_encode
# Arbitrary key used to access the reports in the stash.
REPORTS_KEY = "9d285691-4386-45ad-9a79-d2ec29557bfe"
CLEAR_STASH_AS_BYTES = isomorphic_encode("clear_stash")
Header = Tuple[str, str]
Status = Union[int, Tuple[int, str]]
Response = Tuple[Status, List[Header], str]
def get_request_origin(request: Request) -> str:
return "%s://%s" % (request.url_parts.scheme,
request.url_parts.netloc)
def handle_post_request(request: Request) -> Response:
"""Handles POST request for reports.
Retrieves the report from the request body and stores the report in the
stash. If clear_stash is specified in the query params, clears the stash.
"""
if request.GET.get(CLEAR_STASH_AS_BYTES):
clear_stash(request.server.stash)
return 200, [], "Stash successfully cleared."
store_report(request.server.stash, get_request_origin(request),
request.body.decode("utf-8"))
return 200, [], ""
def handle_get_request(request: Request) -> Response:
"""Handles GET request for reports.
Retrieves and returns all reports from the stash.
"""
headers = [("Content-Type", "application/json")]
reports = take_reports(request.server.stash, get_request_origin(request))
headers.append(("Access-Control-Allow-Origin", "*"))
return 200, headers, json.dumps(reports)
def store_report(stash: Stash, origin: str, report: str) -> None:
"""Stores the report in the stash. Report here is a JSON."""
with stash.lock:
reports_dict = stash.take(REPORTS_KEY)
if not reports_dict:
reports_dict = {}
reports = reports_dict.get(origin, [])
reports.append(report)
reports_dict[origin] = reports
stash.put(REPORTS_KEY, reports_dict)
return None
def clear_stash(stash: Stash) -> None:
"Clears the stash."
stash.take(REPORTS_KEY)
return None
def take_reports(stash: Stash, origin: str) -> List[str]:
"""Takes all the reports from the stash and returns them."""
with stash.lock:
reports_dict = stash.take(REPORTS_KEY)
if not reports_dict:
reports_dict = {}
reports = reports_dict.pop(origin, [])
stash.put(REPORTS_KEY, reports_dict)
return reports
def handle_request(request: Request) -> Response:
"""Handles request to get or store reports."""
if request.method == "POST":
return handle_post_request(request)
if request.method == "GET":
return handle_get_request(request)
return (405, "Method Not Allowed"), [("Content-Type", "application/json")], json.dumps({
"code": 405,
"message": "Only GET or POST methods are supported."
})