tcp_stream_echo/
lib.rs

1// MIT License
2
3// Copyright (c) 2022 Dawid Kubiszewski (dawidkubiszewski@gmail.com)
4
5// Permission is hereby granted, free of charge, to any person obtaining a copy
6// of this software and associated documentation files (the "Software"), to deal
7// in the Software without restriction, including without limitation the rights
8// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9// copies of the Software, and to permit persons to whom the Software is
10// furnished to do so, subject to the following conditions:
11
12// The above copyright notice and this permission notice shall be included in all
13// copies or substantial portions of the Software.
14
15// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21// SOFTWARE.
22
23
24pub mod dkubiszewski {
25    use std::{
26        io::{Read, Write},
27        net::TcpListener,
28        thread,
29    };
30
31    pub struct TcpEcho {
32        chunk_size: usize,
33        port: usize,
34    }
35
36    impl TcpEcho {
37        pub fn new(port: usize, chunk_size: usize) -> Self {
38            Self {
39                port: port,
40                chunk_size: chunk_size,
41            }
42        }
43
44        pub fn serve(&self) {
45            self.serve_with_peek(|_: &[u8]| {});
46        }
47
48        pub fn serve_with_peek(&self, fn_peek: fn(&[u8])) {
49            println!("Starting...");
50            let listener = TcpListener::bind(format!("127.0.0.1:{}", self.port)).unwrap();
51            println!("Started.");
52            let mut connection_counter = 0;
53            for stream in listener.incoming() {
54                connection_counter += 1;
55                // TODO: use logger
56                println!("Open new connection: {}", connection_counter);
57                let mut stream = stream.unwrap();
58                let mut buffer = vec![0u8; self.chunk_size];
59                thread::spawn(move || loop {
60                    let mut write_size = 0;
61                    let read_size = stream.read(&mut buffer).unwrap();
62                    if read_size == 0 {
63                        println!(
64                            "No more data to read closing connection: {}",
65                            connection_counter
66                        );
67                        break;
68                    }
69                    fn_peek(&buffer[..read_size]);
70
71                    while write_size < read_size {
72                        write_size += stream.write(&buffer[..read_size - write_size]).unwrap();
73                    }
74                });
75            }
76        }
77    }
78}
79
80#[cfg(test)]
81mod tests {
82    use std::{
83        io::{Read, Write},
84        net::TcpStream,
85        thread,
86    };
87
88    use crate::dkubiszewski::TcpEcho;
89
90    #[test]
91    fn send_receive() {
92        let sut = TcpEcho::new(5555, 1024);
93
94        thread::spawn(move || {
95            sut.serve();
96        });
97
98        let mut test_stream = TcpStream::connect("localhost:5555").unwrap();
99
100        let test_data: [u8; 4] = [0x10, 0x9, 0x3, 0x1];
101        assert_eq!(test_data.len(), test_stream.write(&test_data).unwrap());
102        let mut result_data = [0; 4];
103        assert_eq!(test_data.len(), test_stream.read(&mut result_data).unwrap());
104
105        assert_eq!(result_data, test_data);
106    }
107
108    #[test]
109    fn send_receive_multiple_chunks() {
110        let sut = TcpEcho::new(5556, 1024);
111
112        thread::spawn(move || {
113            sut.serve();
114        });
115
116        let mut test_stream = TcpStream::connect("localhost:5556").unwrap();
117
118        let test_data2 = [
119            [0x10u8, 0x9, 0x3, 0x1],
120            [0x9u8, 0x8, 0x2, 0x0],
121            [0x11u8, 0xa, 0x4, 0x2],
122        ];
123        for test_data in test_data2 {
124            assert_eq!(test_data.len(), test_stream.write(&test_data).unwrap());
125            let mut result_data = vec![0u8; test_data.len()];
126            assert_eq!(test_data.len(), test_stream.read(&mut result_data).unwrap());
127
128            assert_eq!(result_data, test_data);
129        }
130    }
131
132    #[test]
133    fn send_receive_multiple_connections() {
134        let sut = TcpEcho::new(5557, 1024);
135
136        thread::spawn(move || {
137            sut.serve();
138        });
139
140        let test_data2 = [
141            [0x10u8, 0x9, 0x3, 0x1],
142            [0x9u8, 0x8, 0x2, 0x0],
143            [0x11u8, 0xa, 0x4, 0x2],
144        ];
145
146        for test_data in test_data2 {
147            let mut test_stream = TcpStream::connect("localhost:5557").unwrap();
148
149            assert_eq!(test_data.len(), test_stream.write(&test_data).unwrap());
150            let mut result_data = [0; 4];
151            assert_eq!(test_data.len(), test_stream.read(&mut result_data).unwrap());
152
153            assert_eq!(result_data, test_data);
154        }
155    }
156}