Skip to content

Commit 16eb768

Browse files
amituclaude
andcommitted
feat: convert request_response to pure server with CLI testing
Perfect testing separation: 📡 Server-Only request_response: - Pure Echo protocol server using fastn_p2p::listen() - Loads real identity keys from daemon storage - Uses full fastn-p2p crate for P2P networking - Clear setup instructions for testing 🔧 CLI Client Testing: - Use fastn-p2p CLI commands to test server - echo '{"message":"Hello"}' | fastn-p2p call <peer_id> Echo - Tests complete daemon → P2P → server flow - Validates real coordination architecture Benefits: - Clean separation: server vs client concerns - Real testing: CLI commands test daemon coordination - Production pattern: Server apps use full crate, clients use CLI - Type safety: All proper fastn_p2p types throughout Ready to implement fastn_p2p::listen() Echo protocol server! 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent 924fb71 commit 16eb768

1 file changed

Lines changed: 44 additions & 65 deletions

File tree

examples/src/request_response.rs

Lines changed: 44 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
1-
//! Request/Response Pattern Example (Client + Server)
1+
//! Request/Response Pattern Example (Server-Only)
22
//!
3-
//! Demonstrates both client and server sides of the Echo protocol.
4-
//! Can run as either client (via lightweight fastn-p2p-client) or server (using fastn-p2p server APIs).
3+
//! Pure Echo protocol server using fastn-p2p server APIs.
4+
//! Clients use fastn-p2p CLI commands to test this server.
55
//!
66
//! Usage:
7-
//! Server: cargo run --bin request_response server [identity_name]
8-
//! Client: cargo run --bin request_response client <peer_id52> [message]
7+
//! Server: cargo run --bin request_response [identity_name]
8+
//! Client: echo '{"message":"Hello"}' | fastn-p2p call <server_peer_id> Echo
99
10-
use fastn_p2p_client;
1110
use serde::{Serialize, Deserialize};
1211

