local sendCHN=love.thread.getChannel('HTTP_inputChannel') local recvCHN=love.thread.getChannel('HTTP_outputChannel') local threads={} local threadCount=0 local threadCode=[[ local id=... local http=require'socket.http' local ltn12=require'ltn12' local sendCHN=love.thread.getChannel('HTTP_inputChannel') local recvCHN=love.thread.getChannel('HTTP_outputChannel') local sleep=require'love.timer'.sleep while true do -- local arg=sendCHN:demand() -- Warning: workaround for love.js while sendCHN:getCount()==0 do sleep(.0626) end local arg=sendCHN:pop() if arg._destroy then recvCHN:push{ destroy=true, id=id, } break end -- print("\n------SEND------") for k,v in next,arg do print(k,v) end local data={} local _,code,detail=http.request{ method=arg.method, url=arg.url, headers=arg.headers, source=ltn12.source.string(arg.body), sink=ltn12.sink.table(data), } local result={ pool=arg.pool, poolPtr=arg.poolPtr, code=code, body=table.concat(data), detail=detail, } -- print("\n------RECV------") for k,v in next,result do print(k,v) end recvCHN:push(result) end ]] local msgPool=setmetatable({},{ __index=function(self,k) self[k]={} return self[k] end }) local HTTP={ _msgCount=0, _trigTime=0, _trigInterval=.26, _host=false, } local function addThread(num) for i=1,26 do if num<=0 then break end if not threads[i] then threads[i]=love.thread.newThread(threadCode) threads[i]:start(i) threadCount=threadCount+1 num=num-1 end end end function HTTP.request(arg) arg.method=arg.method or arg.body and 'POST' or 'GET' if arg.url then assert(type(arg.url)=='string',"Field 'url' need string, get "..type(arg.url)) if arg.url:sub(1,7)~='http://' then arg.url='http://'..arg.url end else arg.url=HTTP._host or error("Need url= or set default host with HTTP.setHost") end if arg.path then assert(type(arg.path)=='string',"Field 'path' need string, get "..type(arg.path)) arg.url=arg.url..arg.path end assert(arg.headers==nil or type(arg.headers)=='table',"Field 'headers' need table, get "..type(arg.headers)) if arg.body~=nil then assert(type(arg.body)=='table',"Field 'body' need table, get "..type(arg.body)) arg.body=JSON.encode(arg.body) if not arg.headers then arg.headers={} end TABLE.cover({ ['Content-Type']="application/json", ['Content-Length']=#arg.body, },arg.headers) end if arg.pool==nil then arg.pool='_default' end arg.poolPtr=tostring(msgPool[arg.pool]) sendCHN:push(arg) end function HTTP.reset() for i=1,#threads do threads[i]:release() threads[i]=false end TABLE.clear(msgPool) sendCHN:clear() recvCHN:clear() addThread(threadCount) end function HTTP.setThreadCount(n) assert(type(n)=='number' and n>=1 and n<=26 and n%1==0,"function HTTP.setThreadCount(n): n must be integer from 1 to 26") if n>threadCount then addThread(n-threadCount) else for _=n+1,threadCount do sendCHN:push{_destroy=true} end end end function HTTP.getThreadCount() return threadCount end function HTTP.setInterval(interval) if interval<=0 then interval=1e99 end assert(type(interval)=='number',"Interval must be number") HTTP._trigInterval=interval end function HTTP.clearPool(pool) if pool==nil then pool='_default' end assert(type(pool)=='string',"Pool must be nil or string") HTTP._msgCount=HTTP._msgCount-#msgPool[pool] msgPool[pool]={} end function HTTP.deletePool(pool) assert(type(pool)=='string',"Pool must be nil or string") assert(pool~='_default',"Cannot delete _default pool. What are you doing?") HTTP._msgCount=HTTP._msgCount-#msgPool[pool] msgPool[pool]=nil end function HTTP.pollMsg(pool) if not (type(pool)=='nil' or type(pool)=='string') then error("Pool must be nil or string") end HTTP.update() local p=msgPool[pool or '_default'] if #p>0 then HTTP._msgCount=HTTP._msgCount-1 return table.remove(p) end end function HTTP.setHost(host) assert(type(host)=='string',"Host must be string") if host:sub(1,7)~='http://' then host='http://'..host end HTTP._host=host end function HTTP.update(dt) if dt then HTTP._trigTime=HTTP._trigTime+dt if HTTP._trigTime>HTTP._trigInterval then HTTP._trigTime=HTTP._trigTime%HTTP._trigInterval else return end end while recvCHN:getCount()>0 do local m=recvCHN:pop() if m.destroy then threads[m.id]:release() threads[m.id]=false elseif tostring(msgPool[m.pool])==m.poolPtr then -- If pool were cleared, discard this datapack table.insert(msgPool[m.pool],{ code=m.code, body=m.body, detail=m.detail, }) HTTP._msgCount=HTTP._msgCount+1 end end end setmetatable(HTTP,{__call=function(self,arg) return self.request(arg) end,__metatable=true}) HTTP.reset() return HTTP