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

Simplifies server code, gladly. #106

Merged
merged 1 commit into from
Aug 13, 2016
Merged
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
51 changes: 2 additions & 49 deletions packages/reactotron-app/App/Stores/SessionStore.js
Original file line number Diff line number Diff line change
@@ -1,63 +1,16 @@
import UiStore from './UiStore'
import { ipcRenderer } from 'electron'
import { createServer } from 'reactotron-core-server'

class Session {

ui
bridgeTransport
server

constructor (port = 9090) {
// configure the bridge transport
this.bridgeTransport = {
stop: () => ipcRenderer.send('reactotron.stop'),
send: (command, commandPayload) => ipcRenderer.send('reactotron.send', command, commandPayload),
close: () => ipcRenderer.send('reactotron.close')
}

// javascript a few functions ... :(
let realConnect
let realDisconnect
let realCommand

// our own core-server transport
const createTransport = () => ({ port, onConnect, onDisconnect, onCommand }) => {
// where we grab a ref to the functions we'll callback
realConnect = onConnect
realDisconnect = onDisconnect
realCommand = onCommand

// but return the configured bridge
return this.bridgeTransport
}

// when the main process tells us it's ready
ipcRenderer.on('reactotron.initialized', () => {
// listen for connect messages from the main process & pipe them into our transport
ipcRenderer.on('reactotron.connect', (event, { id, address }) => {
realConnect && realConnect({ id, address })
})
// same for disconnect
ipcRenderer.on('reactotron.disconnect', (event, id) => {
realDisconnect && realDisconnect(id)
})
// and command
ipcRenderer.on('reactotron.command', (event, command, commandPayload) => {
realCommand && realCommand(command, commandPayload)
})

// let's tell the main process it should create the connection
ipcRenderer.send('reactotron.create', port)
})

// let's create our faux server & start it up
this.server = createServer({ port }, createTransport())
this.server = createServer({ port })
this.server.start()

// create the ui store
this.ui = new UiStore(this.server)
}

}

export default Session
44 changes: 0 additions & 44 deletions packages/reactotron-app/TransportBridge.js

This file was deleted.

5 changes: 1 addition & 4 deletions packages/reactotron-app/main.development.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
import { app, BrowserWindow, Menu, shell, ipcMain } from 'electron'
import createBridge from './TransportBridge'
import { app, BrowserWindow, Menu, shell } from 'electron'

let menu
let template
let mainWindow = null
let server = null

