diff --git a/Cargo.lock b/Cargo.lock index 71750810f3c01..c4f5c1b4fe641 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -171,6 +171,21 @@ dependencies = [ "libloading 0.5.2", ] +[[package]] +name = "async-tungstenite" +version = "0.7.0" +source = "git+https://github.com/servo/async-tungstenite.git?branch=openssl#e3abbac0c354907acc47819478fdb8582e96e32e" +dependencies = [ + "futures-io", + "futures-util", + "log", + "openssl", + "pin-project", + "tokio 0.2.21", + "tokio-openssl 0.4.0", + "tungstenite", +] + [[package]] name = "atom" version = "0.3.5" @@ -474,6 +489,15 @@ dependencies = [ "iovec", ] +[[package]] +name = "bytes" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "118cf036fbb97d0816e3c34b2d7a1e8cfc60f68fcf63d550ddbe9bd5f59c213b" +dependencies = [ + "loom", +] + [[package]] name = "bzip2" version = "0.3.3" @@ -815,7 +839,7 @@ dependencies = [ "gaol", "gfx", "gfx_traits", - "http", + "http 0.1.21", "ipc-channel", "keyboard-types", "layout_traits", @@ -1204,7 +1228,7 @@ dependencies = [ "devtools_traits", "embedder_traits", "headers", - "http", + "http 0.1.21", "hyper", "ipc-channel", "log", @@ -1222,7 +1246,7 @@ name = "devtools_traits" version = "0.0.1" dependencies = [ "bitflags", - "http", + "http 0.1.21", "ipc-channel", "malloc_size_of", "malloc_size_of_derive", @@ -1592,6 +1616,21 @@ version = "0.1.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "45dc39533a6cae6da2b56da48edae506bb767ec07370f86f70fc062e9d435869" +[[package]] +name = "futures" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e05b85ec287aac0dc34db7d4a569323df697f9c55b99b15d6b4ef8cde49f613" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + [[package]] name = "futures-channel" version = "0.3.5" @@ -1599,6 +1638,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f366ad74c28cca6ba456d95e6422883cfb4b252a83bed929c83abfdbbf2967d5" dependencies = [ "futures-core", + "futures-sink", ] [[package]] @@ -1613,7 +1653,7 @@ version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab90cde24b3319636588d0c35fe03b1333857621051837ed769faefb4c2162e4" dependencies = [ - "futures", + "futures 0.1.28", "num_cpus", ] @@ -1628,6 +1668,12 @@ dependencies = [ "futures-util", ] +[[package]] +name = "futures-io" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de27142b013a8e869c14957e6d2edeef89e97c289e69d042ee3a49acd8b51789" + [[package]] name = "futures-macro" version = "0.3.5" @@ -1661,9 +1707,13 @@ version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8764574ff08b701a084482c3c7031349104b07ac897393010494beaa18ce32c6" dependencies = [ + "futures-channel", "futures-core", + "futures-io", "futures-macro", + "futures-sink", "futures-task", + "memchr", "pin-project", "pin-utils", "proc-macro-hack", @@ -1690,6 +1740,19 @@ dependencies = [ "log", ] +[[package]] +name = "generator" +version = "0.6.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "add72f17bb81521258fcc8a7a3245b1e184e916bfbe34f0ea89558f440df5c68" +dependencies = [ + "cc", + "libc", + "log", + "rustc_version", + "winapi", +] + [[package]] name = "generic-array" version = "0.12.3" @@ -2327,10 +2390,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a5b34c246847f938a410a03c5458c7fee2274436675e76d8b903c08efc29c462" dependencies = [ "byteorder", - "bytes", + "bytes 0.4.12", "fnv", - "futures", - "http", + "futures 0.1.28", + "http 0.1.21", "indexmap", "log", "slab", @@ -2374,9 +2437,9 @@ checksum = "882ca7d8722f33ce2c2db44f95425d6267ed59ca96ce02acbe58320054ceb642" dependencies = [ "base64 0.10.1", "bitflags", - "bytes", + "bytes 0.4.12", "headers-core", - "http", + "http 0.1.21", "mime", "sha-1", "time", @@ -2388,8 +2451,8 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "967131279aaa9f7c20c7205b45a391638a83ab118e6509b2d0ccbe08de044237" dependencies = [ - "bytes", - "http", + "bytes 0.4.12", + "http 0.1.21", ] [[package]] @@ -2451,7 +2514,18 @@ version = "0.1.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d6ccf5ede3a895d8856620237b2f02972c1bbc78d2965ad7fe8838d4a0ed41f0" dependencies = [ - "bytes", + "bytes 0.4.12", + "fnv", + "itoa", +] + +[[package]] +name = "http" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28d569972648b2c512421b5f2a405ad6ac9666547189d0c5477a3f200f3e02f9" +dependencies = [ + "bytes 0.5.5", "fnv", "itoa", ] @@ -2462,9 +2536,9 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6741c859c1b2463a423a1dbce98d418e6c3c3fc720fb0d45528657320920292d" dependencies = [ - "bytes", - "futures", - "http", + "bytes 0.4.12", + "futures 0.1.28", + "http 0.1.21", "tokio-buf", ] @@ -2489,11 +2563,11 @@ version = "0.12.35" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9dbe6ed1438e1f8ad955a4701e9a944938e9519f6888d12d8558b645e247d5f6" dependencies = [ - "bytes", - "futures", + "bytes 0.4.12", + "futures 0.1.28", "futures-cpupool", "h2", - "http", + "http 0.1.21", "http-body", "httparse", "iovec", @@ -2502,7 +2576,7 @@ dependencies = [ "net2", "rustc_version", "time", - "tokio", + "tokio 0.1.22", "tokio-buf", "tokio-executor", "tokio-io", @@ -2520,15 +2594,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f52657b5cdb2a8067efd29a02e011b7cf656b473ec8a5c34e86645e85d763006" dependencies = [ "antidote", - "bytes", - "futures", + "bytes 0.4.12", + "futures 0.1.28", "hyper", "lazy_static", "linked_hash_set", "openssl", "openssl-sys", "tokio-io", - "tokio-openssl", + "tokio-openssl 0.3.0", ] [[package]] @@ -2539,7 +2613,7 @@ checksum = "3bf0fc731a638339172253834b4ba8d60a9ecbeb4c031fcfcacd25b3cdf6e6c1" dependencies = [ "cookie", "headers", - "http", + "http 0.1.21", "hyper", "mime", "serde", @@ -2611,6 +2685,15 @@ dependencies = [ "adler32", ] +[[package]] +name = "input_buffer" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19a8a95243d5a0398cae618ec29477c6e3cb631152be5c19481f80bc71559754" +dependencies = [ + "bytes 0.5.5", +] + [[package]] name = "io-surface" version = "0.12.1" @@ -3134,6 +3217,17 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "loom" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ecc775857611e1df29abba5c41355cdf540e7e9d4acfdf0f355eefee82330b7" +dependencies = [ + "cfg-if", + "generator", + "scoped-tls 0.1.2", +] + [[package]] name = "lyon_geom" version = "0.14.1" @@ -3387,9 +3481,10 @@ dependencies = [ [[package]] name = "mio" -version = "0.6.18" -source = "git+https://github.com/servo/mio.git?branch=servo#846242c05bacacda9a67033551eb33027f2648fc" +version = "0.6.22" +source = "git+https://github.com/servo/mio.git?branch=servo-mio-0.6.22#f640fd662db03026ca2a1b21ac1e161ec6f8150b" dependencies = [ + "cfg-if", "fuchsia-zircon", "fuchsia-zircon-sys", "iovec", @@ -3527,13 +3622,32 @@ dependencies = [ "spirv_headers", ] +[[package]] +name = "native-tls" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b0d88c06fe90d5ee94048ba40409ef1d9315d86f6f38c2efdaad4fb50c58b2d" +dependencies = [ + "lazy_static", + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + [[package]] name = "net" version = "0.0.1" dependencies = [ + "async-tungstenite", "base64 0.10.1", "brotli", - "bytes", + "bytes 0.4.12", "content-security-policy", "cookie", "crossbeam-channel", @@ -3541,9 +3655,10 @@ dependencies = [ "devtools_traits", "embedder_traits", "flate2", - "futures", + "futures 0.1.28", + "futures 0.3.5", "headers", - "http", + "http 0.1.21", "hyper", "hyper-openssl", "hyper_serde", @@ -3572,12 +3687,13 @@ dependencies = [ "servo_url", "std_test_override", "time", - "tokio", - "tokio-openssl", + "tokio 0.1.22", + "tokio 0.2.21", + "tokio-openssl 0.3.0", + "tungstenite", "url", "uuid", "webrender_api", - "ws", ] [[package]] @@ -3599,7 +3715,7 @@ dependencies = [ "cookie", "embedder_traits", "headers", - "http", + "http 0.1.21", "hyper", "hyper_serde", "image", @@ -3802,6 +3918,12 @@ dependencies = [ "openssl-sys", ] +[[package]] +name = "openssl-probe" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77af24da69f9d9341038eba93a073b1fdaaa1b788221b00a69bce9e762cb32de" + [[package]] name = "openssl-sys" version = "0.9.58" @@ -4067,6 +4189,12 @@ dependencies = [ "syn", ] +[[package]] +name = "pin-project-lite" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282adbf10f2698a7a77f8e983a74b2d18176c19a7fd32a45446139ae7b02b715" + [[package]] name = "pin-utils" version = "0.1.0" @@ -4506,6 +4634,22 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "schannel" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f05ba609c234e60bee0d547fe94a4c7e9da733d1c962cf6e59efa4cd9c8bc75" +dependencies = [ + "lazy_static", + "winapi", +] + +[[package]] +name = "scoped-tls" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "332ffa32bf586782a3efaeb58f127980944bbc8c4d6913a86107ac2a5ab24b28" + [[package]] name = "scoped-tls" version = "1.0.0" @@ -4553,7 +4697,7 @@ dependencies = [ "fxhash", "headers", "html5ever", - "http", + "http 0.1.21", "hyper", "hyper_serde", "image", @@ -4688,7 +4832,7 @@ dependencies = [ "embedder_traits", "euclid", "gfx_traits", - "http", + "http 0.1.21", "hyper", "hyper_serde", "ipc-channel", @@ -4715,6 +4859,29 @@ dependencies = [ "webxr-api", ] +[[package]] +name = "security-framework" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64808902d7d99f78eaddd2b4e2509713babc3dc3c85ad6f4c447680f3c01e535" +dependencies = [ + "bitflags", + "core-foundation 0.7.0", + "core-foundation-sys 0.7.0", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17bf11d99252f512695eb468de5516e5cf75455521e69dfe343f3b74e4748405" +dependencies = [ + "core-foundation-sys 0.7.0", + "libc", +] + [[package]] name = "selectors" version = "0.22.0" @@ -5406,7 +5573,7 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d24114bfcceb867ca7f71a0d3fe45d45619ec47a6fbfa98cb14e14250bfa5d6d" dependencies = [ - "bytes", + "bytes 0.4.12", ] [[package]] @@ -5794,8 +5961,8 @@ version = "0.1.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a09c0b5bb588872ab2f09afa13ee6e9dac11e10a0ec9e8e3ba39a5a5d530af6" dependencies = [ - "bytes", - "futures", + "bytes 0.4.12", + "futures 0.1.28", "mio", "num_cpus", "tokio-codec", @@ -5812,15 +5979,31 @@ dependencies = [ "tokio-uds", ] +[[package]] +name = "tokio" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d099fa27b9702bed751524694adbe393e18b36b204da91eb1cbbbbb4a5ee2d58" +dependencies = [ + "bytes 0.5.5", + "fnv", + "iovec", + "lazy_static", + "mio", + "num_cpus", + "pin-project-lite", + "tokio-macros", +] + [[package]] name = "tokio-buf" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8fb220f46c53859a4b7ec083e41dec9778ff0b1851c0942b211edb89e0ccdc46" dependencies = [ - "bytes", + "bytes 0.4.12", "either", - "futures", + "futures 0.1.28", ] [[package]] @@ -5829,8 +6012,8 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "25b2998660ba0e70d18684de5d06b70b70a3a747469af9dea7618cc59e75976b" dependencies = [ - "bytes", - "futures", + "bytes 0.4.12", + "futures 0.1.28", "tokio-io", ] @@ -5840,7 +6023,7 @@ version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b1de0e32a83f131e002238d7ccde18211c0a5397f60cbfffcb112868c2e0e20e" dependencies = [ - "futures", + "futures 0.1.28", "tokio-executor", ] @@ -5851,7 +6034,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fb2d1b8f4548dbf5e1f7818512e9c406860678f29c300cdf0ebac72d1a3a1671" dependencies = [ "crossbeam-utils", - "futures", + "futures 0.1.28", ] [[package]] @@ -5860,7 +6043,7 @@ version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "297a1206e0ca6302a0eed35b700d292b275256f596e2f3fea7729d5e629b6ff4" dependencies = [ - "futures", + "futures 0.1.28", "tokio-io", "tokio-threadpool", ] @@ -5871,22 +6054,43 @@ version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57fc868aae093479e3131e3d165c93b1c7474109d13c90ec0dda2a1bbfff0674" dependencies = [ - "bytes", - "futures", + "bytes 0.4.12", + "futures 0.1.28", "log", ] +[[package]] +name = "tokio-macros" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0c3acc6aa564495a0f2e1d59fab677cd7f81a19994cfc7f3ad0e64301560389" +dependencies = [ + "proc-macro2 1.0.17", + "quote 1.0.2", + "syn", +] + [[package]] name = "tokio-openssl" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "771d6246b170ae108d67d9963c23f31a579016c016d73bd4bd7d6ef0252afda7" dependencies = [ - "futures", + "futures 0.1.28", "openssl", "tokio-io", ] +[[package]] +name = "tokio-openssl" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c4b08c5f4208e699ede3df2520aca2e82401b2de33f45e96696a074480be594" +dependencies = [ + "openssl", + "tokio 0.2.21", +] + [[package]] name = "tokio-reactor" version = "0.1.12" @@ -5894,7 +6098,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09bc590ec4ba8ba87652da2068d150dcada2cfa2e07faae270a5e0409aa51351" dependencies = [ "crossbeam-utils", - "futures", + "futures 0.1.28", "lazy_static", "log", "mio", @@ -5913,7 +6117,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "edfe50152bc8164fcc456dab7891fa9bf8beaf01c5ee7e1dd43a397c3cf87dee" dependencies = [ "fnv", - "futures", + "futures 0.1.28", ] [[package]] @@ -5922,8 +6126,8 @@ version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "98df18ed66e3b72e742f185882a9e201892407957e45fbff8da17ae7a7c51f72" dependencies = [ - "bytes", - "futures", + "bytes 0.4.12", + "futures 0.1.28", "iovec", "mio", "tokio-io", @@ -5939,7 +6143,7 @@ dependencies = [ "crossbeam-deque", "crossbeam-queue", "crossbeam-utils", - "futures", + "futures 0.1.28", "lazy_static", "log", "num_cpus", @@ -5954,7 +6158,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "93044f2d313c95ff1cb7809ce9a7a05735b012288a888b62d4434fd58c94f296" dependencies = [ "crossbeam-utils", - "futures", + "futures 0.1.28", "slab", "tokio-executor", ] @@ -5965,8 +6169,8 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2a0b10e610b39c38b031a2fcab08e4b82f16ece36504988dcbd81dbba650d82" dependencies = [ - "bytes", - "futures", + "bytes 0.4.12", + "futures 0.1.28", "log", "mio", "tokio-codec", @@ -5980,8 +6184,8 @@ version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab57a4ac4111c8c9dbcf70779f6fc8bc35ae4b2454809febac840ad19bd7e4e0" dependencies = [ - "bytes", - "futures", + "bytes 0.4.12", + "futures 0.1.28", "iovec", "libc", "log", @@ -6038,6 +6242,26 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e604eb7b43c06650e854be16a2a03155743d3752dd1c943f6829e26b7a36e382" +[[package]] +name = "tungstenite" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5c7d464221cb0b538a1cd12f6d9127ed1e6bb7f3ffca98fb3cd4c6e3af8175c" +dependencies = [ + "base64 0.12.0", + "byteorder", + "bytes 0.5.5", + "http 0.2.1", + "httparse", + "input_buffer", + "log", + "native-tls", + "rand", + "sha-1", + "url", + "utf-8", +] + [[package]] name = "typed-arena" version = "2.0.1" @@ -6220,7 +6444,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6395efa4784b027708f7451087e647ec73cc74f5d9bc2e418404248d679a230" dependencies = [ - "futures", + "futures 0.1.28", "log", "try-lock", ] @@ -6231,19 +6455,19 @@ version = "0.1.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "99b53196ba54e91e31ba1e90309a31053218cc1d4697f6e48f7c7e3d255e64fc" dependencies = [ - "bytes", - "futures", + "bytes 0.4.12", + "futures 0.1.28", "headers", - "http", + "http 0.1.21", "hyper", "log", "mime", "mime_guess", - "scoped-tls", + "scoped-tls 1.0.0", "serde", "serde_json", "serde_urlencoded", - "tokio", + "tokio 0.1.22", "tokio-io", "tokio-threadpool", "urlencoding", @@ -6386,14 +6610,14 @@ checksum = "ad517a7e5bb5228bee491b97f5c471b31606a42e89fda90a966d8245a4308e31" dependencies = [ "base64 0.10.1", "cookie", - "http", + "http 0.1.21", "log", "regex", "serde", "serde_derive", "serde_json", "time", - "tokio", + "tokio 0.1.22", "unicode-segmentation", "url", "warp", @@ -6704,12 +6928,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c51a2c47b5798ccc774ffb93ff536aec7c4275d722fd9c740c83cdd1af1f2d94" dependencies = [ "byteorder", - "bytes", + "bytes 0.4.12", "httparse", "log", "mio", "mio-extras", - "openssl", "rand", "sha-1", "slab", diff --git a/Cargo.toml b/Cargo.toml index 5ad9325baf836..72ef22c5053dd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,4 +27,4 @@ opt-level = 3 # = { path = "/path/to/local/checkout" } # This is here to dedupe winapi since mio 0.6 is still using winapi 0.2. -mio = { git = "https://github.com/servo/mio.git", branch = "servo" } +mio = { git = "https://github.com/servo/mio.git", branch = "servo-mio-0.6.22" } diff --git a/components/net/Cargo.toml b/components/net/Cargo.toml index 64f40d556893f..d75c78c0f0a41 100644 --- a/components/net/Cargo.toml +++ b/components/net/Cargo.toml @@ -15,6 +15,7 @@ test = false doctest = false [dependencies] +async-tungstenite = { git = "https://github.com/servo/async-tungstenite.git", branch = "openssl", features = ["tokio-openssl"] } base64 = "0.10.1" brotli = "3" bytes = "0.4" @@ -26,6 +27,7 @@ devtools_traits = { path = "../devtools_traits" } embedder_traits = { path = "../embedder_traits" } flate2 = "1" futures = "0.1" +futures03 = { version = "0.3", package = "futures" } headers = "0.2" http = "0.1" hyper = "0.12" @@ -56,10 +58,11 @@ servo_config = { path = "../config" } servo_url = { path = "../url" } time = "0.1.17" tokio = "0.1" +tokio2 = { version = "0.2", package = "tokio", features = ["sync", "macros", "rt-threaded"] } +tungstenite = "0.11" url = "2.0" uuid = { version = "0.8", features = ["v4"] } webrender_api = { git = "https://github.com/servo/webrender" } -ws = { version = "0.9", features = ["ssl"] } [dev-dependencies] futures = "0.1" diff --git a/components/net/resource_thread.rs b/components/net/resource_thread.rs index 94184c3a0465c..07c77c705f998 100644 --- a/components/net/resource_thread.rs +++ b/components/net/resource_thread.rs @@ -16,7 +16,7 @@ use crate::hsts::HstsList; use crate::http_cache::HttpCache; use crate::http_loader::{http_redirect_fetch, HttpState, HANDLE}; use crate::storage_thread::StorageThreadFactory; -use crate::websocket_loader; +use crate::websocket_loader::{self, HANDLE as WS_HANDLE}; use crossbeam_channel::Sender; use devtools_traits::DevtoolsControlMsg; use embedder_traits::resources::{self, Resource}; @@ -616,6 +616,9 @@ impl CoreResourceManager { // Shut-down the async runtime used by fetch workers. drop(HANDLE.lock().unwrap().take()); + // Shut-down the async runtime used by websocket workers. + drop(WS_HANDLE.lock().unwrap().take()); + debug!("Exited CoreResourceManager"); } @@ -727,8 +730,6 @@ impl CoreResourceManager { action_receiver, http_state.clone(), self.certificate_path.clone(), - http_state.extra_certs.clone(), - http_state.connection_certs.clone(), ); } } diff --git a/components/net/websocket_loader.rs b/components/net/websocket_loader.rs index bece51173bbb1..5f1eb15231a75 100644 --- a/components/net/websocket_loader.rs +++ b/components/net/websocket_loader.rs @@ -2,297 +2,460 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ -use crate::connector::{create_tls_config, ConnectionCerts, ExtraCerts, ALPN_H1}; +//! The websocket handler has three main responsibilities: +//! 1) initiate the initial HTTP connection and process the response +//! 2) ensure any DOM requests for sending/closing are propagated to the network +//! 3) transmit any incoming messages/closing to the DOM +//! +//! In order to accomplish this, the handler uses a long-running loop that selects +//! over events from the network and events from the DOM, using async/await to avoid +//! the need for a dedicated thread per websocket. + +use crate::connector::{create_tls_config, ALPN_H1}; use crate::cookie::Cookie; use crate::fetch::methods::should_be_blocked_due_to_bad_port; use crate::hosts::replace_host; use crate::http_loader::HttpState; +use async_tungstenite::tokio::{connect_async_with_tls_connector, ConnectStream}; +use async_tungstenite::WebSocketStream; use embedder_traits::resources::{self, Resource}; -use http::header::{self, HeaderMap, HeaderName, HeaderValue}; +use futures03::future::TryFutureExt; +use futures03::sink::SinkExt; +use futures03::stream::StreamExt; +use headers::Host; +use http::header::{HeaderMap, HeaderName, HeaderValue}; +use http::uri::Authority; use ipc_channel::ipc::{IpcReceiver, IpcSender}; use ipc_channel::router::ROUTER; use net_traits::request::{RequestBuilder, RequestMode}; use net_traits::{CookieSource, MessageData}; use net_traits::{WebSocketDomAction, WebSocketNetworkEvent}; -use openssl::ssl::SslStream; +use openssl::ssl::ConnectConfiguration; use servo_url::ServoUrl; use std::fs; use std::sync::atomic::{AtomicBool, Ordering}; -use std::sync::Arc; -use std::thread; +use std::sync::{Arc, Mutex}; +use tokio2::runtime::Runtime; +use tokio2::select; +use tokio2::sync::mpsc::{unbounded_channel, UnboundedReceiver}; +use tungstenite::error::Error; +use tungstenite::error::Result as WebSocketResult; +use tungstenite::handshake::client::{Request, Response}; +use tungstenite::http::header::{self as WSHeader, HeaderValue as WSHeaderValue}; +use tungstenite::protocol::CloseFrame; +use tungstenite::Message; use url::Url; -use ws::util::TcpStream; -use ws::{ - CloseCode, Factory, Handler, Handshake, Message, Request, Response as WsResponse, Sender, - WebSocket, -}; -use ws::{Error as WebSocketError, ErrorKind as WebSocketErrorKind, Result as WebSocketResult}; - -/// A client for connecting to a websocket server -#[derive(Clone)] -struct Client<'a> { - origin: &'a str, - protocols: &'a [String], - http_state: &'a Arc, - resource_url: &'a ServoUrl, - event_sender: &'a IpcSender, - protocol_in_use: Option, - certificate_path: Option, - extra_certs: ExtraCerts, - connection_certs: ConnectionCerts, + +// Websockets get their own tokio runtime that's independent of the one used for +// HTTP connections, otherwise a large number of websockets could occupy all workers +// and starve other network traffic. +lazy_static! { + pub static ref HANDLE: Mutex> = Mutex::new(Some(Runtime::new().unwrap())); } -impl<'a> Factory for Client<'a> { - type Handler = Self; +/// Create a tungstenite Request object for the initial HTTP request. +/// This request contains `Origin`, `Sec-WebSocket-Protocol`, `Authorization`, +/// `Cookie`, and `Host` headers as appropriate. +/// Returns an error if any header values are invalid or tungstenite cannot create +/// the desired request. +fn create_request( + resource_url: &ServoUrl, + net_url: &Url, + origin: &str, + protocols: &[String], + host: &Host, + http_state: &HttpState, +) -> WebSocketResult { + let mut builder = Request::get(net_url.as_str()); + let headers = builder.headers_mut().unwrap(); + headers.insert("Origin", WSHeaderValue::from_str(origin)?); + + if !protocols.is_empty() { + let protocols = protocols.join(","); + headers.insert( + "Sec-WebSocket-Protocol", + WSHeaderValue::from_str(&protocols)?, + ); + } + + headers.insert("Host", WSHeaderValue::from_str(&host.to_string())?); - fn connection_made(&mut self, _: Sender) -> Self::Handler { - self.clone() + let mut cookie_jar = http_state.cookie_jar.write().unwrap(); + cookie_jar.remove_expired_cookies_for_url(resource_url); + if let Some(cookie_list) = cookie_jar.cookies_for_url(resource_url, CookieSource::HTTP) { + headers.insert("Cookie", WSHeaderValue::from_str(&cookie_list)?); } - fn connection_lost(&mut self, _: Self::Handler) { - let _ = self.event_sender.send(WebSocketNetworkEvent::Fail); + if resource_url.password().is_some() || resource_url.username() != "" { + let basic = base64::encode(&format!( + "{}:{}", + resource_url.username(), + resource_url.password().unwrap_or("") + )); + headers.insert( + "Authorization", + WSHeaderValue::from_str(&format!("Basic {}", basic))?, + ); } -} -impl<'a> Handler for Client<'a> { - fn build_request(&mut self, url: &Url) -> WebSocketResult { - let mut req = Request::from_url(url)?; - req.headers_mut() - .push(("Origin".to_string(), self.origin.as_bytes().to_owned())); + let request = builder.body(())?; + Ok(request) +} - for protocol in self.protocols { - req.add_protocol(protocol); +/// Process an HTTP response resulting from a WS handshake. +/// This ensures that any `Cookie` or HSTS headers are recognized. +/// Returns an error if the protocol selected by the handshake doesn't +/// match the list of provided protocols in the original request. +fn process_ws_response( + http_state: &HttpState, + response: &Response, + resource_url: &ServoUrl, + protocols: &[String], +) -> Result, Error> { + trace!("processing websocket http response for {}", resource_url); + let mut protocol_in_use = None; + if let Some(protocol_name) = response.headers().get("Sec-WebSocket-Protocol") { + let protocol_name = protocol_name.to_str().unwrap(); + if !protocols.is_empty() && !protocols.iter().any(|p| protocol_name == (*p)) { + return Err(Error::Protocol( + "Protocol in use not in client-supplied protocol list".into(), + )); } + protocol_in_use = Some(protocol_name.to_string()); + } - let mut cookie_jar = self.http_state.cookie_jar.write().unwrap(); - cookie_jar.remove_expired_cookies_for_url(self.resource_url); - if let Some(cookie_list) = cookie_jar.cookies_for_url(self.resource_url, CookieSource::HTTP) - { - req.headers_mut() - .push(("Cookie".into(), cookie_list.as_bytes().to_owned())) + let mut jar = http_state.cookie_jar.write().unwrap(); + // TODO(eijebong): Replace thise once typed headers settled on a cookie impl + for cookie in response.headers().get_all(WSHeader::SET_COOKIE) { + if let Ok(s) = std::str::from_utf8(cookie.as_bytes()) { + if let Some(cookie) = + Cookie::from_cookie_string(s.into(), resource_url, CookieSource::HTTP) + { + jar.push(cookie, resource_url, CookieSource::HTTP); + } } - - Ok(req) } - fn on_open(&mut self, shake: Handshake) -> WebSocketResult<()> { - let mut headers = HeaderMap::new(); - for &(ref name, ref value) in shake.response.headers().iter() { - let name = HeaderName::from_bytes(name.as_bytes()).unwrap(); - let value = HeaderValue::from_bytes(&value).unwrap(); - - headers.insert(name, value); + // We need to make a new header map here because tungstenite depends on + // a more recent version of http than the rest of the network stack, so the + // HeaderMap types are incompatible. + let mut headers = HeaderMap::new(); + for (key, value) in response.headers().iter() { + if let (Ok(key), Ok(value)) = ( + HeaderName::from_bytes(key.as_ref()), + HeaderValue::from_bytes(value.as_ref()), + ) { + headers.insert(key, value); } + } + http_state + .hsts_list + .write() + .unwrap() + .update_hsts_list_from_response(resource_url, &headers); - let mut jar = self.http_state.cookie_jar.write().unwrap(); - // TODO(eijebong): Replace thise once typed headers settled on a cookie impl - for cookie in headers.get_all(header::SET_COOKIE) { - if let Ok(s) = std::str::from_utf8(cookie.as_bytes()) { - if let Some(cookie) = - Cookie::from_cookie_string(s.into(), self.resource_url, CookieSource::HTTP) - { - jar.push(cookie, self.resource_url, CookieSource::HTTP); - } - } - } + Ok(protocol_in_use) +} - self.http_state - .hsts_list - .write() - .unwrap() - .update_hsts_list_from_response(self.resource_url, &headers); - - let _ = self - .event_sender - .send(WebSocketNetworkEvent::ConnectionEstablished { - protocol_in_use: self.protocol_in_use.clone(), - }); - Ok(()) - } +#[derive(Debug)] +enum DomMsg { + Send(Message), + Close(Option<(u16, String)>), +} - fn on_message(&mut self, message: Message) -> WebSocketResult<()> { - let message = match message { - Message::Text(message) => MessageData::Text(message), - Message::Binary(message) => MessageData::Binary(message), - }; - let _ = self - .event_sender - .send(WebSocketNetworkEvent::MessageReceived(message)); +/// Initialize a listener for DOM actions. These are routed from the IPC channel +/// to a tokio channel that the main WS client task uses to receive them. +fn setup_dom_listener( + dom_action_receiver: IpcReceiver, + initiated_close: Arc, +) -> UnboundedReceiver { + let (sender, receiver) = unbounded_channel(); + + ROUTER.add_route( + dom_action_receiver.to_opaque(), + Box::new(move |message| { + let dom_action = message.to().expect("Ws dom_action message to deserialize"); + trace!("handling WS DOM action: {:?}", dom_action); + match dom_action { + WebSocketDomAction::SendMessage(MessageData::Text(data)) => { + if let Err(e) = sender.send(DomMsg::Send(Message::Text(data))) { + warn!("Error sending websocket message: {:?}", e); + } + }, + WebSocketDomAction::SendMessage(MessageData::Binary(data)) => { + if let Err(e) = sender.send(DomMsg::Send(Message::Binary(data))) { + warn!("Error sending websocket message: {:?}", e); + } + }, + WebSocketDomAction::Close(code, reason) => { + if initiated_close.fetch_or(true, Ordering::SeqCst) { + return; + } + let frame = code.map(move |c| (c, reason.unwrap_or_default())); + if let Err(e) = sender.send(DomMsg::Close(frame)) { + warn!("Error closing websocket: {:?}", e); + } + }, + } + }), + ); - Ok(()) - } + receiver +} - fn on_error(&mut self, err: WebSocketError) { - debug!("Error in WebSocket communication: {:?}", err); - let _ = self.event_sender.send(WebSocketNetworkEvent::Fail); - } +/// Listen for WS events from the DOM and the network until one side +/// closes the connection or an error occurs. Since this is an async +/// function that uses the select operation, it will run as a task +/// on the WS tokio runtime. +async fn run_ws_loop( + mut dom_receiver: UnboundedReceiver, + resource_event_sender: IpcSender, + mut stream: WebSocketStream, +) { + loop { + select! { + dom_msg = dom_receiver.recv() => { + trace!("processing dom msg: {:?}", dom_msg); + let dom_msg = match dom_msg { + Some(msg) => msg, + None => break, + }; + match dom_msg { + DomMsg::Send(m) => { + if let Err(e) = stream.send(m).await { + warn!("error sending websocket message: {:?}", e); + } + }, + DomMsg::Close(frame) => { + if let Err(e) = stream.close(frame.map(|(code, reason)| { + CloseFrame { + code: code.into(), + reason: reason.into(), + } + })).await { + warn!("error closing websocket: {:?}", e); + } + }, + } + } + ws_msg = stream.next() => { + trace!("processing WS stream: {:?}", ws_msg); + let msg = match ws_msg { + Some(Ok(msg)) => msg, + Some(Err(e)) => { + warn!("Error in WebSocket communication: {:?}", e); + let _ = resource_event_sender.send(WebSocketNetworkEvent::Fail); + break; + }, + None => { + warn!("Error in WebSocket communication"); + let _ = resource_event_sender.send(WebSocketNetworkEvent::Fail); + break; + } + }; + match msg { + Message::Text(s) => { + let message = MessageData::Text(s); + if let Err(e) = resource_event_sender + .send(WebSocketNetworkEvent::MessageReceived(message)) + { + warn!("Error sending websocket notification: {:?}", e); + break; + } + } - fn on_response(&mut self, res: &WsResponse) -> WebSocketResult<()> { - let protocol_in_use = res.protocol()?; + Message::Binary(v) => { + let message = MessageData::Binary(v); + if let Err(e) = resource_event_sender + .send(WebSocketNetworkEvent::MessageReceived(message)) + { + warn!("Error sending websocket notification: {:?}", e); + break; + } + } - if let Some(protocol_name) = protocol_in_use { - if !self.protocols.is_empty() && !self.protocols.iter().any(|p| protocol_name == (*p)) { - let error = WebSocketError::new( - WebSocketErrorKind::Protocol, - "Protocol in Use not in client-supplied protocol list", - ); - return Err(error); + Message::Ping(_) | Message::Pong(_) => {} + + Message::Close(frame) => { + let (reason, code) = match frame { + Some(frame) => (frame.reason, Some(frame.code.into())), + None => ("".into(), None), + }; + debug!("Websocket connection closing due to ({:?}) {}", code, reason); + let _ = resource_event_sender.send(WebSocketNetworkEvent::Close( + code, + reason.to_string(), + )); + break; + } + } } - self.protocol_in_use = Some(protocol_name.into()); } - Ok(()) } +} - fn on_close(&mut self, code: CloseCode, reason: &str) { - debug!("Connection closing due to ({:?}) {}", code, reason); - let _ = self.event_sender.send(WebSocketNetworkEvent::Close( - Some(code.into()), - reason.to_owned(), - )); +/// Initiate a new async WS connection. Returns an error if the connection fails +/// for any reason, or if the response isn't valid. Otherwise, the endless WS +/// listening loop will be started. +async fn start_websocket( + http_state: Arc, + url: ServoUrl, + resource_event_sender: IpcSender, + protocols: Vec, + client: Request, + tls_config: ConnectConfiguration, + dom_action_receiver: IpcReceiver, +) -> Result<(), Error> { + trace!("starting WS connection to {}", url); + let (stream, response) = connect_async_with_tls_connector(client, Some(tls_config)).await?; + let protocol_in_use = process_ws_response(&http_state, &response, &url, &protocols)?; + + let _ = resource_event_sender + .send(WebSocketNetworkEvent::ConnectionEstablished { protocol_in_use }); + + let initiated_close = Arc::new(AtomicBool::new(false)); + let dom_receiver = setup_dom_listener(dom_action_receiver, initiated_close); + trace!("about to start ws loop for {}", url); + run_ws_loop(dom_receiver, resource_event_sender, stream).await; + Ok(()) +} + +/// Create a new websocket connection for the given request. +fn connect( + req_builder: RequestBuilder, + resource_event_sender: IpcSender, + dom_action_receiver: IpcReceiver, + http_state: Arc, + certificate_path: Option, +) -> Result<(), String> { + let mut req_builder = req_builder; + let protocols = match req_builder.mode { + RequestMode::WebSocket { protocols } => protocols, + _ => { + return Err( + "Received a RequestBuilder with a non-websocket mode in websocket_loader" + .to_string(), + ) + }, + }; + + // https://fetch.spec.whatwg.org/#websocket-opening-handshake + // By standard, we should work with an http(s):// URL (req_url), + // but as ws-rs expects to be called with a ws(s):// URL (net_url) + // we upgrade ws to wss, so we don't have to convert http(s) back to ws(s). + http_state + .hsts_list + .read() + .unwrap() + .apply_hsts_rules(&mut req_builder.url); + + let scheme = req_builder.url.scheme(); + let mut req_url = req_builder.url.clone(); + if scheme == "ws" { + req_url + .as_mut_url() + .set_scheme("http") + .map_err(|()| "couldn't replace scheme".to_string())?; + } else if scheme == "wss" { + req_url + .as_mut_url() + .set_scheme("https") + .map_err(|()| "couldn't replace scheme".to_string())?; } - fn upgrade_ssl_client( - &mut self, - stream: TcpStream, - url: &Url, - ) -> WebSocketResult> { - let certs = match self.certificate_path { - Some(ref path) => fs::read_to_string(path).expect("Couldn't not find certificate file"), - None => resources::read_string(Resource::SSLCertificates), - }; - - let domain = self - .resource_url - .as_url() - .domain() - .ok_or(WebSocketError::new( - WebSocketErrorKind::Protocol, - format!("Unable to parse domain from {}. Needed for SSL.", url), - ))?; - let tls_config = create_tls_config( - &certs, - ALPN_H1, - self.extra_certs.clone(), - self.connection_certs.clone(), - ); - tls_config - .build() - .connect(domain, stream) - .map_err(WebSocketError::from) + if should_be_blocked_due_to_bad_port(&req_url) { + return Err("Port blocked".to_string()); } + + let host_str = req_builder + .url + .host_str() + .ok_or_else(|| "No host string".to_string())?; + let host = replace_host(host_str); + let mut net_url = req_builder.url.clone().into_url(); + net_url.set_host(Some(&host)).map_err(|e| e.to_string())?; + + let host = Host::from( + format!( + "{}{}", + host_str, + req_builder + .url + .port_or_known_default() + .map(|v| format!(":{}", v)) + .unwrap_or("".into()) + ) + .parse::() + .map_err(|e| e.to_string())?, + ); + + let certs = match certificate_path { + Some(ref path) => fs::read_to_string(path).map_err(|e| e.to_string())?, + None => resources::read_string(Resource::SSLCertificates), + }; + + let client = match create_request( + &req_builder.url, + &net_url, + &req_builder.origin.ascii_serialization(), + &protocols, + &host, + &*http_state, + ) { + Ok(c) => c, + Err(e) => return Err(e.to_string()), + }; + + let tls_config = create_tls_config( + &certs, + ALPN_H1, + http_state.extra_certs.clone(), + http_state.connection_certs.clone(), + ); + let tls_config = match tls_config.build().configure() { + Ok(c) => c, + Err(e) => return Err(e.to_string()), + }; + + let resource_event_sender2 = resource_event_sender.clone(); + match HANDLE.lock().unwrap().as_mut() { + Some(handle) => handle.spawn( + start_websocket( + http_state, + req_builder.url.clone(), + resource_event_sender, + protocols, + client, + tls_config, + dom_action_receiver, + ) + .map_err(move |e| { + warn!("Failed to establish a WebSocket connection: {:?}", e); + let _ = resource_event_sender2.send(WebSocketNetworkEvent::Fail); + }), + ), + None => return Err("No runtime available".to_string()), + }; + Ok(()) } +/// Create a new websocket connection for the given request. pub fn init( req_builder: RequestBuilder, resource_event_sender: IpcSender, dom_action_receiver: IpcReceiver, http_state: Arc, certificate_path: Option, - extra_certs: ExtraCerts, - connection_certs: ConnectionCerts, ) { - thread::Builder::new() - .name(format!("WebSocket connection to {}", req_builder.url)) - .spawn(move || { - let mut req_builder = req_builder; - let protocols = match req_builder.mode { - RequestMode::WebSocket { protocols } => protocols, - _ => panic!( - "Received a RequestBuilder with a non-websocket mode in websocket_loader" - ), - }; - - // https://fetch.spec.whatwg.org/#websocket-opening-handshake - // By standard, we should work with an http(s):// URL (req_url), - // but as ws-rs expects to be called with a ws(s):// URL (net_url) - // we upgrade ws to wss, so we don't have to convert http(s) back to ws(s). - http_state - .hsts_list - .read() - .unwrap() - .apply_hsts_rules(&mut req_builder.url); - - let scheme = req_builder.url.scheme(); - let mut req_url = req_builder.url.clone(); - if scheme == "ws" { - req_url.as_mut_url().set_scheme("http").unwrap(); - } else if scheme == "wss" { - req_url.as_mut_url().set_scheme("https").unwrap(); - } - - if should_be_blocked_due_to_bad_port(&req_url) { - debug!("Failed to establish a WebSocket connection: port blocked"); - let _ = resource_event_sender.send(WebSocketNetworkEvent::Fail); - return; - } - - let host = replace_host(req_builder.url.host_str().unwrap()); - let mut net_url = req_builder.url.clone().into_url(); - net_url.set_host(Some(&host)).unwrap(); - - let client = Client { - origin: &req_builder.origin.ascii_serialization(), - protocols: &protocols, - http_state: &http_state, - resource_url: &req_builder.url, - event_sender: &resource_event_sender, - protocol_in_use: None, - certificate_path, - extra_certs, - connection_certs, - }; - let mut ws = WebSocket::new(client).unwrap(); - - if let Err(e) = ws.connect(net_url) { - debug!("Failed to establish a WebSocket connection: {:?}", e); - return; - }; - - let ws_sender = ws.broadcaster(); - let initiated_close = Arc::new(AtomicBool::new(false)); - - ROUTER.add_route( - dom_action_receiver.to_opaque(), - Box::new(move |message| { - let dom_action = message.to().expect("Ws dom_action message to deserialize"); - match dom_action { - WebSocketDomAction::SendMessage(MessageData::Text(data)) => { - if let Err(e) = ws_sender.send(Message::text(data)) { - warn!("Error sending websocket message: {:?}", e); - } - }, - WebSocketDomAction::SendMessage(MessageData::Binary(data)) => { - if let Err(e) = ws_sender.send(Message::binary(data)) { - warn!("Error sending websocket message: {:?}", e); - } - }, - WebSocketDomAction::Close(code, reason) => { - if !initiated_close.fetch_or(true, Ordering::SeqCst) { - match code { - Some(code) => { - if let Err(e) = ws_sender.close_with_reason( - code.into(), - reason.unwrap_or("".to_owned()), - ) { - warn!("Error closing websocket: {:?}", e); - } - }, - None => { - if let Err(e) = ws_sender.close(CloseCode::Status) { - warn!("Error closing websocket: {:?}", e); - } - }, - }; - } - }, - } - }), - ); - - if let Err(e) = ws.run() { - debug!("Failed to run WebSocket: {:?}", e); - let _ = resource_event_sender.send(WebSocketNetworkEvent::Fail); - }; - }) - .expect("Thread spawning failed"); + let resource_event_sender2 = resource_event_sender.clone(); + if let Err(e) = connect( + req_builder, + resource_event_sender, + dom_action_receiver, + http_state, + certificate_path, + ) { + warn!("Error starting websocket: {}", e); + let _ = resource_event_sender2.send(WebSocketNetworkEvent::Fail); + } } diff --git a/servo-tidy.toml b/servo-tidy.toml index c60f1d28b298e..25ee0e9f90706 100644 --- a/servo-tidy.toml +++ b/servo-tidy.toml @@ -21,6 +21,7 @@ rand = [ "servo_rand", "tempfile", "uuid", + "tungstenite", "ws", ] @@ -36,6 +37,14 @@ packages = [ "parking_lot", "parking_lot_core", + # https://github.com/servo/servo/issues/26933 + "futures", + "tokio-openssl", + "tokio", + "http", + "scoped-tls", + "bytes", + # https://github.com/servo/servo/pull/23288#issuecomment-494687746 "gl_generator", diff --git a/tests/wpt/metadata/websockets/cookies/third-party-cookie-accepted.https.html.ini b/tests/wpt/metadata/websockets/cookies/third-party-cookie-accepted.https.html.ini new file mode 100644 index 0000000000000..f854e3e35231f --- /dev/null +++ b/tests/wpt/metadata/websockets/cookies/third-party-cookie-accepted.https.html.ini @@ -0,0 +1,3 @@ +[third-party-cookie-accepted.https.html] + [Test that third-party cookies are accepted for WebSockets.] + expected: FAIL