#![deny(unreachable_pub)]
use futures::future::FutureExt;
use futures::select;
use futures::stream::Stream;
use tracing::debug;
use core::fmt;
use std::collections::HashMap;
use std::iter;
use std::net::{SocketAddr, ToSocketAddrs};
use std::option;
use std::pin::Pin;
use std::slice;
use std::str::{self, FromStr};
use std::task::{Context, Poll};
use tokio::io::ErrorKind;
use url::{Host, Url};
use bytes::Bytes;
use serde::{Deserialize, Serialize};
use serde_repr::{Deserialize_repr, Serialize_repr};
use tokio::io;
use tokio::sync::{mpsc, oneshot};
use tokio::task;
pub type Error = Box<dyn std::error::Error + Send + Sync>;
const VERSION: &str = env!("CARGO_PKG_VERSION");
const LANG: &str = "rust";
pub use tokio_rustls::rustls;
use connection::{Connection, State};
use connector::{Connector, ConnectorOptions};
pub use header::{HeaderMap, HeaderName, HeaderValue};
pub(crate) mod auth_utils;
mod client;
pub mod connection;
mod connector;
mod options;
use crate::options::CallbackArg1;
pub use client::{Client, PublishError, Request};
pub use options::{AuthError, ConnectOptions};
pub mod header;
pub mod jetstream;
pub mod message;
pub mod status;
mod tls;
pub use message::Message;
pub use status::StatusCode;
#[derive(Debug, Deserialize, Default, Clone, Eq, PartialEq)]
pub struct ServerInfo {
#[serde(default)]
pub server_id: String,
#[serde(default)]
pub server_name: String,
#[serde(default)]
pub host: String,
#[serde(default)]
pub port: u16,
#[serde(default)]
pub version: String,
#[serde(default)]
pub auth_required: bool,
#[serde(default)]
pub tls_required: bool,
#[serde(default)]
pub max_payload: usize,
#[serde(default)]
pub proto: i8,
#[serde(default)]
pub client_id: u64,
#[serde(default)]
pub go: String,
#[serde(default)]
pub nonce: String,
#[serde(default)]
pub connect_urls: Vec<String>,
#[serde(default)]
pub client_ip: String,
#[serde(default)]
pub headers: bool,
#[serde(default, rename = "ldm")]
pub lame_duck_mode: bool,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub(crate) enum ServerOp {
Ok,
Info(Box<ServerInfo>),
Ping,
Pong,
Error(ServerError),
Message {
sid: u64,
subject: String,
reply: Option<String>,
payload: Bytes,
headers: Option<HeaderMap>,
status: Option<StatusCode>,
description: Option<String>,
},
}
#[derive(Debug)]
pub enum Command {
Publish {
subject: String,
payload: Bytes,
respond: Option<String>,
headers: Option<HeaderMap>,
},
Subscribe {
sid: u64,
subject: String,
queue_group: Option<String>,
sender: mpsc::Sender<Message>,
},
Unsubscribe {
sid: u64,
max: Option<u64>,
},
Ping,
Flush {
result: oneshot::Sender<Result<(), io::Error>>,
},
TryFlush,
Connect(ConnectInfo),
}
#[derive(Debug)]
pub enum ClientOp {
Publish {
subject: String,
payload: Bytes,
respond: Option<String>,
headers: Option<HeaderMap>,
},
Subscribe {
sid: u64,
subject: String,
queue_group: Option<String>,
},
Unsubscribe {
sid: u64,
max: Option<u64>,
},
Ping,
Pong,
Connect(ConnectInfo),
}
#[derive(Debug)]
struct Subscription {
subject: String,
sender: mpsc::Sender<Message>,
queue_group: Option<String>,
delivered: u64,
max: Option<u64>,
}
pub(crate) struct ConnectionHandler {
connection: Connection,
connector: Connector,
subscriptions: HashMap<u64, Subscription>,
pending_pings: usize,
max_pings: usize,
info_sender: tokio::sync::watch::Sender<ServerInfo>,
}
impl ConnectionHandler {
pub(crate) fn new(
connection: Connection,
connector: Connector,
info_sender: tokio::sync::watch::Sender<ServerInfo>,
) -> ConnectionHandler {
ConnectionHandler {
connection,
connector,
subscriptions: HashMap::new(),
pending_pings: 0,
max_pings: 2,
info_sender,
}
}
pub(crate) async fn process(
&mut self,
mut receiver: mpsc::Receiver<Command>,
) -> Result<(), io::Error> {
loop {
select! {
maybe_command = receiver.recv().fuse() => {
match maybe_command {
Some(command) => if let Err(err) = self.handle_command(command).await {
println!("error handling command {}", err);
}
None => {
break;
}
}
}
maybe_op_result = self.connection.read_op().fuse() => {
match maybe_op_result {
Ok(Some(server_op)) => if let Err(err) = self.handle_server_op(server_op).await {
println!("error handling operation {}", err);
}
Ok(None) => {
if let Err(err) = self.handle_disconnect().await {
println!("error handling operation {}", err);
} else {
}
}
Err(op_err) => {
if let Err(err) = self.handle_disconnect().await {
println!("error reconnecting {}. original error={}", err, op_err);
}
},
}
}
}
}
self.connection.flush().await?;
Ok(())
}
async fn handle_server_op(&mut self, server_op: ServerOp) -> Result<(), io::Error> {
match server_op {
ServerOp::Ping => {
self.connection.write_op(ClientOp::Pong).await?;
self.connection.flush().await?;
}
ServerOp::Pong => {
debug!("received PONG");
self.pending_pings = self.pending_pings.saturating_sub(1);
}
ServerOp::Error(error) => {
self.connector
.events_tx
.try_send(Event::ServerError(error))
.ok();
}
ServerOp::Message {
sid,
subject,
reply,
payload,
headers,
status,
description,
} => {
if let Some(subscription) = self.subscriptions.get_mut(&sid) {
let message = Message {
subject,
reply,
payload,
headers,
status,
description,
};
match subscription.sender.try_send(message) {
Ok(_) => {
subscription.delivered += 1;
if let Some(max) = subscription.max {
if subscription.delivered.ge(&max) {
self.subscriptions.remove(&sid);
}
}
}
Err(mpsc::error::TrySendError::Full(_)) => {
self.connector
.events_tx
.send(Event::SlowConsumer(sid))
.await
.ok();
}
Err(mpsc::error::TrySendError::Closed(_)) => {
self.subscriptions.remove(&sid);
self.connection
.write_op(ClientOp::Unsubscribe { sid, max: None })
.await?;
self.connection.flush().await?;
}
}
}
}
ServerOp::Info(info) => {
if info.lame_duck_mode {
self.connector
.events_tx
.send(Event::LameDuckMode)
.await
.ok();
}
}
_ => {
}
}
Ok(())
}
async fn handle_command(&mut self, command: Command) -> Result<(), io::Error> {
match command {
Command::Unsubscribe { sid, max } => {
if let Some(subscription) = self.subscriptions.get_mut(&sid) {
subscription.max = max;
match subscription.max {
Some(n) => {
if subscription.delivered >= n {
self.subscriptions.remove(&sid);
}
}
None => {
self.subscriptions.remove(&sid);
}
}
if let Err(err) = self
.connection
.write_op(ClientOp::Unsubscribe { sid, max })
.await
{
println!("Send failed with {:?}", err);
}
}
}
Command::Ping => {
debug!(
"PING command. Pending pings {}, max pings {}",
self.pending_pings, self.max_pings
);
self.pending_pings += 1;
if self.pending_pings > self.max_pings {
debug!(
"pendings pings {}, max pings {}. disconnecting",
self.pending_pings, self.max_pings
);
self.handle_disconnect().await?;
}
if let Err(_err) = self.connection.write_op(ClientOp::Ping).await {
self.handle_disconnect().await?;
}
self.connection.flush().await?;
}
Command::Flush { result } => {
if let Err(_err) = self.connection.flush().await {
if let Err(err) = self.handle_disconnect().await {
result.send(Err(err)).map_err(|_| {
io::Error::new(io::ErrorKind::Other, "one shot failed to be received")
})?;
} else if let Err(err) = self.connection.flush().await {
result.send(Err(err)).map_err(|_| {
io::Error::new(io::ErrorKind::Other, "one shot failed to be received")
})?;
}
} else {
result.send(Ok(())).map_err(|_| {
io::Error::new(io::ErrorKind::Other, "one shot failed to be received")
})?;
}
}
Command::TryFlush => {
self.connection.flush().await?;
}
Command::Subscribe {
sid,
subject,
queue_group,
sender,
} => {
let subscription = Subscription {
sender,
delivered: 0,
max: None,
subject: subject.to_owned(),
queue_group: queue_group.to_owned(),
};
self.subscriptions.insert(sid, subscription);
if let Err(err) = self
.connection
.write_op(ClientOp::Subscribe {
sid,
subject,
queue_group,
})
.await
{
println!("Sending Subscribe failed with {:?}", err);
}
}
Command::Publish {
subject,
payload,
respond,
headers,
} => {
while let Err(err) = self
.connection
.write_op(ClientOp::Publish {
subject: subject.clone(),
payload: payload.clone(),
respond: respond.clone(),
headers: headers.clone(),
})
.await
{
self.handle_disconnect().await?;
println!("Sending Publish failed with {:?}", err);
}
}
Command::Connect(connect_info) => {
while let Err(_err) = self
.connection
.write_op(ClientOp::Connect(connect_info.clone()))
.await
{
self.handle_disconnect().await?;
}
}
}
Ok(())
}
async fn handle_disconnect(&mut self) -> io::Result<()> {
self.pending_pings = 0;
self.connector.events_tx.try_send(Event::Disconnect).ok();
self.connector.state_tx.send(State::Disconnected).ok();
self.handle_reconnect().await?;
Ok(())
}
async fn handle_reconnect(&mut self) -> Result<(), io::Error> {
let (info, connection) = self.connector.connect().await?;
self.connection = connection;
self.info_sender.send(info).map_err(|err| {
std::io::Error::new(
ErrorKind::Other,
format!("failed to send info update: {}", err),
)
})?;
self.subscriptions
.retain(|_, subscription| !subscription.sender.is_closed());
for (sid, subscription) in &self.subscriptions {
self.connection
.write_op(ClientOp::Subscribe {
sid: *sid,
subject: subscription.subject.to_owned(),
queue_group: subscription.queue_group.to_owned(),
})
.await
.unwrap();
}
self.connector.events_tx.try_send(Event::Reconnect).ok();
Ok(())
}
}
pub async fn connect_with_options<A: ToServerAddrs>(
addrs: A,
options: ConnectOptions,
) -> Result<Client, io::Error> {
let ping_interval = options.ping_interval;
let flush_interval = options.flush_interval;
let (events_tx, mut events_rx) = mpsc::channel(128);
let (state_tx, state_rx) = tokio::sync::watch::channel(State::Pending);
let mut connector = Connector::new(
addrs,
ConnectorOptions {
tls_required: options.tls_required,
certificates: options.certificates,
client_key: options.client_key,
client_cert: options.client_cert,
tls_client_config: options.tls_client_config,
auth: options.auth,
no_echo: options.no_echo,
connection_timeout: options.connection_timeout,
name: options.name,
},
events_tx,
state_tx,
)?;
let (info, connection) = connector.try_connect().await?;
let (info_sender, info_watcher) = tokio::sync::watch::channel(info);
let mut connection_handler = ConnectionHandler::new(connection, connector, info_sender);
let (sender, receiver) = mpsc::channel(options.sender_capacity);
let client = Client::new(
info_watcher,
state_rx,
sender.clone(),
options.subscription_capacity,
options.inbox_prefix,
options.request_timeout,
);
tokio::spawn({
let sender = sender.clone();
async move {
loop {
match sender.send(Command::Ping).await {
Ok(()) => {}
Err(_) => return,
}
tokio::time::sleep(ping_interval).await;
}
}
});
tokio::spawn(async move {
loop {
tokio::time::sleep(flush_interval).await;
match sender.send(Command::TryFlush).await {
Ok(()) => {}
Err(_) => return,
}
}
});
task::spawn(async move {
while let Some(event) = events_rx.recv().await {
options.event_callback.call(event).await
}
});
task::spawn(async move { connection_handler.process(receiver).await });
Ok(client)
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Event {
Reconnect,
Disconnect,
LameDuckMode,
SlowConsumer(u64),
ServerError(ServerError),
ClientError(ClientError),
}
impl fmt::Display for Event {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Event::Reconnect => write!(f, "reconnected"),
Event::Disconnect => write!(f, "disconnected"),
Event::LameDuckMode => write!(f, "lame duck mode detected"),
Event::SlowConsumer(sid) => write!(f, "slow consumers for subscription {}", sid),
Event::ServerError(err) => write!(f, "server error: {}", err),
Event::ClientError(err) => write!(f, "client error: {}", err),
}
}
}
pub async fn connect<A: ToServerAddrs>(addrs: A) -> Result<Client, io::Error> {
connect_with_options(addrs, ConnectOptions::default()).await
}
pub struct Subscriber {
sid: u64,
receiver: mpsc::Receiver<Message>,
sender: mpsc::Sender<Command>,
}
impl Subscriber {
fn new(
sid: u64,
sender: mpsc::Sender<Command>,
receiver: mpsc::Receiver<Message>,
) -> Subscriber {
Subscriber {
sid,
sender,
receiver,
}
}
pub async fn unsubscribe(&mut self) -> io::Result<()> {
self.sender
.send(Command::Unsubscribe {
sid: self.sid,
max: None,
})
.await
.map_err(|err| io::Error::new(ErrorKind::Other, err))?;
self.receiver.close();
Ok(())
}
pub async fn unsubscribe_after(&mut self, unsub_after: u64) -> io::Result<()> {
self.sender
.send(Command::Unsubscribe {
sid: self.sid,
max: Some(unsub_after),
})
.await
.map_err(|err| io::Error::new(ErrorKind::Other, err))?;
Ok(())
}
}
impl Drop for Subscriber {
fn drop(&mut self) {
self.receiver.close();
tokio::spawn({
let sender = self.sender.clone();
let sid = self.sid;
async move {
sender
.send(Command::Unsubscribe { sid, max: None })
.await
.ok();
}
});
}
}
impl Stream for Subscriber {
type Item = Message;
fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
self.receiver.poll_recv(cx)
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum CallbackError {
Client(ClientError),
Server(ServerError),
}
impl std::fmt::Display for CallbackError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Client(error) => write!(f, "{}", error),
Self::Server(error) => write!(f, "{}", error),
}
}
}
impl From<ServerError> for CallbackError {
fn from(server_error: ServerError) -> Self {
CallbackError::Server(server_error)
}
}
impl From<ClientError> for CallbackError {
fn from(client_error: ClientError) -> Self {
CallbackError::Client(client_error)
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum ServerError {
AuthorizationViolation,
SlowConsumer(u64),
Other(String),
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum ClientError {
Other(String),
}
impl std::fmt::Display for ClientError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Other(error) => write!(f, "nats: {}", error),
}
}
}
impl ServerError {
fn new(error: String) -> ServerError {
match error.to_lowercase().as_str() {
"authorization violation" => ServerError::AuthorizationViolation,
other => ServerError::Other(other.to_string()),
}
}
}
impl std::fmt::Display for ServerError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::AuthorizationViolation => write!(f, "nats: authorization violation"),
Self::SlowConsumer(sid) => write!(f, "nats: subscription {} is a slow consumer", sid),
Self::Other(error) => write!(f, "nats: {}", error),
}
}
}
#[derive(Clone, Debug, Serialize)]
pub struct ConnectInfo {
pub verbose: bool,
pub pedantic: bool,
#[serde(rename = "jwt")]
pub user_jwt: Option<String>,
pub nkey: Option<String>,
#[serde(rename = "sig")]
pub signature: Option<String>,
pub name: Option<String>,
pub echo: bool,
pub lang: String,
pub version: String,
pub protocol: Protocol,
pub tls_required: bool,
pub user: Option<String>,
pub pass: Option<String>,
pub auth_token: Option<String>,
pub headers: bool,
pub no_responders: bool,
}
#[derive(Serialize_repr, Deserialize_repr, PartialEq, Eq, Debug, Clone, Copy)]
#[repr(u8)]
pub enum Protocol {
Original = 0,
Dynamic = 1,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct ServerAddr(Url);
impl FromStr for ServerAddr {
type Err = io::Error;
fn from_str(input: &str) -> Result<Self, Self::Err> {
let url: Url = if input.contains("://") {
input.parse()
} else {
format!("nats://{}", input).parse()
}
.map_err(|e| {
io::Error::new(
ErrorKind::InvalidInput,
format!("NATS server URL is invalid: {}", e),
)
})?;
Self::from_url(url)
}
}
impl ServerAddr {
pub fn from_url(url: Url) -> io::Result<Self> {
if url.scheme() != "nats" && url.scheme() != "tls" {
return Err(std::io::Error::new(
ErrorKind::InvalidInput,
format!("invalid scheme for NATS server URL: {}", url.scheme()),
));
}
Ok(Self(url))
}
pub fn into_inner(self) -> Url {
self.0
}
pub fn tls_required(&self) -> bool {
self.0.scheme() == "tls"
}
pub fn has_user_pass(&self) -> bool {
self.0.username() != ""
}
pub fn host(&self) -> &str {
match self.0.host() {
Some(Host::Domain(_)) | Some(Host::Ipv4 { .. }) => self.0.host_str().unwrap(),
Some(Host::Ipv6 { .. }) => {
let host = self.0.host_str().unwrap();
&host[1..host.len() - 1]
}
None => "",
}
}
pub fn port(&self) -> u16 {
self.0.port().unwrap_or(4222)
}
pub fn username(&self) -> Option<String> {
let user = self.0.username();
if user.is_empty() {
None
} else {
Some(user.to_string())
}
}
pub fn password(&self) -> Option<String> {
self.0.password().map(|pwd| pwd.to_string())
}
pub fn socket_addrs(&self) -> io::Result<impl Iterator<Item = SocketAddr>> {
(self.host(), self.port()).to_socket_addrs()
}
}
pub trait ToServerAddrs {
type Iter: Iterator<Item = ServerAddr>;
fn to_server_addrs(&self) -> io::Result<Self::Iter>;
}
impl ToServerAddrs for ServerAddr {
type Iter = option::IntoIter<ServerAddr>;
fn to_server_addrs(&self) -> io::Result<Self::Iter> {
Ok(Some(self.clone()).into_iter())
}
}
impl ToServerAddrs for str {
type Iter = option::IntoIter<ServerAddr>;
fn to_server_addrs(&self) -> io::Result<Self::Iter> {
self.parse::<ServerAddr>()
.map(|addr| Some(addr).into_iter())
}
}
impl ToServerAddrs for String {
type Iter = option::IntoIter<ServerAddr>;
fn to_server_addrs(&self) -> io::Result<Self::Iter> {
(**self).to_server_addrs()
}
}
impl<'a> ToServerAddrs for &'a [ServerAddr] {
type Iter = iter::Cloned<slice::Iter<'a, ServerAddr>>;
fn to_server_addrs(&self) -> io::Result<Self::Iter> {
Ok(self.iter().cloned())
}
}
impl<T: ToServerAddrs + ?Sized> ToServerAddrs for &T {
type Iter = T::Iter;
fn to_server_addrs(&self) -> io::Result<Self::Iter> {
(**self).to_server_addrs()
}
}
pub(crate) enum Authorization {
None,
Token(String),
UserAndPassword(String, String),
NKey(String),
Jwt(
String,
CallbackArg1<String, std::result::Result<String, AuthError>>,
),
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn server_address_ipv6() {
let address = ServerAddr::from_str("nats://[::]").unwrap();
assert_eq!(address.host(), "::")
}
#[test]
fn serverr_address_ipv4() {
let address = ServerAddr::from_str("nats://127.0.0.1").unwrap();
assert_eq!(address.host(), "127.0.0.1")
}
#[test]
fn serverr_address_domain() {
let address = ServerAddr::from_str("nats://example.com").unwrap();
assert_eq!(address.host(), "example.com")
}
}