✅ Phase 10.1: Zero-Copy Detection APIs Implementation Complete
Co-authored-by: moe-charm <217100418+moe-charm@users.noreply.github.com>
This commit is contained in:
175
apps/tinyproxy_nyash/proxy_server.nyash
Normal file
175
apps/tinyproxy_nyash/proxy_server.nyash
Normal file
@ -0,0 +1,175 @@
|
||||
// 🌐 Tinyproxy Nyash - HTTP Proxy Server Implementation
|
||||
// Phase 10.1: Zero-copy detection testing with real HTTP traffic
|
||||
|
||||
// ProxyServer - Main proxy server implementation
|
||||
static box ProxyServer {
|
||||
init { server_socket, upstream_buffer, downstream_buffer, port, running }
|
||||
|
||||
main() {
|
||||
me.port = 8080
|
||||
me.running = true
|
||||
me.upstream_buffer = new BufferBox()
|
||||
me.downstream_buffer = new BufferBox()
|
||||
me.server_socket = new SocketBox()
|
||||
|
||||
print("🌐 Tinyproxy Nyash starting on port " + me.port)
|
||||
|
||||
// Bind to port
|
||||
local bind_result = me.server_socket.bind("127.0.0.1", me.port)
|
||||
print("Bind result: " + bind_result)
|
||||
|
||||
// Start listening
|
||||
local listen_result = me.server_socket.listen(10)
|
||||
print("Listen result: " + listen_result)
|
||||
|
||||
// Main proxy loop
|
||||
me.accept_connections()
|
||||
|
||||
return "Proxy server stopped"
|
||||
}
|
||||
|
||||
accept_connections() {
|
||||
local connection_count = 0
|
||||
|
||||
print("🔄 Proxy server ready - waiting for connections...")
|
||||
|
||||
loop(connection_count < 5 and me.running) {
|
||||
print("Waiting for connection " + (connection_count + 1))
|
||||
|
||||
local client_socket = me.server_socket.accept()
|
||||
if (client_socket != null) {
|
||||
print("✅ New client connected")
|
||||
me.handle_client(client_socket)
|
||||
connection_count = connection_count + 1
|
||||
} else {
|
||||
print("❌ Failed to accept connection")
|
||||
}
|
||||
}
|
||||
|
||||
print("🏁 Proxy server finished handling " + connection_count + " connections")
|
||||
}
|
||||
|
||||
handle_client(client_socket) {
|
||||
// Read HTTP request from client
|
||||
local request_data = client_socket.read()
|
||||
if (request_data == null) {
|
||||
print("❌ Failed to read request from client")
|
||||
return false
|
||||
}
|
||||
|
||||
print("📥 Received request from client")
|
||||
|
||||
// Parse HTTP request to extract target server
|
||||
local target_info = me.parse_http_request(request_data)
|
||||
if (target_info == null) {
|
||||
print("❌ Failed to parse HTTP request")
|
||||
me.send_error_response(client_socket)
|
||||
return false
|
||||
}
|
||||
|
||||
print("🎯 Targeting: " + target_info.get("host") + ":" + target_info.get("port"))
|
||||
|
||||
// ⭐ Zero-copy test: Check if we can share request data without copying
|
||||
local shared_result = me.relay_data(request_data)
|
||||
print("Zero-copy result: " + shared_result)
|
||||
|
||||
// Connect to target server
|
||||
local upstream_socket = new SocketBox()
|
||||
local connect_result = upstream_socket.connect(target_info.get("host"), target_info.get("port"))
|
||||
|
||||
if (connect_result.toString() == "true") {
|
||||
print("✅ Connected to upstream server")
|
||||
|
||||
// Forward request to upstream server
|
||||
upstream_socket.write(request_data)
|
||||
print("📤 Forwarded request to upstream")
|
||||
|
||||
// Read response from upstream
|
||||
local response_data = upstream_socket.read()
|
||||
if (response_data != null) {
|
||||
print("📥 Received response from upstream")
|
||||
|
||||
// Forward response back to client
|
||||
client_socket.write(response_data)
|
||||
print("📤 Forwarded response to client")
|
||||
}
|
||||
|
||||
upstream_socket.close()
|
||||
} else {
|
||||
print("❌ Failed to connect to upstream server")
|
||||
me.send_error_response(client_socket)
|
||||
}
|
||||
|
||||
client_socket.close()
|
||||
return true
|
||||
}
|
||||
|
||||
relay_data(client_data) {
|
||||
// ⭐ Phase 10: Zero-copy detection testing
|
||||
|
||||
// Test 1: Check if upstream buffer shares data with client data
|
||||
if (me.upstream_buffer.is_shared_with(client_data)) {
|
||||
return "✅ Zero-copy achieved!"
|
||||
} else {
|
||||
print("❌ Unnecessary copy detected in initial check")
|
||||
}
|
||||
|
||||
// Test 2: Create shared reference and test again
|
||||
local shared_buffer = me.upstream_buffer.share_reference(client_data)
|
||||
if (shared_buffer != null) {
|
||||
print("📊 Created shared reference")
|
||||
|
||||
// Test if the shared buffer is actually sharing memory
|
||||
if (me.upstream_buffer.is_shared_with(shared_buffer)) {
|
||||
return "✅ Zero-copy sharing successful!"
|
||||
} else {
|
||||
return "❌ Share reference failed to create shared memory"
|
||||
}
|
||||
}
|
||||
|
||||
return "❌ Share reference operation failed"
|
||||
}
|
||||
|
||||
parse_http_request(request_data) {
|
||||
// Simple HTTP request parser - extract host and port
|
||||
local request_str = request_data.toString()
|
||||
local result = new MapBox()
|
||||
|
||||
// Default values
|
||||
result.set("host", "httpbin.org")
|
||||
result.set("port", 80)
|
||||
result.set("method", "GET")
|
||||
|
||||
// In a real implementation, we would parse the HTTP headers
|
||||
// For this test, we'll use a fixed target
|
||||
return result
|
||||
}
|
||||
|
||||
send_error_response(client_socket) {
|
||||
local error_response = "HTTP/1.1 502 Bad Gateway\r\nContent-Length: 0\r\n\r\n"
|
||||
client_socket.write(error_response)
|
||||
}
|
||||
|
||||
stop() {
|
||||
me.running = false
|
||||
if (me.server_socket != null) {
|
||||
me.server_socket.close()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Entry point
|
||||
static box Main {
|
||||
init { console }
|
||||
|
||||
main() {
|
||||
me.console = new ConsoleBox()
|
||||
me.console.log("🚀 Starting Tinyproxy Nyash - Phase 10.1")
|
||||
|
||||
local proxy = new ProxyServer()
|
||||
local result = proxy.main()
|
||||
|
||||
me.console.log("🏁 Proxy result: " + result)
|
||||
return "Phase 10.1 Complete"
|
||||
}
|
||||
}
|
||||
@ -150,6 +150,35 @@ impl BufferBox {
|
||||
Box::new(StringBox::new("Error: slice() requires integer indices"))
|
||||
}
|
||||
}
|
||||
|
||||
/// ⭐ Phase 10: Zero-copy detection - check if buffer is shared with another buffer
|
||||
pub fn is_shared_with(&self, other: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
|
||||
if let Some(other_buffer) = other.as_any().downcast_ref::<BufferBox>() {
|
||||
// Check if the Arc pointers are the same (shared memory)
|
||||
let is_shared = Arc::ptr_eq(&self.data, &other_buffer.data);
|
||||
Box::new(BoolBox::new(is_shared))
|
||||
} else {
|
||||
// Not a BufferBox, so definitely not shared
|
||||
Box::new(BoolBox::new(false))
|
||||
}
|
||||
}
|
||||
|
||||
/// ⭐ Phase 10: Share reference - create a zero-copy shared reference to this buffer's data
|
||||
pub fn share_reference(&self, _data: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
|
||||
// Create a new BufferBox that shares the same Arc as this buffer
|
||||
let shared_buffer = BufferBox {
|
||||
data: Arc::clone(&self.data), // Share THIS buffer's data
|
||||
base: BoxBase::new(), // New ID but shared data
|
||||
};
|
||||
Box::new(shared_buffer)
|
||||
}
|
||||
|
||||
/// ⭐ Phase 10: Memory footprint - get current memory usage in bytes
|
||||
pub fn memory_footprint(&self) -> Box<dyn NyashBox> {
|
||||
let data = self.data.read().unwrap();
|
||||
let bytes = data.len() + std::mem::size_of::<BufferBox>();
|
||||
Box::new(IntegerBox::new(bytes as i64))
|
||||
}
|
||||
}
|
||||
|
||||
// Clone implementation for BufferBox (needed since RwLock doesn't auto-derive Clone)
|
||||
|
||||
@ -77,6 +77,33 @@ impl NyashInterpreter {
|
||||
let end = self.execute_expression(&arguments[1])?;
|
||||
Ok(buffer_box.slice(start, end))
|
||||
}
|
||||
// ⭐ Phase 10: Zero-copy detection APIs
|
||||
"is_shared_with" => {
|
||||
if arguments.len() != 1 {
|
||||
return Err(RuntimeError::InvalidOperation {
|
||||
message: format!("is_shared_with() expects 1 argument, got {}", arguments.len()),
|
||||
});
|
||||
}
|
||||
let other = self.execute_expression(&arguments[0])?;
|
||||
Ok(buffer_box.is_shared_with(other))
|
||||
}
|
||||
"share_reference" => {
|
||||
if arguments.len() != 1 {
|
||||
return Err(RuntimeError::InvalidOperation {
|
||||
message: format!("share_reference() expects 1 argument, got {}", arguments.len()),
|
||||
});
|
||||
}
|
||||
let data = self.execute_expression(&arguments[0])?;
|
||||
Ok(buffer_box.share_reference(data))
|
||||
}
|
||||
"memory_footprint" => {
|
||||
if !arguments.is_empty() {
|
||||
return Err(RuntimeError::InvalidOperation {
|
||||
message: format!("memory_footprint() expects 0 arguments, got {}", arguments.len()),
|
||||
});
|
||||
}
|
||||
Ok(buffer_box.memory_footprint())
|
||||
}
|
||||
_ => Err(RuntimeError::InvalidOperation {
|
||||
message: format!("Unknown method '{}' for BufferBox", method),
|
||||
})
|
||||
|
||||
67
test_zero_copy_detection.nyash
Normal file
67
test_zero_copy_detection.nyash
Normal file
@ -0,0 +1,67 @@
|
||||
// ⭐ Phase 10: Zero-copy Detection API Test
|
||||
// Test the new BufferBox zero-copy methods
|
||||
|
||||
static box Main {
|
||||
init { console }
|
||||
|
||||
main() {
|
||||
me.console = new ConsoleBox()
|
||||
me.console.log("🧪 Testing Zero-copy Detection APIs")
|
||||
|
||||
// Test 1: Basic buffer creation and memory footprint
|
||||
local buffer1 = new BufferBox()
|
||||
local buffer2 = new BufferBox()
|
||||
|
||||
me.console.log("Created two BufferBox instances")
|
||||
|
||||
// Test memory footprint API
|
||||
local footprint1 = buffer1.memory_footprint()
|
||||
me.console.log("Buffer1 memory footprint: " + footprint1)
|
||||
|
||||
// Test 2: is_shared_with() - should return false for independent buffers
|
||||
local shared_check1 = buffer1.is_shared_with(buffer2)
|
||||
me.console.log("Independent buffers shared: " + shared_check1)
|
||||
|
||||
// Test 3: share_reference() - create shared buffer
|
||||
local shared_buffer = buffer1.share_reference(buffer2)
|
||||
me.console.log("Created shared reference")
|
||||
|
||||
// Test 4: Check if shared buffer actually shares data with buffer1
|
||||
local shared_check2 = buffer1.is_shared_with(shared_buffer)
|
||||
me.console.log("Shared buffer shares data with buffer1: " + shared_check2)
|
||||
|
||||
// Test 5: Memory footprint should be similar for shared buffers
|
||||
local footprint_shared = shared_buffer.memory_footprint()
|
||||
me.console.log("Shared buffer memory footprint: " + footprint_shared)
|
||||
|
||||
// Test 6: Write data and check sharing
|
||||
me.console.log("Writing data to test sharing...")
|
||||
|
||||
// Create test data
|
||||
local test_data = new ArrayBox()
|
||||
test_data.push(72) // 'H'
|
||||
test_data.push(101) // 'e'
|
||||
test_data.push(108) // 'l'
|
||||
test_data.push(108) // 'l'
|
||||
test_data.push(111) // 'o'
|
||||
|
||||
buffer1.write(test_data)
|
||||
me.console.log("Written test data to buffer1")
|
||||
|
||||
local length1 = buffer1.length()
|
||||
me.console.log("Buffer1 length after write: " + length1)
|
||||
|
||||
// Test 7: Check if shared buffer reflects the data
|
||||
local shared_length = shared_buffer.length()
|
||||
me.console.log("Shared buffer length: " + shared_length)
|
||||
|
||||
if (length1.toString() == shared_length.toString()) {
|
||||
me.console.log("✅ Zero-copy sharing working correctly!")
|
||||
} else {
|
||||
me.console.log("❌ Zero-copy sharing failed")
|
||||
}
|
||||
|
||||
me.console.log("🎉 Zero-copy detection test complete")
|
||||
return "Test completed"
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user