1312
// Echo Protocol Definition
@@ -34,78 +33,58 @@ pub enum EchoError {
3433

3534
type EchoResult = Result<EchoResponse, EchoError>;
3635

37-
#[fastn_p2p_client::main]
36+
#[fastn_p2p::main]
3837
async fn main() -> Result<(), Box<dyn std::error::Error>> {
3938
let args: Vec<String> = std::env::args().collect();
4039

41-
if args.len() < 2 {
42-
eprintln!("Usage:");
43-
eprintln!(" {} server [identity_name] # Start Echo protocol server", args[0]);
44-
eprintln!(" {} client <peer_id52> [message] # Send Echo request to server", args[0]);
45-
eprintln!("");
46-
eprintln!("Two-daemon testing setup:");
47-
eprintln!(" 1. Terminal 1: FASTN_HOME=/tmp/alice fastn-p2p daemon");
48-
eprintln!(" 2. Terminal 2: FASTN_HOME=/tmp/bob fastn-p2p daemon");
49-
eprintln!(" 3. Terminal 3: FASTN_HOME=/tmp/alice {} server alice", args[0]);
50-
eprintln!(" 4. Terminal 4: FASTN_HOME=/tmp/bob {} client <alice_id52> \"Hello!\"", args[0]);
51-
return Ok(());
52-
}
40+
let identity = args.get(1).unwrap_or(&"alice".to_string()).clone();
5341

54-
match args[1].as_str() {
55-
"server" => {
56-
let identity = args.get(2).unwrap_or(&"alice".to_string()).clone();
57-
run_server(identity).await
58-
}
59-
"client" => {
60-
if args.len() < 3 {
61-
return Err("Client mode requires peer_id52 argument".into());
62-
}
63-
let target_id52 = &args[2];
64-
let message = args.get(3).unwrap_or(&"Hello P2P via daemon!".to_string()).clone();
65-
run_client(target_id52, message).await
66-
}
67-
_ => {
68-
return Err("First argument must be 'server' or 'client'".into());
69-
}
70-
}
42+
println!("🎧 Starting Echo protocol server for identity: {}", identity);
43+
println!("📡 Testing setup:");
44+
println!(" 1. Make sure daemon is running: fastn-p2p daemon");
45+
println!(" 2. Create identity: fastn-p2p create-identity {}", identity);
46+
println!(" 3. Add protocol: fastn-p2p add-protocol {} --protocol Echo --config '{{\"max_length\": 1000}}'", identity);
47+
println!(" 4. Set online: fastn-p2p identity-online {}", identity);
48+
println!(" 5. Test with CLI: echo '{{\"message\":\"Hello\"}}' | fastn-p2p call <peer_id> Echo");
49+
println!("");
50+
51+
run_server(identity).await
7152
}
7253

7354
async fn run_server(identity: String) -> Result<(), Box<dyn std::error::Error>> {
7455
println!("🎧 Starting Echo protocol server for identity: {}", identity);
75-
println!("📡 Server will handle Echo requests via fastn-p2p server APIs");
7656

77-
// TODO: Use fastn_p2p server APIs to listen for Echo protocol requests
78-
// This will use the clean server APIs from fastn-p2p crate
79-
todo!("Implement Echo protocol server using fastn_p2p::listen() or similar server API");
57+
// Load identity private key (server applications need the full fastn-p2p crate)
58+
let private_key = load_identity_key(&identity).await?;
59+
println!("🔑 Loaded identity key for: {} ({})", identity, private_key.public_key().id52());
60+
61+
// Start P2P server listening for Echo protocol requests
62+
println!("📡 Starting P2P listener for Echo protocol...");
63+
fastn_p2p::listen(private_key)
64+
.handle_requests(EchoProtocol::Echo, echo_handler)
65+
.await?;
66+
67+
Ok(())
8068
}
8169

82-
async fn run_client(
83-
target_id52: &str,
84-
message: String,
85-
) -> Result<(), Box<dyn std::error::Error>> {
86-
// Parse target peer ID to PublicKey for type safety
87-
let target_peer: fastn_p2p_client::PublicKey = target_id52.parse()
88-
.map_err(|e| format!("Invalid peer ID '{}': {}", target_id52, e))?;
89-
90-
println!("📤 Sending '{}' to {} via daemon", message, target_peer.id52());
91-
92-
let request = EchoRequest { message };
70+
/// Load identity private key from current environment
71+
async fn load_identity_key(identity: &str) -> Result<fastn_p2p::SecretKey, Box<dyn std::error::Error>> {
72+
// Use FASTN_HOME environment variable to locate identity
73+
let fastn_home = std::env::var("FASTN_HOME")
74+
.unwrap_or_else(|_| {
75+
let home = std::env::var("HOME").unwrap_or("/tmp".to_string());
76+
format!("{}/.fastn", home)
77+
});
9378

94-
// Use lightweight client that routes through daemon
95-
let result: EchoResult = fastn_p2p_client::call(
96-
"bob", // From bob identity (daemon manages keys)
97-
target_peer, // To target peer (alice)
98-
"Echo", // Echo protocol
99-
"default", // Default Echo instance
100-
request // Request data
101-
).await?;
102-
103-
match result {
104-
Ok(response) => println!("✅ Response: {}", response.echoed),
105-
Err(error) => println!("❌ Error: {:?}", error),
106-
}
79+
let identity_dir = std::path::PathBuf::from(fastn_home)
80+
.join("identities")
81+
.join(identity);
10782

108-
Ok(())
83+
// Load the private key using fastn-id52
84+
match fastn_p2p::SecretKey::load_from_dir(&identity_dir, "identity") {
85+
Ok((_id52, secret_key)) => Ok(secret_key),
86+
Err(e) => Err(format!("Failed to load identity '{}': {}", identity, e).into()),
87+
}
10988
}
11089

11190
/// Echo request handler (server-side logic)

0 commit comments

Comments
 (0)