From a6bd8eeebbadd3fc52dde1e183decb82fa2fd0c9 Mon Sep 17 00:00:00 2001 From: ishikabhoyar Date: Sun, 22 Jun 2025 12:44:00 +0530 Subject: [PATCH] Refactor WebSocket input handling to support structured messages and improve error logging --- Frontend/src/components/EditorArea.jsx | 15 +++-- new-backend/executor/executor.go | 88 ++++++++++++++++---------- 2 files changed, 64 insertions(+), 39 deletions(-) diff --git a/Frontend/src/components/EditorArea.jsx b/Frontend/src/components/EditorArea.jsx index 72369db..cc88414 100644 --- a/Frontend/src/components/EditorArea.jsx +++ b/Frontend/src/components/EditorArea.jsx @@ -869,18 +869,19 @@ This project is a VS Code Clone built with React and Monaco Editor. It features return; } - if (!textToSend.trim()) { - console.warn("Cannot send empty input"); - return; - } - try { // Add the input to the terminal display 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); - 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 setUserInput(""); diff --git a/new-backend/executor/executor.go b/new-backend/executor/executor.go index 687f3b8..0fafd6c 100644 --- a/new-backend/executor/executor.go +++ b/new-backend/executor/executor.go @@ -3,6 +3,7 @@ package executor import ( "bytes" "context" + "encoding/json" "fmt" "io" "log" @@ -122,21 +123,37 @@ func (e *CodeExecutor) handleTerminalInput(submissionID string, conn *websocket. for { _, message, err := conn.ReadMessage() if err != nil { - log.Printf("WebSocket read error: %v", err) + log.Printf("Error reading WebSocket message: %v", err) break } - // If there's an input channel, send the input - e.inputMutex.RLock() - if inputChan, exists := e.inputChannels[submissionID]; exists { - select { - case inputChan <- string(message): - log.Printf("Input sent to process: %s", string(message)) - default: - log.Printf("Input channel is full or closed, input ignored") - } + // Try to parse the message as JSON first + var inputMessage struct { + 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 { + case inputChan <- inputText: + log.Printf("Input sent to process: %s", inputText) + default: + 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 @@ -235,28 +252,29 @@ func (e *CodeExecutor) executeCode(submission *models.CodeSubmission) { // executePython executes Python code func (e *CodeExecutor) executePython(submission *models.CodeSubmission, tempDir string, langConfig config.LanguageConfig) { - // Write code to file - codeFile := filepath.Join(tempDir, "code"+langConfig.FileExt) - if err := os.WriteFile(codeFile, []byte(submission.Code), 0644); err != nil { - submission.Status = "failed" - submission.Output = "Failed to write code file: " + err.Error() - return - } + // Write code to file + codeFile := filepath.Join(tempDir, "code"+langConfig.FileExt) + if err := os.WriteFile(codeFile, []byte(submission.Code), 0644); err != nil { + submission.Status = "failed" + submission.Output = "Failed to write code file: " + err.Error() + return + } - // Setup Docker run command - cmd := exec.Command( - "docker", "run", "--rm", "-i", - "--network=none", - "--memory="+langConfig.MemoryLimit, - "--cpu-quota="+fmt.Sprintf("%d", int(float64(100000)*0.1)), // 10% CPU - "--pids-limit=20", - "-v", tempDir+":/code", - langConfig.Image, - "python", "/code/code.py", - ) + // Setup Docker run command with unbuffered Python output + cmd := exec.Command( + "docker", "run", "--rm", "-i", + "--network=none", + "--memory="+langConfig.MemoryLimit, + "--cpu-quota="+fmt.Sprintf("%d", int(float64(100000)*0.1)), // 10% CPU + "--pids-limit=20", + "-v", tempDir+":/code", + "-e", "PYTHONUNBUFFERED=1", // Force Python to be unbuffered + langConfig.Image, + "python", "-u", "/code/code.py", // Add -u flag for unbuffered I/O + ) - // Execute the code with input handling - e.executeWithIO(cmd, submission, time.Duration(langConfig.TimeoutSec)*time.Second) + // Execute with increased timeout for interactive programs + e.executeWithIO(cmd, submission, time.Duration(langConfig.TimeoutSec)*time.Second) } // executeJava executes Java code @@ -605,7 +623,13 @@ func (e *CodeExecutor) executeWithIO(cmd *exec.Cmd, submission *models.CodeSubmi if !ok { 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(): return }