Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

proposal for migrating to fastify for performance reasons #155

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 1 addition & 5 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,3 @@
require('dotenv').config()

const app = require('./lib/server')()
const port = process.env.PORT || 3000
app.listen(port, () => {
console.log('Listening at http://localhost:' + port)
})
require('./lib/server')
13 changes: 0 additions & 13 deletions lib/channel-is-banned-middleware.js

This file was deleted.

101 changes: 57 additions & 44 deletions lib/event-bus.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
'use strict'
Uzlopak marked this conversation as resolved.
Show resolved Hide resolved

const Redis = require('ioredis')
const EventEmitter = require('events')

Expand All @@ -6,56 +8,67 @@ const EventEmitter = require('events')
* allowing payloads to be received by one instance of Smee and sent by
* others. This allows Smee to be multi-instanced!
*/
module.exports = class EventBus {
constructor () {
this.events = new EventEmitter()

// If Redis isn't enabled, don't try to connect
if (!process.env.REDIS_URL) return
function EventBus ({ logger = console } = {}) {
if (!(this instanceof EventBus)) {
return new EventBus()
}
this.events = new EventEmitter()

this.opts = {
connection: process.env.REDIS_URL,
namespace: 'global'
}
// If Redis isn't enabled, don't try to connect
if (!process.env.REDIS_URL) {
logger.warn('Redis not enabled; events will not be shared between instances')
return
}

// Need two Redis clients; one cannot subscribe and publish.
this.sub = new Redis(this.opts.connection)
this.pub = new Redis(this.opts.connection)
this.opts = {
connection: process.env.REDIS_URL,
namespace: 'global'
}

// Subscribe to the Redis event channel
this.sub.subscribe(this.opts.namespace)
// Need two Redis clients; one cannot subscribe and publish.
this.sub = new Redis(this.opts.connection)
this.pub = new Redis(this.opts.connection)

// When we get a message, parse it and
// throw it over to the EventEmitter.
this.sub.on('message', (_, message) => {
const json = JSON.parse(message)
return this.emitLocalEvent(json)
})
}
// Subscribe to the Redis event channel
this.sub.subscribe(this.opts.namespace)

/**
* Emit an event to this machine's in-memory EventEmitter
* @param {object} opts
* @param {string} opts.channel - Channel name
* @param {any} opts.payload
*/
emitLocalEvent ({ channel, payload }) {
return this.events.emit(channel, payload)
}
logger.info(`Redis enabled; events will be shared between instances using ${this.opts.namespace} as the namespace`)

/**
* Emit an event to the Redis bus, which will tell every subscriber about it
* @param {object} opts
* @param {string} opts.channel - Channel name
* @param {any} opts.payload
*/
async emitEvent (opts) {
// Only emit local events if Redis isn't configured
if (!process.env.REDIS_URL) {
return this.emitLocalEvent(opts)
} else {
const message = JSON.stringify(opts)
return this.pub.publish(this.opts.namespace, message)
// When we get a message, parse it and
// throw it over to the EventEmitter.
this.sub.on('message', (_, message) => {
const channel = message.slice(0, message.indexOf(':'))
if (this.events.listenerCount(channel)) {
return this.events.emit(channel, message.slice(channel.length + 1))
}
}
})
}

/**
* Emit an event to this machine's in-memory EventEmitter
* @param {object} opts
* @param {string} opts.channel - Channel name
* @param {any} opts.payload
*/
EventBus.prototype.emitLocalEvent = function (opts) {
return this.events.emit(opts.channel, opts.payload)
}

/**
* Emit an event to the Redis bus, which will tell every subscriber about it
* @param {object} opts
* @param {string} opts.channel - Channel name
* @param {any} opts.payload
*/
EventBus.prototype.emitEvent = process.env.REDIS_URL
? async function (opts) {
return this.pub.publish(this.opts.namespace, opts.channel + ':' + JSON.stringify(opts.payload))
}
// Only emit local events if Redis isn't configured

: async function (opts) {
this.emitLocalEvent(opts)
}

module.exports = EventBus
19 changes: 19 additions & 0 deletions lib/force-https.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
'use strict'

const fastifyPlugin = require('fastify-plugin')

module.exports = fastifyPlugin(function (fastify, opts, next) {
fastify.addHook('onRequest', function (req, reply, done) {
if (req.protocol !== 'https') {
if (req.method === 'GET' || req.method === 'HEAD') {
reply.redirect(`https://${req.hostname}${req.url}`)
return
} else {
reply.code(403).send('Please use HTTPS when submitting data to this server.')
return
}
}
done()
})
next()
})
25 changes: 15 additions & 10 deletions lib/keep-alive.js
Original file line number Diff line number Diff line change
@@ -1,19 +1,24 @@
'use strict'

module.exports = class KeepAlive {
constructor (callback, delay) {
this.callback = callback
this.delay = delay
constructor (interval) {
this.replies = new Set()
this.delay = interval
this.timer = setTimeout(this.ping.bind(this), interval)
}

start () {
this.id = setInterval(this.callback, this.delay)
ping () {
for (const reply of this.replies) {
reply?.ssePing()
}
this.timer.refresh()
}

stop () {
clearInterval(this.id)
start (reply) {
this.replies.add(reply)
}

reset () {
this.stop()
this.start()
stop (reply) {
this.replies.delete(reply)
}
}
62 changes: 0 additions & 62 deletions lib/redis-bus.js

This file was deleted.

Loading