Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for rustls websocket connection as feature ws-rustls-tokio #687

Merged
merged 9 commits into from
Dec 19, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .github/workflows/rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,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,9 @@ headers = { version = "0.4", 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.25", optional = true }
rustls-pki-types = { version = "1", optional = true }
webpki-roots = { version = "0.26", 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 Down Expand Up @@ -81,6 +84,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", "rustls-pki-types", "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
13 changes: 8 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -108,18 +108,20 @@ 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.19.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.19.0", default-features = false, features = ["http-rustls-tls"] }
for HTTPS or WSS use the corresponding features.
```toml
web3 = { version = "_", default-features = false, features = ["http-rustls-tls", "ws-rustls-tokio"] }
```

_Note: To fully replicate the default features also add `signing` & `ipc-tokio` features_.

# Cargo Features

The library supports following features:
Expand All @@ -129,6 +131,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
9 changes: 1 addition & 8 deletions src/transports/http.rs
Original file line number Diff line number Diff line change
Expand Up @@ -196,15 +196,8 @@ mod tests {
use jsonrpc_core::ErrorCode;
use std::net::TcpListener;

fn port_is_available(port: u16) -> bool {
match TcpListener::bind(("127.0.0.1", port)) {
Ok(_) => true,
Err(_) => false,
}
}

fn get_available_port() -> Option<u16> {
(3001..65535).find(|port| port_is_available(*port))
Some(TcpListener::bind(("127.0.0.1", 0)).ok()?.local_addr().ok()?.port())
}

async fn server(req: hyper::Request<hyper::Body>) -> hyper::Result<hyper::Response<hyper::Body>> {
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
46 changes: 42 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")
alexheretic marked this conversation as resolved.
Show resolved Hide resolved
))]
{
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,32 @@ 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 rustls_pki_types::ServerName;
use std::convert::TryFrom;
use tokio_rustls::rustls::{ClientConfig, RootCertStore};

let client_conf = ClientConfig::builder()
.with_root_certificates({
let mut root_cert_store = RootCertStore::empty();
root_cert_store.extend(webpki_roots::TLS_SERVER_ROOTS.iter().cloned());
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}"))))?
.to_owned();

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 +547,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
Loading