From 5c21b6a15f9d15ba796e02bb0dc2b32269712801 Mon Sep 17 00:00:00 2001 From: MathieuSevignyLavallee <89943988+MathieuSevignyLavallee@users.noreply.github.com> Date: Fri, 15 Nov 2024 19:35:41 -0500 Subject: [PATCH] optimize and cleanup --- nginx/Dockerfile | 3 - nginx/conf.d/default.conf | 5 +- nginx/njs/main.js | 69 +++++++-------- test/stressTest/class/student.js | 38 ++++++--- test/stressTest/class/teacher.js | 45 ++++++---- test/stressTest/main.js | 142 +++++++++++++------------------ 6 files changed, 143 insertions(+), 159 deletions(-) diff --git a/nginx/Dockerfile b/nginx/Dockerfile index 9f1280d..a201ecf 100644 --- a/nginx/Dockerfile +++ b/nginx/Dockerfile @@ -59,9 +59,6 @@ RUN chown -R nginx:nginx /var/cache/nginx \ # Verify the configuration # RUN nginx -t --dry-run -# Switch to non-root user -USER nginx - # Expose HTTP port EXPOSE 80 diff --git a/nginx/conf.d/default.conf b/nginx/conf.d/default.conf index 20715be..509a0a4 100644 --- a/nginx/conf.d/default.conf +++ b/nginx/conf.d/default.conf @@ -1,8 +1,5 @@ js_import njs/main.js; -js_set $room_cache_check main.checkCache; -js_set $room_cache_set main.setCache; - map $http_upgrade $connection_upgrade { default upgrade; '' close; @@ -59,4 +56,4 @@ server { proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } -} +} \ No newline at end of file diff --git a/nginx/njs/main.js b/nginx/njs/main.js index 5b5e146..d824272 100644 --- a/nginx/njs/main.js +++ b/nginx/njs/main.js @@ -1,8 +1,9 @@ -const roomCache = new Map(); - async function fetchRoomInfo(r) { try { - let res = await r.subrequest(`/api/room/${r.variables.room_id}`, { method: 'GET' }); + // Make request to API to get room info + let res = await r.subrequest('/api/room/' + r.variables.room_id, { + method: 'GET' + }); if (res.status !== 200) { r.error(`Failed to fetch room info: ${res.status}`); @@ -10,7 +11,7 @@ async function fetchRoomInfo(r) { } let room = JSON.parse(res.responseText); - r.error(`Debug: Room info fetched: ${JSON.stringify(room)}`); + r.error(`Debug: Room info: ${JSON.stringify(room)}`); // Debug log return room; } catch (error) { r.error(`Error fetching room info: ${error}`); @@ -18,48 +19,36 @@ async function fetchRoomInfo(r) { } } -function checkCache(r) { - let room = roomCache.get(r.variables.room_id); - if (room) { - r.error(`Cache hit for room_id: ${r.variables.room_id}`); - r.return(200, JSON.stringify(room)); - } else { - r.error(`Cache miss for room_id: ${r.variables.room_id}`); - r.return(404); - } -} - -function setCache(r) { - let room = JSON.parse(r.responseBody); - roomCache.set(r.variables.room_id, room); - r.error(`Cached room info: ${JSON.stringify(room)}`); -} - async function routeWebSocket(r) { - let room = roomCache.get(r.variables.room_id); + try { + const roomInfo = await fetchRoomInfo(r); - if (!room) { - r.error(`Cache miss. Fetching room info for: ${r.variables.room_id}`); - room = await fetchRoomInfo(r); - - if (!room || !room.host) { - r.error(`Invalid room info for room_id: ${r.variables.room_id}`); + if (!roomInfo || !roomInfo.host) { + r.error(`Debug: Invalid room info: ${JSON.stringify(roomInfo)}`); r.return(404, 'Room not found or invalid'); return; } - roomCache.set(r.variables.room_id, room); // Cache the result - } else { - r.error(`Cache hit for room_id: ${r.variables.room_id}`); + // Make sure the host includes protocol if not already present + let proxyUrl = roomInfo.host; + if (!proxyUrl.startsWith('http://') && !proxyUrl.startsWith('https://')) { + proxyUrl = 'http://' + proxyUrl; + } + + r.error(`Debug: Original URL: ${r.uri}`); + r.error(`Debug: Setting proxy target to: ${proxyUrl}`); + r.error(`Debug: Headers: ${JSON.stringify(r.headersIn)}`); + + // Set the proxy target variable + r.variables.proxy_target = proxyUrl; + + // Redirect to the websocket proxy + r.internalRedirect('@websocket_proxy'); + + } catch (error) { + r.error(`WebSocket routing error: ${error}`); + r.return(500, 'Internal routing error'); } - - let proxyUrl = room.host.startsWith('http://') || room.host.startsWith('https://') - ? room.host - : `http://${room.host}`; - - r.error(`Routing WebSocket to: ${proxyUrl}`); - r.variables.proxy_target = proxyUrl; - r.internalRedirect('@websocket_proxy'); } -export default { routeWebSocket, checkCache, setCache }; +export default { routeWebSocket }; \ No newline at end of file diff --git a/test/stressTest/class/student.js b/test/stressTest/class/student.js index a166a9a..890b425 100644 --- a/test/stressTest/class/student.js +++ b/test/stressTest/class/student.js @@ -8,20 +8,32 @@ export class Student { } connectToRoom(baseUrl) { - this.socket = io(baseUrl, { - path: `/api/room/${this.roomName}/socket`, - transports: ['websocket'],autoConnect: true, - reconnection: true, - reconnectionAttempts: 10, - reconnectionDelay: 10000, - timeout: 20000, + return new Promise((resolve, reject) => { + try { + this.socket = io(baseUrl, { + path: `/api/room/${this.roomName}/socket`, + transports: ['websocket'], + autoConnect: true, + reconnection: true, + reconnectionAttempts: 10, + reconnectionDelay: 10000, + timeout: 20000, + }); + + this.socket.on('connect', () => { + this.joinRoom(this.roomName, this.username); + resolve(this.socket); + }); + + this.socket.on('error', (error) => { + reject(new Error(`Connection error: ${error.message}`)); + }); + + } catch (error) { + console.error(`Error connecting ${this.name} to room ${this.roomId}:`, error.message); + reject(error); + } }); - - this.socket.on('connect', () => { - this.joinRoom(this.roomName,this.username); - }); - - return this.socket; } joinRoom(roomName, username) { diff --git a/test/stressTest/class/teacher.js b/test/stressTest/class/teacher.js index 4f950ba..0f895c1 100644 --- a/test/stressTest/class/teacher.js +++ b/test/stressTest/class/teacher.js @@ -8,23 +8,36 @@ export class Teacher { } connectToRoom(baseUrl) { - this.socket = io(baseUrl, { - path: `/api/room/${this.roomName}/socket`, - transports: ['websocket'],autoConnect: true, - reconnection: true, - reconnectionAttempts: 10, - reconnectionDelay: 10000, - timeout: 20000, + return new Promise((resolve, reject) => { + try { + this.socket = io(baseUrl, { + path: `/api/room/${this.roomName}/socket`, + transports: ['websocket'], + autoConnect: true, + reconnection: true, + reconnectionAttempts: 10, + reconnectionDelay: 10000, + timeout: 20000, + }); + + this.socket.on('connect', () => { + this.createRoom(this.roomName); + resolve(this.socket); + }); + + this.socket.on('error', (error) => { + reject(new Error(`Connection error: ${error.message}`)); + }); + + this.socket.on('create-success', () => { + + }); + + } catch (error) { + console.error(`Error connecting ${this.name} to room ${this.roomId}:`, error.message); + reject(error); + } }); - - this.socket.on('connect', () => { - this.createRoom(this.roomName); - }); - - this.socket.on('create-success', () => { - }); - - return this.socket; } createRoom() { diff --git a/test/stressTest/main.js b/test/stressTest/main.js index 6067689..ec1b092 100644 --- a/test/stressTest/main.js +++ b/test/stressTest/main.js @@ -3,126 +3,102 @@ import { Student } from './class/student.js'; import { Teacher } from './class/teacher.js'; const BASE_URL = 'http://localhost'; -const user = { - username: 'admin@example.com', - password: 'adminPassword', -}; - -const numberRooms = 30; -const studentPerRoom = 59; // Max is 60; 1 slot is reserved for the teacher - +const user = { username: 'admin@example.com', password: 'adminPassword' }; +const numberRooms = 5; +const studentPerRoom = 59; // Max is 60; 1 slot reserved for the teacher const roomAssociations = {}; const allSockets = []; // Track all active WebSocket connections -async function createRoomsAndTeachers(token) { - const roomCreationPromises = []; - const teachers = []; - - for (let index = 0; index < numberRooms; index++) { - roomCreationPromises.push( - createRoomContainer(BASE_URL, token).then((room) => { - if (room?.id) { - const teacher = new Teacher(`teacher_${index}`, room.id); - teachers.push(teacher); - - roomAssociations[room.id] = { - teacher, - students: [], - }; - - // Track teacher WebSocket for cleanup - if (teacher.socket) { - allSockets.push(teacher.socket); - } - } - }) - ); - } - - await Promise.allSettled(roomCreationPromises); - console.log(`Created ${Object.keys(roomAssociations).length} rooms with associated teachers.`); - return teachers; -} - -async function connectTeachersToRooms(teachers) { - const teacherConnectionPromises = teachers.map(async (teacher) => { - await teacher.connectToRoom(BASE_URL); - if (teacher.socket) { - allSockets.push(teacher.socket); // Track WebSocket +async function createRoomContainers(token) { + const roomCreationPromises = Array.from({ length: numberRooms }, async () => { + const room = await createRoomContainer(BASE_URL, token); + if (room?.id) { + roomAssociations[room.id] = { teacher: null, students: [] }; + console.log(`Created room with ID: ${room.id}`); + } else { + console.warn('Failed to create a room.'); } }); - await Promise.allSettled(teacherConnectionPromises); - console.log('All teachers connected to their rooms.'); + await Promise.allSettled(roomCreationPromises); + console.log(`Created ${Object.keys(roomAssociations).length} room containers.`); +} + +async function addAndConnectTeachers() { + const teacherCreationPromises = Object.keys(roomAssociations).map(async (roomId, index) => { + const teacher = new Teacher(`teacher_${index}`, roomId); + const socket = await teacher.connectToRoom(BASE_URL); + + if (socket.connected) { + allSockets.push(socket); + roomAssociations[roomId].teacher = teacher; + console.log(`Teacher ${teacher.username} connected to room ${roomId}.`); + } else { + console.warn(`Failed to connect teacher_${index} to room ${roomId}`); + } + }); + + await Promise.allSettled(teacherCreationPromises); + console.log('All teachers added and connected to their respective rooms.'); } async function addAndConnectStudents() { - const studentCreationPromises = []; - - Object.entries(roomAssociations).forEach(([roomId, association], roomIndex) => { - for (let i = 0; i < studentPerRoom; i++) { + const studentCreationPromises = Object.entries(roomAssociations).flatMap(([roomId, association], roomIndex) => + Array.from({ length: studentPerRoom }, async (_, i) => { const student = new Student(`student_${roomIndex}_${i}`, roomId); - association.students.push(student); + const socket = await student.connectToRoom(BASE_URL); - studentCreationPromises.push( - student.connectToRoom(BASE_URL).then(() => { - if (student.socket) { - allSockets.push(student.socket); // Track WebSocket - } - }) - ); - } - }); + if (socket.connected) { + allSockets.push(socket); + association.students.push(student); + console.log(`Student ${student.username} connected to room ${roomId}.`); + } else { + console.warn(`Failed to connect student_${roomIndex}_${i} to room ${roomId}`); + } + }) + ); await Promise.allSettled(studentCreationPromises); - console.log('All students connected to their respective rooms.'); + console.log('All students added and connected to their respective rooms.'); } function closeAllSockets() { - console.log('Closing all WebSocket connections...'); + console.log('Closing all Socket.IO connections...'); allSockets.forEach((socket) => { - try { - if (socket.readyState === socket.OPEN) { - socket.close(); // Gracefully close the WebSocket - console.log('Closed WebSocket connection.'); + if (socket && socket.connected) { + try { + socket.disconnect(); + console.log('Disconnected Socket.IO connection.'); + } catch (error) { + console.error('Error disconnecting Socket.IO socket:', error.message); } - } catch (error) { - console.error('Error closing WebSocket:', error.message); } }); - console.log('All WebSocket connections closed.'); + console.log('All Socket.IO connections disconnected.'); } async function main() { try { const token = await attemptLoginOrRegister(BASE_URL, user.username, user.password); - if (!token) { - console.error('Failed to log in. Exiting...'); - return; - } + if (!token) throw new Error('Failed to log in.'); - const teachers = await createRoomsAndTeachers(token); - await connectTeachersToRooms(teachers); - await addAndConnectStudents(); + await createRoomContainers(token); + await addAndConnectTeachers(); + await addAndConnectStudents(); console.log('All tasks completed.'); } catch (error) { console.error('An error occurred:', error.message); - } finally { - closeAllSockets(); } } -// Handle script termination (Ctrl+C) +// Handle script termination and exit process.on('SIGINT', () => { console.log('Script interrupted (Ctrl+C).'); closeAllSockets(); - process.exit(0); // Exit cleanly + process.exit(0); }); -// Handle script exit -process.on('exit', () => { - closeAllSockets(); -}); +process.on('exit', closeAllSockets); main();