@@ -4,11 +4,13 @@ use super::{
44 packet:: { RconPacket , RconPacketType } ,
55 MAX_LEN_CLIENTBOUND ,
66} ;
7- use crate :: errors:: RconProtocolError ;
7+ use crate :: errors:: { timeout_err , RconProtocolError } ;
88use bytes:: { BufMut , BytesMut } ;
9+ use std:: time:: Duration ;
910use tokio:: {
1011 io:: { self , AsyncReadExt , AsyncWriteExt , Error } ,
1112 net:: TcpStream ,
13+ time:: timeout,
1214} ;
1315
1416/// Struct that stores the connection and other state of the RCON protocol with the server.
@@ -34,12 +36,16 @@ use tokio::{
3436#[ derive( Debug ) ]
3537pub struct RconClient {
3638 socket : TcpStream ,
39+ timeout : Option < Duration > ,
3740}
3841
3942impl RconClient {
4043 /// Construct an [`RconClient`] that connects to the given host and port.
4144 /// Note: to authenticate use the `authenticate` method, this method does not take a password.
4245 ///
46+ /// Clients constructed this way will wait arbitrarily long (maybe forever!) to recieve
47+ /// a response from the server. To set a timeout, see [`with_timeout`] or [`set_timeout`].
48+ ///
4349 /// # Arguments
4450 /// * `host` - A string slice that holds the hostname of the server to connect to.
4551 /// * `port` - The port to connect to.
@@ -49,7 +55,40 @@ impl RconClient {
4955 pub async fn new ( host : & str , port : u16 ) -> io:: Result < Self > {
5056 let connection = TcpStream :: connect ( format ! ( "{host}:{port}" ) ) . await ?;
5157
52- Ok ( Self { socket : connection } )
58+ Ok ( Self {
59+ socket : connection,
60+ timeout : None ,
61+ } )
62+ }
63+
64+ /// Construct an [`RconClient`] that connects to the given host and port, and a connection
65+ /// timeout.
66+ /// Note: to authenticate use the `authenticate` method, this method does not take a password.
67+ ///
68+ /// Note that timeouts are not precise, and may vary on the order of milliseconds, because
69+ /// of the way the async event loop works.
70+ ///
71+ /// # Arguments
72+ /// * `host` - A string slice that holds the hostname of the server to connect to.
73+ /// * `port` - The port to connect to.
74+ /// * `timeout` - A duration to wait for each response to arrive in.
75+ ///
76+ /// # Errors
77+ /// Returns `Err` if there was a network error.
78+ pub async fn with_timeout ( host : & str , port : u16 , timeout : Duration ) -> io:: Result < Self > {
79+ let mut client = Self :: new ( host, port) . await ?;
80+ client. set_timeout ( Some ( timeout) ) ;
81+
82+ Ok ( client)
83+ }
84+
85+ /// Change the timeout for future requests.
86+ ///
87+ /// # Arguments
88+ /// * `timeout` - an option specifying the duration to wait for a response.
89+ /// if none, the client may wait forever.
90+ pub fn set_timeout ( & mut self , timeout : Option < Duration > ) {
91+ self . timeout = timeout;
5392 }
5493
5594 /// Disconnect from the server and close the RCON connection.
@@ -70,7 +109,36 @@ impl RconClient {
70109 /// # Errors
71110 /// Returns the raw `tokio::io::Error` if there was a network error.
72111 /// Returns an apprpriate [`RconProtocolError`] if the authentication failed for other reasons.
112+ /// Also returns an error if a timeout is set, and the response is not recieved in that timeframe.
73113 pub async fn authenticate ( & mut self , password : & str ) -> io:: Result < ( ) > {
114+ let to = self . timeout ;
115+ let fut = self . authenticate_raw ( password) ;
116+
117+ match to {
118+ None => fut. await ,
119+ Some ( d) => timeout ( d, fut) . await . unwrap_or ( timeout_err ( ) ) ,
120+ }
121+ }
122+
123+ /// Run the given command on the server and return the result.
124+ ///
125+ /// # Arguments
126+ /// * `command` - A string slice that holds the command to run. Must be ASCII and under 1446 bytes in length.
127+ ///
128+ /// # Errors
129+ /// Returns an error if there was a network issue or an [`RconProtocolError`] for other failures.
130+ /// Also returns an error if a timeout was set and a response was not recieved in that timeframe.
131+ pub async fn run_command ( & mut self , command : & str ) -> io:: Result < String > {
132+ let to = self . timeout ;
133+ let fut = self . run_command_raw ( command) ;
134+
135+ match to {
136+ None => fut. await ,
137+ Some ( d) => timeout ( d, fut) . await . unwrap_or ( timeout_err ( ) ) ,
138+ }
139+ }
140+
141+ async fn authenticate_raw ( & mut self , password : & str ) -> io:: Result < ( ) > {
74142 let packet =
75143 RconPacket :: new ( 1 , RconPacketType :: Login , password. to_string ( ) ) . map_err ( Error :: from) ?;
76144
@@ -91,14 +159,7 @@ impl RconClient {
91159 Ok ( ( ) )
92160 }
93161
94- /// Run the given command on the server and return the result.
95- ///
96- /// # Arguments
97- /// * `command` - A string slice that holds the command to run. Must be ASCII and under 1446 bytes in length.
98- ///
99- /// # Errors
100- /// Returns an error if there was a network issue or an [`RconProtocolError`] for other failures.
101- pub async fn run_command ( & mut self , command : & str ) -> io:: Result < String > {
162+ async fn run_command_raw ( & mut self , command : & str ) -> io:: Result < String > {
102163 let packet = RconPacket :: new ( 1 , RconPacketType :: RunCommand , command. to_string ( ) )
103164 . map_err ( Error :: from) ?;
104165
0 commit comments