Refactor WebSocket input handling to support structured messages and improve error logging

This commit is contained in:
ishikabhoyar
2025-06-22 12:44:00 +05:30
parent c529a48f31
commit a6bd8eeebb
2 changed files with 64 additions and 39 deletions

View File

@@ -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("");

View File

@@ -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
} }