134
135
136
137
138
139
140
141
142
143
144
145
146
147
...
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
...
391
392
393
394
395
396
397
398
399
400
401
402
403
404
...
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
|
aid: uint64 -- 0 if logged out
aid_issue: lib.store.timepoint
who: &lib.store.actor -- who we're logged in as, if aid ~= 0
peer: lib.store.inet
reqtype: lib.http.mime.t -- negotiated content type
method: lib.http.method.t
live_last: lib.store.timepoint
-- cache
ui_hue: uint16
navbar: lib.mem.ptr(int8)
actorcache: lib.mem.cache(lib.mem.ptr(lib.store.actor),32) -- naive cache to avoid unnecessary queries
-- private
varbuf: lib.mem.ptr(int8)
vbofs: &int8
................................................................................
else return nil, 0 end
end
terra convo:pgetv(name: rawstring)
var s,l = self:getv(name)
return pstring { ptr = s, ct = l }
end
local urimatch = macro(function(uri, ptn)
return `lib.net.mg_globmatch(ptn, [#ptn], uri.ptr, uri.ct+1)
end)
local route = {} -- these are defined in route.t, as they need access to renderers
terra route.dispatch_http :: {&convo, lib.mem.ptr(int8), lib.http.method.t} -> {}
local mimetypes = {
{'html', 'text/html'};
{'json', 'application/json'};
{'mkdown', 'text/markdown'};
................................................................................
reqtype = lib.http.mime.none;
peer = peer, live_last = 0;
} co.varbuf.ptr = nil
co.navbar.ptr = nil
co.actorcache.top = 0
co.actorcache.cur = 0
co.ui_hue = server.cfg.ui_hue
-- first, check for an accept header. if it's there, we need to
-- iterate over the values and pick the highest-priority one
do var acc = lib.http.findheader(msg, 'Accept')
-- TODO handle q-value
if acc ~= nil and acc.ptr ~= nil then
var [mimevar] = [lib.mem.ref(int8)] { ptr = acc.ptr }
................................................................................
end
uri.ct = msg.uri.len
else uri.ct = urideclen end
lib.dbg('routing URI ', {uri.ptr, uri.ct})
if lib.str.ncmp('GET', msg.method.ptr, msg.method.len) == 0 then
co.method = [lib.http.method.get]
route.dispatch_http(&co, uri, [lib.http.method.get])
elseif lib.str.ncmp('POST', msg.method.ptr, msg.method.len) == 0 then
co.method = [lib.http.method.get]
route.dispatch_http(&co, uri, [lib.http.method.post])
elseif lib.str.ncmp('HEAD', msg.method.ptr, msg.method.len) == 0 then
co.method = [lib.http.method.head]
route.dispatch_http(&co, uri, [lib.http.method.head])
elseif lib.str.ncmp('OPTIONS', msg.method.ptr, msg.method.len) == 0 then
co.method = [lib.http.method.options]
route.dispatch_http(&co, uri, [lib.http.method.options])
else
co:complain(400,'unknown method','you have submitted an invalid http request')
end
if co.aid ~= 0 then lib.mem.heapf(co.who) end
if co.varbuf.ptr ~= nil then co.varbuf:free() end
if co.navbar.ptr ~= nil then co.navbar:free() end
co.actorcache:free()
end
end
end;
|
>
>
<
<
<
<
>
<
|
<
<
<
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
...
330
331
332
333
334
335
336
337
338
339
340
341
342
343
...
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
...
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
|
aid: uint64 -- 0 if logged out
aid_issue: lib.store.timepoint
who: &lib.store.actor -- who we're logged in as, if aid ~= 0
peer: lib.store.inet
reqtype: lib.http.mime.t -- negotiated content type
method: lib.http.method.t
live_last: lib.store.timepoint
uploads: lib.mem.vec(lib.http.upload)
body: lib.str.t
-- cache
ui_hue: uint16
navbar: lib.mem.ptr(int8)
actorcache: lib.mem.cache(lib.mem.ptr(lib.store.actor),32) -- naive cache to avoid unnecessary queries
-- private
varbuf: lib.mem.ptr(int8)
vbofs: &int8
................................................................................
else return nil, 0 end
end
terra convo:pgetv(name: rawstring)
var s,l = self:getv(name)
return pstring { ptr = s, ct = l }
end
local route = {} -- these are defined in route.t, as they need access to renderers
terra route.dispatch_http :: {&convo, lib.mem.ptr(int8), lib.http.method.t} -> {}
local mimetypes = {
{'html', 'text/html'};
{'json', 'application/json'};
{'mkdown', 'text/markdown'};
................................................................................
reqtype = lib.http.mime.none;
peer = peer, live_last = 0;
} co.varbuf.ptr = nil
co.navbar.ptr = nil
co.actorcache.top = 0
co.actorcache.cur = 0
co.ui_hue = server.cfg.ui_hue
co.body.ptr = msg.body.ptr co.body.ct = msg.body.len
-- first, check for an accept header. if it's there, we need to
-- iterate over the values and pick the highest-priority one
do var acc = lib.http.findheader(msg, 'Accept')
-- TODO handle q-value
if acc ~= nil and acc.ptr ~= nil then
var [mimevar] = [lib.mem.ref(int8)] { ptr = acc.ptr }
................................................................................
end
uri.ct = msg.uri.len
else uri.ct = urideclen end
lib.dbg('routing URI ', {uri.ptr, uri.ct})
if lib.str.ncmp('GET', msg.method.ptr, msg.method.len) == 0 then
co.method = [lib.http.method.get]
elseif lib.str.ncmp('POST', msg.method.ptr, msg.method.len) == 0 then
co.method = [lib.http.method.post]
elseif lib.str.ncmp('HEAD', msg.method.ptr, msg.method.len) == 0 then
co.method = [lib.http.method.head]
elseif lib.str.ncmp('OPTIONS', msg.method.ptr, msg.method.len) == 0 then
co.method = [lib.http.method.options]
else
co:complain(400,'unknown method','you have submitted an invalid http request')
goto fail
end
-- check for a content-type header, and see if it's a multipart/
-- form-data encoded POST request so we can handle file uploads
co.uploads.sz = 0 co.uploads.run = 0
if co.method == [lib.http.method.post] then
var ctt = lib.http.findheader(msg, 'Content-Type')
if ctt ~= nil then
lib.dbg('found content type', {ctt.ptr,ctt.ct})
if lib.str.ncmp(ctt.ptr,'multipart/form-data;',20) == 0 then
var p = lib.str.ffw(ctt.ptr + 20,ctt.ct-20)
if lib.str.ncmp(p,'boundary=',9) ~= 0 then
co:complain(400,'bad request','unrecognized content-type')
goto fail
end
var boundary = pstring {ptr=p+9,ct=ctt.ct - ((p - ctt.ptr) + 9)}
lib.dbg('got boundary ',{boundary.ptr,boundary.ct})
co.method = lib.http.method.post_file
co.uploads:init(8)
var bsr = (lib.str.acc{}):compose('\r\n--',boundary,'\r\n'):finalize()
var upmap = lib.str.splitmap(co.body,bsr,8)
-- first entry may not be preceded by header-break
if lib.str.find(upmap(0), pstring {
ptr = bsr.ptr + 2, ct = bsr.ct - 2
}):ref() then
upmap(0).ptr = upmap(0).ptr + (bsr.ct - 2)
upmap(0).ct = upmap(0).ct - (bsr.ct - 2)
end
-- last entry is weird
do var lsr = (lib.str.acc{}):compose('\r\n--',boundary,'--\r\n'):finalize()
var lsent = upmap.ptr + (upmap.ct - 1)
var halt = lib.str.find(@lsent, lsr)
if halt:ref() then
lsent.ct = halt.ptr - lsent.ptr
end
lsr:free() end
for i=0,upmap.ct do
var hdrbrk = lib.str.find(upmap(i), lib.str.plit'\r\n\r\n')
if hdrbrk:ref() then
lib.dbg('got new entry')
var hdrtxt = pstring {upmap(i).ptr,upmap(i).ct - hdrbrk.ct}
var hdrs = lib.str.splitmap(hdrtxt, '\r\n',6)
var ctt = pstring.null()
var ctd = pstring.null()
for j=0, hdrs.ct do
var brk = lib.str.find(hdrs(j),lib.str.plit':')
if brk:ref() then
var hdr = pstring{hdrs(j).ptr,hdrs(j).ct - brk.ct}
var val = pstring{brk.ptr+1, brk.ct-1}:ffw()
if hdr:cmp(lib.str.plit'Content-Type') then
ctt = val
elseif hdr:cmp(lib.str.plit'Content-Disposition') then
ctd = val
end
end
end
if ctd:ref() then
var ctdvals = lib.str.splitmap(ctd, ';', 4) defer ctdvals:free()
if ctdvals(0):cmp(lib.str.plit'form-data') and ctdvals.ct > 1 then
lib.dbg('found form data')
var fld = pstring.null()
var file = pstring.null()
for j=1, ctdvals.ct do var v = ctdvals(j):ffw()
var x = lib.str.find(v,lib.str.plit'=')
if x:ref() then
var key = pstring{v.ptr, v.ct - x.ct}
var val = pstring{x.ptr + 1, x.ct - 1}
var decval, ofs, sp = lib.str.toknext(val,@';',true)
if key:cmp(lib.str.plit'name') then
fld = decval
elseif key:cmp(lib.str.plit'filename') then
file = decval
else decval:free() end
end
end
if fld:ref() then
var nextup = co.uploads:new()
if ctt:ref() then
nextup.ctype = ctt
else
nextup.ctype = pstring.null()
end
nextup.body = pstring {
ptr = hdrbrk.ptr + 4;
ct = hdrbrk.ct - 4;
}
nextup.ctype = ctt
nextup.field = fld
nextup.filename = file
end
end
end
end
end
bsr:free()
upmap:free()
end
end
end
route.dispatch_http(&co, uri, co.method)
if co.uploads.run > 0 then
for i=0,co.uploads.sz do
co.uploads(i).filename:free()
co.uploads(i).field:free()
end
co.uploads:free()
end
::fail::
if co.aid ~= 0 then lib.mem.heapf(co.who) end
if co.varbuf.ptr ~= nil then co.varbuf:free() end
if co.navbar.ptr ~= nil then co.navbar:free() end
co.actorcache:free()
end
end
end;
|