Skip to content

Commit

Permalink
Add support for rustls websocket connection
Browse files Browse the repository at this point in the history
  • Loading branch information
alexheretic committed Apr 21, 2023
1 parent 68f2a6d commit a072266
Show file tree
Hide file tree
Showing 6 changed files with 61 additions and 10 deletions.
6 changes: 6 additions & 0 deletions .github/workflows/rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,12 @@ jobs:
command: check
toolchain: stable
args: --no-default-features --features ws-tls-tokio
- name: Checking ws-rustls-tokio
uses: actions-rs/cargo@master
with:
command: check
toolchain: stable
args: --no-default-features --features ws-rustls-tokio
- name: Checking ws-async-std
uses: actions-rs/cargo@master
with:
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ target
Cargo.lock
*.swp
.idea/
.vscode
4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ headers = { version = "0.3", optional = true }
# async-native-tls = { git = "https://github.com/async-email/async-native-tls.git", rev = "b5b5562d6cea77f913d4cbe448058c031833bf17", optional = true, default-features = false }
# Temporarily use forked version released to crates.io
async-native-tls = { package = "web3-async-native-tls", version = "0.4", optional = true, default-features = false }
tokio-rustls = { version = "0.23.4", optional = true }
webpki-roots = { version = "0.22.6", optional = true }
async-std = { version = "1.6", optional = true }
tokio = { version = "1.0", optional = true, features = ["full"] }
tokio-stream = { version = "0.1", optional = true }
Expand All @@ -56,6 +58,7 @@ getrandom = { version = "0.2", features = ["js"], optional = true }
wasm-bindgen = { version = "0.2.68", optional = true, features = ["serde-serialize"] }
wasm-bindgen-futures = { version = "0.4.18", optional = true }


[dev-dependencies]
# For examples
env_logger = "0.10"
Expand All @@ -80,6 +83,7 @@ signing = ["secp256k1", "once_cell"]
ws-tokio = ["soketto", "url", "tokio", "tokio-util", "headers"]
ws-async-std = ["soketto", "url", "async-std", "headers"]
ws-tls-tokio = ["async-native-tls", "async-native-tls/runtime-tokio", "ws-tokio"]
ws-rustls-tokio = ["tokio-rustls", "webpki-roots", "ws-tokio"]
ws-tls-async-std = ["async-native-tls", "async-native-tls/runtime-async-std", "ws-async-std"]
ipc-tokio = ["tokio", "tokio-stream", "tokio-util"]
arbitrary_precision = ["serde_json/arbitrary_precision", "jsonrpc-core/arbitrary_precision"]
Expand Down
9 changes: 5 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -108,16 +108,16 @@ web3.api::<CustomNamespace>().custom_method().wait().unwrap()

Currently, Windows does not support IPC, which is enabled in the library by default.
To compile, you need to disable the IPC feature:
```
web3 = { version = "0.17.0", default-features = false, features = ["http"] }
```toml
web3 = { version = "_", default-features = false, features = ["http"] }
```

# Avoiding OpenSSL dependency

On Linux, `native-tls` is implemented using OpenSSL. To avoid that dependency
for HTTPS use the corresponding feature.
```
web3 = { version = "0.17.0", default-features = false, features = ["http-rustls-tls"] }
```toml
web3 = { version = "_", default-features = false, features = ["http-rustls-tls", "signing", "ws-rustls-tokio", "ipc-tokio"] }
```

# Cargo Features
Expand All @@ -129,6 +129,7 @@ The library supports following features:
- `http-rustls-tls` - Enables TLS support via `reqwest/rustls-tls` for HTTP transport (implies `http`).
- `ws-tokio` - Enables WS transport using `tokio` runtime.
- `ws-tls-tokio` - Enables TLS support for WS transport (implies `ws-tokio`; default).
- `ws-rustls-tokio` - Enables rustls TLS support for WS transport (implies `ws-tokio`).
- `ws-async-std` - Enables WS transport using `async-std` runtime.
- `ws-tls-async-std` - Enables TLS support for WS transport (implies `ws-async-std`).
- `ipc-tokio` - Enables IPC transport using `tokio` runtime (default).
Expand Down
4 changes: 2 additions & 2 deletions src/transports/ipc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -394,7 +394,7 @@ mod test {
})
);

