Skip to content

Commit fbc35a6

Browse files
committed
Attribute handler system
1 parent cfb6a23 commit fbc35a6

File tree

17 files changed

+2164
-150
lines changed

17 files changed

+2164
-150
lines changed

Cargo.lock

Lines changed: 32 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,6 @@ tokio = { version = "1", features = ["full"] }
2525
uuid = { version = "1", features = ["v4"] }
2626
scopeguard = "1.2.0"
2727

28-
2928
tcrypt = { git = "https://github.com/thatonetoast/tcrypt.git", tag = "0.1.1-1" }
30-
tnet-macros = { path = "./tnet-macros" }
29+
tnet-macros = { path = "./tnet-macros" }
30+

README.md

Lines changed: 140 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ A robust async TCP networking library for Rust that provides:
77
- Keep-alive mechanisms
88
- Broadcast capabilities
99
- Automatic reconnection
10+
- Relay/proxy functionality
1011

1112

1213
## Features
@@ -18,6 +19,9 @@ A robust async TCP networking library for Rust that provides:
1819
- 📢 **Broadcasting** - Send messages to multiple clients
1920
- 🔌 **Reconnection** - Resilient connections with automatic reconnection and exponential backoff
2021
- 🚀 **Async/Await** - Built on tokio for high performance
22+
- 🌐 **Relay/Proxy** - Network traffic relay with the phantom client/server system
23+
- 🎯 **Attribute Macros** - Easy handler registration with the `#[tlisten_for("PACKET_TYPE")]` macro
24+
- 🏷️ **Derive Macros** - Generate packet header types with `#[derive(PacketHeader)]`
2125

2226

2327
## Example Usage
@@ -116,28 +120,51 @@ impl ImplResource for MyResource {
116120
}
117121
}
118122

119-
#[tokio::main]
120-
async fn main() {
121-
// Define handlers
122-
async fn handle_ok(
123-
socket: TSocket<MySession>,
124-
packet: MyPacket,
125-
pools: PoolRef<MySession>,
126-
resources: ResourceRef<MyResource>
127-
) {
128-
println!("Received packet: {:?}", packet);
129-
socket.send(MyPacket::ok()).await.unwrap();
130-
}
123+
// Define packet handlers with the tlisten_for attribute macro
124+
#[tlisten_for("LOGIN")]
125+
async fn handle_login(
126+
sources: HandlerSources<MySession, MyResource>,
127+
packet: MyPacket
128+
) {
129+
let mut socket = sources.socket;
130+
println!("Processing login request");
131+
132+
// Access resources if needed
133+
let mut resources = sources.resources.write().await;
134+
resources.data.push("Login processed".to_string());
135+
136+
// Send response
137+
socket.send(MyPacket::ok()).await.unwrap();
138+
}
131139

132-
async fn handle_error(
133-
socket: TSocket<MySession>,
134-
error: Error,
135-
pools: PoolRef<MySession>,
136-
resources: ResourceRef<MyResource>
137-
) {
138-
println!("Error occurred: {:?}", error);
139-
}
140+
#[tlisten_for("LOGOUT")]
141+
async fn handle_logout(
142+
sources: HandlerSources<MySession, MyResource>,
143+
packet: MyPacket
144+
) {
145+
let mut socket = sources.socket;
146+
println!("Processing logout request");
147+
socket.send(MyPacket::ok()).await.unwrap();
148+
}
149+
150+
// For packets without specific handlers, we use these default handlers
151+
async fn handle_ok(
152+
sources: HandlerSources<MySession, MyResource>,
153+
packet: MyPacket
154+
) {
155+
println!("Received packet: {:?}", packet);
156+
sources.socket.send(MyPacket::ok()).await.unwrap();
157+
}
140158

159+
async fn handle_error(
160+
sources: HandlerSources<MySession, MyResource>,
161+
error: Error
162+
) {
163+
println!("Error occurred: {:?}", error);
164+
}
165+
166+
#[tokio::main]
167+
async fn main() {
141168
// Create and configure server
142169
let server = AsyncListener::new(
143170
("127.0.0.1", 8080),
@@ -157,6 +184,9 @@ async fn main() {
157184
}))
158185
);
159186

