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