working socket integration
This commit is contained in:
@@ -1,6 +1,8 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
@@ -14,19 +16,24 @@ import (
|
||||
|
||||
"github.com/arnab-afk/monaco/model"
|
||||
"github.com/arnab-afk/monaco/queue"
|
||||
"github.com/gorilla/websocket"
|
||||
)
|
||||
|
||||
// ExecutionService handles code execution for multiple languages
|
||||
type ExecutionService struct {
|
||||
mu sync.Mutex
|
||||
queue *queue.JobQueue
|
||||
mu sync.Mutex
|
||||
queue *queue.JobQueue
|
||||
wsConnections map[string]*websocket.Conn // Map of submission ID to WebSocket connection
|
||||
wsInputChannels map[string]chan string // Map of submission ID to input channel
|
||||
}
|
||||
|
||||
// NewExecutionService creates a new execution service
|
||||
func NewExecutionService() *ExecutionService {
|
||||
log.Println("Initializing execution service with 3 concurrent workers")
|
||||
return &ExecutionService{
|
||||
queue: queue.NewJobQueue(35), // 3 concurrent executions max
|
||||
queue: queue.NewJobQueue(3), // 3 concurrent executions max
|
||||
wsConnections: make(map[string]*websocket.Conn),
|
||||
wsInputChannels: make(map[string]chan string),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -483,3 +490,223 @@ func (s *ExecutionService) GetQueueStats() map[string]int {
|
||||
stats["queue_length"], stats["running_jobs"], stats["max_workers"])
|
||||
return stats
|
||||
}
|
||||
|
||||
// HandleWebSocket handles a WebSocket connection for a code submission
|
||||
func (s *ExecutionService) HandleWebSocket(conn *websocket.Conn, submission *model.CodeSubmission) {
|
||||
// Store the WebSocket connection
|
||||
s.mu.Lock()
|
||||
s.wsConnections[submission.ID] = conn
|
||||
|
||||
// Create an input channel for this submission
|
||||
inputChan := make(chan string, 10) // Buffer size of 10
|
||||
s.wsInputChannels[submission.ID] = inputChan
|
||||
s.mu.Unlock()
|
||||
|
||||
// Clean up when done
|
||||
defer func() {
|
||||
s.mu.Lock()
|
||||
delete(s.wsConnections, submission.ID)
|
||||
delete(s.wsInputChannels, submission.ID)
|
||||
s.mu.Unlock()
|
||||
conn.Close()
|
||||
}()
|
||||
|
||||
// Start a goroutine to read input from the WebSocket
|
||||
go func() {
|
||||
for {
|
||||
// Read message from WebSocket
|
||||
messageType, message, err := conn.ReadMessage()
|
||||
if err != nil {
|
||||
log.Printf("[WS-%s] Error reading message: %v", submission.ID, err)
|
||||
break
|
||||
}
|
||||
|
||||
// Only process text messages
|
||||
if messageType == websocket.TextMessage {
|
||||
// Send the input to the input channel
|
||||
inputChan <- string(message)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
// Execute the code
|
||||
submission.Status = "running"
|
||||
submission.StartedAt = time.Now()
|
||||
|
||||
log.Printf("[WS-JOB-%s] Starting WebSocket execution for language: %s",
|
||||
submission.ID, submission.Language)
|
||||
|
||||
// Execute the code based on the language
|
||||
s.executeLanguageSpecificWithWebSocket(submission, inputChan, conn)
|
||||
}
|
||||
|
||||
// executeLanguageSpecificWithWebSocket runs code in the appropriate language with WebSocket I/O
|
||||
func (s *ExecutionService) executeLanguageSpecificWithWebSocket(submission *model.CodeSubmission, inputChan chan string, conn *websocket.Conn) {
|
||||
log.Printf("[WS-EXEC-%s] Selecting execution environment for language: %s",
|
||||
submission.ID, submission.Language)
|
||||
|
||||
switch submission.Language {
|
||||
case "python":
|
||||
log.Printf("[WS-EXEC-%s] Executing Python code", submission.ID)
|
||||
s.executePythonWithWebSocket(submission, inputChan, conn)
|
||||
case "java":
|
||||
log.Printf("[WS-EXEC-%s] Executing Java code", submission.ID)
|
||||
s.executeJavaWithWebSocket(submission, inputChan, conn)
|
||||
case "c":
|
||||
log.Printf("[WS-EXEC-%s] Executing C code", submission.ID)
|
||||
s.executeCWithWebSocket(submission, inputChan, conn)
|
||||
case "cpp":
|
||||
log.Printf("[WS-EXEC-%s] Executing C++ code", submission.ID)
|
||||
s.executeCppWithWebSocket(submission, inputChan, conn)
|
||||
default:
|
||||
log.Printf("[WS-EXEC-%s] ERROR: Unsupported language: %s", submission.ID, submission.Language)
|
||||
submission.Status = "failed"
|
||||
output := "Unsupported language: " + submission.Language
|
||||
submission.Output = output
|
||||
|
||||
// Send error message to WebSocket
|
||||
conn.WriteMessage(websocket.TextMessage, []byte(output))
|
||||
}
|
||||
|
||||
// Update submission status
|
||||
submission.CompletedAt = time.Now()
|
||||
submission.Status = "completed"
|
||||
}
|
||||
|
||||
// executePythonWithWebSocket runs Python code with WebSocket for I/O
|
||||
func (s *ExecutionService) executePythonWithWebSocket(submission *model.CodeSubmission, inputChan chan string, conn *websocket.Conn) {
|
||||
log.Printf("[WS-PYTHON-%s] Preparing Python WebSocket execution", submission.ID)
|
||||
startTime := time.Now()
|
||||
|
||||
// Send initial message to client
|
||||
conn.WriteMessage(websocket.TextMessage, []byte("Starting Python execution...\n"))
|
||||
|
||||
// Create a command to run Python in a Docker container
|
||||
cmd := exec.Command("docker", "run", "--rm", "-i",
|
||||
"--network=none", // No network access
|
||||
"--memory=100m", // Memory limit
|
||||
"--cpu-period=100000", // CPU quota period
|
||||
"--cpu-quota=10000", // 10% CPU
|
||||
"--ulimit", "nofile=64:64", // File descriptor limits
|
||||
"python:3.9", "python", "-c", submission.Code)
|
||||
|
||||
// Get stdin pipe
|
||||
stdin, err := cmd.StdinPipe()
|
||||
if err != nil {
|
||||
log.Printf("[WS-PYTHON-%s] Failed to create stdin pipe: %v", submission.ID, err)
|
||||
conn.WriteMessage(websocket.TextMessage, []byte("Error: Failed to create stdin pipe\n"))
|
||||
return
|
||||
}
|
||||
|
||||
// Get stdout and stderr pipes
|
||||
stdout, err := cmd.StdoutPipe()
|
||||
if err != nil {
|
||||
log.Printf("[WS-PYTHON-%s] Failed to create stdout pipe: %v", submission.ID, err)
|
||||
conn.WriteMessage(websocket.TextMessage, []byte("Error: Failed to create stdout pipe\n"))
|
||||
return
|
||||
}
|
||||
|
||||
stderr, err := cmd.StderrPipe()
|
||||
if err != nil {
|
||||
log.Printf("[WS-PYTHON-%s] Failed to create stderr pipe: %v", submission.ID, err)
|
||||
conn.WriteMessage(websocket.TextMessage, []byte("Error: Failed to create stderr pipe\n"))
|
||||
return
|
||||
}
|
||||
|
||||
// Start the command
|
||||
if err := cmd.Start(); err != nil {
|
||||
log.Printf("[WS-PYTHON-%s] Failed to start command: %v", submission.ID, err)
|
||||
conn.WriteMessage(websocket.TextMessage, []byte(fmt.Sprintf("Error: Failed to start command: %v\n", err)))
|
||||
return
|
||||
}
|
||||
|
||||
// Create a context with timeout
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
||||
defer cancel()
|
||||
|
||||
// Create a channel to signal when the command is done
|
||||
done := make(chan struct{})
|
||||
|
||||
// Start a goroutine to handle command completion
|
||||
go func() {
|
||||
err := cmd.Wait()
|
||||
if err != nil {
|
||||
log.Printf("[WS-PYTHON-%s] Command failed: %v", submission.ID, err)
|
||||
conn.WriteMessage(websocket.TextMessage, []byte(fmt.Sprintf("\nExecution failed: %v\n", err)))
|
||||
} else {
|
||||
log.Printf("[WS-PYTHON-%s] Command completed successfully", submission.ID)
|
||||
conn.WriteMessage(websocket.TextMessage, []byte("\nExecution completed successfully\n"))
|
||||
}
|
||||
close(done)
|
||||
}()
|
||||
|
||||
// Start a goroutine to read from stdout and stderr
|
||||
go func() {
|
||||
scanner := bufio.NewScanner(io.MultiReader(stdout, stderr))
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
log.Printf("[WS-PYTHON-%s] Output: %s", submission.ID, line)
|
||||
conn.WriteMessage(websocket.TextMessage, []byte(line+"\n"))
|
||||
}
|
||||
}()
|
||||
|
||||
// Handle input from the WebSocket
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case input := <-inputChan:
|
||||
log.Printf("[WS-PYTHON-%s] Received input: %s", submission.ID, input)
|
||||
// Write the input to stdin
|
||||
_, err := io.WriteString(stdin, input+"\n")
|
||||
if err != nil {
|
||||
log.Printf("[WS-PYTHON-%s] Failed to write to stdin: %v", submission.ID, err)
|
||||
}
|
||||
case <-ctx.Done():
|
||||
return
|
||||
case <-done:
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
// Wait for the command to complete or timeout
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
log.Printf("[WS-PYTHON-%s] Execution timed out after 30 seconds", submission.ID)
|
||||
conn.WriteMessage(websocket.TextMessage, []byte("\nExecution timed out after 30 seconds\n"))
|
||||
cmd.Process.Kill()
|
||||
case <-done:
|
||||
// Command completed
|
||||
}
|
||||
|
||||
elapsed := time.Since(startTime)
|
||||
log.Printf("[WS-PYTHON-%s] Python execution completed in %v", submission.ID, elapsed)
|
||||
|
||||
// Update submission result
|
||||
submission.CompletedAt = time.Now()
|
||||
submission.Status = "completed"
|
||||
}
|
||||
|
||||
// executeJavaWithWebSocket runs Java code with WebSocket for I/O
|
||||
func (s *ExecutionService) executeJavaWithWebSocket(submission *model.CodeSubmission, inputChan chan string, conn *websocket.Conn) {
|
||||
// For now, just send a message that this is not implemented
|
||||
conn.WriteMessage(websocket.TextMessage, []byte("Java WebSocket execution not yet implemented\n"))
|
||||
submission.Status = "failed"
|
||||
submission.Output = "Java WebSocket execution not yet implemented"
|
||||
}
|
||||
|
||||
// executeCWithWebSocket runs C code with WebSocket for I/O
|
||||
func (s *ExecutionService) executeCWithWebSocket(submission *model.CodeSubmission, inputChan chan string, conn *websocket.Conn) {
|
||||
// For now, just send a message that this is not implemented
|
||||
conn.WriteMessage(websocket.TextMessage, []byte("C WebSocket execution not yet implemented\n"))
|
||||
submission.Status = "failed"
|
||||
submission.Output = "C WebSocket execution not yet implemented"
|
||||
}
|
||||
|
||||
// executeCppWithWebSocket runs C++ code with WebSocket for I/O
|
||||
func (s *ExecutionService) executeCppWithWebSocket(submission *model.CodeSubmission, inputChan chan string, conn *websocket.Conn) {
|
||||
// For now, just send a message that this is not implemented
|
||||
conn.WriteMessage(websocket.TextMessage, []byte("C++ WebSocket execution not yet implemented\n"))
|
||||
submission.Status = "failed"
|
||||
submission.Output = "C++ WebSocket execution not yet implemented"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user