Skip to content
This repository has been archived by the owner on Aug 3, 2023. It is now read-only.

Commit

Permalink
Get https websockets working
Browse files Browse the repository at this point in the history
It turns out websocket upgrades with HTTP/2 require an HTTP extension,
which Cloudflare doesn't currently support: https://datatracker.ietf.org/doc/html/rfc8441

To avoid this, enable HTTP/1 for the remote client.

This required an update to rustls to allow enabling http1.
  • Loading branch information
jyn514 committed Dec 13, 2021
1 parent 5131a94 commit 28a1b6f
Show file tree
Hide file tree
Showing 8 changed files with 82 additions and 60 deletions.
73 changes: 41 additions & 32 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 4 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ futures-util = "0.3"
globset = "0.4.6"
http = "0.2.1"
hyper = { version = "0.14.7", features = ["http2", "server", "runtime"] }
hyper-rustls = "0.22.1"
hyper-rustls = "0.23"
ignore = "0.4.17"
indicatif = "0.15.0"
log = "0.4.11"
Expand All @@ -49,7 +49,8 @@ prettytable-rs = "0.8.0"
rand = "0.8.3"
regex = "1.4.1"
reqwest = { version = "0.11.3", features = ["blocking", "json", "multipart"] }
rustls = "0.19.1"
rustls = "0.20.2"
rustls-pemfile = "0.2.1"
semver = "1.0.3"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0.60"
Expand All @@ -63,7 +64,7 @@ text_io = "0.1.8"
tokio = { version = "1.5.0", default-features = false, features = ["io-std", "time", "macros", "process", "signal", "sync"] }
tokio-native-tls = "0.3.0"
tokio-retry = "0.3"
tokio-rustls = "0.22.0"
tokio-rustls = "0.23.0"
tokio-stream = "0.1.5"
tokio-tungstenite = "0.14.0"
toml = "0.5.8"
Expand Down
8 changes: 3 additions & 5 deletions src/commands/dev/edge/server/http.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use super::preview_request;
use crate::commands::dev::utils::{get_path_as_str, rewrite_redirect};
use crate::commands::dev::{Protocol, ServerConfig};
use crate::commands::dev::{self, Protocol, ServerConfig};
use crate::terminal::emoji;

use std::sync::{Arc, Mutex};
Expand All @@ -9,8 +9,7 @@ use anyhow::Result;
use chrono::prelude::*;
use hyper::service::{make_service_fn, service_fn};
use hyper::upgrade::OnUpgrade;
use hyper::{Body, Client as HyperClient, Server};
use hyper_rustls::HttpsConnector;
use hyper::Server;
use tokio::sync::oneshot::{Receiver, Sender};

