Created
May 5, 2012 18:26
-
-
Save dodo/2604577 to your computer and use it in GitHub Desktop.
stateful http server
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
fs = require 'fs' | |
path = require 'path' | |
http = require 'http' | |
{ EventEmitter } = require 'events' | |
socketio = require 'socket.io' | |
Cookies = require 'cookies' | |
hat = require 'hat' | |
{ Template } = require 'dynamictemplate' | |
render = require 'dynamictemplate/render' | |
# store code | |
class Sessions extends EventEmitter | |
constructor: -> | |
@sessions = {} | |
@i = 0 | |
manage: (req, res, sid, callback) -> | |
console.log "?", sid | |
sid ?= hat() | |
# session | |
console.log "?", sid | |
ses = @sessions[sid] ? @create sid | |
# emits | |
ses.emit("url:#{req.url}", req, res) | |
console.log "save", ses.id | |
callback(null, ses) | |
ses.emit('request', req, res) | |
create: (sid) -> | |
console.log "create" | |
# FIXME dispose old session? | |
@emit 'session', @sessions[sid] = ses = new EventEmitter | |
ses.setId = (id) => | |
console.log "change id" | |
delete @sessions[ses.id] | |
@sessions[id] = ses | |
ses.id = id | |
ses.id = sid | |
return ses | |
get: (sid) -> | |
@sessions[sid] | |
# sessions store | |
sessions = new Sessions | |
# http server | |
server = http.createServer (req, res) -> | |
if req.url is "/favicon.ico" | |
res.writeHead 404 | |
return do res.end | |
if req.url is "/domevents.js" | |
res.writeHead 200, 'Content-Type':"text/javascript" | |
return fs.createReadStream( | |
require.resolve 'domevents/events.browser').pipe(res) | |
console.log req.method, req.url, req.headers.cookie | |
cookies = new Cookies req, res | |
sessions.manage req, res, cookies.get('sid'), (err, ses) -> | |
cookies.set('sid', ses.id) | |
res.writeHead 200, 'Content-Type':"text/html" | |
# websocket management | |
io = socketio.listen server | |
io.configure -> | |
io.set 'transports', ['websocket']#, 'xhr-polling'] | |
io.disable 'log' | |
io.sockets.on 'connection', (socket) -> | |
console.log "ws connection" | |
socket.on 'sid', (sid) -> | |
ses = sessions.get(sid) | |
console.log "delegate ws to", ses?.id | |
ses?.emit 'websocket', socket | |
sessions.on 'session', (ses) -> | |
eventnames = {} | |
ses.websocket = ws = new EventEmitter | |
old_emit = ws.emit | |
ws.emit = (event) -> | |
return old_emit.call(ws, arguments...) if event is 'newListener' | |
console.warn "No websocket to receive Event '#{event}'" | |
ws.on 'newListener', (event) -> | |
console.log "new event listener", event | |
eventnames[event] = true | |
ses.on 'websocket', (socket) -> | |
console.log "#{socket? and 'reset' or 'init'} websocket", @id | |
for event in Object.keys(eventnames) | |
listeners = ws.listeners(event) | |
if listeners.length is 0 | |
delete eventnames[event] | |
else | |
socket.on(event, old_emit.bind(ws, event)) | |
ws.emit = socket.emit.bind(socket) | |
# views | |
indexView = (ses, state) -> | |
new Template doctype:on, schema:5, pretty:on, -> | |
@$html -> | |
@$head -> | |
@$title "stateful test - #{ses.id}" | |
@$body -> | |
@$p "sid:#{ses.id}" | |
@$p "sessions:#{Object.keys(sessions.sessions).length}" | |
@$p "reload:#{state.reload - 1}" # this is after state.reload++ | |
@$p -> @$a href:'/', "home" | |
@$p -> | |
@$a href:'/session', "new session" | |
@text "(#{state.newsid}x clicked)" | |
@$p -> | |
@$button id:'red', "button" | |
@text "(#{state.clicked}x clicked)" | |
@$p -> | |
@text "history:" | |
@$ul -> | |
@$li "#{x}" for x in state.history | |
# scripts | |
@$script src:"/domevents.js" | |
@$script src:"/socket.io/socket.io.js" | |
# client code | |
@$script "var sid='#{ses.id}';(" + ( -> | |
console.log "loaded" | |
socket = io.connect() | |
socket.on 'connect', -> | |
socket.emit "sid", sid | |
button = new DomEventEmitter document.getElementById 'red' | |
button.on 'click', (ev) -> | |
console.log "button clicked!" | |
socket.emit 'button:click' | |
socket.on 'state:clicked', (c) -> | |
console.log "click count:", c | |
) + ").call(this)" | |
# main code | |
sessions.on 'session', (ses) -> | |
state = | |
reload:0 | |
newsid:0 | |
clicked:0 | |
history: [] | |
ses.on 'request', (req, res) -> | |
render(indexView ses, state).pipe(res) | |
state.reload++ | |
ses.on 'url:/session', (req, res) -> | |
state.history.unshift @id | |
@setId hat() | |
state.newsid++ | |
ses.websocket.on 'button:click', -> | |
console.log "button clicked!", ses.id | |
ses.websocket.emit 'state:clicked', ++state.clicked | |
# start the server | |
console.log "listen 3000" | |
server.listen 3000 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
session based instead of route based.
the idea is to have the same interface for http like for stream/connection/session based protocols (eg tcp, udp, websocket, xmpp, etc)
dunno if this is a good idea, but it's at least worth a try.