187+
// Create connection pools if needed
188+
server.with_pool("authenticated").await;
189+
160190
// Run the server
161191
server.run().await;
162192
}
@@ -186,11 +216,52 @@ async fn main() {
186216
// Send a packet and get response
187217
let response = client.send_recv(MyPacket::ok()).await.unwrap();
188218
println!("Server response: {:?}", response);
219+
220+
// Send a login packet
221+
let mut login_packet = MyPacket::ok();
222+
login_packet.header = "LOGIN".to_string();
223+
let login_response = client.send_recv(login_packet).await.unwrap();
224+
println!("Login response: {:?}", login_response);
189225
}
190226
```
191227

192228
## Advanced Usage
193229

230+
### Packet Header Enum with Derive Macro
231+
232+
```rust
233+
use tnet::prelude::*;
234+
235+
// Define packet headers as an enum
236+
#[derive(Debug, Clone, PacketHeader)]
237+
enum MyHeaders {
238+
Login,
239+
Logout,
240+
Message,
241+
KeepAlive,
242+
Error,
243+
Ok,
244+
}
245+
246+
// Now you can use MyHeaders with automatic string conversion
247+
#[tlisten_for("Login")]
248+
async fn handle_login(sources: HandlerSources<MySession, MyResource>, packet: MyPacket) {
249+
// Login logic here
250+
251+
// The enum provides automatic string conversion
252+
let response_header = MyHeaders::Ok.to_string();
253+
let mut response = MyPacket::ok();
254+
response.header = response_header;
255+
256+
sources.socket.send(response).await.unwrap();
257+
}
258+
259+
// Parse strings to enum values
260+
fn process_header(header_str: &str) -> Result<MyHeaders, String> {
261+
header_str.parse::<MyHeaders>()
262+
}
263+
```
264+
194265
### Auto-Reconnection
195266

196267
The client can automatically reconnect when the connection is lost, preserving session state:
@@ -287,6 +358,55 @@ match client.send_recv(MyPacket::ok()).await {
287358
}
288359
```
289360

361+
### Network Relay/Proxy with PhantomClient and PhantomListener
362+
363+
The phantom system allows relaying packets through an intermediary server:
364+
365+
```rust
366+
// 1. Set up a phantom listener (relay server)
367+
let phantom_listener = PhantomListener::new(Some(("127.0.0.1".to_string(), 9090))).await;
368+
369+
// 2. Create a client config for the destination server
370+
let client_config = ClientConfig {
371+
encryption_config: EncryptionConfig::default_on(),
372+
server_addr: "destination.server.com".to_string(),
373+
server_port: 8080,
374+
user: Some("user".to_string()),
375+
pass: Some("pass".to_string()),
376+
};
377+
378+
// 3. Create a phantom configuration
379+
let phantom_conf = PhantomConf {
380+
header: "relay",
381+
username: Some("user"),
382+
password: Some("pass"),
383+
server_addr: "destination.server.com",
384+
server_port: 8080,
385+
enc_conf: EncryptionConfig::default_on(),
386+
};
387+
388+
// 4. Create a packet to send to the destination
389+
let packet_to_relay = MyPacket::ok();
390+
391+
// 5. Create a phantom packet that wraps the real packet
392+
let phantom_packet = PhantomPacket::produce_from_conf(&phantom_conf, &packet_to_relay);
393+
394+
// 6. Connect to the phantom server and send the relay request
395+
let mut client = AsyncClient::<PhantomPacket>::new("127.0.0.1", 9090)
396+
.await
397+
.unwrap();
398+
399+
// 7. Send and get response
400+
let relay_response = client.send_recv(phantom_packet).await.unwrap();
401+
402+
// 8. Extract the response from the destination server
403+
if let Some(response_data) = relay_response.recv_packet {
404+
// Parse the response from the destination
405+
let destination_response: MyPacket = serde_json::from_str(&response_data).unwrap();
406+
println!("Response from destination: {:?}", destination_response);
407+
}
408+
```
409+
290410
## License
291411

292412
MIT

0 commit comments

Comments
 (0)