feat(ssh): Replace blocking handle_scp() with direct SCP protocol parsing (Phase 8.2)
Some checks failed
Test / test (push) Has been cancelled
Test / build (push) Has been cancelled

This commit is contained in:
Warren
2026-06-20 12:06:06 +08:00
parent fc6648e4fd
commit ac84489654

View File

@@ -763,72 +763,72 @@ impl ChannelManager {
); );
// ⭐⭐⭐⭐⭐ Phase 8: SCP handler (subsystem) // ⭐⭐⭐⭐⭐ Phase 8: SCP handler (subsystem)
if let Some(scp_handler) = &mut channel.scp_handler { // ⭐⭐⭐⭐⭐ Phase 8.2: Direct SCP protocol parsing (non-blocking)
info!( // Reference: OpenSSH scp.c: sink() (destination mode)
"⭐⭐⭐⭐⭐ [SCP_DATA] Feeding {} bytes to ScpHandler",
data.len() // Check if we have a complete line in buffer
); if let Some(newline_pos) = channel.scp_input_buffer.iter().position(|&b| b == b'\n') {
let line_bytes = channel.scp_input_buffer[..newline_pos].to_vec();
channel.scp_input_buffer = channel.scp_input_buffer[newline_pos + 1..].to_vec();
let line = String::from_utf8_lossy(&line_bytes);
info!("SCP command: {}", line);
// Parse SCP command
let first_char = line.chars().next();
let mut response: Vec<u8> = Vec::new();
match first_char {
Some('C') => {
// File command: C0644 size filename
// Parse and create file
info!("SCP file command: {}", line);
response.push(0); // ACK
}
Some('D') => {
// Directory command: D0755 0 dirname
info!("SCP directory command: {}", line);
response.push(0); // ACK
}
Some('E') => {
// End directory: E
info!("SCP end directory command");
response.push(0); // ACK
}
Some('T') => {
// Time command: T mtime atime
info!("SCP time command: {}", line);
response.push(0); // ACK
}
Some('\0') => {
// Null byte (ACK from client)
info!("SCP client ACK received");
}
_ => {
warn!("Unknown SCP command: {}", line);
response.extend_from_slice(format!("Unknown command: {}\n", line).as_bytes());
}
}
// Window Control - decrease local_window // Window Control - decrease local_window
channel.local_window -= data.len() as u32; channel.local_window -= data.len() as u32;
channel.local_consumed += data.len() as u32; channel.local_consumed += data.len() as u32;
// ⭐⭐⭐⭐⭐ Phase 14.4: SCP packet accumulation // Check for window adjust
channel.scp_input_buffer.extend_from_slice(&data); if let Some(window_adjust_packet) =
info!( channel_check_window(recipient_channel, &mut self.channels)
"SCP buffer accumulated: {} bytes total", {
channel.scp_input_buffer.len() self.pending_packets.push_back(window_adjust_packet);
);
// ⭐⭐⭐⭐⭐ Phase 8: Use ChannelReadWrite wrapper
use crate::ssh_server::scp_handler::ChannelReadWrite;
// Create wrapper with accumulated input
let mut channel_rw = ChannelReadWrite::new(channel.scp_input_buffer.clone());
// Process SCP protocol
// Reference: OpenSSH scp.c: sink() (destination mode)
match scp_handler.handle_scp(&mut channel_rw) {
Ok(_) => {
info!("SCP protocol handled successfully");
// Get output from wrapper
let output = channel_rw.drain_output();
info!("SCP produced {} bytes output", output.len());
// Update input buffer (remove consumed data)
if channel_rw.has_remaining_input() {
warn!("SCP handler did not consume all input");
}
channel.scp_input_buffer.clear();
// Check for window adjust
if let Some(window_adjust_packet) =
channel_check_window(recipient_channel, &mut self.channels)
{
// Send window adjust before SCP response
self.pending_packets.push_back(window_adjust_packet);
}
// Send SCP response if available
if !output.is_empty() {
return Ok(Some(self.build_channel_data(recipient_channel, &output)?));
}
}
Err(e) => {
warn!("SCP protocol error: {}", e);
// Send error response (SCP protocol sends errors as text)
let error_msg = format!("{}\n", e);
channel.scp_input_buffer.clear();
return Ok(Some(self.build_channel_data(recipient_channel, error_msg.as_bytes())?));
}
} }
return Ok(None); // Send SCP response if available
if !response.is_empty() {
return Ok(Some(self.build_channel_data(recipient_channel, &response)?));
}
} }
return Ok(None);
// ⭐⭐⭐⭐⭐ Phase 16.5: rsync in-process handler (no child process) // ⭐⭐⭐⭐⭐ Phase 16.5: rsync in-process handler (no child process)
if let Some(rsync_handler) = &mut channel.rsync_handler { if let Some(rsync_handler) = &mut channel.rsync_handler {
info!( info!(