| """ |
| This script handles requests for the "main" and "signal" resources. |
| The "main" resource serves an HTML page that either initiates or acts as the |
| target for a prerendering speculation rule. The "signal" resource is used to |
| coordinate between the initiator and the prerendered page, ensuring the |
| prerendering is complete before proceeding. It uses query parameters to |
| determine its behavior and coordinate the process. |
| """ |
| |
| import os |
| import copy |
| import time |
| from urllib.parse import parse_qs |
| |
| def encode_query(query_dict): |
| encoded_pairs = [f"{key}={value[0]}" if value[0] !="" else key for key, value in query_dict.items()] |
| return '&'.join(encoded_pairs) |
| |
| def calculate_signal_path(url_parts): |
| url_parts = copy.deepcopy(url_parts) |
| query_dict = parse_qs(url_parts.query, keep_blank_values=True) |
| query_dict['type'] = ['signal'] |
| return url_parts._replace(query=encode_query(query_dict)).geturl() |
| |
| def calculate_prerendering_path(url_parts): |
| query_dict = parse_qs(url_parts.query) |
| query_dict['isprerendering'] = [True] |
| return url_parts._replace(query=encode_query(query_dict)).geturl() |
| |
| def main_resource_handler(request, response): |
| is_prerendering = b"isprerendering" in request.GET |
| uid = request.GET.get(b"uid") |
| signal_path = calculate_signal_path(request.url_parts) |
| content = '' |
| if not is_prerendering: |
| # First request, which is the initiator |
| template_path = os.path.join(os.path.dirname(__file__), "pus-initiator-page-template.html") |
| prerendering_path = calculate_prerendering_path(request.url_parts) |
| with open(template_path, "r") as f: |
| content = f.read().replace("{{signal_url}}", signal_path).replace( |
| "{{prerendering_url}}", prerendering_path) |
| else: |
| template_path = os.path.join(os.path.dirname( |
| __file__), "pus-page-template.html") |
| with open(template_path, "r") as f: |
| content = f.read().replace("{{signal_path}}", signal_path) |
| response.headers.set(b"Content-Type", b"text/html") |
| response.status = 200 |
| response.content = content.encode('utf-8') |
| |
| def signal_handler(request, response): |
| is_prerendering = b"isprerendering" in request.GET |
| uid = request.GET.get(b"uid") |
| if is_prerendering: |
| with request.server.stash.lock: |
| request.server.stash.put(uid, "ok") |
| else: |
| # This will hang until the gate is released |
| while True: |
| with request.server.stash.lock: |
| if request.server.stash.take(uid) is None: |
| time.sleep(0.1) |
| else: |
| break |
| response.headers.set(b"Content-Type", b"text/javascript") |
| response.status = 200 |
| response.content = "console.error('orz')".encode('utf-8') |
| |
| def main(request, response): |
| resource_router = { |
| b"main":main_resource_handler, |
| b"signal": signal_handler |
| } |
| resource_type = request.GET.get(b"type") |
| resource_handler = resource_router.get(resource_type) |
| if resource_handler is not None: |
| return resource_handler(request, response) |
| response.status = 400 # Bad Request |
| response.content = b"Invalid resource type" |
| return |