Introduce test infrastructure for QuicTransport (#22844)

This change introduces test infrastructure for QuicTransport.
See also: https://github.com/web-platform-tests/rfcs/blob/master/rfcs/quic.md

tools/quic contains the test server and files needed by the server such as
certificate files (TODO: we will switch to the same certificate used by
wptserve once aioquic 0.8.8 is released).

tools/quic/quic_transport_server.py is based on
https://github.com/aiortc/aioquic/blob/master/examples/http3_server.py

webtransport/quic contains a test example and a sample custom handler.

This change doesn't contain a means to run the QuicTransport server
automatically.

Tracking issue: #19114
diff --git a/tools/quic/README.md b/tools/quic/README.md
new file mode 100644
index 0000000..5a64b70
--- /dev/null
+++ b/tools/quic/README.md
@@ -0,0 +1,30 @@
+This directory contains
+[QUIC](https://tools.ietf.org/html/draft-ietf-quic-transport) related tools.
+
+# QuicTransport
+[quic_transport_server.py](./quic_transport_server.py) implements a simple
+[QuicTransport](https://tools.ietf.org/html/draft-vvv-webtransport-quic) server
+for testing. It uses [aioquic](https://github.com/aiortc/aioquic/), and test
+authors can implement custom handlers by putting python scripts in
+[wpt/webtransport/quic/handlers/](../../webtransport/quic/handlers/).
+
+## Custom Handlers
+The QuicTransportServer calls functions defined in each handler script.
+
+ - handle_client_indication is called during the client indication process.
+   This function is called with three arguments:
+   
+   - connection: aioquic.asyncio.QuicConnectionProtocol
+   - origin: str The origin of the initiator.
+   - query: Dict[str, str] The dictionary of query parameters of the URL of the
+            connection.
+
+   A handler can abort the client indication process either by raising an
+   exception or closing the connection.
+	    
+ - handle_event is called when a QuicEvent arrives.
+   - connection: aioquic.asyncio.QuicConnectionProtocol
+   - event: aioquic.quic.events.QuicEvent
+
+   This function is not called until the client indication process finishes
+   successfully.
diff --git a/tools/quic/certs/README.md b/tools/quic/certs/README.md
new file mode 100644
index 0000000..216cf13
--- /dev/null
+++ b/tools/quic/certs/README.md
@@ -0,0 +1,12 @@
+To generate cert.key and cert.pem:
+
+ 1. Remove web-platform.test.key and web-platform.test.pem in ../../certs.
+ 1. From the root, run
+    `./wpt serve --config tools/quic/certs/config.json` and terminate it
+    after it has started up.
+ 1. Move tools/certs/web-platform.test.key to tools/quic/certs/cert.key.
+ 1. Move tools/certs/web-platform.test.pem to tools/quic/certs/cert.pem.
+ 1. Recover the original web-platform.test.key and web-platform.test.pem in
+    ../../certs.
+
+See also: ../../certs/README.md
\ No newline at end of file
diff --git a/tools/quic/certs/cert.key b/tools/quic/certs/cert.key
new file mode 100644
index 0000000..993cdfa
--- /dev/null
+++ b/tools/quic/certs/cert.key
@@ -0,0 +1,28 @@
+-----BEGIN PRIVATE KEY-----
+MIIEwAIBADANBgkqhkiG9w0BAQEFAASCBKowggSmAgEAAoIBAQDbyPuiBGpxhavF
+3j7pI6g+A0gC4BTLTqMObKSTkQWsjq1GOd2LA1lwTPLObwrvhIUFzbwoIbOwoPMe
+MkjssFCHG3FMj56cKiAQ2DFI6dK5PjGUVNSRxk/F4Hh2Zx9DTENl/Eb/cRT2yuu+
+W9HCu/BWfbWwlwwN5vxyneCoh5cBB/jd1KTORguYpuatHb85AD5BRhYLXwHF7yVH
+NVxHeuGlK31yuYCHNKvBHDgZF5Tp8FqKXnVU+PlKXCSU5602c2U5xHIFvKTyiZlM
+cYXNSUp0lcNw/iTSVtsPE4k1stu6qkWdT7H/uU+GUB+aLqO+svA/s9GGX8HZgt+r
+vSFE8lIJAgMBAAECggEBAJA96D9djIoygxhaEompmCoStzkD3UHMuyClVqFuRP4J
+qVh0c5xfN1yHc7bdk5y8KR00966S574c81G3CLslv8Pb09C+VQcCcob7i+ThaCWg
+1qMVxWhicUpZVlXGufLN41HUbrgIfAy4Al2tHw4hj8sDt7FMgGHDXZzPVnjke8r1
+O9YiJl1Qx4L7vMWruGa9QWjFgHnG+uhaKjsL2v7JQOGy5t8aboVyb7h8rGg/mC+e
+HIYOucV1aEMgYVaAnhGsKMHkx5A1xWpXBSruG+GRBx/kXWZ+kCNckLXuVdrhq4HI
+AdbxIzqQTPMXpO3RAujyrxkHabENMPA/FGH4szmdLoECgYEA9z8pe7/vSlWgfhsF
+z5QnwWHyFjruhgD/2sa4LB/cmwTQdGw8E5TNHDbCgmS499DZUXIZuBOTekdEVDQa
+ng8VyL3o7Dms+5iPi5cqscp1KkjLEMyPpqs4JTuixRpjmMfycdxVTpXhcuqnJpTL
+QC9pR5N/zZcAMDlBv0Fzc8T78XkCgYEA45DueWGHVf2u4uMYyWxyZhaNDagl13yx
+/oSSUTzoLvSpGQxKkv+fxSNqL3nu5Ia6uD4Gu5NubP4Hr/VeSKRfmkT1luvFcVfC
+kn8r8bssZq855AVJxXa5K1auWjCuFHj0pYf56sfhkPxpY0RQEgkvuE3iosQ12gFX
+vw147FtQURECgYEA85RpVP45S31iOPp8Vg16wRyyeE4ksSYI6kr+JJJbLummSBxd
+b1kYXSRhqj56r8I0ZvXG+r9men/9hAs08eSgrHzUHO2RSuj4+ie6Kx/vH/JJBErT
+dvqVvLCs4gvmdRz+8EeGT35/dkxQ0kSinKBY0ugwb6XEzL2L1VUw3awCHdkCgYEA
+qtQIgOv6uU2ndEDAQax8MDCrkF3yklHUGFkSsZNERMN7EQeOD81+9XFBbARflgOh
+tV8ylKr3ETCdOrS6I1PpRJiRt8qjvBMCSBDZPyygBzFxBsAFggs+s87tMV0rwMiP
+9pcdv+ZuaPVic5c7eF6XCQbGpCMgvdeWNCB77woZP9ECgYEAlobkPGDYCy/RaViU
+Fbq5Go6w0pMVnLzYbn4Gh1AJPeQKISqXtJZ7tqpdW+i7qzkLw74ELaYCBR2ZElrj
+EVe5aROx6TFN9RnjkFnyv9LeyYL+YPc8AIwVUCeSPikSGLFpJfa/jwDmWh3vHmmA
+NRUP40wbtBi42C2udrTxUWsHxqc=
+-----END PRIVATE KEY-----
diff --git a/tools/quic/certs/cert.pem b/tools/quic/certs/cert.pem
new file mode 100644
index 0000000..7e70f1b
--- /dev/null
+++ b/tools/quic/certs/cert.pem
@@ -0,0 +1,240 @@
+Certificate:
+    Data:
+        Version: 3 (0x2)
+        Serial Number: 919492 (0xe07c4)
+        Signature Algorithm: sha256WithRSAEncryption
+        Issuer: CN=web-platform-tests
+        Validity
+            Not Before: Apr 20 11:20:56 2020 GMT
+            Not After : Apr 18 11:20:56 2030 GMT
+        Subject: CN=web-platform.test
+        Subject Public Key Info:
+            Public Key Algorithm: rsaEncryption
+                RSA Public-Key: (2048 bit)
+                Modulus:
+                    00:db:c8:fb:a2:04:6a:71:85:ab:c5:de:3e:e9:23:
+                    a8:3e:03:48:02:e0:14:cb:4e:a3:0e:6c:a4:93:91:
+                    05:ac:8e:ad:46:39:dd:8b:03:59:70:4c:f2:ce:6f:
+                    0a:ef:84:85:05:cd:bc:28:21:b3:b0:a0:f3:1e:32:
+                    48:ec:b0:50:87:1b:71:4c:8f:9e:9c:2a:20:10:d8:
+                    31:48:e9:d2:b9:3e:31:94:54:d4:91:c6:4f:c5:e0:
+                    78:76:67:1f:43:4c:43:65:fc:46:ff:71:14:f6:ca:
+                    eb:be:5b:d1:c2:bb:f0:56:7d:b5:b0:97:0c:0d:e6:
+                    fc:72:9d:e0:a8:87:97:01:07:f8:dd:d4:a4:ce:46:
+                    0b:98:a6:e6:ad:1d:bf:39:00:3e:41:46:16:0b:5f:
+                    01:c5:ef:25:47:35:5c:47:7a:e1:a5:2b:7d:72:b9:
+                    80:87:34:ab:c1:1c:38:19:17:94:e9:f0:5a:8a:5e:
+                    75:54:f8:f9:4a:5c:24:94:e7:ad:36:73:65:39:c4:
+                    72:05:bc:a4:f2:89:99:4c:71:85:cd:49:4a:74:95:
+                    c3:70:fe:24:d2:56:db:0f:13:89:35:b2:db:ba:aa:
+                    45:9d:4f:b1:ff:b9:4f:86:50:1f:9a:2e:a3:be:b2:
+                    f0:3f:b3:d1:86:5f:c1:d9:82:df:ab:bd:21:44:f2:
+                    52:09
+                Exponent: 65537 (0x10001)
+        X509v3 extensions:
+            X509v3 Basic Constraints: 
+                CA:FALSE
+            X509v3 Subject Key Identifier: 
+                79:96:E1:96:F3:7C:41:2B:3B:9F:1B:0D:D6:A9:B8:B7:4F:E6:61:5C
+            X509v3 Authority Key Identifier: 
+                keyid:F6:B9:DA:16:07:05:BB:6F:C9:63:30:C6:67:40:CB:03:D3:1E:90:EC
+
+            X509v3 Key Usage: 
+                Digital Signature, Non Repudiation, Key Encipherment
+            X509v3 Extended Key Usage: 
+                TLS Web Server Authentication
+            X509v3 Subject Alternative Name: 
+                DNS:web-platform.test, DNS:op8.web-platform.test, DNS:op7.web-platform.test, DNS:op9.web-platform.test, DNS:op4.web-platform.test, DNS:not-web-platform.test, DNS:op6.web-platform.test, DNS:op3.web-platform.test, DNS:op2.web-platform.test, DNS:op1.web-platform.test, DNS:www.web-platform.test, DNS:op5.web-platform.test, DNS:op88.web-platform.test, DNS:op98.web-platform.test, DNS:op85.web-platform.test, DNS:op89.web-platform.test, DNS:op66.web-platform.test, DNS:op72.web-platform.test, DNS:op24.web-platform.test, DNS:op41.web-platform.test, DNS:op79.web-platform.test, DNS:op91.web-platform.test, DNS:op59.web-platform.test, DNS:op39.web-platform.test, DNS:op60.web-platform.test, DNS:op58.web-platform.test, DNS:op28.web-platform.test, DNS:www1.web-platform.test, DNS:op14.web-platform.test, DNS:op69.web-platform.test, DNS:op40.web-platform.test, DNS:op74.web-platform.test, DNS:op31.web-platform.test, DNS:op18.web-platform.test, DNS:op73.web-platform.test, DNS:op77.web-platform.test, DNS:op12.web-platform.test, DNS:op54.web-platform.test, DNS:op63.web-platform.test, DNS:op71.web-platform.test, DNS:op95.web-platform.test, DNS:op16.web-platform.test, DNS:op36.web-platform.test, DNS:op27.web-platform.test, DNS:op29.web-platform.test, DNS:op94.web-platform.test, DNS:op44.web-platform.test, DNS:op33.web-platform.test, DNS:op84.web-platform.test, DNS:op32.web-platform.test, DNS:op61.web-platform.test, DNS:op70.web-platform.test, DNS:www2.web-platform.test, DNS:op43.web-platform.test, DNS:op78.web-platform.test, DNS:op26.web-platform.test, DNS:op76.web-platform.test, DNS:op52.web-platform.test, DNS:op99.web-platform.test, DNS:op86.web-platform.test, DNS:op46.web-platform.test, DNS:op17.web-platform.test, DNS:op90.web-platform.test, DNS:op93.web-platform.test, DNS:op10.web-platform.test, DNS:op55.web-platform.test, DNS:op47.web-platform.test, DNS:op51.web-platform.test, DNS:op45.web-platform.test, DNS:op80.web-platform.test, DNS:op68.web-platform.test, DNS:op49.web-platform.test, DNS:op57.web-platform.test, DNS:op35.web-platform.test, DNS:op67.web-platform.test, DNS:op92.web-platform.test, DNS:op15.web-platform.test, DNS:op13.web-platform.test, DNS:op75.web-platform.test, DNS:op64.web-platform.test, DNS:op97.web-platform.test, DNS:op37.web-platform.test, DNS:op56.web-platform.test, DNS:op62.web-platform.test, DNS:op82.web-platform.test, DNS:op25.web-platform.test, DNS:op11.web-platform.test, DNS:op50.web-platform.test, DNS:op38.web-platform.test, DNS:op83.web-platform.test, DNS:op81.web-platform.test, DNS:op20.web-platform.test, DNS:op21.web-platform.test, DNS:op23.web-platform.test, DNS:op42.web-platform.test, DNS:op22.web-platform.test, DNS:op65.web-platform.test, DNS:op96.web-platform.test, DNS:op87.web-platform.test, DNS:op19.web-platform.test, DNS:op53.web-platform.test, DNS:op30.web-platform.test, DNS:op48.web-platform.test, DNS:op34.web-platform.test, DNS:op6.not-web-platform.test, DNS:op3.not-web-platform.test, DNS:op2.not-web-platform.test, DNS:op5.not-web-platform.test, DNS:www.not-web-platform.test, DNS:www.www.web-platform.test, DNS:op7.not-web-platform.test, DNS:op4.not-web-platform.test, DNS:op8.not-web-platform.test, DNS:op9.not-web-platform.test, DNS:op1.not-web-platform.test, DNS:op36.not-web-platform.test, DNS:op53.not-web-platform.test, DNS:op50.not-web-platform.test, DNS:op24.not-web-platform.test, DNS:op31.not-web-platform.test, DNS:op95.not-web-platform.test, DNS:op83.not-web-platform.test, DNS:www2.not-web-platform.test, DNS:op73.not-web-platform.test, DNS:op19.not-web-platform.test, DNS:op21.not-web-platform.test, DNS:op81.not-web-platform.test, DNS:op70.not-web-platform.test, DNS:op78.not-web-platform.test, DNS:op40.not-web-platform.test, DNS:op25.not-web-platform.test, DNS:op65.not-web-platform.test, DNS:www.www2.web-platform.test, DNS:op80.not-web-platform.test, DNS:op52.not-web-platform.test, DNS:op68.not-web-platform.test, DNS:op45.not-web-platform.test, DNS:op71.not-web-platform.test, DNS:op72.not-web-platform.test, DNS:op90.not-web-platform.test, DNS:op89.not-web-platform.test, DNS:op49.not-web-platform.test, DNS:op77.not-web-platform.test, DNS:op79.not-web-platform.test, DNS:op82.not-web-platform.test, DNS:www.www1.web-platform.test, DNS:op12.not-web-platform.test, DNS:op39.not-web-platform.test, DNS:op44.not-web-platform.test, DNS:www1.not-web-platform.test, DNS:op58.not-web-platform.test, DNS:op14.not-web-platform.test, DNS:op30.not-web-platform.test, DNS:op62.not-web-platform.test, DNS:op61.not-web-platform.test, DNS:op92.not-web-platform.test, DNS:op29.not-web-platform.test, DNS:op98.not-web-platform.test, DNS:op64.not-web-platform.test, DNS:op26.not-web-platform.test, DNS:op22.not-web-platform.test, DNS:op94.not-web-platform.test, DNS:op38.not-web-platform.test, DNS:op33.not-web-platform.test, DNS:op23.not-web-platform.test, DNS:op57.not-web-platform.test, DNS:op54.not-web-platform.test, DNS:op85.not-web-platform.test, DNS:op46.not-web-platform.test, DNS:op97.not-web-platform.test, DNS:op32.not-web-platform.test, DNS:op60.not-web-platform.test, DNS:op96.not-web-platform.test, DNS:op51.not-web-platform.test, DNS:op41.not-web-platform.test, DNS:op35.not-web-platform.test, DNS:op99.not-web-platform.test, DNS:op42.not-web-platform.test, DNS:op67.not-web-platform.test, DNS:op37.not-web-platform.test, DNS:op48.not-web-platform.test, DNS:op55.not-web-platform.test, DNS:op56.not-web-platform.test, DNS:op84.not-web-platform.test, DNS:op34.not-web-platform.test, DNS:op69.not-web-platform.test, DNS:op11.not-web-platform.test, DNS:op93.not-web-platform.test, DNS:www1.www.web-platform.test, DNS:op86.not-web-platform.test, DNS:op13.not-web-platform.test, DNS:op20.not-web-platform.test, DNS:op76.not-web-platform.test, DNS:op27.not-web-platform.test, DNS:op17.not-web-platform.test, DNS:op75.not-web-platform.test, DNS:op15.not-web-platform.test, DNS:op47.not-web-platform.test, DNS:op18.not-web-platform.test, DNS:op63.not-web-platform.test, DNS:op28.not-web-platform.test, DNS:op43.not-web-platform.test, DNS:op66.not-web-platform.test, DNS:www2.www.web-platform.test, DNS:op91.not-web-platform.test, DNS:op74.not-web-platform.test, DNS:op59.not-web-platform.test, DNS:op88.not-web-platform.test, DNS:op87.not-web-platform.test, DNS:op10.not-web-platform.test, DNS:op16.not-web-platform.test, DNS:www1.www2.web-platform.test, DNS:www2.www2.web-platform.test, DNS:www2.www1.web-platform.test, DNS:www1.www1.web-platform.test, DNS:www.www.not-web-platform.test, DNS:xn--lve-6lad.web-platform.test, DNS:www1.www.not-web-platform.test, DNS:www.www2.not-web-platform.test, DNS:www2.www.not-web-platform.test, DNS:www.www1.not-web-platform.test, DNS:www2.www2.not-web-platform.test, DNS:www2.www1.not-web-platform.test, DNS:www1.www1.not-web-platform.test, DNS:www1.www2.not-web-platform.test, DNS:xn--lve-6lad.www.web-platform.test, DNS:xn--lve-6lad.not-web-platform.test, DNS:www.xn--lve-6lad.web-platform.test, DNS:www2.xn--lve-6lad.web-platform.test, DNS:xn--lve-6lad.www2.web-platform.test, DNS:xn--lve-6lad.www1.web-platform.test, DNS:www1.xn--lve-6lad.web-platform.test, DNS:xn--lve-6lad.www.not-web-platform.test, DNS:www.xn--lve-6lad.not-web-platform.test, DNS:xn--lve-6lad.www1.not-web-platform.test, DNS:www2.xn--lve-6lad.not-web-platform.test, DNS:www1.xn--lve-6lad.not-web-platform.test, DNS:xn--lve-6lad.www2.not-web-platform.test, DNS:xn--n8j6ds53lwwkrqhv28a.web-platform.test, DNS:xn--lve-6lad.xn--lve-6lad.web-platform.test, DNS:www.xn--n8j6ds53lwwkrqhv28a.web-platform.test, DNS:xn--n8j6ds53lwwkrqhv28a.not-web-platform.test, DNS:xn--n8j6ds53lwwkrqhv28a.www.web-platform.test, DNS:www1.xn--n8j6ds53lwwkrqhv28a.web-platform.test, DNS:xn--n8j6ds53lwwkrqhv28a.www2.web-platform.test, DNS:xn--n8j6ds53lwwkrqhv28a.www1.web-platform.test, DNS:www2.xn--n8j6ds53lwwkrqhv28a.web-platform.test, DNS:xn--lve-6lad.xn--lve-6lad.not-web-platform.test, DNS:www.xn--n8j6ds53lwwkrqhv28a.not-web-platform.test, DNS:xn--n8j6ds53lwwkrqhv28a.www.not-web-platform.test, DNS:xn--n8j6ds53lwwkrqhv28a.www2.not-web-platform.test, DNS:www1.xn--n8j6ds53lwwkrqhv28a.not-web-platform.test, DNS:www2.xn--n8j6ds53lwwkrqhv28a.not-web-platform.test, DNS:xn--n8j6ds53lwwkrqhv28a.www1.not-web-platform.test, DNS:xn--n8j6ds53lwwkrqhv28a.xn--lve-6lad.web-platform.test, DNS:xn--lve-6lad.xn--n8j6ds53lwwkrqhv28a.web-platform.test, DNS:xn--n8j6ds53lwwkrqhv28a.xn--lve-6lad.not-web-platform.test, DNS:xn--lve-6lad.xn--n8j6ds53lwwkrqhv28a.not-web-platform.test, DNS:xn--n8j6ds53lwwkrqhv28a.xn--n8j6ds53lwwkrqhv28a.web-platform.test, DNS:xn--n8j6ds53lwwkrqhv28a.xn--n8j6ds53lwwkrqhv28a.not-web-platform.test
+    Signature Algorithm: sha256WithRSAEncryption
+         0b:8b:55:6a:b4:65:a8:2a:3b:c0:62:0e:85:d5:dc:99:9f:3f:
+         d0:b7:ff:cb:b6:68:e5:ae:d3:a2:4e:c8:d4:aa:34:95:e4:9e:
+         4b:e4:74:e4:c4:ec:11:31:67:e5:a6:da:85:6b:0f:d7:64:b7:
+         e7:eb:fe:09:b6:88:e1:74:1d:0c:15:a3:2e:01:03:fc:67:9c:
+         7c:ba:e6:fb:52:1d:58:cd:68:a3:55:8c:9b:57:bb:89:32:10:
+         41:71:99:a3:1a:dc:25:2d:d0:b9:1a:af:e6:76:40:d7:f5:23:
+         a9:d8:59:45:8a:ab:53:53:87:cc:14:b3:87:de:1d:01:99:7a:
+         4f:72:47:69:9c:98:73:94:79:90:25:70:02:ec:75:b2:77:11:
+         28:97:cd:43:3d:d5:68:38:73:71:fd:a9:0b:a2:ed:2d:1c:6e:
+         e1:05:d4:30:a6:64:04:10:ca:e8:e6:e0:7a:a9:f4:26:94:6b:
+         a4:28:aa:0c:eb:3e:e0:ef:d2:be:0b:70:e4:dd:4c:fc:a2:87:
+         0c:24:83:72:e2:e4:6f:25:a8:ee:c5:12:89:d9:dd:8f:82:14:
+         c3:d6:06:ae:1d:bb:7b:3c:fc:9b:27:56:50:b4:28:44:60:80:
+         17:87:1c:b1:ce:6d:77:b4:d1:2b:32:e8:d0:ba:06:6f:4d:04:
+         ff:60:fe:e3
+-----BEGIN CERTIFICATE-----
+MIIgvDCCH6SgAwIBAgIDDgfEMA0GCSqGSIb3DQEBCwUAMB0xGzAZBgNVBAMMEndl
+Yi1wbGF0Zm9ybS10ZXN0czAeFw0yMDA0MjAxMTIwNTZaFw0zMDA0MTgxMTIwNTZa
+MBwxGjAYBgNVBAMMEXdlYi1wbGF0Zm9ybS50ZXN0MIIBIjANBgkqhkiG9w0BAQEF
+AAOCAQ8AMIIBCgKCAQEA28j7ogRqcYWrxd4+6SOoPgNIAuAUy06jDmykk5EFrI6t
+RjndiwNZcEzyzm8K74SFBc28KCGzsKDzHjJI7LBQhxtxTI+enCogENgxSOnSuT4x
+lFTUkcZPxeB4dmcfQ0xDZfxG/3EU9srrvlvRwrvwVn21sJcMDeb8cp3gqIeXAQf4
+3dSkzkYLmKbmrR2/OQA+QUYWC18Bxe8lRzVcR3rhpSt9crmAhzSrwRw4GReU6fBa
+il51VPj5SlwklOetNnNlOcRyBbyk8omZTHGFzUlKdJXDcP4k0lbbDxOJNbLbuqpF
+nU+x/7lPhlAfmi6jvrLwP7PRhl/B2YLfq70hRPJSCQIDAQABo4IeBDCCHgAwCQYD
+VR0TBAIwADAdBgNVHQ4EFgQUeZbhlvN8QSs7nxsN1qm4t0/mYVwwHwYDVR0jBBgw
+FoAU9rnaFgcFu2/JYzDGZ0DLA9MekOwwCwYDVR0PBAQDAgXgMBMGA1UdJQQMMAoG
+CCsGAQUFBwMBMIIdjwYDVR0RBIIdhjCCHYKCEXdlYi1wbGF0Zm9ybS50ZXN0ghVv
+cDgud2ViLXBsYXRmb3JtLnRlc3SCFW9wNy53ZWItcGxhdGZvcm0udGVzdIIVb3A5
+LndlYi1wbGF0Zm9ybS50ZXN0ghVvcDQud2ViLXBsYXRmb3JtLnRlc3SCFW5vdC13
+ZWItcGxhdGZvcm0udGVzdIIVb3A2LndlYi1wbGF0Zm9ybS50ZXN0ghVvcDMud2Vi
+LXBsYXRmb3JtLnRlc3SCFW9wMi53ZWItcGxhdGZvcm0udGVzdIIVb3AxLndlYi1w
+bGF0Zm9ybS50ZXN0ghV3d3cud2ViLXBsYXRmb3JtLnRlc3SCFW9wNS53ZWItcGxh
+dGZvcm0udGVzdIIWb3A4OC53ZWItcGxhdGZvcm0udGVzdIIWb3A5OC53ZWItcGxh
+dGZvcm0udGVzdIIWb3A4NS53ZWItcGxhdGZvcm0udGVzdIIWb3A4OS53ZWItcGxh
+dGZvcm0udGVzdIIWb3A2Ni53ZWItcGxhdGZvcm0udGVzdIIWb3A3Mi53ZWItcGxh
+dGZvcm0udGVzdIIWb3AyNC53ZWItcGxhdGZvcm0udGVzdIIWb3A0MS53ZWItcGxh
+dGZvcm0udGVzdIIWb3A3OS53ZWItcGxhdGZvcm0udGVzdIIWb3A5MS53ZWItcGxh
+dGZvcm0udGVzdIIWb3A1OS53ZWItcGxhdGZvcm0udGVzdIIWb3AzOS53ZWItcGxh
+dGZvcm0udGVzdIIWb3A2MC53ZWItcGxhdGZvcm0udGVzdIIWb3A1OC53ZWItcGxh
+dGZvcm0udGVzdIIWb3AyOC53ZWItcGxhdGZvcm0udGVzdIIWd3d3MS53ZWItcGxh
+dGZvcm0udGVzdIIWb3AxNC53ZWItcGxhdGZvcm0udGVzdIIWb3A2OS53ZWItcGxh
+dGZvcm0udGVzdIIWb3A0MC53ZWItcGxhdGZvcm0udGVzdIIWb3A3NC53ZWItcGxh
+dGZvcm0udGVzdIIWb3AzMS53ZWItcGxhdGZvcm0udGVzdIIWb3AxOC53ZWItcGxh
+dGZvcm0udGVzdIIWb3A3My53ZWItcGxhdGZvcm0udGVzdIIWb3A3Ny53ZWItcGxh
+dGZvcm0udGVzdIIWb3AxMi53ZWItcGxhdGZvcm0udGVzdIIWb3A1NC53ZWItcGxh
+dGZvcm0udGVzdIIWb3A2My53ZWItcGxhdGZvcm0udGVzdIIWb3A3MS53ZWItcGxh
+dGZvcm0udGVzdIIWb3A5NS53ZWItcGxhdGZvcm0udGVzdIIWb3AxNi53ZWItcGxh
+dGZvcm0udGVzdIIWb3AzNi53ZWItcGxhdGZvcm0udGVzdIIWb3AyNy53ZWItcGxh
+dGZvcm0udGVzdIIWb3AyOS53ZWItcGxhdGZvcm0udGVzdIIWb3A5NC53ZWItcGxh
+dGZvcm0udGVzdIIWb3A0NC53ZWItcGxhdGZvcm0udGVzdIIWb3AzMy53ZWItcGxh
+dGZvcm0udGVzdIIWb3A4NC53ZWItcGxhdGZvcm0udGVzdIIWb3AzMi53ZWItcGxh
+dGZvcm0udGVzdIIWb3A2MS53ZWItcGxhdGZvcm0udGVzdIIWb3A3MC53ZWItcGxh
+dGZvcm0udGVzdIIWd3d3Mi53ZWItcGxhdGZvcm0udGVzdIIWb3A0My53ZWItcGxh
+dGZvcm0udGVzdIIWb3A3OC53ZWItcGxhdGZvcm0udGVzdIIWb3AyNi53ZWItcGxh
+dGZvcm0udGVzdIIWb3A3Ni53ZWItcGxhdGZvcm0udGVzdIIWb3A1Mi53ZWItcGxh
+dGZvcm0udGVzdIIWb3A5OS53ZWItcGxhdGZvcm0udGVzdIIWb3A4Ni53ZWItcGxh
+dGZvcm0udGVzdIIWb3A0Ni53ZWItcGxhdGZvcm0udGVzdIIWb3AxNy53ZWItcGxh
+dGZvcm0udGVzdIIWb3A5MC53ZWItcGxhdGZvcm0udGVzdIIWb3A5My53ZWItcGxh
+dGZvcm0udGVzdIIWb3AxMC53ZWItcGxhdGZvcm0udGVzdIIWb3A1NS53ZWItcGxh
+dGZvcm0udGVzdIIWb3A0Ny53ZWItcGxhdGZvcm0udGVzdIIWb3A1MS53ZWItcGxh
+dGZvcm0udGVzdIIWb3A0NS53ZWItcGxhdGZvcm0udGVzdIIWb3A4MC53ZWItcGxh
+dGZvcm0udGVzdIIWb3A2OC53ZWItcGxhdGZvcm0udGVzdIIWb3A0OS53ZWItcGxh
+dGZvcm0udGVzdIIWb3A1Ny53ZWItcGxhdGZvcm0udGVzdIIWb3AzNS53ZWItcGxh
+dGZvcm0udGVzdIIWb3A2Ny53ZWItcGxhdGZvcm0udGVzdIIWb3A5Mi53ZWItcGxh
+dGZvcm0udGVzdIIWb3AxNS53ZWItcGxhdGZvcm0udGVzdIIWb3AxMy53ZWItcGxh
+dGZvcm0udGVzdIIWb3A3NS53ZWItcGxhdGZvcm0udGVzdIIWb3A2NC53ZWItcGxh
+dGZvcm0udGVzdIIWb3A5Ny53ZWItcGxhdGZvcm0udGVzdIIWb3AzNy53ZWItcGxh
+dGZvcm0udGVzdIIWb3A1Ni53ZWItcGxhdGZvcm0udGVzdIIWb3A2Mi53ZWItcGxh
+dGZvcm0udGVzdIIWb3A4Mi53ZWItcGxhdGZvcm0udGVzdIIWb3AyNS53ZWItcGxh
+dGZvcm0udGVzdIIWb3AxMS53ZWItcGxhdGZvcm0udGVzdIIWb3A1MC53ZWItcGxh
+dGZvcm0udGVzdIIWb3AzOC53ZWItcGxhdGZvcm0udGVzdIIWb3A4My53ZWItcGxh
+dGZvcm0udGVzdIIWb3A4MS53ZWItcGxhdGZvcm0udGVzdIIWb3AyMC53ZWItcGxh
+dGZvcm0udGVzdIIWb3AyMS53ZWItcGxhdGZvcm0udGVzdIIWb3AyMy53ZWItcGxh
+dGZvcm0udGVzdIIWb3A0Mi53ZWItcGxhdGZvcm0udGVzdIIWb3AyMi53ZWItcGxh
+dGZvcm0udGVzdIIWb3A2NS53ZWItcGxhdGZvcm0udGVzdIIWb3A5Ni53ZWItcGxh
+dGZvcm0udGVzdIIWb3A4Ny53ZWItcGxhdGZvcm0udGVzdIIWb3AxOS53ZWItcGxh
+dGZvcm0udGVzdIIWb3A1My53ZWItcGxhdGZvcm0udGVzdIIWb3AzMC53ZWItcGxh
+dGZvcm0udGVzdIIWb3A0OC53ZWItcGxhdGZvcm0udGVzdIIWb3AzNC53ZWItcGxh
+dGZvcm0udGVzdIIZb3A2Lm5vdC13ZWItcGxhdGZvcm0udGVzdIIZb3AzLm5vdC13
+ZWItcGxhdGZvcm0udGVzdIIZb3AyLm5vdC13ZWItcGxhdGZvcm0udGVzdIIZb3A1
+Lm5vdC13ZWItcGxhdGZvcm0udGVzdIIZd3d3Lm5vdC13ZWItcGxhdGZvcm0udGVz
+dIIZd3d3Lnd3dy53ZWItcGxhdGZvcm0udGVzdIIZb3A3Lm5vdC13ZWItcGxhdGZv
+cm0udGVzdIIZb3A0Lm5vdC13ZWItcGxhdGZvcm0udGVzdIIZb3A4Lm5vdC13ZWIt
+cGxhdGZvcm0udGVzdIIZb3A5Lm5vdC13ZWItcGxhdGZvcm0udGVzdIIZb3AxLm5v
+dC13ZWItcGxhdGZvcm0udGVzdIIab3AzNi5ub3Qtd2ViLXBsYXRmb3JtLnRlc3SC
+Gm9wNTMubm90LXdlYi1wbGF0Zm9ybS50ZXN0ghpvcDUwLm5vdC13ZWItcGxhdGZv
+cm0udGVzdIIab3AyNC5ub3Qtd2ViLXBsYXRmb3JtLnRlc3SCGm9wMzEubm90LXdl
+Yi1wbGF0Zm9ybS50ZXN0ghpvcDk1Lm5vdC13ZWItcGxhdGZvcm0udGVzdIIab3A4
+My5ub3Qtd2ViLXBsYXRmb3JtLnRlc3SCGnd3dzIubm90LXdlYi1wbGF0Zm9ybS50
+ZXN0ghpvcDczLm5vdC13ZWItcGxhdGZvcm0udGVzdIIab3AxOS5ub3Qtd2ViLXBs
+YXRmb3JtLnRlc3SCGm9wMjEubm90LXdlYi1wbGF0Zm9ybS50ZXN0ghpvcDgxLm5v
+dC13ZWItcGxhdGZvcm0udGVzdIIab3A3MC5ub3Qtd2ViLXBsYXRmb3JtLnRlc3SC
+Gm9wNzgubm90LXdlYi1wbGF0Zm9ybS50ZXN0ghpvcDQwLm5vdC13ZWItcGxhdGZv
+cm0udGVzdIIab3AyNS5ub3Qtd2ViLXBsYXRmb3JtLnRlc3SCGm9wNjUubm90LXdl
+Yi1wbGF0Zm9ybS50ZXN0ghp3d3cud3d3Mi53ZWItcGxhdGZvcm0udGVzdIIab3A4
+MC5ub3Qtd2ViLXBsYXRmb3JtLnRlc3SCGm9wNTIubm90LXdlYi1wbGF0Zm9ybS50
+ZXN0ghpvcDY4Lm5vdC13ZWItcGxhdGZvcm0udGVzdIIab3A0NS5ub3Qtd2ViLXBs
+YXRmb3JtLnRlc3SCGm9wNzEubm90LXdlYi1wbGF0Zm9ybS50ZXN0ghpvcDcyLm5v
+dC13ZWItcGxhdGZvcm0udGVzdIIab3A5MC5ub3Qtd2ViLXBsYXRmb3JtLnRlc3SC
+Gm9wODkubm90LXdlYi1wbGF0Zm9ybS50ZXN0ghpvcDQ5Lm5vdC13ZWItcGxhdGZv
+cm0udGVzdIIab3A3Ny5ub3Qtd2ViLXBsYXRmb3JtLnRlc3SCGm9wNzkubm90LXdl
+Yi1wbGF0Zm9ybS50ZXN0ghpvcDgyLm5vdC13ZWItcGxhdGZvcm0udGVzdIIad3d3
+Lnd3dzEud2ViLXBsYXRmb3JtLnRlc3SCGm9wMTIubm90LXdlYi1wbGF0Zm9ybS50
+ZXN0ghpvcDM5Lm5vdC13ZWItcGxhdGZvcm0udGVzdIIab3A0NC5ub3Qtd2ViLXBs
+YXRmb3JtLnRlc3SCGnd3dzEubm90LXdlYi1wbGF0Zm9ybS50ZXN0ghpvcDU4Lm5v
+dC13ZWItcGxhdGZvcm0udGVzdIIab3AxNC5ub3Qtd2ViLXBsYXRmb3JtLnRlc3SC
+Gm9wMzAubm90LXdlYi1wbGF0Zm9ybS50ZXN0ghpvcDYyLm5vdC13ZWItcGxhdGZv
+cm0udGVzdIIab3A2MS5ub3Qtd2ViLXBsYXRmb3JtLnRlc3SCGm9wOTIubm90LXdl
+Yi1wbGF0Zm9ybS50ZXN0ghpvcDI5Lm5vdC13ZWItcGxhdGZvcm0udGVzdIIab3A5
+OC5ub3Qtd2ViLXBsYXRmb3JtLnRlc3SCGm9wNjQubm90LXdlYi1wbGF0Zm9ybS50
+ZXN0ghpvcDI2Lm5vdC13ZWItcGxhdGZvcm0udGVzdIIab3AyMi5ub3Qtd2ViLXBs
+YXRmb3JtLnRlc3SCGm9wOTQubm90LXdlYi1wbGF0Zm9ybS50ZXN0ghpvcDM4Lm5v
+dC13ZWItcGxhdGZvcm0udGVzdIIab3AzMy5ub3Qtd2ViLXBsYXRmb3JtLnRlc3SC
+Gm9wMjMubm90LXdlYi1wbGF0Zm9ybS50ZXN0ghpvcDU3Lm5vdC13ZWItcGxhdGZv
+cm0udGVzdIIab3A1NC5ub3Qtd2ViLXBsYXRmb3JtLnRlc3SCGm9wODUubm90LXdl
+Yi1wbGF0Zm9ybS50ZXN0ghpvcDQ2Lm5vdC13ZWItcGxhdGZvcm0udGVzdIIab3A5
+Ny5ub3Qtd2ViLXBsYXRmb3JtLnRlc3SCGm9wMzIubm90LXdlYi1wbGF0Zm9ybS50
+ZXN0ghpvcDYwLm5vdC13ZWItcGxhdGZvcm0udGVzdIIab3A5Ni5ub3Qtd2ViLXBs
+YXRmb3JtLnRlc3SCGm9wNTEubm90LXdlYi1wbGF0Zm9ybS50ZXN0ghpvcDQxLm5v
+dC13ZWItcGxhdGZvcm0udGVzdIIab3AzNS5ub3Qtd2ViLXBsYXRmb3JtLnRlc3SC
+Gm9wOTkubm90LXdlYi1wbGF0Zm9ybS50ZXN0ghpvcDQyLm5vdC13ZWItcGxhdGZv
+cm0udGVzdIIab3A2Ny5ub3Qtd2ViLXBsYXRmb3JtLnRlc3SCGm9wMzcubm90LXdl
+Yi1wbGF0Zm9ybS50ZXN0ghpvcDQ4Lm5vdC13ZWItcGxhdGZvcm0udGVzdIIab3A1
+NS5ub3Qtd2ViLXBsYXRmb3JtLnRlc3SCGm9wNTYubm90LXdlYi1wbGF0Zm9ybS50
+ZXN0ghpvcDg0Lm5vdC13ZWItcGxhdGZvcm0udGVzdIIab3AzNC5ub3Qtd2ViLXBs
+YXRmb3JtLnRlc3SCGm9wNjkubm90LXdlYi1wbGF0Zm9ybS50ZXN0ghpvcDExLm5v
+dC13ZWItcGxhdGZvcm0udGVzdIIab3A5My5ub3Qtd2ViLXBsYXRmb3JtLnRlc3SC
+Gnd3dzEud3d3LndlYi1wbGF0Zm9ybS50ZXN0ghpvcDg2Lm5vdC13ZWItcGxhdGZv
+cm0udGVzdIIab3AxMy5ub3Qtd2ViLXBsYXRmb3JtLnRlc3SCGm9wMjAubm90LXdl
+Yi1wbGF0Zm9ybS50ZXN0ghpvcDc2Lm5vdC13ZWItcGxhdGZvcm0udGVzdIIab3Ay
+Ny5ub3Qtd2ViLXBsYXRmb3JtLnRlc3SCGm9wMTcubm90LXdlYi1wbGF0Zm9ybS50
+ZXN0ghpvcDc1Lm5vdC13ZWItcGxhdGZvcm0udGVzdIIab3AxNS5ub3Qtd2ViLXBs
+YXRmb3JtLnRlc3SCGm9wNDcubm90LXdlYi1wbGF0Zm9ybS50ZXN0ghpvcDE4Lm5v
+dC13ZWItcGxhdGZvcm0udGVzdIIab3A2My5ub3Qtd2ViLXBsYXRmb3JtLnRlc3SC
+Gm9wMjgubm90LXdlYi1wbGF0Zm9ybS50ZXN0ghpvcDQzLm5vdC13ZWItcGxhdGZv
+cm0udGVzdIIab3A2Ni5ub3Qtd2ViLXBsYXRmb3JtLnRlc3SCGnd3dzIud3d3Lndl
+Yi1wbGF0Zm9ybS50ZXN0ghpvcDkxLm5vdC13ZWItcGxhdGZvcm0udGVzdIIab3A3
+NC5ub3Qtd2ViLXBsYXRmb3JtLnRlc3SCGm9wNTkubm90LXdlYi1wbGF0Zm9ybS50
+ZXN0ghpvcDg4Lm5vdC13ZWItcGxhdGZvcm0udGVzdIIab3A4Ny5ub3Qtd2ViLXBs
+YXRmb3JtLnRlc3SCGm9wMTAubm90LXdlYi1wbGF0Zm9ybS50ZXN0ghpvcDE2Lm5v
+dC13ZWItcGxhdGZvcm0udGVzdIIbd3d3MS53d3cyLndlYi1wbGF0Zm9ybS50ZXN0
+ght3d3cyLnd3dzIud2ViLXBsYXRmb3JtLnRlc3SCG3d3dzIud3d3MS53ZWItcGxh
+dGZvcm0udGVzdIIbd3d3MS53d3cxLndlYi1wbGF0Zm9ybS50ZXN0gh13d3cud3d3
+Lm5vdC13ZWItcGxhdGZvcm0udGVzdIIeeG4tLWx2ZS02bGFkLndlYi1wbGF0Zm9y
+bS50ZXN0gh53d3cxLnd3dy5ub3Qtd2ViLXBsYXRmb3JtLnRlc3SCHnd3dy53d3cy
+Lm5vdC13ZWItcGxhdGZvcm0udGVzdIIed3d3Mi53d3cubm90LXdlYi1wbGF0Zm9y
+bS50ZXN0gh53d3cud3d3MS5ub3Qtd2ViLXBsYXRmb3JtLnRlc3SCH3d3dzIud3d3
+Mi5ub3Qtd2ViLXBsYXRmb3JtLnRlc3SCH3d3dzIud3d3MS5ub3Qtd2ViLXBsYXRm
+b3JtLnRlc3SCH3d3dzEud3d3MS5ub3Qtd2ViLXBsYXRmb3JtLnRlc3SCH3d3dzEu
+d3d3Mi5ub3Qtd2ViLXBsYXRmb3JtLnRlc3SCInhuLS1sdmUtNmxhZC53d3cud2Vi
+LXBsYXRmb3JtLnRlc3SCInhuLS1sdmUtNmxhZC5ub3Qtd2ViLXBsYXRmb3JtLnRl
+c3SCInd3dy54bi0tbHZlLTZsYWQud2ViLXBsYXRmb3JtLnRlc3SCI3d3dzIueG4t
+LWx2ZS02bGFkLndlYi1wbGF0Zm9ybS50ZXN0giN4bi0tbHZlLTZsYWQud3d3Mi53
+ZWItcGxhdGZvcm0udGVzdIIjeG4tLWx2ZS02bGFkLnd3dzEud2ViLXBsYXRmb3Jt
+LnRlc3SCI3d3dzEueG4tLWx2ZS02bGFkLndlYi1wbGF0Zm9ybS50ZXN0giZ4bi0t
+bHZlLTZsYWQud3d3Lm5vdC13ZWItcGxhdGZvcm0udGVzdIImd3d3LnhuLS1sdmUt
+NmxhZC5ub3Qtd2ViLXBsYXRmb3JtLnRlc3SCJ3huLS1sdmUtNmxhZC53d3cxLm5v
+dC13ZWItcGxhdGZvcm0udGVzdIInd3d3Mi54bi0tbHZlLTZsYWQubm90LXdlYi1w
+bGF0Zm9ybS50ZXN0gid3d3cxLnhuLS1sdmUtNmxhZC5ub3Qtd2ViLXBsYXRmb3Jt
+LnRlc3SCJ3huLS1sdmUtNmxhZC53d3cyLm5vdC13ZWItcGxhdGZvcm0udGVzdIIp
+eG4tLW44ajZkczUzbHd3a3JxaHYyOGEud2ViLXBsYXRmb3JtLnRlc3SCK3huLS1s
+dmUtNmxhZC54bi0tbHZlLTZsYWQud2ViLXBsYXRmb3JtLnRlc3SCLXd3dy54bi0t
+bjhqNmRzNTNsd3drcnFodjI4YS53ZWItcGxhdGZvcm0udGVzdIIteG4tLW44ajZk
+czUzbHd3a3JxaHYyOGEubm90LXdlYi1wbGF0Zm9ybS50ZXN0gi14bi0tbjhqNmRz
+NTNsd3drcnFodjI4YS53d3cud2ViLXBsYXRmb3JtLnRlc3SCLnd3dzEueG4tLW44
+ajZkczUzbHd3a3JxaHYyOGEud2ViLXBsYXRmb3JtLnRlc3SCLnhuLS1uOGo2ZHM1
+M2x3d2tycWh2MjhhLnd3dzIud2ViLXBsYXRmb3JtLnRlc3SCLnhuLS1uOGo2ZHM1
+M2x3d2tycWh2MjhhLnd3dzEud2ViLXBsYXRmb3JtLnRlc3SCLnd3dzIueG4tLW44
+ajZkczUzbHd3a3JxaHYyOGEud2ViLXBsYXRmb3JtLnRlc3SCL3huLS1sdmUtNmxh
+ZC54bi0tbHZlLTZsYWQubm90LXdlYi1wbGF0Zm9ybS50ZXN0gjF3d3cueG4tLW44
+ajZkczUzbHd3a3JxaHYyOGEubm90LXdlYi1wbGF0Zm9ybS50ZXN0gjF4bi0tbjhq
+NmRzNTNsd3drcnFodjI4YS53d3cubm90LXdlYi1wbGF0Zm9ybS50ZXN0gjJ4bi0t
+bjhqNmRzNTNsd3drcnFodjI4YS53d3cyLm5vdC13ZWItcGxhdGZvcm0udGVzdIIy
+d3d3MS54bi0tbjhqNmRzNTNsd3drcnFodjI4YS5ub3Qtd2ViLXBsYXRmb3JtLnRl
+c3SCMnd3dzIueG4tLW44ajZkczUzbHd3a3JxaHYyOGEubm90LXdlYi1wbGF0Zm9y
+bS50ZXN0gjJ4bi0tbjhqNmRzNTNsd3drcnFodjI4YS53d3cxLm5vdC13ZWItcGxh
+dGZvcm0udGVzdII2eG4tLW44ajZkczUzbHd3a3JxaHYyOGEueG4tLWx2ZS02bGFk
+LndlYi1wbGF0Zm9ybS50ZXN0gjZ4bi0tbHZlLTZsYWQueG4tLW44ajZkczUzbHd3
+a3JxaHYyOGEud2ViLXBsYXRmb3JtLnRlc3SCOnhuLS1uOGo2ZHM1M2x3d2tycWh2
+MjhhLnhuLS1sdmUtNmxhZC5ub3Qtd2ViLXBsYXRmb3JtLnRlc3SCOnhuLS1sdmUt
+NmxhZC54bi0tbjhqNmRzNTNsd3drcnFodjI4YS5ub3Qtd2ViLXBsYXRmb3JtLnRl
+c3SCQXhuLS1uOGo2ZHM1M2x3d2tycWh2MjhhLnhuLS1uOGo2ZHM1M2x3d2tycWh2
+MjhhLndlYi1wbGF0Zm9ybS50ZXN0gkV4bi0tbjhqNmRzNTNsd3drcnFodjI4YS54
+bi0tbjhqNmRzNTNsd3drcnFodjI4YS5ub3Qtd2ViLXBsYXRmb3JtLnRlc3QwDQYJ
+KoZIhvcNAQELBQADggEBAAuLVWq0ZagqO8BiDoXV3JmfP9C3/8u2aOWu06JOyNSq
+NJXknkvkdOTE7BExZ+Wm2oVrD9dkt+fr/gm2iOF0HQwVoy4BA/xnnHy65vtSHVjN
+aKNVjJtXu4kyEEFxmaMa3CUt0Lkar+Z2QNf1I6nYWUWKq1NTh8wUs4feHQGZek9y
+R2mcmHOUeZAlcALsdbJ3ESiXzUM91Wg4c3H9qQui7S0cbuEF1DCmZAQQyujm4Hqp
+9CaUa6QoqgzrPuDv0r4LcOTdTPyihwwkg3Li5G8lqO7FEonZ3Y+CFMPWBq4du3s8
+/JsnVlC0KERggBeHHLHObXe00Ssy6NC6Bm9NBP9g/uM=
+-----END CERTIFICATE-----
diff --git a/tools/quic/certs/config.json b/tools/quic/certs/config.json
new file mode 100644
index 0000000..72a6d05
--- /dev/null
+++ b/tools/quic/certs/config.json
@@ -0,0 +1,17 @@
+{
+    "ports": {
+        "http": [],
+        "https": ["auto"],
+        "ws": [],
+        "wss": []
+    },
+    "check_subdomains": false,
+    "ssl": {
+        "type": "openssl",
+        "openssl": {
+            "duration": 3650,
+            "force_regenerate": false,
+            "base_path": "tools/certs"
+        }
+    }
+}
diff --git a/tools/quic/quic_transport_server.py b/tools/quic/quic_transport_server.py
new file mode 100644
index 0000000..10d8174
--- /dev/null
+++ b/tools/quic/quic_transport_server.py
@@ -0,0 +1,244 @@
+#!/usr/bin/env python3
+import argparse
+import asyncio
+import io
+import logging
+import os
+import re
+import struct
+import urllib.parse
+from typing import Dict, Optional
+
+from aioquic.asyncio import QuicConnectionProtocol, serve
+from aioquic.quic.configuration import QuicConfiguration
+from aioquic.quic.connection import END_STATES
+from aioquic.quic.events import StreamDataReceived, QuicEvent
+from aioquic.tls import SessionTicket
+
+SERVER_NAME = 'aioquic-transport'
+
+handlers_path = None
+
+
+class EventHandler:
+    def __init__(self, connection: QuicConnectionProtocol, global_dict: Dict):
+        self.connection = connection
+        self.global_dict = global_dict
+
+    def handle_client_indication(
+            self,
+            origin: str,
+            query: Dict[str, str]) -> None:
+        name = 'handle_client_indication'
+        if name in self.global_dict:
+            self.global_dict[name](self.connection, origin, query)
+
+    def handle_event(self, event: QuicEvent) -> None:
+        name = 'handle_event'
+        if name in self.global_dict:
+            self.global_dict[name](self.connection, event)
+
+
+class QuicTransportProtocol(QuicConnectionProtocol):
+    def __init__(self, *args, **kwargs) -> None:
+        super().__init__(*args, **kwargs)
+        self.streams = dict()
+        self.pending_events = []
+        self.client_indication_finished = False
+        self.client_indication_data = b''
+        self.handler = None
+
+    def quic_event_received(self, event: QuicEvent) -> None:
+        prefix = '!!'
+        logging.log(logging.INFO, 'QUIC event: %s' % type(event))
+        try:
+            if (not self.client_indication_finished and
+                    isinstance(event, StreamDataReceived) and
+                    event.stream_id == 2):
+                # client indication process
+                self.client_indication_data += event.data
+                if event.end_stream:
+                    prefix = 'Client inditation error: '
+                    self.process_client_indication()
+                    if self.is_closing_or_closed():
+                        return
+                    prefix = 'Event handling Error: '
+                    for e in self.pending_events:
+                        self.handler.handle_event(e)
+                    self.pending_events.clear()
+            elif not self.client_indication_finished:
+                self.pending_events.append(event)
+            elif self.handler is not None:
+                prefix = 'Event handling Error: '
+                self.handler.handle_event(event)
+        except Exception as e:
+            self.handler = None
+            logging.log(logging.WARN, prefix + str(e))
+            self.close()
+
+    def parse_client_indication(self, bs):
+        while True:
+            key_b = bs.read(2)
+            if len(key_b) == 0:
+                return
+            length_b = bs.read(2)
+            if len(key_b) != 2:
+                raise Exception('failed to get "Key" field')
+            if len(length_b) != 2:
+                raise Exception('failed to get "Length" field')
+            key = struct.unpack('!H', key_b)[0]
+            length = struct.unpack('!H', length_b)[0]
+            value = bs.read(length)
+            if len(value) != length:
+                raise Exception('truncated "Value" field')
+            yield (key, value)
+
+    def process_client_indication(self) -> None:
+        origin = None
+        origin_string = None
+        path = None
+        path_string = None
+        KEY_ORIGIN = 0
+        KEY_PATH = 1
+        for (key, value) in self.parse_client_indication(
+                io.BytesIO(self.client_indication_data)):
+            if key == KEY_ORIGIN:
+                origin_string = value.decode()
+                origin = urllib.parse.urlparse(origin_string)
+            elif key == KEY_PATH:
+                path_string = value.decode()
+                path = urllib.parse.urlparse(path_string)
+            else:
+                # We must ignore unrecognized fields.
+                pass
+        logging.log(logging.INFO,
+                    'origin = %s, path = %s' % (origin_string, path_string))
+        if origin is None:
+            raise Exception('No origin is given')
+        if path is None:
+            raise Exception('No path is given')
+        if origin.scheme != 'https' and origin.scheme != 'http':
+            raise Exception('Invalid origin: %s' % origin_string)
+        if origin.netloc == '':
+            raise Exception('Invalid origin: %s' % origin_string)
+
+        # To make the situation simple we accept only simple path strings.
+        m = re.compile('^/([a-zA-Z0-9\._\-]+)$').match(path.path)
+        if m is None:
+            raise Exception('Invalid path: %s' % path_string)
+
+        handler_name = m.group(1)
+        query = dict(urllib.parse.parse_qsl(path.query))
+        self.handler = self.create_event_handler(handler_name)
+        self.handler.handle_client_indication(origin_string, query)
+        if self.is_closing_or_closed():
+            return
+        self.client_indication_finished = True
+        logging.log(logging.INFO, 'Client indication finished')
+
+    def create_event_handler(self, handler_name: str) -> None:
+        global_dict = {}
+        with open(handlers_path + '/' + handler_name) as f:
+            exec(f.read(), global_dict)
+        return EventHandler(self, global_dict)
+
+    def is_closing_or_closed(self) -> bool:
+        if self._quic._close_pending:
+            return True
+        if self._quic._state in END_STATES:
+            return True
+        return False
+
+
+class SessionTicketStore:
+    '''
+    Simple in-memory store for session tickets.
+    '''
+
+    def __init__(self) -> None:
+        self.tickets: Dict[bytes, SessionTicket] = {}
+
+    def add(self, ticket: SessionTicket) -> None:
+        self.tickets[ticket.ticket] = ticket
+
+    def pop(self, label: bytes) -> Optional[SessionTicket]:
+        return self.tickets.pop(label, None)
+
+
+if __name__ == '__main__':
+    parser = argparse.ArgumentParser(description='QUIC server')
+    parser.add_argument(
+        '-c',
+        '--certificate',
+        type=str,
+        required=True,
+        help='load the TLS certificate from the specified file',
+    )
+    parser.add_argument(
+        '--host',
+        type=str,
+        default='::',
+        help='listen on the specified address (defaults to ::)',
+    )
+    parser.add_argument(
+        '--port',
+        type=int,
+        default=4433,
+        help='listen on the specified port (defaults to 4433)',
+    )
+    parser.add_argument(
+        '-k',
+        '--private-key',
+        type=str,
+        required=True,
+        help='load the TLS private key from the specified file',
+    )
+    parser.add_argument(
+        '--handlers-path',
+        type=str,
+        required=True,
+        help='the directory path of QuicTransport event handlers',
+    )
+    parser.add_argument(
+        '-v',
+        '--verbose',
+        action='store_true',
+        help='increase logging verbosity'
+    )
+    args = parser.parse_args()
+
+    logging.basicConfig(
+        format='%(asctime)s %(levelname)s %(name)s %(message)s',
+        level=logging.DEBUG if args.verbose else logging.INFO,
+    )
+
+    configuration = QuicConfiguration(
+        alpn_protocols=['wq-vvv-01'] + ['siduck'],
+        is_client=False,
+        max_datagram_frame_size=65536,
+    )
+
+    handlers_path = os.path.abspath(os.path.expanduser(args.handlers_path))
+    logging.log(logging.INFO, 'port = %s' % args.port)
+    logging.log(logging.INFO, 'handlers path = %s' % handlers_path)
+
+    # load SSL certificate and key
+    configuration.load_cert_chain(args.certificate, args.private_key)
+
+    ticket_store = SessionTicketStore()
+
+    loop = asyncio.get_event_loop()
+    loop.run_until_complete(
+        serve(
+            args.host,
+            args.port,
+            configuration=configuration,
+            create_protocol=QuicTransportProtocol,
+            session_ticket_fetcher=ticket_store.pop,
+            session_ticket_handler=ticket_store.add,
+        )
+    )
+    try:
+        loop.run_forever()
+    except KeyboardInterrupt:
+        pass
diff --git a/tools/wptserve/wptserve/sslutils/openssl.py b/tools/wptserve/wptserve/sslutils/openssl.py
index aea1c73..64f6d5f 100644
--- a/tools/wptserve/wptserve/sslutils/openssl.py
+++ b/tools/wptserve/wptserve/sslutils/openssl.py
@@ -402,6 +402,8 @@
 
     def _generate_host_cert(self, hosts):
         host = hosts[0]
+        if not self.force_regenerate:
+            self._load_ca_cert()
         if self._ca_key_path is None:
             self._generate_ca(hosts)
         ca_key_path = self._ca_key_path
diff --git a/webtransport/quic/client-indication.any.js b/webtransport/quic/client-indication.any.js
new file mode 100644
index 0000000..15baa5f
--- /dev/null
+++ b/webtransport/quic/client-indication.any.js
@@ -0,0 +1,32 @@
+// META: quic=true
+// META: script=/common/get-host-info.sub.js
+
+const PORT = 8983;
+const {ORIGINAL_HOST: HOST, ORIGIN} = get_host_info();
+const BASE = `quic-transport://${HOST}:${PORT}`;
+
+promise_test(async (test) => {
+  function onClosed() {
+    assert_unreached('The closed promise should be ' +
+                     'fulfilled or rejected after getting a PASS signal.');
+  }
+  const qt = new QuicTransport(
+    `${BASE}/client-indication.quic.py?origin=${ORIGIN}`);
+  qt.closed.then(test.step_func(onClosed), test.step_func(onClosed));
+
+  const streams = qt.receiveStreams();
+  const {done, value} = await streams.getReader().read();
+  assert_false(done, 'getting an incoming stream');
+
+  const readable = value.readable.pipeThrough(new TextDecoderStream());
+  const reader = readable.getReader();
+  let result = '';
+  while (true) {
+    const {done, value} = await reader.read();
+    if (done) {
+        break;
+    }
+    result += value;
+  }
+  assert_equals(result, 'PASS');
+}, 'Client indication');
diff --git a/webtransport/quic/handlers/README.md b/webtransport/quic/handlers/README.md
new file mode 100644
index 0000000..22073e7
--- /dev/null
+++ b/webtransport/quic/handlers/README.md
@@ -0,0 +1,2 @@
+This directory contains custom handlers for testing QuicTransport. Please see
+https://github.com/web-platform-tests/wpt/tools/quic.
\ No newline at end of file
diff --git a/webtransport/quic/handlers/client-indication.quic.py b/webtransport/quic/handlers/client-indication.quic.py
new file mode 100644
index 0000000..da0701c
--- /dev/null
+++ b/webtransport/quic/handlers/client-indication.quic.py
@@ -0,0 +1,28 @@
+import asyncio
+import logging
+
+from aioquic.asyncio import QuicConnectionProtocol
+from aioquic.quic.events import QuicEvent
+from typing import Dict
+
+
+async def notify_pass(connection: QuicConnectionProtocol):
+    _, writer = await connection.create_stream(is_unidirectional=True)
+    writer.write(b'PASS')
+    writer.write_eof()
+
+
+def handle_client_indication(connection: QuicConnectionProtocol,
+                             origin: str, query: Dict[str, str]):
+    logging.log(logging.INFO, 'origin = %s, query = %s' % (origin, query))
+    if 'origin' not in query or query['origin'] != origin:
+        logging.log(logging.WARN, 'Client indication failure: invalid origin')
+        connection.close()
+        return
+
+    loop = asyncio.get_event_loop()
+    loop.create_task(notify_pass(connection))
+
+
+def handle_event(connection: QuicConnectionProtocol, event: QuicEvent) -> None:
+    pass