import json from wptserve.utils import isomorphic_decode def main(request, response): """Helper handler for Beacon tests. It handles two forms of requests: STORE: A URL with a query string of the form 'cmd=store&id='. Stores the receipt of a sendBeacon() request along with its validation result, returning HTTP 200 OK. if "preflightExpected" exists in the query, this handler responds to CORS preflights. STAT: A URL with a query string of the form 'cmd=stat&id='. Retrieves the results of test for the given id and returns them as a JSON array and HTTP 200 OK status code. Due to the eventual read-once nature of the stash, results for a given test are only guaranteed to be returned once, though they may be returned multiple times. An entry may contain following members. - error: An error string. null if there is no error. - type: The content-type header of the request "(missing)" if there is no content-type header in the request. Example response bodies: - [{error: null, type: "text/plain;charset=UTF8"}] - [{error: "some validation details"}] - [] Common parameters: cmd - the command, 'store' or 'stat'. id - the unique identifier of the test. """ id = request.GET.first(b"id") command = request.GET.first(b"cmd").lower() # Append CORS headers if needed. if b"origin" in request.GET: response.headers.set(b"Access-Control-Allow-Origin", request.GET.first(b"origin")) if b"credentials" in request.GET: response.headers.set(b"Access-Control-Allow-Credentials", request.GET.first(b"credentials")) # Handle the 'store' and 'stat' commands. if command == b"store": error = None # Only store the actual POST requests, not any preflight/OPTIONS # requests we may get. if request.method == u"POST": payload = b"" contentType = request.headers[b"Content-Type"] \ if b"Content-Type" in request.headers else b"(missing)" if b"form-data" in contentType: if b"payload" in request.POST: # The payload was sent as a FormData. payload = request.POST.first(b"payload") else: # A FormData was sent with an empty payload. pass else: # The payload was sent as either a string, Blob, or BufferSource. payload = request.body payload_parts = list(filter(None, payload.split(b":"))) if len(payload_parts) > 0: payload_size = int(payload_parts[0]) # Confirm the payload size sent matches with the number of # characters sent. if payload_size != len(payload): error = u"expected %d characters but got %d" % ( payload_size, len(payload)) else: # Confirm the payload contains the correct characters. for i in range(len(payload)): if i <= len(payload_parts[0]): continue c = payload[i:i+1] if c != b"*": error = u"expected '*' at index %d but got '%s''" % ( i, isomorphic_decode(c)) break # Store the result in the stash so that it can be retrieved # later with a 'stat' command. request.server.stash.put(id, { u"error": error, u"type": isomorphic_decode(contentType) }) elif request.method == u"OPTIONS": # If we expect a preflight, then add the cors headers we expect, # otherwise log an error as we shouldn't send a preflight for all # requests. if b"preflightExpected" in request.GET: response.headers.set(b"Access-Control-Allow-Headers", b"content-type") response.headers.set(b"Access-Control-Allow-Methods", b"POST") else: error = u"Preflight not expected." request.server.stash.put(id, {u"error": error}) elif command == b"stat": test_data = request.server.stash.take(id) results = [test_data] if test_data else [] response.headers.set(b"Content-Type", b"text/plain") response.content = json.dumps(results) else: response.status = 400 # BadRequest