<commit_before>'use strict'

const errcode = require('../core/errcode')
const co = require('co')
const log = require('./log')
const utils = require('./utils')

class Router
{
	constructor(socketServer)
	{
		this.routes = new Map()
		this.usersBySocket = new Map()
		this.clientsByUserID = new Map()
		this.socketServer = socketServer
		this.log = log.create('router')
	}
	
	addUser(userID, socket, userData, privileges, client)
	{
		let clients = this.clientsByUserID.get(userID)
		if (!clients)
		{
			clients = {}
			this.clientsByUserID.set(userID, clients)
		}
		
		clients[client] = socket
		
		this.usersBySocket.set(socket, {
			userID,
			socket,
			userData,
			privileges,
			client,
		})
	}
	
	removeUser(socket)
	{
		const data = this.usersBySocket.get(socket)
		if (!data)
		{
			return false
		}
		
		const clients = this.clientsByUserID.get(data.userID)
		delete clients[data.client]
		
		if (Object.keys(clients).length === 0)
		{
			this.clientsByUserID.delete(data.userID)
		}
		
		return this.usersBySocket.delete(socket)
	}
	
	addRoute(path, handler, options)
	{
		if (this.routes.has(path))
		{
			throw new Error('Duplicate route: ' + path)
		}
		
		this.routes.set(path, { handler, options: options || {} })
	}
	
	*dispatch(path, socket, params)
	{
		const route = this.routes.get(path)
		if (!route)
		{
			return errcode.routeNotFound(path)
		}
		
		let request = { params, socket }
		
		if (route.options.authenticated)
		{
			request.user = this.usersBySocket.get(socket)
			if (!request.user)
			{
				return errcode.authenticationRequired()
			}
		}
		
		return yield route.handler(request)
	}
	
	start()
	{
		this.socketServer.on('connection', socket =>
		{
			socket.on('message', message => this.onMessage(socket, message))
		})
	}
	
	respond(socket, data, correlation)
	{
		let response
		if (data instanceof errcode.MetagameError)
		{
			response = data
		}
		else
		{
			response = { data }
		}
		
		response.workerID = utils.getWorkerID()
		response.correlation = correlation
		socket.send(JSON.stringify(response))
		
		this.log.debug({ response }, 'response sent')
	}
	
	onMessage(socket, message)
	{
		let requestData
		try
		{
			requestData = JSON.parse(message)
		}
		catch (err)
		{
			this.respond(socket, errcode.messageParsingFailed())
			return
		}
		
		this.log.debug({ requestData }, 'message received')
		
		const self = this
		co(function*()
		{
			let data
			try
			{
				data = yield self.dispatch(requestData.path, socket, requestData.params || {})
			}
			catch (err)
			{
				self.log.error(err)
				data = errcode.internalError()
			}
			self.respond(socket, data, requestData.correlation)
		}).catch(err =>
		{
			self.log.error(err)
		})
	}
}

module.exports = Router<commit_msg>Record time taken to service requests<commit_after>'use strict'

const errcode = require('../core/errcode')
const co = require('co')
const log = require('./log')
const utils = require('./utils')

class Router
{
	constructor(socketServer)
	{
		this.routes = new Map()
		this.usersBySocket = new Map()
		this.clientsByUserID = new Map()
		this.socketServer = socketServer
		this.log = log.create('router')
	}
	
	addUser(userID, socket, userData, privileges, client)
	{
		let clients = this.clientsByUserID.get(userID)
		if (!clients)
		{
			clients = {}
			this.clientsByUserID.set(userID, clients)
		}
		
		clients[client] = socket
		
		this.usersBySocket.set(socket, {
			userID,
			socket,
			userData,
			privileges,
			client,
		})
	}
	
	removeUser(socket)
	{
		const data = this.usersBySocket.get(socket)
		if (!data)
		{
			return false
		}
		
		const clients = this.clientsByUserID.get(data.userID)
		delete clients[data.client]
		
		if (Object.keys(clients).length === 0)
		{
			this.clientsByUserID.delete(data.userID)
		}
		
		return this.usersBySocket.delete(socket)
	}
	
	addRoute(path, handler, options)
	{
		if (this.routes.has(path))
		{
			throw new Error('Duplicate route: ' + path)
		}
		
		this.routes.set(path, { handler, options: options || {} })
	}
	
	*dispatch(path, socket, params)
	{
		const route = this.routes.get(path)
		if (!route)
		{
			return errcode.routeNotFound(path)
		}
		
		let request = { params, socket }
		
		if (route.options.authenticated)
		{
			request.user = this.usersBySocket.get(socket)
			if (!request.user)
			{
				return errcode.authenticationRequired()
			}
		}
		
		return yield route.handler(request)
	}
	
	start()
	{
		this.socketServer.on('connection', socket =>
		{
			socket.on('message', message => this.onMessage(socket, message))
		})
	}
	
	respond(socket, data, correlation, timeTaken)
	{
		let response
		if (data instanceof errcode.MetagameError)
		{
			response = data
		}
		else
		{
			response = { data }
		}
		
		response.workerID = utils.getWorkerID()
		response.correlation = correlation
		response.timeTaken = timeTaken
		
		socket.send(JSON.stringify(response))
		
		this.log.debug({ response }, 'response sent')
	}
	
	onMessage(socket, message)
	{
		let requestData
		try
		{
			requestData = JSON.parse(message)
		}
		catch (err)
		{
			this.respond(socket, errcode.messageParsingFailed())
			return
		}
		
		const receivedAt = Date.now()
		this.log.debug({ requestData }, 'message received')
		
		const self = this
		co(function*()
		{
			let data
			try
			{
				data = yield self.dispatch(requestData.path, socket, requestData.params || {})
			}
			catch (err)
			{
				self.log.error(err)
				data = errcode.internalError()
			}
			
			const timeTaken = Date.now() - receivedAt
			self.respond(socket, data, requestData.correlation, timeTaken)
		}).catch(err =>
		{
			self.log.error(err)
		})
	}
}

module.exports = Router
{'fraction_non_alphanumeric': 0.13525970572593513, 'fraction_numerical': 0.0003545470661230278, 'mean_word_length': 3.1333333333333333, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 9, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4175102', 'n_tokens_mistral': 2332, 'n_tokens_neox': 1949, 'n_words': 373}