if (process.env.NODE_ENV === 'development') {
require('electron-debug')()
Expand All @@ -28,7 +26,6 @@ app.on('ready', () => {
mainWindow.webContents.on('did-finish-load', () => {
mainWindow.show()
mainWindow.focus()
server = createBridge(mainWindow.webContents)
})

mainWindow.on('closed', () => {
Expand Down
1 change: 0 additions & 1 deletion packages/reactotron-core-client/scripts/send.js
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,6 @@ client.configure({
shotgun()
},
onDisconnect: () => console.log('disconnected'),
onCommand: command => console.log({inbound: command}),
plugins: CorePlugins
})

Expand Down
129 changes: 67 additions & 62 deletions packages/reactotron-core-server/src/index.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import R from 'ramda'
import { merge, length, find, propEq, without, contains, forEach, pluck, reject } from 'ramda'
import Commands from './commands'
import validate from './validation'
import { observable, computed, asFlat } from 'mobx'
import defaultTransport from './transport'
import socketIO from 'socket.io'

const DEFAULTS = {
port: 9090, // the port to live (required)
Expand All @@ -16,21 +16,12 @@ const DEFAULTS = {
class Server {

// the configuration options
@observable options = R.merge({}, DEFAULTS)
@observable options = merge({}, DEFAULTS)
started = false
messageId = 0
subscriptions = []

/**
* Holds the transport which bridges this server to a real socket. We need
* this for electron to proxy ipc between the main process & renderer.
*/
transport = null

/**
* The function which creates a transport. Called on start().
*/
createTransport
partialConnections = []
io

/**
* Holds the commands the client has sent.
Expand All @@ -46,81 +37,97 @@ class Server {
* How many people are connected?
*/
@computed get connectionCount () {
return R.length(this.connections)
return length(this.connections)
}

constructor (createTransport) {
if (createTransport) {
this.createTransport = createTransport
} else {
this.createTransport = defaultTransport
}
this.send = this.send.bind(this)
}

findConnectionById = connection => R.find(R.propEq('id', connection), this.connections)
findConnectionById = id => find(propEq('id', id), this.connections)
findPartialConnectionById = id => find(propEq('id', id), this.partialConnections)

/**
* Set the configuration options.
*/
configure (options = {}) {
// options get merged & validated before getting set
const newOptions = R.merge(this.options, options)
const newOptions = merge(this.options, options)
validate(newOptions)
this.options = newOptions
return this
}

/**
* Starts the server.
* Starts the server
*/
start () {
this.started = true
const { port, onStart } = this.options
const { onCommand, onConnect, onDisconnect } = this.options
let partialConnections = []

// start listening
this.transport = this.createTransport({
port,

// fires when we the transport receives a new connection
onConnect: ({id, address}) => {
const connection = { id, address }
partialConnections.push(connection)
onConnect && onConnect(connection)

// resend the subscriptions to the client upon connecting
this.stateValuesSendSubscriptions()
},

// fires when we say goodbye to someone
onDisconnect: id => {
const connection = this.findConnectionById(id)
this.connections.remove(connection)
onDisconnect && onDisconnect(connection)
},

// fires when the transport gives us a connection
onCommand: (id, {type, payload}) => {
this.io = socketIO(port)

// register events
this.io.on('connection', socket => {
// a wild client appears
const partialConnection = {
id: socket.id,
address: socket.request.connection.remoteAddress,
socket
}

// tuck them away in a "almost connected status"
this.partialConnections.push(partialConnection)

// trigger onConnect
onConnect(partialConnection)

// when this client disconnects
socket.on('disconnect', () => {
onDisconnect(socket.id)
// remove them from the list partial list
this.partialConnections = reject(propEq('id', socket.id), this.partialConnections)

// remove them from the main connections list
const severingConnection = find(propEq('id', socket.id), this.connections)
if (severingConnection) {
this.connections.remove(severingConnection)
onDisconnect && onDisconnect(severingConnection)
}
})

// when we receive a command from the client
socket.on('command', ({ type, payload }) => {
this.messageId++
const date = new Date()
const fullCommand = { type, payload, messageId: this.messageId, date }

// for client intros
if (type === 'client.intro') {
const partialConnection = R.find(R.propEq('id', id), partialConnections)
// find them in the partial connection list
const partialConnection = find(propEq('id', socket.id), this.partialConnections)

// add their address in
fullCommand.payload.address = partialConnection.address
partialConnections = R.without([partialConnection], partialConnections)

// remove them from the partial connections list
this.partialConnections = reject(propEq('id', socket.id), this.partialConnections)

// bestow the payload onto the connection
const connection = { ...partialConnection, ...payload }
const connection = merge(payload, { id: socket.id, address: partialConnection.address })

// then trigger the connection
this.connections.push(connection)
}

onCommand(fullCommand)
this.commands.addCommand(fullCommand)
}
onCommand(fullCommand)
})

// resend the subscriptions to the client upon connecting
this.stateValuesSendSubscriptions()
})

// trigger the start message
Expand All @@ -130,13 +137,13 @@ class Server {
}

/**
* Stops the server.
* Stops the server
*/
stop () {
const { onStop } = this.options
this.started = false
this.transport.stop()
this.transport.close()
forEach(s => s && s.connected && s.disconnect(), pluck('socket', this.connections))
this.io.close()

// trigger the stop message
onStop && onStop()
Expand All @@ -148,7 +155,7 @@ class Server {
* Sends a command to the client
*/
send (type, payload) {
this.transport.send('command', { type, payload })
this.io.sockets.emit('command', { type, payload })
}

/**
Expand Down Expand Up @@ -184,7 +191,7 @@ class Server {
*/
stateValuesSubscribe (path) {
// prevent duplicates
if (R.contains(path, this.subscriptions)) return
if (contains(path, this.subscriptions)) return
// subscribe
this.subscriptions.push(path)
this.stateValuesSendSubscriptions()
Expand All @@ -195,8 +202,8 @@ class Server {
*/
stateValuesUnsubscribe (path) {
// if it doesn't exist, jet
if (!R.contains(path, this.subscriptions)) return
this.subscriptions = R.without([path], this.subscriptions)
if (!contains(path, this.subscriptions)) return
this.subscriptions = without([path], this.subscriptions)
this.stateValuesSendSubscriptions()
}

Expand All @@ -213,10 +220,8 @@ class Server {
export default Server

// convenience factory function
export const createServer = (options, createTransport) => {
const server = new Server(createTransport)
export const createServer = (options) => {
const server = new Server()
server.configure(options)
return server
}

export const createDefaultTransport = defaultTransport
Loading