tx.write(r#"{"jsonrpc": "2.0", "id": 1, "result": {"test": 1}}"#.as_ref())
tx.write_all(r#"{"jsonrpc": "2.0", "id": 1, "result": {"test": 1}}"#.as_ref())
.await
.unwrap();
tx.flush().await.unwrap();
Expand All @@ -417,7 +417,7 @@ mod test {

let response_bytes = r#"{"jsonrpc": "2.0", "id": 2, "result": {"test": "string1"}}"#;
for chunk in response_bytes.as_bytes().chunks(3) {
tx.write(chunk).await.unwrap();
tx.write_all(chunk).await.unwrap();
tx.flush().await.unwrap();
}
}
Expand Down
47 changes: 43 additions & 4 deletions src/transports/ws.rs
Original file line number Diff line number Diff line change
Expand Up @@ -134,8 +134,17 @@ impl WsServerTask {
let stream = async_native_tls::connect(host, stream).await?;
MaybeTlsStream::Tls(compat::compat(stream))
}
#[cfg(not(any(feature = "ws-tls-tokio", feature = "ws-tls-async-std")))]
panic!("The library was compiled without TLS support. Enable ws-tls-tokio or ws-tls-async-std feature.");
#[cfg(all(
feature = "ws-rustls-tokio",
not(feature = "ws-tls-tokio"),
not(feature = "ws-tls-async-std")
))]
{
let stream = tokio_rustls_connect(host, stream).await?;
MaybeTlsStream::Tls(compat::compat(stream))
}
#[cfg(not(any(feature = "ws-tls-tokio", feature = "ws-tls-async-std", feature = "ws-rustls-tokio")))]
panic!("The library was compiled without TLS support. Enable ws-tls-tokio, ws-rustls-tokio or ws-tls-async-std feature.");
} else {
let stream = compat::compat(stream);
MaybeTlsStream::Plain(stream)
Expand Down Expand Up @@ -245,6 +254,33 @@ impl WsServerTask {
}
}

#[cfg(feature = "ws-rustls-tokio")]
async fn tokio_rustls_connect(
host: &str,
stream: tokio::net::TcpStream,
) -> error::Result<tokio_rustls::client::TlsStream<tokio::net::TcpStream>> {
use std::convert::TryFrom;
use tokio_rustls::rustls::{ClientConfig, OwnedTrustAnchor, RootCertStore, ServerName};

let client_conf = ClientConfig::builder()
.with_safe_defaults()
.with_root_certificates({
let mut root_cert_store = RootCertStore::empty();
root_cert_store.add_server_trust_anchors(webpki_roots::TLS_SERVER_ROOTS.0.iter().map(|ta| {
OwnedTrustAnchor::from_subject_spki_name_constraints(ta.subject, ta.spki, ta.name_constraints)
}));
root_cert_store
})
.with_no_client_auth();

let dnsname = ServerName::try_from(host)
.map_err(|err| error::Error::Transport(TransportError::Message(format!("Invalid host: {err}"))))?;

Ok(tokio_rustls::TlsConnector::from(Arc::new(client_conf))
.connect(dnsname, stream)
.await?)
}

fn as_data_stream<T: Unpin + futures::AsyncRead + futures::AsyncWrite>(
receiver: soketto::connection::Receiver<T>,
) -> impl Stream<Item = Result<Vec<u8>, soketto::connection::Error>> {
Expand Down Expand Up @@ -512,13 +548,16 @@ pub mod compat {
/// TLS stream type for tokio runtime.
#[cfg(feature = "ws-tls-tokio")]
pub type TlsStream = Compat<async_native_tls::TlsStream<tokio::net::TcpStream>>;
/// Rustls TLS stream type for tokio runtime.
#[cfg(all(feature = "ws-rustls-tokio", not(feature = "ws-tls-tokio")))]
pub type TlsStream = Compat<tokio_rustls::client::TlsStream<tokio::net::TcpStream>>;
/// Dummy TLS stream type.
#[cfg(not(feature = "ws-tls-tokio"))]
#[cfg(all(not(feature = "ws-tls-tokio"), not(feature = "ws-rustls-tokio")))]
pub type TlsStream = TcpStream;

/// Create new TcpStream object.
pub async fn raw_tcp_stream(addrs: String) -> io::Result<tokio::net::TcpStream> {
Ok(tokio::net::TcpStream::connect(addrs).await?)
tokio::net::TcpStream::connect(addrs).await
}

/// Wrap given argument into compatibility layer.
Expand Down

0 comments on commit a072266

Please sign in to comment.