forked from rkh/almost-sinatra
-
Notifications
You must be signed in to change notification settings - Fork 4
/
berliner.coffee
17 lines (16 loc) · 3.51 KB
/
berliner.coffee
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
[J,M,duc,euc,n,e,w]=[JSON,Math,decodeURIComponent,encodeURIComponent,((p)->p.replace(/\/*$/,'').replace /^\/?/,'/'),((d,s)->d[k]=(if d[k]instanceof Array then d[k].concat v else v)for k,v of s;d),(s,c)->s.split(/\s+/).map c]
[http,url,qs,fs,async,WS,E,haml,ejs,mime]=w 'http url querystring fs async faye-websocket vault-cipher haml ejs mime',require
class A
constructor:(@request,@response,@_m)->
[h,c]=[{},(@request.headers.cookie||'').split /\s*;\s*/];c.map((p)->q=p.split '=';h[duc q[0]]=duc q[1]);A.cipher().decrypt h.session||'',(x,j)=>
delete h.session;@params={splat:[]};@cookies=h;@session=(if x then{}else J.parse(j));@_u=url.parse(@request.url,true);@_h='Set-Cookie':[];e @,A.h;@_b=A.r.filter((r)=>r[0]==@_m&&r[2].test @_u.pathname)[0]
@_c if @_b then[@_a,d]=[[],@_u.pathname.match @_b[2]];(v=duc d[i+1];@_a.push v;if k=='*' then @params.splat.push v else @params[k]=v)for k,i in @_b[1];e @params,(if /\bapplication\/x-www-form-urlencoded\b/.test @request.headers['content-type']then qs.parse @request.body else @_u.query)
_x:(c)->@_c= =>p=@_u.pathname;if p.split('/').indexOf('..')>=0 then @response.writeHead 400,{};@response.end() else async.series A.b.filter((f)=>p.substr(0,f.c.length)==f.c).map((f)=> =>f.apply @,arguments).concat =>if @_b then c.call @,(=>@_b[3].apply @,@_a)else fs.readFile (A.public||'./public')+p,(x,f)=>if x then @response.writeHead 404,{};@response.end()else @response.writeHead 200,'Content-Length':f.length,'Content-Type':mime.lookup(p)+'; charset=utf-8';@response.end f
render:(s)->s=(if s==undefined then ''else String s);A.cipher().encrypt J.stringify(@session),(x,j)=>h={};e h,'Set-Cookie':['session=' + euc(j) + '; Path=/; HttpOnly'],'Content-Type':'text/html; charset=utf-8','Content-Length':new Buffer(s,'utf8').length if s;e h,@_h;@response.writeHead @_s||200,h;@response.end s
cookie:(c)->(v=if typeof v=='string'then value:v else v;s=euc(k)+'='+euc(v.value)+'; Path='+v.path||'/';s+='; Domain='+v.domain if v.domain;s+='; Expires='+v.expires.toGMTString() if v.expires;s+='; HttpOnly' if v.http;s+='; Secure' if v.secure;e @_h,'Set-Cookie':s)for k,v of c
e A.prototype,headers:((o)->e @_h,o),status:((n)->@_s=parseInt n,10),redirect:((u,s=303)->@status s;@headers 'Location':u;@render()),haml:((n)->A.v 'haml',n,(x,t)=>@render haml(t) @),ejs:((n,o)->A.v 'ejs',n,(x,t)=>@render ejs.render t,e o?.locals||{},A.h),puts:(s)->console.log s
e A,r:[],c:[''],context:((p,f)->@c.push n p;f @;@c.pop()),b:[],before:((b)->b.c=@c.join '';@b.push b),h:{},helpers:((o)->e @h,o),t:{},template:((n,t)->@t[n]=t),v:((t,n,c)->if f=@t[n+'.'+t]then c null,f else fs.readFile (@views||'./views')+'/'+n+'.'+t,(x,f)->c null,f.toString()),run:((q)->http.createServer(@call).on('upgrade',@ws).listen q||4567),cipher:->new E(@session_secret)
w 'get post put delete patch head options websocket eventsource',(v)->A[v]=(p,f)->p=n(@c.join('')+n p);o=(p.match(/[\/\.](\*|:[a-z\_\$][a-z0-9\_\$]*)/g)||[]).map((s)->s.replace /^[^a-z0-9\_\$\*]*/,'');m=new RegExp('^'+p.replace(/([\/\.])/g,'\\$1').replace(/\*|:[a-z\_\$][a-z0-9\_\$]*/ig,'(.+?)')+'$');@r.push [v.toUpperCase(),o,m,f]
A.call=(req,res)->req.setEncoding 'utf8';b='';req.on('data',(s)->b+=s);req.on 'end',->req.body=b;ES=WS.EventSource;k=ES.isEventSource req;a=new A(req,res,if k then 'EVENTSOURCE' else req.method);if k then(a._x (h)->s=a.socket=new ES req,res;h();s.addEventListener 'close',->s=null)else(a._x (h)->r=h();if typeof r=='string'then @render r)
A.ws=(r,s,h)->a=new A(r,s,'WEBSOCKET');a._x (H)->w=a.socket=new WS r,s,h;H();w.addEventListener 'close',->w=null
module.exports=A