Skip to content

Instantly share code, notes, and snippets.

@vird
Last active August 29, 2015 14:01
Show Gist options
  • Save vird/e5f6d59e262f2f6c8eaf to your computer and use it in GitHub Desktop.
Save vird/e5f6d59e262f2f6c8eaf to your computer and use it in GitHub Desktop.
Replacement for connect static serve. More performance less features
I spent some time for building solution for me. I think it will be useful for someone.
Usage notes
Note 1. It's iced coffee script, not regular coffee script
Note 2. It need some setup
npm install connect ect connect-route iced-coffee-script mime
mkdir public
put some file in public/favicon.ico
mkdir views
put some file in views/index.ect
compare
app.use (new (require './cache')).middleware()
vs
app.use connect.static 'public'
siege (as nodejs module, localhost) compare 6600-7180 vs 2230-2290 rps
siege (as linux package, LAN) compare 2800-3050 vs 1490-1650 trans/sec
test cmd
siege http://192.168.1.100:3000/favicon.ico -q -r30 -c100 -d0
tested on i7-2640M 2.80GHz
nodejs v0.10.12
mime = require 'mime'
fs = require 'fs'
class Cache
# TODO cache invalidation on watch ???
# cache_timeout : 1000 # test
# interval_timeout: 1000
cache_timeout : 60*60*1000 # 1 hour
interval_timeout: 60*1000 # 1 min
interval : null
static_cache: {}
charset : 'utf-8'
constructor:()->
@static_cache = {}
get : (key)->
if (ret = @static_cache[key])?
ret.last_hit = new Date
return ret
return null
start : ()->
@interval = setInterval ()=>
now = (new Date) - 0
for k,v of @static_cache
if now - v.last_hit > @cache_timeout
delete @static_cache[k]
return
, @interval_timeout
middleware : ()->
@start()
(req, res)=>
key = req.url
if (cache_item = @get key)?
res.setHeader "Content-Type", cache_item.header_content_type
res.end cache_item.body
return
else
path = "public#{req.url}"
if /\.\./.test path # not really needed
res.end "not available via security policy"
return
# TODO read file one time
await fs.exists path, defer result
if !result
res.end "no such file #{path}"
return
header_content_type = mime.lookup path
if /text/.test header_content_type
header_content_type += "; charset=#{@charset}"
res.setHeader "Content-Type", header_content_type
fs.createReadStream(path).pipe res
# put_file
new_cache_item = new Cache_item
await fs.readFile path, defer err, body
if err
p err
return
new_cache_item.header_content_type = header_content_type
new_cache_item.body = body
@static_cache[key] = new_cache_item
return
return
class Cache_item
header_content_type : ""
body : null
last_hit : 0
constructor:()->
@last_hit = new Date
module.exports = Cache
connect = require 'connect'
http = require 'http'
ect = require 'ect'
ectRender = ect
watch : true
root : 'views'
ext : '.ect'
app = connect()
app.use ectRender.compiler
root : 'views'
gzip : true
app.use (require 'connect-route') (r)->
r.get '/', (req, res, next)->
res.end 'Hello from Connect!\n'
r.get '/ect', (req, res, next)->
res.setHeader "Content-Type", "text/html; charset=utf-8"
res.end ectRender.render 'index.ect'
return
# for compare
# app.use (require 'st')('public')
# app.use connect.static 'public'
# app.use (require 'serve-static') 'public'
app.use (new (require './cache')).middleware()
http.createServer(app).listen(3000)
siege = require 'siege'
siege()
.on(3000)
# .get('/')
# .get('/index.html')
# .get('/ect')
.get('/favicon.ico')
.attack()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment