hydro_test/external_client/
http_hello.rs

1use hydro_lang::keyed_singleton::{BoundedValue, KeyedSingleton};
2use hydro_lang::keyed_stream::KeyedStream;
3use hydro_lang::*;
4
5pub fn http_hello_server<'a, P>(
6    in_stream: KeyedStream<u64, String, Process<'a, P>, Unbounded, TotalOrder>,
7) -> KeyedSingleton<u64, String, Process<'a, P>, BoundedValue> {
8    in_stream
9        .inspect_with_key(q!(|(id, line)| println!(
10            "Received line from client #{}: '{}'",
11            id, line
12        )))
13        .fold_early_stop(
14            q!(|| String::new()),
15            q!(|buffer, line| {
16                buffer.push_str(&line);
17                buffer.push_str("\r\n");
18
19                // Check if this is an empty line (end of HTTP headers)
20                line.trim().is_empty()
21            }),
22        )
23        .map_with_key(q!(|(id, raw_request)| {
24            let lines: Vec<&str> = raw_request.lines().collect();
25
26            // Parse request line
27            let request_line = lines.first().unwrap_or(&"");
28            let parts: Vec<&str> = request_line.split_whitespace().collect();
29            let method = parts.first().unwrap_or(&"GET");
30            let path = parts.get(1).unwrap_or(&"/");
31            let version_str = parts.get(2).unwrap_or(&"HTTP/1.1");
32            let version = if version_str.ends_with("1.0") { 0 } else { 1 };
33
34            // Extract specific headers we need
35            let mut user_agent = "Unknown";
36            let mut host = "Unknown";
37
38            for line in &lines[1..] {
39                if line.trim().is_empty() {
40                    break;
41                }
42                if let Some(colon_pos) = line.find(':') {
43                    let name = line[..colon_pos].trim();
44                    let value = line[colon_pos + 1..].trim();
45
46                    match name.to_lowercase().as_str() {
47                        "user-agent" => user_agent = value,
48                        "host" => host = value,
49                        _ => {}
50                    }
51                }
52            }
53
54            let html_content = format!(
55                "<!DOCTYPE html>\
56                <html><head><title>Hello from Hydro!</title></head>\
57                <body>\
58                <h1>🌊 Hello from Hydro HTTP Server!</h1>\
59                <p><strong>Your browser:</strong> {}</p>\
60                <p><strong>Host:</strong> {}</p>\
61                <p><strong>Method:</strong> {}</p>\
62                <p><strong>Path:</strong> {}</p>\
63                <p><strong>HTTP Version:</strong> 1.{}</p>\
64                <p><em>Connection #{}</em></p>\
65                <hr>\
66                <h3>Raw Request:</h3>\
67                <pre>{}</pre>\
68                </body></html>",
69                user_agent, host, method, path, version, id, raw_request
70            );
71
72            let response = format!(
73                "HTTP/1.1 200 OK\r\n\
74                 Content-Type: text/html; charset=utf-8\r\n\
75                 Content-Length: {}\r\n\
76                 Connection: close\r\n\
77                 \r\n\
78                 {}",
79                html_content.len(),
80                html_content
81            );
82            response
83        }))
84        .inspect_with_key(q!(|(id, response)| println!(
85            "Sending HTTP response to client #{}: {} bytes",
86            id,
87            response.len()
88        )))
89}