Initial commit
This commit is contained in:
@@ -0,0 +1,102 @@
|
||||
local socketutil = require("socketutil")
|
||||
local ltn12 = require("ltn12")
|
||||
local logger = require("logger")
|
||||
local socket = require("socket")
|
||||
local http = require("socket.http")
|
||||
local JSON = require("json")
|
||||
local RawHttp = require("raw_http")
|
||||
local const = require("./const")
|
||||
|
||||
local function decode_json(content)
|
||||
if not content or content == "" then return {} end
|
||||
local ok, decoded = pcall(JSON.decode, content)
|
||||
if ok and decoded then return decoded end
|
||||
return nil
|
||||
end
|
||||
|
||||
local function format_network_error(detail, stage, host, port)
|
||||
detail = tostring(detail or "network unreachable")
|
||||
local where = ""
|
||||
if host and port then where = " to " .. tostring(host) .. ":" .. tostring(port) end
|
||||
local hint = " Check the server URL and Wi-Fi."
|
||||
if stage == "tcp" then
|
||||
hint = " kovi is not reachable on that TCP port from the reader. Check the host firewall and that both devices are on the same LAN."
|
||||
elseif stage == "url" then
|
||||
hint = " Check the server URL."
|
||||
end
|
||||
return false, {
|
||||
error = "kovi " .. tostring(stage or "network") .. " error" .. where .. ": " .. detail .. "." .. hint,
|
||||
kind = "network",
|
||||
stage = stage,
|
||||
detail = detail,
|
||||
}
|
||||
end
|
||||
|
||||
local function interpret(code, resp_headers, status, content)
|
||||
local parsed = decode_json(content)
|
||||
local numeric_code = tonumber(code)
|
||||
if numeric_code and numeric_code >= 200 and numeric_code < 300 then
|
||||
if parsed then return true, parsed end
|
||||
return false, { error = "kovi returned a successful response that was not valid JSON.", kind = "response" }
|
||||
end
|
||||
logger.err("[kovi] HTTP error", code, status, content)
|
||||
if parsed and parsed.error then return false, parsed end
|
||||
return false, {
|
||||
error = "kovi returned HTTP " .. tostring(code) .. (status and (" (" .. tostring(status) .. ")") or ""),
|
||||
kind = "http",
|
||||
status = numeric_code,
|
||||
}
|
||||
end
|
||||
|
||||
local function koreader_http(method, target, headers, body)
|
||||
local sink = {}
|
||||
local request = {
|
||||
method = method,
|
||||
url = target,
|
||||
headers = headers,
|
||||
sink = ltn12.sink.table(sink),
|
||||
redirect = false,
|
||||
}
|
||||
if body ~= nil then request.source = ltn12.source.string(body) end
|
||||
|
||||
socketutil:set_timeout(socketutil.LARGE_BLOCK_TIMEOUT, socketutil.LARGE_TOTAL_TIMEOUT)
|
||||
local ok, code, resp_headers, status = pcall(function()
|
||||
return socket.skip(1, http.request(request))
|
||||
end)
|
||||
socketutil:reset_timeout()
|
||||
if not ok then
|
||||
return nil, nil, nil, nil, { stage = "http", detail = tostring(code) }
|
||||
end
|
||||
if resp_headers == nil then
|
||||
return nil, nil, nil, nil, { stage = "http", detail = tostring(status or code or "network unreachable") }
|
||||
end
|
||||
return code, resp_headers, status, table.concat(sink), nil
|
||||
end
|
||||
|
||||
return function(method, target, headers, body)
|
||||
headers = headers or {}
|
||||
headers["Accept"] = headers["Accept"] or "application/json"
|
||||
headers["Accept-Encoding"] = "identity"
|
||||
headers["User-Agent"] = "kovi-KOReader/" .. tostring(const.VERSION)
|
||||
|
||||
logger.dbg("[kovi] API", method, target)
|
||||
|
||||
-- LAN pairing is normally plain HTTP. Use a minimal direct TCP HTTP client first;
|
||||
-- this avoids platform-specific LuaSocket HTTP quirks and gives useful TCP errors.
|
||||
if target:match("^http://") then
|
||||
local code, resp_headers, status, content, raw_err = RawHttp.request(method, target, headers, body, 12)
|
||||
if code then return interpret(code, resp_headers, status, content) end
|
||||
logger.warn("[kovi] direct HTTP failed", raw_err and raw_err.stage, raw_err and raw_err.detail)
|
||||
|
||||
-- Fall back to KOReader's standard HTTP stack, matching built-in plugins.
|
||||
local f_code, f_headers, f_status, f_content, fallback_err = koreader_http(method, target, headers, body)
|
||||
if f_code then return interpret(f_code, f_headers, f_status, f_content) end
|
||||
local best = raw_err or fallback_err or { stage = "network", detail = "network unreachable" }
|
||||
return format_network_error(best.detail, best.stage, best.host, best.port)
|
||||
end
|
||||
|
||||
-- KOReader's bundled socket.http is used by built-in plugins for HTTPS URLs.
|
||||
local code, resp_headers, status, content, err = koreader_http(method, target, headers, body)
|
||||
if not code then return format_network_error(err and err.detail, err and err.stage) end
|
||||
return interpret(code, resp_headers, status, content)
|
||||
end
|
||||
Reference in New Issue
Block a user