Refactor WebSocket input handling to support structured messages and improve error logging
This commit is contained in:
@@ -869,18 +869,19 @@ This project is a VS Code Clone built with React and Monaco Editor. It features
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!textToSend.trim()) {
|
|
||||||
console.warn("Cannot send empty input");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Add the input to the terminal display
|
// Add the input to the terminal display
|
||||||
setTerminalOutput(prev => [...prev, { type: 'command', content: `> ${textToSend}` }]);
|
setTerminalOutput(prev => [...prev, { type: 'command', content: `> ${textToSend}` }]);
|
||||||
|
|
||||||
// Send the input via WebSocket with a newline character
|
// Send the input via WebSocket
|
||||||
console.log("Sending input:", textToSend);
|
console.log("Sending input:", textToSend);
|
||||||
activeSocket.send(textToSend + "\n");
|
|
||||||
|
// Instead of just sending the raw input, send a formatted input message
|
||||||
|
// This helps the backend identify it as user input rather than a command
|
||||||
|
activeSocket.send(JSON.stringify({
|
||||||
|
type: "input",
|
||||||
|
content: textToSend
|
||||||
|
}));
|
||||||
|
|
||||||
// Clear the input field
|
// Clear the input field
|
||||||
setUserInput("");
|
setUserInput("");
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package executor
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"log"
|
"log"
|
||||||
@@ -122,21 +123,37 @@ func (e *CodeExecutor) handleTerminalInput(submissionID string, conn *websocket.
|
|||||||
for {
|
for {
|
||||||
_, message, err := conn.ReadMessage()
|
_, message, err := conn.ReadMessage()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("WebSocket read error: %v", err)
|
log.Printf("Error reading WebSocket message: %v", err)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
// If there's an input channel, send the input
|
// Try to parse the message as JSON first
|
||||||
e.inputMutex.RLock()
|
var inputMessage struct {
|
||||||
if inputChan, exists := e.inputChannels[submissionID]; exists {
|
Type string `json:"type"`
|
||||||
|
Content string `json:"content"`
|
||||||
|
}
|
||||||
|
|
||||||
|
inputText := string(message)
|
||||||
|
if err := json.Unmarshal(message, &inputMessage); err == nil && inputMessage.Type == "input" {
|
||||||
|
// It's a structured input message
|
||||||
|
inputText = inputMessage.Content
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now get the input channel
|
||||||
|
e.inputMutex.Lock()
|
||||||
|
inputChan, exists := e.inputChannels[submissionID]
|
||||||
|
e.inputMutex.Unlock()
|
||||||
|
|
||||||
|
if exists {
|
||||||
select {
|
select {
|
||||||
case inputChan <- string(message):
|
case inputChan <- inputText:
|
||||||
log.Printf("Input sent to process: %s", string(message))
|
log.Printf("Input sent to process: %s", inputText)
|
||||||
default:
|
default:
|
||||||
log.Printf("Input channel is full or closed, input ignored")
|
log.Printf("Failed to send input: channel full or closed")
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
log.Printf("No input channel for submission %s", submissionID)
|
||||||
}
|
}
|
||||||
e.inputMutex.RUnlock()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// When connection is closed, unregister it
|
// When connection is closed, unregister it
|
||||||
@@ -243,7 +260,7 @@ func (e *CodeExecutor) executePython(submission *models.CodeSubmission, tempDir
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Setup Docker run command
|
// Setup Docker run command with unbuffered Python output
|
||||||
cmd := exec.Command(
|
cmd := exec.Command(
|
||||||
"docker", "run", "--rm", "-i",
|
"docker", "run", "--rm", "-i",
|
||||||
"--network=none",
|
"--network=none",
|
||||||
@@ -251,11 +268,12 @@ func (e *CodeExecutor) executePython(submission *models.CodeSubmission, tempDir
|
|||||||
"--cpu-quota="+fmt.Sprintf("%d", int(float64(100000)*0.1)), // 10% CPU
|
"--cpu-quota="+fmt.Sprintf("%d", int(float64(100000)*0.1)), // 10% CPU
|
||||||
"--pids-limit=20",
|
"--pids-limit=20",
|
||||||
"-v", tempDir+":/code",
|
"-v", tempDir+":/code",
|
||||||
|
"-e", "PYTHONUNBUFFERED=1", // Force Python to be unbuffered
|
||||||
langConfig.Image,
|
langConfig.Image,
|
||||||
"python", "/code/code.py",
|
"python", "-u", "/code/code.py", // Add -u flag for unbuffered I/O
|
||||||
)
|
)
|
||||||
|
|
||||||
// Execute the code with input handling
|
// Execute with increased timeout for interactive programs
|
||||||
e.executeWithIO(cmd, submission, time.Duration(langConfig.TimeoutSec)*time.Second)
|
e.executeWithIO(cmd, submission, time.Duration(langConfig.TimeoutSec)*time.Second)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -605,7 +623,13 @@ func (e *CodeExecutor) executeWithIO(cmd *exec.Cmd, submission *models.CodeSubmi
|
|||||||
if !ok {
|
if !ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
stdin.Write([]byte(input + "\n"))
|
log.Printf("Received input from WebSocket: %s", input)
|
||||||
|
// Write input with a single newline - don't add extra newlines
|
||||||
|
_, err := stdin.Write([]byte(input + "\n"))
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Error writing to stdin: %v", err)
|
||||||
|
e.sendToTerminals(submission.ID, models.NewErrorMessage("input_error", "Failed to send input to process"))
|
||||||
|
}
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user