mirror of
https://github.com/koreader/koreader
synced 2024-11-04 12:00:25 +00:00
fadee1f5dc
Basically: * Use `extend` for class definitions * Use `new` for object instantiations That includes some minor code cleanups along the way: * Updated `Widget`'s docs to make the semantics clearer. * Removed `should_restrict_JIT` (it's been dead code since https://github.com/koreader/android-luajit-launcher/pull/283) * Minor refactoring of LuaSettings/LuaData/LuaDefaults/DocSettings to behave (mostly, they are instantiated via `open` instead of `new`) like everything else and handle inheritance properly (i.e., DocSettings is now a proper LuaSettings subclass). * Default to `WidgetContainer` instead of `InputContainer` for stuff that doesn't actually setup key/gesture events. * Ditto for explicit `*Listener` only classes, make sure they're based on `EventListener` instead of something uselessly fancier. * Unless absolutely necessary, do not store references in class objects, ever; only values. Instead, always store references in instances, to avoid both sneaky inheritance issues, and sneaky GC pinning of stale references. * ReaderUI: Fix one such issue with its `active_widgets` array, with critical implications, as it essentially pinned *all* of ReaderUI's modules, including their reference to the `Document` instance (i.e., that was a big-ass leak). * Terminal: Make sure the shell is killed on plugin teardown. * InputText: Fix Home/End/Del physical keys to behave sensibly. * InputContainer/WidgetContainer: If necessary, compute self.dimen at paintTo time (previously, only InputContainers did, which might have had something to do with random widgets unconcerned about input using it as a baseclass instead of WidgetContainer...). * OverlapGroup: Compute self.dimen at *init* time, because for some reason it needs to do that, but do it directly in OverlapGroup instead of going through a weird WidgetContainer method that it was the sole user of. * ReaderCropping: Under no circumstances should a Document instance member (here, self.bbox) risk being `nil`ed! * Kobo: Minor code cleanups.
139 lines
5.7 KiB
Lua
139 lines
5.7 KiB
Lua
--[[--
|
|
This module contains miscellaneous helper functions specific to our usage of LuaSocket/LuaSec.
|
|
]]
|
|
|
|
local Version = require("version")
|
|
local http = require("socket.http")
|
|
local https = require("ssl.https")
|
|
local ltn12 = require("ltn12")
|
|
local socket = require("socket")
|
|
|
|
local socketutil = {
|
|
-- Init to the default LuaSocket/LuaSec values
|
|
block_timeout = 60,
|
|
total_timeout = -1,
|
|
}
|
|
|
|
--- Builds a sensible UserAgent that fits Wikipedia's UA policy <https://meta.wikimedia.org/wiki/User-Agent_policy>
|
|
local socket_ua = http.USERAGENT
|
|
socketutil.USER_AGENT = "KOReader/" .. Version:getShortVersion() .. " (https://koreader.rocks/) " .. socket_ua:gsub(" ", "/")
|
|
-- Monkey-patch it in LuaSocket, as it already takes care of inserting the appropriate header to its requests.
|
|
http.USERAGENT = socketutil.USER_AGENT
|
|
|
|
--- Common timeout values
|
|
-- Large content
|
|
socketutil.LARGE_BLOCK_TIMEOUT = 10
|
|
socketutil.LARGE_TOTAL_TIMEOUT = 30
|
|
-- File downloads
|
|
socketutil.FILE_BLOCK_TIMEOUT = 15
|
|
socketutil.FILE_TOTAL_TIMEOUT = 60
|
|
-- Upstream defaults
|
|
socketutil.DEFAULT_BLOCK_TIMEOUT = 60
|
|
socketutil.DEFAULT_TOTAL_TIMEOUT = -1
|
|
|
|
--- Update the timeout values.
|
|
-- Note that this only affects socket polling,
|
|
-- c.f., LuaSocket's timeout_getretry @ src/timeout.c & usage in src/usocket.c
|
|
-- Moreover, the timeout is actually *reset* between polls (via timeout_markstart, e.g. in buffer_meth_receive).
|
|
-- So, in practice, this timeout only helps *very* bad connections (on one end or the other),
|
|
-- and you'd be hard-pressed to ever hit the *total* timeout, since the starting point is reset extremely often.
|
|
-- In our case, we want to enforce an *actual* limit on how much time we're willing to block for, start to finish.
|
|
-- We do that via the custom sinks below, which will start ticking as soon as the first chunk of data is received.
|
|
-- To simplify, in most cases, the socket timeout matters *before* we receive data,
|
|
-- and the sink timeout *once* we've started receiving data (at which point the socket timeout is reset every chunk).
|
|
-- In practice, that means you don't want to set block_timeout too low,
|
|
-- as that's what the socket timeout will end up using most of the time.
|
|
-- Note that name resolution happens earlier and one level lower (e.g., glibc),
|
|
-- so name resolution delays will fall outside of these timeouts.
|
|
function socketutil:set_timeout(block_timeout, total_timeout)
|
|
self.block_timeout = block_timeout or 5
|
|
self.total_timeout = total_timeout or 15
|
|
|
|
-- Also update the actual LuaSocket & LuaSec constants, because:
|
|
-- 1. LuaSocket's `open` does a `settimeout` *after* create with this constant
|
|
-- 2. KOSync updates it to a stupidly low value
|
|
http.TIMEOUT = self.block_timeout
|
|
https.TIMEOUT = self.block_timeout
|
|
end
|
|
|
|
--- Reset timeout values to LuaSocket defaults.
|
|
function socketutil:reset_timeout()
|
|
self.block_timeout = self.DEFAULT_BLOCK_TIMEOUT
|
|
self.total_timeout = self.DEFAULT_TOTAL_TIMEOUT
|
|
|
|
http.TIMEOUT = self.block_timeout
|
|
https.TIMEOUT = self.block_timeout
|
|
end
|
|
|
|
--- Monkey-patch LuaSocket's `socket.tcp` in order to honor tighter timeouts, to avoid blocking the UI for too long.
|
|
-- NOTE: While we could use a custom `create` function for HTTP LuaSocket `request`s,
|
|
-- with HTTPS, the way LuaSocket/LuaSec handles those is much more finicky,
|
|
-- because LuaSocket's adjustrequest function (in http.lua) passes the adjusted nreqt table to it,
|
|
-- but only when it does the automagic scheme handling, not when it's set by the caller :/.
|
|
-- And LuaSec's own `request` function overload *forbids* setting create, because of similar shenanigans...
|
|
-- TL;DR: Just monkey-patching socket.tcp directly will affect both HTTP & HTTPS
|
|
-- without us having to maintain a tweaked version of LuaSec's `https.tcp` function...
|
|
local real_socket_tcp = socket.tcp
|
|
function socketutil.tcp()
|
|
-- Based on https://stackoverflow.com/a/6021774
|
|
local req_sock = real_socket_tcp()
|
|
req_sock:settimeout(socketutil.block_timeout, "b")
|
|
req_sock:settimeout(socketutil.total_timeout, "t")
|
|
return req_sock
|
|
end
|
|
socket.tcp = socketutil.tcp
|
|
|
|
--- Various timeout return codes
|
|
socketutil.TIMEOUT_CODE = "timeout" -- from LuaSocket's io.c
|
|
socketutil.SSL_HANDSHAKE_CODE = "wantread" -- from LuaSec's ssl.c
|
|
socketutil.SINK_TIMEOUT_CODE = "sink timeout" -- from our own socketutil
|
|
|
|
-- NOTE: Use os.time() for simplicity's sake (we don't really need subsecond precision).
|
|
-- LuaSocket itself is already using gettimeofday anyway (although it does the maths, like ffi/util's getTimestamp).
|
|
--- Custom version of `ltn12.sink.table` that honors total_timeout
|
|
function socketutil.table_sink(t)
|
|
if socketutil.total_timeout < 0 then
|
|
return ltn12.sink.table(t)
|
|
end
|
|
|
|
local start_ts = os.time()
|
|
t = t or {}
|
|
local f = function(chunk, err)
|
|
if chunk then
|
|
if os.time() - start_ts > socketutil.total_timeout then
|
|
return nil, socketutil.SINK_TIMEOUT_CODE
|
|
end
|
|
table.insert(t, chunk)
|
|
end
|
|
return 1
|
|
end
|
|
return f, t
|
|
end
|
|
|
|
--- Custom version of `ltn12.sink.file` that honors total_timeout
|
|
function socketutil.file_sink(handle, io_err)
|
|
if socketutil.total_timeout < 0 then
|
|
return ltn12.sink.file(handle, io_err)
|
|
end
|
|
|
|
if handle then
|
|
local start_ts = os.time()
|
|
return function(chunk, err)
|
|
if not chunk then
|
|
handle:close()
|
|
return 1
|
|
else
|
|
if os.time() - start_ts > socketutil.total_timeout then
|
|
handle:close()
|
|
return nil, socketutil.SINK_TIMEOUT_CODE
|
|
end
|
|
return handle:write(chunk)
|
|
end
|
|
end
|
|
else
|
|
return nil, io_err or "unable to open file"
|
|
end
|
|
end
|
|
|
|
return socketutil
|