Skip to main content

btp/
lib.rs

1//! src/lib.rs
2
3//! # Welcome
4//! Btp is stands for 'Blog Transfer Protocol' which developed while developing this project.
5//! The protocol has a basic structure, a 'header' which consists of 10 bytes, and the rest of the message, which can be reach 4GiB.
6//!
7//! This protocol handles a basic transmission task between server and client.
8//! To add this project to repository
9//! ```bash
10//! cargo add btp
11//! ```
12//! or with TLS,
13//! ```bash
14//! cargo add btp --features tls
15//! ```
16//!
17//! After you added the library to project, It will be accessible under the crate name btp.
18//!
19//! # Examples
20//! Let me teach the structure with examples here;
21//!
22//! ---
23//! ## NO TLS
24//!
25//! Example below is not using any safe transmission with TLS encryption,
26//! but bare and crystal clear transmission without any security feature,
27//! this type of communication may cause some
28//!
29//! ```
30//! # {
31//! type Error = Box<dyn std::error::Error + Send + Sync>;
32//! use btp::server::BtpListener;
33//! use btp::message::BtpMessage;
34//! use btp::socket::{BtpConfig, BtpSocket};
35//! use tokio::net::TcpListener;
36//! // Add the items from libraries.
37//!
38//! #[tokio::main]
39//! async fn main() -> Result<(), Error> {
40//!     // Create a listener from a tcp listener for port 0.
41//!     let listener = TcpListener::bind("127.0.0.1:0").await?;
42//!     let server_addr = listener.local_addr()?;
43//!     let btp_conf = BtpConfig::from_addr(server_addr);
44//!
45//!     // Spawn another async thread to handle incoming btp connection.
46//!     let server_task = tokio::spawn(async move {
47//!         // Assign that listener to a BtpListener.
48//!         let btp_listener = BtpListener::from(listener, btp_conf).await;
49//!         // Wait for someone to connect it.
50//!         let mut server_socket = btp_listener.accept().await?;
51//!
52//!         // When connection established, wait for the incoming message.
53//!         // And save it safely.
54//!         let (_file_opt, msg_opt) = server_socket.read().await?;
55//!         let msg = msg_opt.ok_or("No message received")?;
56//!         // Message should be same with the one you sent.
57//!         assert_eq!(msg.body, "Hello, BTP!");
58//!
59//!         // Response back with the confirmation message.
60//!         let response = BtpMessage::from_str("ACK");
61//!         server_socket.write_message(response).await?;
62//!         Ok::<_, Error>(())
63//!     });
64//!
65//!     // Create a config for client and connect to btp port you've listened.
66//!     let client_conf = BtpConfig::from_addr(server_addr);
67//!     let mut client_socket = BtpSocket::connect(client_conf).await?;
68//!
69//!     // Prepare the request and send it.
70//!     let request = BtpMessage::from_str("Hello, BTP!");
71//!     client_socket.write_message(request).await?;
72//!
73//!     // Wait for the response and handle it safely, then verify it.
74//!     let (_file_opt, msg_opt) = client_socket.read().await?;
75//!     let response = msg_opt.ok_or("No response received")?;
76//!     assert_eq!(response.body, "ACK");
77//!
78//!     // Wait for server to finish jobs.
79//!     server_task.await??;
80//!     Ok(())
81//! }
82//!
83//! # }
84//! ```
85//! ---
86//! # With TLS
87//!
88//! ```no_run
89//! type Error = Box<dyn std::error::Error + Send + Sync>;
90//! use btp::message::BtpMessage;
91//! use btp::server::BtpListenerTls;
92//! use btp::socket::{BtpConfig, BtpSocket};
93//! use rustls::ServerConfig;
94//! use rustls::pki_types::{PrivateKeyDer, PrivatePkcs8KeyDer};
95//! use std::sync::Arc;
96//! use tokio::net::TcpListener;
97//! use tokio_rustls::TlsConnector;
98//!
99//! fn make_test_tls_config() -> (Arc<ServerConfig>, Vec<u8>) { ... }
100//!
101//! #[tokio::main]
102//! async fn main() -> Result<(), Error> {
103//!     // Created a listener, then a TLS configurator and cert_der for conf.
104//!     let listener = TcpListener::bind("127.0.0.1:0").await?;
105//!     let server_addr = listener.local_addr()?;
106//!     let btp_conf = BtpConfig::from_addr(server_addr);
107//!     let (tls_conf, cert_der) = make_test_tls_config();
108//!
109//!     // Create a root certification store for client with cert_der you've created.
110//!     let mut root_store = rustls::RootCertStore::empty();
111//!     root_store.add(cert_der.into()).unwrap();
112//!
113//!     // Create the TLS config for client with that root store.
114//!     let client_config = rustls::ClientConfig::builder()
115//!         .with_root_certificates(root_store)
116//!         .with_no_client_auth();
117//!
118//!     let server_task = tokio::spawn(async move {
119//!         // Create a thread to assign listener to BtpListenerTls.
120//!         let btp_listener = BtpListenerTls::from(listener, btp_conf, tls_conf).await;
121//!
122//!         // Wait for the client to connect.
123//!         let mut server_socket = btp_listener.accept().await?;
124//!
125//!         // Wait for the message after client connects.
126//!         let (_file_opt, msg_opt) = server_socket.read().await?;
127//!
128//!         // Handle it safely and validate it.
129//!         let msg = msg_opt.ok_or("No message received")?;
130//!         assert_eq!(msg.body, "Hello, TLS BTP!");
131//!
132//!         // Response to client back.
133//!         let response = BtpMessage::from_str("TLS ACK");
134//!         server_socket.write_message(response).await?;
135//!
136//!         Ok::<_, Error>(())
137//!     });
138//!
139//!     // Create the tls_connector for client, then connect to server.
140//!     let connector = TlsConnector::from(Arc::new(client_config));
141//!     let client_conf = BtpConfig::from_addr(server_addr);
142//!
143//!     // Wait for connecting to server.
144//!     let mut client_socket = BtpSocket::connect_tls(connector, client_conf).await?;
145//!
146//!     // Send message.
147//!     let request = BtpMessage::from_str("Hello, TLS BTP!");
148//!     client_socket.write_message(request).await?;
149//!
150//!     // Take the response back, handle it correctly and verify.
151//!     let (_file_opt, msg_opt) = client_socket.read().await?;
152//!     let response = msg_opt.ok_or("No response received")?;
153//!     assert_eq!(response.body, "TLS ACK");
154//!
155//!     // Wait for server to complete its jobs.
156//!     server_task.await??;
157//!
158//!     Ok(())
159//! }
160//!
161//! ```
162//!
163
164/// For client connections.
165#[cfg(feature = "client")]
166pub mod client;
167
168/// For parsing messages and files and it headers safely, without any issue.
169pub mod message;
170
171/// For server connections, brings [`BtpListener`].
172///
173/// [`BtpListener`]: server::BtpListener
174#[cfg(feature = "server")]
175pub mod server;
176
177/// For transmitting messages and files safely.
178pub mod socket;
179
180/// Actual current version of the protocol. Used in [`BtpConfig`]
181///
182/// [`BtpConfig`]: socket::BtpConfig
183pub const VERSION: [u8; 3] = [0, 1, 2];
184
185#[cfg(test)]
186#[cfg(feature = "server")]
187#[cfg(feature = "client")]
188mod tests {
189    use super::*;
190    use crate::message::{BtpFile, BtpMessage, StatusCode};
191    use crate::socket::{BtpConfig, BtpSocket};
192    use std::io::Write;
193    use tempfile::tempdir;
194    use tokio::net::TcpListener;
195
196    fn make_test_file(content: &str, name: &str) -> BtpFile {
197        let mut temp_file = tempfile::NamedTempFile::new().unwrap();
198        write!(temp_file, "{}", content).unwrap();
199        let file = temp_file.reopen().unwrap();
200        BtpFile::from([StatusCode::File as u8], VERSION, file, name.to_string()).unwrap()
201    }
202
203    mod tests {
204        use std::net::SocketAddr;
205
206        use tokio::net::TcpStream;
207
208        use super::*;
209        use crate::server::BtpListener;
210        type Error = Box<dyn std::error::Error + Send + Sync>;
211        #[tokio::test]
212        async fn test_message_exchange_no_tls() -> Result<(), Error> {
213            let listener = TcpListener::bind("127.0.0.1:0").await?;
214            let server_addr = listener.local_addr()?;
215            let btp_conf = BtpConfig::from_addr(server_addr);
216            let server_task = tokio::spawn(async move {
217                let btp_listener = BtpListener::from(listener, btp_conf).await;
218                let mut server_socket = btp_listener.accept().await?;
219                let (_file_opt, msg_opt) = server_socket.read().await?;
220                let msg = msg_opt.ok_or("No message received")?;
221                assert_eq!(msg.body, "Hello, BTP!");
222                let response = BtpMessage::from_str("ACK");
223                server_socket.write_message(response).await?;
224
225                Ok::<_, Error>(())
226            });
227            let client_conf = BtpConfig::from_addr(server_addr);
228            let mut client_socket = BtpSocket::connect(client_conf).await?;
229
230            let request = BtpMessage::from_str("Hello, BTP!");
231            client_socket.write_message(request).await?;
232
233            let (_file_opt, msg_opt) = client_socket.read().await?;
234            let response = msg_opt.ok_or("No response received")?;
235            assert_eq!(response.body, "ACK");
236
237            server_task.await??;
238
239            Ok(())
240        }
241
242        #[tokio::test]
243        async fn test_file_transfer_no_tls() -> Result<(), Error> {
244            let tmp_dir = tempdir()?;
245            let file_path = tmp_dir.path().join("test.txt");
246            let original_content = "Hello, this is a test file.";
247            tokio::fs::write(&file_path, original_content).await?;
248
249            let listener = TcpListener::bind("127.0.0.1:0").await?;
250            let server_addr = listener.local_addr()?;
251            let btp_conf = BtpConfig::from_addr(server_addr);
252
253            let server_task = tokio::spawn(async move {
254                let btp_listener = BtpListener::from(listener, btp_conf).await;
255                let mut server_socket = btp_listener.accept().await?;
256
257                let (file_opt, _msg_opt) = server_socket.read().await?;
258                let file = file_opt.ok_or("No file received")?;
259                assert_eq!(file.file_content, original_content);
260
261                let ack = BtpMessage::from_str("FILE_RECEIVED");
262                server_socket.write_message(ack).await?;
263
264                Ok::<_, Error>(())
265            });
266
267            let client_conf = BtpConfig::from_addr(server_addr);
268            let mut client_socket = BtpSocket::connect(client_conf).await?;
269
270            let file = make_test_file(original_content, "test.txt");
271            client_socket.write_file(file).await?;
272
273            let (_file_opt, msg_opt) = client_socket.read().await?;
274            let ack = msg_opt.ok_or("No acknowledgment received")?;
275            assert_eq!(ack.body, "FILE_RECEIVED");
276
277            server_task.await??;
278
279            Ok(())
280        }
281
282        #[tokio::test]
283        async fn test_full_session_no_tls() -> Result<(), Error> {
284            let listener = TcpListener::bind("127.0.0.1:0").await?;
285            let server_addr = listener.local_addr()?;
286            let btp_conf = BtpConfig::from_addr(server_addr);
287
288            let server_task = tokio::spawn(async move {
289                let btp_listener = BtpListener::from(listener, btp_conf).await;
290                let server_socket = btp_listener.accept().await?;
291
292                server_socket.attach_handler(
293                    |msg: BtpMessage, _: BtpSocket<TcpStream, SocketAddr>| {
294                        println!("Server received: {}", msg.body);
295                    },
296                    |_file: BtpFile, _: BtpSocket<TcpStream, SocketAddr>| {
297                        println!("Server received a file");
298                    },
299                )?;
300
301                tokio::time::sleep(tokio::time::Duration::from_millis(100)).await;
302                Ok::<_, Error>(())
303            });
304
305            let client_conf = BtpConfig::from_addr(server_addr);
306            let mut client_socket = BtpSocket::connect(client_conf).await?;
307
308            let msg = BtpMessage::from_str("Hello, echo!");
309            client_socket.write_message(msg).await?;
310
311            tokio::time::sleep(tokio::time::Duration::from_millis(50)).await;
312
313            server_task.await??;
314
315            Ok(())
316        }
317    }
318
319    #[cfg(feature = "tls")]
320    mod tls_tests {
321
322        type Error = Box<dyn std::error::Error + Send + Sync>;
323        use crate::server::BtpListenerTls;
324
325        use super::*;
326        use rustls::ServerConfig;
327        use rustls::pki_types::{PrivateKeyDer, PrivatePkcs8KeyDer};
328        use std::sync::Arc;
329        use tokio_rustls::TlsConnector;
330
331        fn make_test_tls_config() -> (Arc<ServerConfig>, Vec<u8>) {
332            let certified = rcgen::generate_simple_self_signed(vec!["127.0.0.1".into()]).unwrap();
333
334            let cert_der = certified.cert.der().to_vec();
335
336            let server_cert = rustls::pki_types::CertificateDer::from(certified.cert.der().clone());
337
338            let key_der = PrivateKeyDer::Pkcs8(PrivatePkcs8KeyDer::from(
339                certified.signing_key.serialize_der(),
340            ));
341
342            let config = ServerConfig::builder()
343                .with_no_client_auth()
344                .with_single_cert(vec![server_cert], key_der)
345                .unwrap();
346
347            (Arc::new(config), cert_der)
348        }
349
350        #[tokio::test]
351        async fn test_message_exchange_with_tls() -> Result<(), Error> {
352            let listener = TcpListener::bind("127.0.0.1:0").await?;
353            let server_addr = listener.local_addr()?;
354            let btp_conf = BtpConfig::from_addr(server_addr);
355            let (tls_conf, cert_der) = make_test_tls_config();
356
357            let mut root_store = rustls::RootCertStore::empty();
358            root_store.add(cert_der.into()).unwrap();
359
360            let client_config = rustls::ClientConfig::builder()
361                .with_root_certificates(root_store)
362                .with_no_client_auth();
363
364            let server_task = tokio::spawn(async move {
365                let btp_listener = BtpListenerTls::from(listener, btp_conf, tls_conf).await;
366                let mut server_socket = btp_listener.accept().await?;
367
368                let (_file_opt, msg_opt) = server_socket.read().await?;
369                let msg = msg_opt.ok_or("No message received")?;
370                assert_eq!(msg.body, "Hello, TLS BTP!");
371
372                let response = BtpMessage::from_str("TLS ACK");
373                server_socket.write_message(response).await?;
374
375                Ok::<_, Error>(())
376            });
377
378            let connector = TlsConnector::from(Arc::new(client_config));
379            let client_conf = BtpConfig::from_addr(server_addr);
380            let mut client_socket = BtpSocket::connect_tls(connector, client_conf).await?;
381
382            let request = BtpMessage::from_str("Hello, TLS BTP!");
383            client_socket.write_message(request).await?;
384
385            let (_file_opt, msg_opt) = client_socket.read().await?;
386            let response = msg_opt.ok_or("No response received")?;
387            assert_eq!(response.body, "TLS ACK");
388
389            server_task.await??;
390
391            Ok(())
392        }
393
394        #[tokio::test]
395        async fn test_file_transfer_with_tls() -> Result<(), Error> {
396            let tmp_dir = tempdir()?;
397            let file_path = tmp_dir.path().join("tls_test.txt");
398            let original_content = "TLS file transfer test content.";
399            tokio::fs::write(&file_path, original_content).await?;
400
401            let listener = TcpListener::bind("127.0.0.1:0").await?;
402            let server_addr = listener.local_addr()?;
403            let btp_conf = BtpConfig::from_addr(server_addr);
404            let (tls_conf, cert_der) = make_test_tls_config();
405
406            let mut root_store = rustls::RootCertStore::empty();
407            root_store.add(cert_der.into()).unwrap();
408
409            let client_config = rustls::ClientConfig::builder()
410                .with_root_certificates(root_store)
411                .with_no_client_auth();
412
413            let server_task = tokio::spawn(async move {
414                let btp_listener = BtpListenerTls::from(listener, btp_conf, tls_conf).await;
415                let mut server_socket = btp_listener.accept().await?;
416
417                let (file_opt, _msg_opt) = server_socket.read().await?;
418                let file = file_opt.ok_or("No file received")?;
419                assert_eq!(file.file_content, original_content);
420
421                let ack = BtpMessage::from_str("TLS FILE RECEIVED");
422                server_socket.write_message(ack).await?;
423
424                Ok::<_, Error>(())
425            });
426
427            let connector = TlsConnector::from(Arc::new(client_config));
428            let client_conf = BtpConfig::from_addr(server_addr);
429            let mut client_socket = BtpSocket::connect_tls(connector, client_conf).await?;
430
431            let file = make_test_file(original_content, "tls_test.txt");
432            client_socket.write_file(file).await?;
433
434            let (_file_opt, msg_opt) = client_socket.read().await?;
435            let ack = msg_opt.ok_or("No acknowledgment received")?;
436            assert_eq!(ack.body, "TLS FILE RECEIVED");
437
438            server_task.await??;
439
440            Ok(())
441        }
442    }
443}