pub async fn http(
Expand All @@ -21,8 +20,7 @@ pub async fn http(
shutdown_channel: (Receiver<()>, Sender<()>),
) -> Result<()> {
// set up https client to connect to the preview service
let https = HttpsConnector::with_native_roots();
let client = HyperClient::builder().build::<_, Body>(https);
let client = dev::client();

let listening_address = server_config.listening_address;

Expand Down
8 changes: 3 additions & 5 deletions src/commands/dev/edge/server/https.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use super::preview_request;
use crate::commands::dev::utils::{get_path_as_str, rewrite_redirect};
use crate::commands::dev::{tls, Protocol, ServerConfig};
use crate::commands::dev::{self, tls, Protocol, ServerConfig};
use crate::terminal::emoji;
use crate::terminal::message::{Message, StdOut};
use std::sync::{Arc, Mutex};
Expand All @@ -11,8 +11,7 @@ use futures_util::{stream::StreamExt, FutureExt};

use hyper::service::{make_service_fn, service_fn};
use hyper::upgrade::OnUpgrade;
use hyper::{Body, Client as HyperClient, Server};
use hyper_rustls::HttpsConnector;
use hyper::Server;
use tokio::net::TcpListener;
use tokio::sync::oneshot::{Receiver, Sender};

Expand All @@ -25,8 +24,7 @@ pub async fn https(
tls::generate_cert()?;

// set up https client to connect to the preview service
let https = HttpsConnector::with_native_roots();
let client = HyperClient::builder().build::<_, Body>(https);
let client = dev::client();

let listening_address = server_config.listening_address;

Expand Down
7 changes: 3 additions & 4 deletions src/commands/dev/gcs/server/http.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use super::preview_request;
use crate::commands::dev;
use crate::commands::dev::gcs::headers::destructure_response;
use crate::commands::dev::server_config::ServerConfig;
use crate::commands::dev::utils::{get_path_as_str, rewrite_redirect};
Expand All @@ -9,15 +10,13 @@ use std::sync::{Arc, Mutex};
use anyhow::Result;
use chrono::prelude::*;
use hyper::service::{make_service_fn, service_fn};
use hyper::{Body, Client as HyperClient, Response, Server};
use hyper_rustls::HttpsConnector;
use hyper::{Response, Server};

/// performs all logic that takes an incoming request
/// and routes it to the Workers runtime preview service
pub async fn http(server_config: ServerConfig, preview_id: Arc<Mutex<String>>) -> Result<()> {
// set up https client to connect to the preview service
let https = HttpsConnector::with_native_roots();
let client = HyperClient::builder().build::<_, Body>(https);
let client = dev::client();

let listening_address = server_config.listening_address;

Expand Down
7 changes: 3 additions & 4 deletions src/commands/dev/gcs/server/https.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use super::preview_request;
use crate::commands::dev;
use crate::commands::dev::gcs::headers::destructure_response;
use crate::commands::dev::server_config::ServerConfig;
use crate::commands::dev::tls;
Expand All @@ -11,8 +12,7 @@ use anyhow::Result;
use chrono::prelude::*;
use futures_util::{FutureExt, StreamExt};
use hyper::service::{make_service_fn, service_fn};
use hyper::{Body, Client as HyperClient, Response, Server};
use hyper_rustls::HttpsConnector;
use hyper::{Response, Server};
use tokio::net::TcpListener;

/// performs all logic that takes an incoming request
Expand All @@ -21,8 +21,7 @@ pub async fn https(server_config: ServerConfig, preview_id: Arc<Mutex<String>>)
tls::generate_cert()?;

// set up https client to connect to the preview service
let https = HttpsConnector::with_native_roots();
let client = HyperClient::builder().build::<_, Body>(https);
let client = dev::client();

let listening_address = server_config.listening_address;

Expand Down
13 changes: 13 additions & 0 deletions src/commands/dev/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ mod socket;
mod tls;
mod utils;

use hyper::client::HttpConnector;
use hyper::Body;
use hyper_rustls::HttpsConnector;
pub use server_config::Protocol;
pub use server_config::ServerConfig;

Expand All @@ -17,6 +20,16 @@ use crate::terminal::styles;

use anyhow::Result;

fn client() -> hyper::Client<HttpsConnector<HttpConnector>> {
let builder = hyper_rustls::HttpsConnectorBuilder::new()
.with_native_roots()
.https_or_http();
// Cloudflare doesn't currently support websockets with HTTP/2.
// Allow using HTTP/1.1 for websocket connections.
let https = builder.enable_http1().build();
hyper::Client::builder().build::<_, Body>(https)
}

/// `wrangler dev` starts a server on a dev machine that routes incoming HTTP requests
/// to a Cloudflare Workers runtime and returns HTTP responses
#[allow(clippy::too_many_arguments)]
Expand Down
19 changes: 12 additions & 7 deletions src/commands/dev/tls/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@ use anyhow::Result;
use core::task::{Context, Poll};
use fs::File;
use futures_util::stream::Stream;
use rustls::internal::pemfile;
use rustls::{NoClientAuth, ServerConfig};
use rustls::{server::NoClientAuth, ServerConfig};
use std::path::PathBuf;
use std::pin::Pin;
use std::sync::Arc;
Expand All @@ -30,10 +29,13 @@ pub(super) fn get_tls_acceptor() -> Result<TlsAcceptor> {
let key = load_private_key(privkey)?;

// Do not use client certificate authentication.
let mut cfg = ServerConfig::new(NoClientAuth::new());
let cfg = ServerConfig::builder()
.with_safe_defaults()
.with_client_cert_verifier(NoClientAuth::new());

// Select a certificate to use.
cfg.set_single_cert(certs, key)
let cfg = cfg
.with_single_cert(certs, key)
.map_err(|e| io_error(format!("{}", e)))?;

Ok(TlsAcceptor::from(Arc::new(cfg)))
Expand Down Expand Up @@ -71,7 +73,10 @@ fn load_certs(file: PathBuf) -> io::Result<Vec<rustls::Certificate>> {
let mut reader = io::BufReader::new(certfile);

// Load and return certificate.
pemfile::certs(&mut reader).map_err(|_| io_error("failed to load certificate".into()))
match rustls_pemfile::certs(&mut reader) {
Ok(certs) => Ok(certs.into_iter().map(rustls::Certificate).collect()),
Err(_) => Err(io_error("failed to load certificate".into())),
}
}

// Load private key from file.
Expand All @@ -81,10 +86,10 @@ fn load_private_key(file: PathBuf) -> io::Result<rustls::PrivateKey> {
let mut reader = io::BufReader::new(keyfile);

// Load and return a single private key.
let keys = pemfile::pkcs8_private_keys(&mut reader)
let mut keys = rustls_pemfile::pkcs8_private_keys(&mut reader)
.map_err(|_| io_error("failed to load private key".into()))?;
if keys.len() != 1 {
return Err(io_error("expected a single private key".into()));
}
Ok(keys[0].clone())
Ok(rustls::PrivateKey(keys.pop().unwrap()))
}

0 comments on commit 28a1b6f

Please sign in to comment.