parsav  Diff

Differences From Artifact [d4dcecb4e5]:

To Artifact [a1d0408148]:


   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;