local http = require "luci.http" local nixio = require "nixio" local ltn12 = require "luci.ltn12" local table = require "table" local util = require "luci.util" module("luci.controller.istore_backend", package.seeall) local BLOCKSIZE = 2048 local ISTOREOS_PORT = 3000 function index() entry({"istore"}, call("istore_backend")).leaf=true end function sink_socket(sock, io_err) if sock then return function(chunk, err) if not chunk then return 1 else return sock:send(chunk) end end else return ltn12.sink.error(io_err or "unable to send socket") end end local function session_retrieve(sid, allowed_users) local sdat = util.ubus("session", "get", { ubus_rpc_session = sid }) if type(sdat) == "table" and type(sdat.values) == "table" and type(sdat.values.token) == "string" and (not allowed_users or util.contains(allowed_users, sdat.values.username)) then return sid, sdat.values end return nil, nil end function istore_backend() local sock = nixio.connect("127.0.0.1", ISTOREOS_PORT) if not sock then http.status(500, "connect failed") return end local input = {} input[#input+1] = http.getenv("REQUEST_METHOD") .. " " .. http.getenv("REQUEST_URI") .. " HTTP/1.1" local req = http.context.request local start = "HTTP_" local start_len = string.len(start) local ctype = http.getenv("CONTENT_TYPE") if ctype then input[#input+1] = "Content-Type: " .. ctype end for k, v in pairs(req.message.env) do if string.sub(k, 1, start_len) == start and not string.find(k, "FORWARDED") then input[#input+1] = string.sub(k, start_len+1, string.len(k)) .. ": " .. v end end local sid = http.getcookie("sysauth") if sid then local sid, sdat = session_retrieve(sid, nil) if sdat ~= nil then input[#input+1] = "X-Forwarded-Sid: " .. sid input[#input+1] = "X-Forwarded-Token: " .. sdat.token end end input[#input+1] = "X-Forwarded-For: " .. http.getenv("REMOTE_HOST") ..":".. http.getenv("REMOTE_PORT") local num = tonumber(http.getenv("CONTENT_LENGTH")) or 0 input[#input+1] = "Content-Length: " .. tostring(num) input[#input+1] = "\r\n" local source = ltn12.source.cat(ltn12.source.string(table.concat(input, "\r\n")), http.source()) local ret = ltn12.pump.all(source, sink_socket(sock, "write sock error")) if ret ~= 1 then sock:close() http.status(500, "proxy error") return end local linesrc = sock:linesource() local line, code, error = linesrc() if not line then sock:close() http.status(500, "response parse failed") return end local protocol, status, msg = line:match("^([%w./]+) ([0-9]+) (.*)") if not protocol then sock:close() http.status(500, "response protocol error") return end num = tonumber(status) or 0 http.status(num, msg) local chunked = 0 line = linesrc() while line and line ~= "" do local key, val = line:match("^([%w-]+)%s?:%s?(.*)") if key and key ~= "Status" then if key == "Transfer-Encoding" and val == "chunked" then chunked = 1 end if key ~= "Connection" and key ~= "Transfer-Encoding" then http.header(key, val) end end line = linesrc() end if not line then sock:close() http.status(500, "parse header failed") return end local body_buffer = linesrc(true) if chunked == 1 then ltn12.pump.all(chunksource(sock, body_buffer), http.write) else local body_source = ltn12.source.cat(ltn12.source.string(body_buffer), sock:blocksource()) ltn12.pump.all(body_source, http.write) end sock:close() end function chunksource(sock, buffer) buffer = buffer or "" return function() local output local _, endp, count = buffer:find("^([0-9a-fA-F]+);?.-\r\n") while not count and #buffer <= 1024 do local newblock, code = sock:recv(1024 - #buffer) if not newblock then return nil, code end buffer = buffer .. newblock _, endp, count = buffer:find("^([0-9a-fA-F]+);?.-\r\n") end count = tonumber(count, 16) if not count then return nil, -1, "invalid encoding" elseif count == 0 then return nil elseif count + 2 <= #buffer - endp then output = buffer:sub(endp+1, endp+count) buffer = buffer:sub(endp+count+3) return output else output = buffer:sub(endp+1, endp+count) buffer = "" if count - #output > 0 then local remain, code = sock:recvall(count-#output) if not remain then return nil, code end output = output .. remain count, code = sock:recvall(2) else count, code = sock:recvall(count+2-#buffer+endp) end if not count then return nil, code end return output end end end