#!/usr/bin/env python3
"""
POC server for the BackgroundContents browser-initiated navigation bug.

Runs two HTTP servers on localhost:
  * APP_PORT     -> the "hosted app" origin (http://localhost:APP_PORT),
                    serves webroot/index.html inside the hosted app's extent.
  * VICTIM_PORT  -> the "victim" origin (http://127.0.0.1:VICTIM_PORT). All
                    requests have their Sec-Fetch-* headers logged.

The bug is demonstrated when the request to http://127.0.0.1:VICTIM_PORT/victim
arrives with `Sec-Fetch-Site: none` even though it was triggered by a
renderer-controlled window.open() from a cross-site page.
"""

import http.server
import os
import sys
import threading

ROOT = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'webroot')
LOGFILE = os.path.join(os.path.dirname(os.path.abspath(__file__)),
                       'victim-requests.log')


class AppHandler(http.server.SimpleHTTPRequestHandler):
    def __init__(self, *a, **kw):
        super().__init__(*a, directory=ROOT, **kw)

    def log_message(self, fmt, *args):
        sys.stderr.write('[app   :%d] ' % APP_PORT + (fmt % args) + '\n')


class VictimHandler(http.server.BaseHTTPRequestHandler):
    def do_GET(self):
        sfs = self.headers.get('Sec-Fetch-Site', '<missing>')
        sfm = self.headers.get('Sec-Fetch-Mode', '<missing>')
        sfd = self.headers.get('Sec-Fetch-Dest', '<missing>')
        ref = self.headers.get('Referer', '<missing>')
        line = ('[victim:%d] %s %s  Sec-Fetch-Site=%s  '
                'Sec-Fetch-Mode=%s  Sec-Fetch-Dest=%s  Referer=%s'
                % (VICTIM_PORT, self.command, self.path, sfs, sfm, sfd, ref))
        sys.stderr.write(line + '\n')
        sys.stderr.flush()
        with open(LOGFILE, 'a') as f:
            f.write(line + '\n')
        if sfs.lower() == 'none':
            sys.stderr.write(
                '[victim:%d] *** BUG CONFIRMED: renderer-controlled URL '
                'loaded with Sec-Fetch-Site: none ***\n' % VICTIM_PORT)
            sys.stderr.flush()
        self.send_response(200)
        self.send_header('Content-Type', 'text/html')
        self.end_headers()
        self.wfile.write(b'<!doctype html><title>victim</title>victim page')

    def log_message(self, fmt, *args):
        pass  # already logged above


class ReusableServer(http.server.ThreadingHTTPServer):
    allow_reuse_address = True


def serve(port, handler):
    httpd = ReusableServer(('127.0.0.1', port), handler)
    httpd.serve_forever()


APP_PORT = 28080
VICTIM_PORT = 28081

if __name__ == '__main__':
    open(LOGFILE, 'w').close()
    t1 = threading.Thread(target=serve, args=(APP_PORT, AppHandler),
                          daemon=True)
    t2 = threading.Thread(target=serve, args=(VICTIM_PORT, VictimHandler),
                          daemon=True)
    t1.start()
    t2.start()
    sys.stderr.write('App server    : http://localhost:%d/index.html\n'
                     % APP_PORT)
    sys.stderr.write('Victim server : http://127.0.0.1:%d/\n' % VICTIM_PORT)
    sys.stderr.write('Victim log    : %s\n' % LOGFILE)
    sys.stderr.flush()
    threading.Event().wait()
