]> git.ipfire.org Git - thirdparty/public-inbox.git/log
thirdparty/public-inbox.git
2 months agolinkify: give <a> tags to gemini:// URLs
Eric Wong [Fri, 11 Apr 2025 18:35:51 +0000 (18:35 +0000)] 
linkify: give <a> tags to gemini:// URLs

While I still think serving the Gemini text and protocol is
unnecessary bloat on our part, there are web browsers with
gemini:// support, so we might as well support it.

3 months agohttp: send `100 Continue' responses for uploads
Eric Wong [Fri, 11 Apr 2025 01:38:34 +0000 (01:38 +0000)] 
http: send `100 Continue' responses for uploads

curl(1) defaults to sending a `Expect: 100-continue' header and
waits 1 second for a `100' response when using the `-T' switch
to upload via PUT.  We'll unconditionally respond with a `100'
response to save curl users 1 second.  Expecting curl users to
to disable the Expect: header via `-HExpect:' on every invocation
seems unreasonable, so our behavior should be tailored to curl
default behavior as much as possible.

We can't delegate the decision to send 100 or not to the
underlying Plack app (e.g. PublicInbox::WWW) since PSGI specs
don't provide a convenient callback interface for dealing with
trickled env->{'psgi.input'} reads.  Of course, having to
synchronously wait on env->{'psgi.input'}->read would be
unacceptable since that would prevent the server process from
servicing other concurrent clients.

3 months agodaemon: support backlog= listen parameter
Eric Wong [Tue, 8 Apr 2025 20:49:58 +0000 (20:49 +0000)] 
daemon: support backlog= listen parameter

For -netd instances using multiple listeners, it's easiest for me
to configure all ListenStream directives in a single
systemd.socket(5) unit.  Unfortunately, this seems to force all
listeners to use the same Backlog= value, and juggling
multiple systemd.socket(5) units for a single systemd service
seems tedious.

So support setting the backlog= parameter on the public-inbox-*d
command-line on a per-listener basis; allowing us to override
the backlog value of inherited listeners and also to set the
backlog for listeners we bind ourselves.  This makes it possible
for non-systemd users to easily configure per-listener backlogs,
as well.

3 months agodaemon: use unpack_sockaddr_* for clarity
Eric Wong [Tue, 8 Apr 2025 20:49:57 +0000 (20:49 +0000)] 
daemon: use unpack_sockaddr_* for clarity

unpack_sockaddr_un and unpack_sockaddr_in have been around since
Perl 5.002 in the mid-1990s, so be explicit and use them instead
of relying on wantarray caller context.  We'll also omit the
$host check for ($port == 0) since zero is not a valid TCP port.

3 months agohttp: refuse to deal with >4GB chunks in uploads
Eric Wong [Sat, 5 Apr 2025 17:44:13 +0000 (17:44 +0000)] 
http: refuse to deal with >4GB chunks in uploads

The `hex' perlop will return an NV (typically 64-bit double) on
UV (unsigned int) overflow and warns on larger values.  While
64-bit integer builds of 32-bit perl (e.g. Debian i386) can
handle 64-bit numbers, there are builds of perl which still use
32-bit integers nowadays (e.g. OpenBSD 7.x i386).

It's unlikely we'll ever see chunks even close to 4GB, so just
cap it at 8 hex characters and drop clients which send larger
amounts.

3 months agot/httpd-https: test SSL session reuse
Eric Wong [Thu, 3 Apr 2025 08:46:19 +0000 (08:46 +0000)] 
t/httpd-https: test SSL session reuse

Reusing SSL sessions is one avenue to improve performance on
high-latency networks.  For now, we can support the built-in
session cache of OpenSSL.  For multi-process setups, using
using Cache::FastMmap will likely be the way to go...

3 months agot/httpd-https: fix extra CRLF in request
Eric Wong [Thu, 3 Apr 2025 08:46:18 +0000 (08:46 +0000)] 
t/httpd-https: fix extra CRLF in request

I only noticed this via manual inspection since the test doesn't
reuse the socket afterwards.

3 months agods: close: always call SSL_close on TLS sockets
Eric Wong [Thu, 3 Apr 2025 08:46:17 +0000 (08:46 +0000)] 
ds: close: always call SSL_close on TLS sockets

Instead of relying on the explicit ->shutdn API, just
use the overloaded ->close so $env->{'psgix.io'} callers
can use it directly.  This makes it easier to ensure
proper shutdown so SSL session reuse can be a possiblity.

3 months agodoc: TODO and release notes updates
Eric Wong [Wed, 2 Apr 2025 20:17:54 +0000 (20:17 +0000)] 
doc: TODO and release notes updates

3 months agodoc: mknews: fix uninitialized variable
Eric Wong [Wed, 2 Apr 2025 19:09:49 +0000 (19:09 +0000)] 
doc: mknews: fix uninitialized variable

SCRIPT_NAME is required by Plack, so ensure this mock env
has it, as well.

Fixes: 1b130400 (www: omit SERVER_PORT for standard port redirects, 2025-03-28)
3 months agohttp: support trailers for chunked requests
Eric Wong [Wed, 2 Apr 2025 19:00:15 +0000 (19:00 +0000)] 
http: support trailers for chunked requests

While HTTP/1.1 trailers are not widely supported by other
software at the moment, they may be someday.  Trailers are
useful for things such as calculating checksums and signatures
on streaming uploads of unseekable data.  So add support for
them in case they're needed someday, perhaps for allow
uploading mail or ActivityPub messages over HTTP.

3 months agohttp: fix and test Trailer: rejection
Eric Wong [Wed, 2 Apr 2025 19:00:14 +0000 (19:00 +0000)] 
http: fix and test Trailer: rejection

We need to check for the existence of Trailers after successful
parsing.  I actually intend to support HTTP trailers, and I
noticed this while working on adding support for them.

3 months agot/httpd-corner: modernize test
Eric Wong [Wed, 2 Apr 2025 19:00:13 +0000 (19:00 +0000)] 
t/httpd-corner: modernize test

We'll rely on autodie more.  We'll also declare internal
subroutines as `my' scalars to ensure they can't be accessed by
other tests when we reuse test processes in `check-run'.  We'll
also set avoid setting TCP_NODELAY for cases where it's not
necessary to save a handfull of cycles.

More corner case tests will be added soon to ensure we can
handle HTTPS termination for Varnish (or other caches).

3 months agohttp: use autodie::open
Eric Wong [Wed, 2 Apr 2025 19:00:12 +0000 (19:00 +0000)] 
http: use autodie::open

A small step towards making fatal messages more consistent.

3 months agodaemon: allow per-listener servername= and serverport=
Eric Wong [Fri, 28 Mar 2025 10:34:10 +0000 (10:34 +0000)] 
daemon: allow per-listener servername= and serverport=

Being able to specify per-listener servername= with the
`--listen' CLI arg is helpful for running an Tor .onion NNTP
server with the same config file (and thus
publicinbox.nntpserver) as a public-facing NNTP endpoint.

For HTTP, servername= and serverport= allow overriding the
SERVER_NAME and SERVER_PORT PSGI environment variables,
respectively.  These are useful for generating full URLs
for clients which don't send the HTTP `Host:' header.

These currently have no effect aside from wasting memory for
POP3 and IMAP listeners.  serverport= isn't used by NNTP,
either.

3 months agowww: omit SERVER_PORT for standard port redirects
Eric Wong [Fri, 28 Mar 2025 10:34:09 +0000 (10:34 +0000)] 
www: omit SERVER_PORT for standard port redirects

For HTTP requests without a `Host:' header, we can omit
the server port component of the URL if using port 443
for `https' or 80 for plain `http'.  We'll also consistently
use SCRIPT_NAME in all of our tests since Plack requires
it.

3 months agolei ls-mail-source: propagate errors to caller
Eric Wong [Thu, 27 Mar 2025 23:20:47 +0000 (23:20 +0000)] 
lei ls-mail-source: propagate errors to caller

LeiInput already handles exceptions via `die', so just die
rather than using lei->err() to report to stderr without
affecting the exit code.

I noticed this problem while making some changes to our NNTP
implementation.

3 months agonntp: avoid repeated rand() calls
Eric Wong [Thu, 27 Mar 2025 23:20:46 +0000 (23:20 +0000)] 
nntp: avoid repeated rand() calls

We only need to generate the secret salt once, so initialize
it early to avoid potentially expensive `rand' ops in repeated
calls to wildmat2re.  We'll also stringify it early to hopefully
improve CoW sharing and reduce fragmentation.

3 months agonntp: avoid modifying $_[0] in RE conversions
Eric Wong [Thu, 27 Mar 2025 23:20:45 +0000 (23:20 +0000)] 
nntp: avoid modifying $_[0] in RE conversions

I've been getting occasional t/nntp.t warnings about
uninitialized variables which I'm not able to reproduce
reliably.  My current theory is that modifying $_[0] may get
wonky when it's happening across several layers of the call
stack, so stop doing it since it's unlikely to yield any
real world speedups and only made the code more difficult
to understand, here.

3 months agonntp: avoid uninitialized vars from bogus LIST args
Eric Wong [Thu, 27 Mar 2025 23:20:44 +0000 (23:20 +0000)] 
nntp: avoid uninitialized vars from bogus LIST args

We can only use the `list_' prefix in NNTP.pm for subcommands
which we intend to dispatch from client requests.  So prefix
the list_*_i subs with a `_' to prevent `args_ok' from firing
on a non-prototyped subroutine.

I noticed this problem in the previous change to reduce code
duplication between LIST subcommands.

3 months agonntp: share common LIST code to reduce duplication
Eric Wong [Thu, 27 Mar 2025 23:20:43 +0000 (23:20 +0000)] 
nntp: share common LIST code to reduce duplication

Deduplicating code here will make a future change to wildmat2re
easier-to-review.

3 months agotests: get rid of most Data::Dumper usage
Eric Wong [Thu, 27 Mar 2025 23:20:42 +0000 (23:20 +0000)] 
tests: get rid of most Data::Dumper usage

`explain' from Test::More already uses Data::Dumper, so
it's unnecessary to set it up and call it ourselves.
This saves a bunch of wonky code in t/nntp.t, as well.

3 months agot/lg2_cfg: fix RE for multi-line preservation
Eric Wong [Sun, 30 Mar 2025 17:57:12 +0000 (17:57 +0000)] 
t/lg2_cfg: fix RE for multi-line preservation

I forgot to run the test on a machine with libgit2 1.8+ before
pushing :x

3 months agouse git(1) for configs if libgit2 <1.8
Eric Wong [Fri, 28 Mar 2025 03:32:09 +0000 (03:32 +0000)] 
use git(1) for configs if libgit2 <1.8

libgit2 multiline support appeared broken before 1.8.  I noticed
the following commits in libgit2 seemed relevant to us, at least:

dff05bc30 (Multiline config values not preserved on saving, 2021-11-25)
de9a76b92 (config: properly handle multiline quotes, 2023-12-14)

3 months agocontent_digest_dbg: more compact display w/ git_quote
Eric Wong [Wed, 26 Mar 2025 20:13:03 +0000 (20:13 +0000)] 
content_digest_dbg: more compact display w/ git_quote

Make WWW /$MSGID/d/ endpoints and `lei mail-diff' output
less verbose by getting rid of Data::Dumper in favor of
a lightly-modified git_quote output.  The extra [] pairs
from Data::Dumper were unnecessary noise and the quoting
used by git_quote should be more familiar to git users.
Unfortunately, git's escaping of NUL bytes to `\000' is
a bit noisy, so we'll undo that by substituting to `\0'.

3 months agosearch: improve xap_helper mset error reporting
Eric Wong [Wed, 26 Mar 2025 03:35:02 +0000 (03:35 +0000)] 
search: improve xap_helper mset error reporting

For user errors generating bad queries, dumping exception
messages from Xapian::QueryParser can be helpful.  So
unconditionally pass the stderr FD as a regular file to
xap_helper processes for both lei and WWW search users.

3 months agotreewide: use git_quote for diagnostic dumps
Eric Wong [Wed, 26 Mar 2025 03:35:01 +0000 (03:35 +0000)] 
treewide: use git_quote for diagnostic dumps

git_quote output is probably less alien to most users than
the Perl-specific Data::Dumper, so use it for any messages
which hit stdout/stderr or syslog.

3 months agowww: hoist out sanitize_local_paths sub for solver
Eric Wong [Wed, 26 Mar 2025 03:35:00 +0000 (03:35 +0000)] 
www: hoist out sanitize_local_paths sub for solver

SearchView and ViewVCS both benefit from local path sanitation
in diagnostic messages of the WWW UI.

3 months agosolver: use git_quote on filenames for diagnostics
Eric Wong [Wed, 26 Mar 2025 03:34:59 +0000 (03:34 +0000)] 
solver: use git_quote on filenames for diagnostics

git_quote output makes more sense than relying on \Q..\E from
Perl or even Data::Dumper since these filenames are from git
output and more users are likely familiar with git-style quoting
than anything Perl-specific.

3 months agotest_common: fix require_git skip message
Eric Wong [Sat, 22 Mar 2025 20:55:43 +0000 (20:55 +0000)] 
test_common: fix require_git skip message

We need to use `sprintf("%vd")' to format the required version
v-string for human consumption.

3 months agolimiter: refactor to reduce code duplication
Eric Wong [Fri, 21 Mar 2025 22:22:30 +0000 (22:22 +0000)] 
limiter: refactor to reduce code duplication

PlackLimiter, Qspawn, and ViewVCS all have roughly the same
code around our base Limiter package, so put everything around
a new Limiter->may_start subroutine.  PlackLimiter loses some
stats as a result but that's logged anyways and I doubt the
customizable error message was worth the effort.

We now have ckhup and 499 (client disconnect) handling for all
PSGI uses of Limiter, as well.

t/qspawn.t changes were required since the original ->finalize
logic now relies on on_destroy; but none of the existing PSGI
code using Qspawn required changes.

3 months agoqspawn: rename {psgi_env} => {env}
Eric Wong [Fri, 21 Mar 2025 22:22:29 +0000 (22:22 +0000)] 
qspawn: rename {psgi_env} => {env}

There's no need to make a distinction here, and
it will help us make the limiter code more flexible
and reusable in next commits.

3 months agoplack_limiter: PSGI middleware to limit concurrency
Eric Wong [Thu, 20 Mar 2025 00:05:35 +0000 (00:05 +0000)] 
plack_limiter: PSGI middleware to limit concurrency

While processing several concurrent requests within the same
worker process is helpful to exploit parallelism in git blob
lookups and smooth out delays; excessive parallelism is harmful
since it allows too much memory to be allocated at once for zlib
buffers and such.

While PublicInbox::WWW already uses the limiter for certain
expensive endpoints (e.g. /s/ and anything using Qspawn); some
long-running endpoints with many inexpensive steps (e.g. /T/,
/t/, /d/, *.atom, *.mbox.gz, etc.) can end up using a large
amount of memory for gzip buffers despite being fair to other
responses and being able to stream >500 messages/sec on 2010-era
hardware.

So give sysadmins an option to balance between smoothing out
delays in blob retrieval and memory usage required to compress
and spew out chunks of potentially large multi-email responses.

3 months agolg2: disable strict hash verification
Eric Wong [Tue, 18 Mar 2025 08:30:27 +0000 (08:30 +0000)] 
lg2: disable strict hash verification

Unlike git(1), libgit2 verifies the SHA-(1|256) of objects it
reads by default.  This verification results in a large (nearly
100% w/ SHA1DC) performance penalty for us.  Since our libgit2
code only reads (and never writes objects), just follow git(1)
and skip verification for normal reads.

This brings our libgit2-based Gcf2 batch loop performance closer
to that of the `git cat-file --batch-command' as shown in the
new xt/lg2_cmp.t developer test.  However, Gcf2Client still uses
a more verbose (but more flexible) input format and the Perl
gcf2_loop still incurs normal Perl method dispatch overheads.

3 months agowww_static: path_info_raw: support non-HTTP(S) schemes
Eric Wong [Fri, 14 Mar 2025 09:22:05 +0000 (09:22 +0000)] 
www_static: path_info_raw: support non-HTTP(S) schemes

We may add support for gemini:// which supports CGI-like
protocols like PSGI, so relaxing the HTTP(S) URL scheme
requirement seems to make sense.

3 months agodaemon: define %TLS_ONLY hash
Eric Wong [Fri, 14 Mar 2025 09:22:04 +0000 (09:22 +0000)] 
daemon: define %TLS_ONLY hash

Defining TLS-only protocols only once makes it easier to support
new protocols in the future since we can rely on only updating
this new hash instead of having to update regexps in other
places.

3 months agowww: disable legacy encoded Message-IDs for non-v1
Eric Wong [Thu, 13 Mar 2025 20:35:04 +0000 (20:35 +0000)] 
www: disable legacy encoded Message-IDs for non-v1

For a while in the very early days of the v1 format, we
supported SHA-1 checksums of the Message-ID in the URL.  This
never affected v2 inboxes nor extindex, and those URLs would've
been broken if run through public-inbox-convert, anyways.

4 months agoXapHelper.pm: fix QP_FLAGS initialization
Eric Wong [Thu, 6 Mar 2025 20:34:42 +0000 (20:34 +0000)] 
XapHelper.pm: fix QP_FLAGS initialization

We can't use $PublicInbox::Search::QP_FLAGS until after
load_xapian is called.  Failure to set the correct query parser
flags was causing failures in
`PI_NO_CXX=1 TEST_DAEMON_XH=-X0 prove -bw t/imapd.t'
since phrase parsing was broken with the Perl bindings
XapHelper implementation.

Now, the `check-xh0' and `check-xh1' targets both pass with
PI_NO_CXX=1 set to disable the C++ xap_helper.

Fixes: fa6a7919 (xap_helper: enable FLAG_PURE_NOT in external process, 2025-02-23)
4 months agoxap_helper: drop qp_extra_done flag and conditions
Eric Wong [Thu, 6 Mar 2025 20:34:41 +0000 (20:34 +0000)] 
xap_helper: drop qp_extra_done flag and conditions

As with -d (directories), the -Q (extra query prefixes) flag is
already part of the cache key so there's no need to initialize
it lazily.

While we're at it, drop a `map' op from the cache key generation
for the Perl implementation.

4 months agosearch: unoverload {relevance} in options
Eric Wong [Wed, 5 Mar 2025 23:26:47 +0000 (23:26 +0000)] 
search: unoverload {relevance} in options

Take the lead from xap_helper and split out handling of {asc}
and {sort_col} fields while keeping the {relevance} field as a
boolean.  As with xap_helper, {sort_col} < 0 means
Xapian::BoolWeight will be used for docid ordering.

4 months agosearch: use common do_enquire for MiscSearch
Eric Wong [Wed, 5 Mar 2025 23:26:46 +0000 (23:26 +0000)] 
search: use common do_enquire for MiscSearch

We can consistently use a {sort_col} search option as in
xap_helper to allow do_enquire to be more generic across
searches.  This lets us avoid the need for a specialized
implementation in MiscSearch.

4 months agolei: use C++ xap_helper if available
Eric Wong [Wed, 5 Mar 2025 07:18:36 +0000 (07:18 +0000)] 
lei: use C++ xap_helper if available

The C++ version of xap_helper allows `thread:' subqueries,
non-fragile parsing of git approxidate with `d:', `dt:' and
`rt:' prefixes, and possibly more features in the future not
available in the SWIG or XS Xapian bindings.

There's no point in lei supporting the Perl version of XapHelper
since lei isn't expected to deal with abusive clients making
expensive queries, so lei will only use the C++ version.

We spawn xap_helper in every lei worker process to guarantee
resource availability without having to resort to preforking.

While pre-forking and on-demand thread||process creation was
considered, I decided having a lingering xap_helper process
probably wasn't worth the startup time improvement and instead
prefer to minimize idle memory use.

The whole implementation is a bit strange since lei support was
an after-thought for xap_helper and we wrap the async_mset API
to make it synchronous once again to reduce the code impact for
code shared with public-facing daemons.

Finally, test_lei in TestCommon gets tweaked to avoid repeatedly
rebuilding in a new directory with every test_lei use in our test
suite.

4 months agoxap_helper.h: share ThreadFieldProcessor across DBs
Eric Wong [Wed, 5 Mar 2025 07:18:35 +0000 (07:18 +0000)] 
xap_helper.h: share ThreadFieldProcessor across DBs

Unlike notmuch, we don't actually use the QueryParser from
initialization and can stop storing it in the class.  Instead,
we rely on the global `cur_srch' at runtime for both which has
both the Xapian::Databaase and Xapian::QueryParser objects.  So
stop carrying a copy of the ThreadFieldProcessor object for
every DB connection we have since some public non-extindex-users
may have many DBs and we can probably save some memory this way.

4 months agosearch: avoid `git rev-parse' if using C++ xap_helper
Eric Wong [Wed, 5 Mar 2025 07:18:34 +0000 (07:18 +0000)] 
search: avoid `git rev-parse' if using C++ xap_helper

The C++ xap_helper can work with the Xapian query parser API to
run run git_date_parse(), so there's no need to do janky string
substitutions as we do for the Perl bindings.

4 months agoxap_helper: use libgit2 git_date_parse in C++ impl
Eric Wong [Wed, 5 Mar 2025 07:18:33 +0000 (07:18 +0000)] 
xap_helper: use libgit2 git_date_parse in C++ impl

Using proper Xapian RangeProcessor and FieldProcessor subclasses
via the Xapian API means our `d:', `dt:' and `rt:' prefixes can
use git `approxidate' interpretation rules inside quoted
subqueries with the `thread:' prefix.

The only incompatibility is the `rt:' field which no longer
takes seconds with <5 characters (e.g. `rt:0..' as was in
t/xap_helper.t), but that'd be broken anyways in real-world use
which is run through `git rev-parse --since='.

4 months agosearch: make `d:' search prefix consistently date-only
Eric Wong [Wed, 5 Mar 2025 07:18:32 +0000 (07:18 +0000)] 
search: make `d:' search prefix consistently date-only

While `d:' previously treated YYYYMMDD and YYYY-MM-DD as
low-precision by assuming 00:00:00 for the HH:MM:SS portion, it
would not do so for dates passed to git-rev-parse (e.g.
"last.year") since the HH:MM:SS of the current time would be
used by git.  So stop remapping `d:' in queries to `dt:' and
continue using `d:' in the YYYYMMDD column.

This does break things like `d:2.hours.ago..' by causing too
many (or too few) results to be returned due to lack of
precision, but I expect small time ranges of less than one
day to be of limited use.

`dt:' remains a higher-precision field for searching on both
date and time from the Date: header, while `d:' is now always
date-only.

4 months agolistener: don't set listen backlog on inherited sockets
Eric Wong [Wed, 5 Mar 2025 00:45:36 +0000 (00:45 +0000)] 
listener: don't set listen backlog on inherited sockets

By using the listen(2) backlog as-is when inheriting (from
systemd or similar), we can give the sysadmin more control on
controlling overload on a per-listener basis.  For systemd
users, this means the `Backlog=' parameter in systemd.socket(5)
can be respected and configured to give certain sockets a
smaller backlog (perhaps combined with with per-listener
`multi-accept' parameter on sockets with the standard (huge)
backlog).

For sockets we create, continue to use INT_MAX and let the
kernel clamp it to whatever system-wide limit there is
(e.g. `net.core.somaxconn' sysctl on Linux).

4 months agot/xap_helper: bail on build failure if TEST_XH_CXX_ONLY
Eric Wong [Thu, 27 Feb 2025 00:08:19 +0000 (00:08 +0000)] 
t/xap_helper: bail on build failure if TEST_XH_CXX_ONLY

We shouldn't transparently fall back to the Perl bindings test
on build failures if the tester wants to explicitly test the C++
implementation.

4 months agoimap: support external xap_helper process
Eric Wong [Thu, 27 Feb 2025 00:08:18 +0000 (00:08 +0000)] 
imap: support external xap_helper process

Moving Xapian requests to an external process prevents expensive
searches from affecting non-search IMAP commands or any other
non-search requests within a -netd process if IMAP is sharing a
process with other public-facing protocols.

4 months agodaemon: slightly simplify xap_helper spawning
Eric Wong [Thu, 27 Feb 2025 00:08:17 +0000 (00:08 +0000)] 
daemon: slightly simplify xap_helper spawning

Avoid typing the fully-qualified global $XHC variable twice in
spawn_xh.  We'll also delay loading of XhcMset until successful
spawn and improve the error message in case of error.  There's
also no need to conditionally local-ize
$PublicInbox::Search::XHC, so do it unconditionally to reduce
branches.

4 months agosearch: remove xhc_start_maybe
Eric Wong [Thu, 27 Feb 2025 00:08:16 +0000 (00:08 +0000)] 
search: remove xhc_start_maybe

We start xap_helper processes early in both public-facing
daemons to ensure maximum sharing.  lei may be different
since lingering processes likely sit idle for long periods
and we don't want to tie up RAM or swap space for idle
processes.

4 months agoxh_thread_fp: optimize OR query generation
Eric Wong [Sun, 23 Feb 2025 13:13:35 +0000 (13:13 +0000)] 
xh_thread_fp: optimize OR query generation

For Xapian >= 1.4.10 users, using `|=' will reduce allocations.
`|=' still works for Xapian 1.3.2 .. 1.4.9 users, and I'm not
sure if Xapian 1.2.x is relevant anymore, especially for our
new C++-only features.

While operator overloading is often confusing and frustrating to
me when reading someone else's code, the optimization seems worth
it since (AFAIK) there's no other way to get the allocation
reduction.

cf. Olly in xapian-discuss <20250222043050.GA17282@survex.com>

4 months agoxap_helper: avoid temporary std::set in thread fp
Eric Wong [Sun, 23 Feb 2025 13:13:34 +0000 (13:13 +0000)] 
xap_helper: avoid temporary std::set in thread fp

Instead of creating a temporary std::set to store THREADIDs,
just inject the Xapian::Query::OP_OR operations directly into
the query we return.

Unlike notmuch(1), we can do this without risking redundant
query ops from repeated THREADIDs since use since we use a
column instead of a term for THREADID.  Column use allowed
us to use .set_collapse_key to deduplicate the intermediate
mset, already.

4 months agoxap_helper: enable FLAG_PURE_NOT in external process
Eric Wong [Sun, 23 Feb 2025 13:13:33 +0000 (13:13 +0000)] 
xap_helper: enable FLAG_PURE_NOT in external process

Since public-facing WWW uses an external process with
a non-blocking socket, clients will only see 503 errors
if the search is overloaded.  While allowing pure NOT
queries is expensive, there are already many possible
ways of triggering expensive queries so it's probably not
a problem to allow one more.

4 months agosearchidx: doc: note ->add_message is v1+tests only
Eric Wong [Thu, 20 Feb 2025 22:14:31 +0000 (22:14 +0000)] 
searchidx: doc: note ->add_message is v1+tests only

Noticed while seeing if --xapian-only is a worthwhile
addition to -extindex.

4 months agosearch: index References: for thread:GHOST-MSGID
Eric Wong [Thu, 20 Feb 2025 22:14:30 +0000 (22:14 +0000)] 
search: index References: for thread:GHOST-MSGID

To search for messages in a thread with a ghost msgid,
we need to be able to search against msgids in the
References: header since (by definition) ghosts don't
show up as any Message-ID: we've indexed.

This should make our implementation of `thread:MSGID'
queries equivalent in capability to `thread:THREADID'
of notmuch.

4 months agoxap_helper: support thread:{SUBQUERY} via C++
Eric Wong [Thu, 20 Feb 2025 22:14:29 +0000 (22:14 +0000)] 
xap_helper: support thread:{SUBQUERY} via C++

Stealing the idea and code from notmuch to perform subqueries
within search.  One major internal difference from notmuch is we
store THREADID as numeric column in Xapian whereas notmuch
stores a boolean term.  The use of a column lets us use
set_collapse_key to deduplicate results within Xapian itself.

The other difference from notmuch is we avoid exposing the
numeric THREADID since they're unstable and not reproducible in
mirrors, thus we also support `thread:MSGID' instead of
`thread:THREADID' in brace-less queries.

4 months agoxap_helper: switch C++ implementation to AGPL-3
Eric Wong [Thu, 20 Feb 2025 22:14:28 +0000 (22:14 +0000)] 
xap_helper: switch C++ implementation to AGPL-3

GPL-2 approxidate code won't work with the XS/SWIG version, so
it looks like we'll keep calling `git rev-parse' in both
versions for the time being.  Meanwhile, it's more valuable to
be able to take GPL-3+ code from notmuch for thread:{} query
parsing.

4 months agosearchidx: don't index Base-85 w/ CRLF endings
Eric Wong [Wed, 19 Feb 2025 10:10:32 +0000 (10:10 +0000)] 
searchidx: don't index Base-85 w/ CRLF endings

I encountered a false positive search result from a CRLF message
with a Base-85 patch in it.  It turns out our Base-85 filtering
code didn't account for the possibility of "\r" showing up in
patch messages, so just ignore all trailing spaces (not just
horizontal spaces) in index_diff().

While we're at it, exclude horizontal whitespace and CR
consistently from Base-85-looking quoted text in
index_body_text(), too, since I'm sure there's messages with
CRCRLF in the wild, too...

4 months agocfgwr: fix non-libgit2 case
Eric Wong [Wed, 19 Feb 2025 12:39:26 +0000 (12:39 +0000)] 
cfgwr: fix non-libgit2 case

libgit2 isn't installed on all my test machines, and the
addition of libgit2 support was careless in that it failed to
test the non-libgit2 case thoroughly :x

Fixes: a8073f6c (lg2: use cfgwr_commit to write to configs using libgit2, 2025-02-15)
4 months agolg2: use cfgwr_commit to write to configs using libgit2
Eric Wong [Sat, 15 Feb 2025 11:10:11 +0000 (11:10 +0000)] 
lg2: use cfgwr_commit to write to configs using libgit2

libgit2 allows us to avoid process spawning overhead when
writing to the config file.  While the performance difference is
unlikely to matter in real-world use, it should improve
maintainability of tests by allowing us to write tests with
`public-inbox-init' with a smaller performance penalty (as
opposed to using `PublicInbox::IO::write_file' to setup
configs).

4 months agorename Gcf2 -> Lg2 for general libgit2 use
Eric Wong [Sat, 15 Feb 2025 11:10:10 +0000 (11:10 +0000)] 
rename Gcf2 -> Lg2 for general libgit2 use

We'll be exploring libgit2 for writing (and possibly reading) config
files, so the `gcf2' (git-cat-file-2) name isn't appropriate
anymore for the package name.  We'll still keep `gcf2_' for
subroutine names related to git-cat-file-like behavior.

4 months agocfgwrite: new module to batch commit writes
Eric Wong [Sat, 15 Feb 2025 11:10:09 +0000 (11:10 +0000)] 
cfgwrite: new module to batch commit writes

Hoisting git config writing code into a separate module will
make it easier to use libgit2 to avoid process spawning overhead
in a future change.

4 months agot/watch_v1_v2_mix_no_modify: load PublicInbox::Config
Eric Wong [Sat, 15 Feb 2025 11:10:08 +0000 (11:10 +0000)] 
t/watch_v1_v2_mix_no_modify: load PublicInbox::Config

For testing with TEST_RUN_MODE=0 (which spawns new processes
instead of reusing them), we can't rely on packages being loaded
by subprocesses influencing the main Perl process.  Thus we must
load PublicInbox::Config explicitly to prevent failures with
this run mode.

4 months agotest_common: key2sub: detect syntax errors early
Eric Wong [Sat, 15 Feb 2025 11:10:07 +0000 (11:10 +0000)] 
test_common: key2sub: detect syntax errors early

Instead of attempting to use an `undef' $sub as a coderef;
detect compile errors early and die immediately if `eval'
raises an exception.

4 months agoimport: clobber $? if global init.defaultBranch fails
Eric Wong [Sat, 15 Feb 2025 11:10:06 +0000 (11:10 +0000)] 
import: clobber $? if global init.defaultBranch fails

We don't want $? influencing further error checking downstream,
such as thw _wq_done_wait call in public-inbox-clone when
git-config(1) invocations which set $?=0 are replaced with
in-process libgit2 config function calls.

4 months agoedit: use autodie and favor flush over autoflush
Eric Wong [Sat, 15 Feb 2025 11:10:05 +0000 (11:10 +0000)] 
edit: use autodie and favor flush over autoflush

Using a single ->flush reduces iops for the (default) non-raw
case and autodie for `open' makes error messages more
consistent.

4 months agofavor run_wait() over CORE::system()
Eric Wong [Sat, 15 Feb 2025 11:10:04 +0000 (11:10 +0000)] 
favor run_wait() over CORE::system()

run_wait() can use vfork(2) which saves memory in large
processes, such as public-inbox-extindex when dealing with giant
messages.  While vfork is unlikely to matter for real-world uses
of public-inbox-edit, PublicInbox::Spawn is a sunk cost treewide
our `make check-run' test target avoids spawning new Perl
processes for most things in script/*, so there can be a small
savings for testing.

4 months agoimap_searchqp: avoid occasional P::RD hint spew
Eric Wong [Fri, 14 Feb 2025 09:38:55 +0000 (09:38 +0000)] 
imap_searchqp: avoid occasional P::RD hint spew

It turns out Inline::C::Parser::RecDescent increments $::RD_HINT
when Inline::C rebuilds are necessary, thus causing verbose log
spew on bogus IMAP search queries in our own use of
Parse::RecDescent.  So, local-ize $::RD_HINT as we do with other
$::RD_* vars to avoid Inline::C influencing our IMAP query
parsing.

Triggering the increment of $::RD_HINT via
Inline::C::Parser::RecDescent may be achieved by forcing an
Inline::C rebuild, such as clearing the contents of
~/.cache/public-inbox/inline-c/* or
$ENV{PERL_INLINE_DIRECTORY}/* (but leaving one of those
top-level directories to trigger Inline::C rebuilds.  This bug
mainly manifested in t/imap_searchqp.t under the `make
check-run' test target which reuses existing processes to speed
up tests, though it could manifest in real-world -imapd (and
-netd) usage if the process spawning the daemon built Inline::C
code.

4 months agodaemon: make xap_helper socket non-blocking
Eric Wong [Tue, 11 Feb 2025 03:55:36 +0000 (03:55 +0000)] 
daemon: make xap_helper socket non-blocking

A non-blocking socket for public-facing searches is ideal on
overloaded servers which receive more searches than it can
handle in a short time span.  A non-blocking socket here
prevents a sendmsg(2) call from blocking the main event loop
and ensures non-search requests can still be processed even
if all xap_helper subprocesses are overwhelmed.

$search_xh_pid is hoisted out to TestCommon for use with the new
slow-xh-search test and renamed $find_xh_pid to avoid confusion
with Xapian search functionality.  We'll also drop a redundant
call to find the xap_helper PID in psgi_v2.t while transitioning
to $find_xh_pid.

4 months agowww: mbox: break out of loop on exceptions
Eric Wong [Tue, 11 Feb 2025 03:55:35 +0000 (03:55 +0000)] 
www: mbox: break out of loop on exceptions

In case async_mset fails due to resource limitations, ensure we
stop trying to make expensive search queries ASAP.

4 months agosearch: async_mset: always run callback on exceptions
Eric Wong [Tue, 11 Feb 2025 03:55:34 +0000 (03:55 +0000)] 
search: async_mset: always run callback on exceptions

To produce consistent error behavior, ensure we always run
the user-supplied callback on exceptions when using in-process
Xapian (as opposed to the external xap_helper process).

4 months agogit_http_backend: input_prepare: die on unrecoverable errors
Eric Wong [Mon, 10 Feb 2025 21:09:34 +0000 (21:09 +0000)] 
git_http_backend: input_prepare: die on unrecoverable errors

We shouldn't be guarding against errors at this level to return
500 errors, but rather we can rely on existing eval wrappers in
the stack (e.g. Plack::Util::run_app and Qspawn->parse_hdr_done)
to capture errors and return the HTTP 500 status.

4 months agolei_mirror: use write_file to append configs
Eric Wong [Mon, 10 Feb 2025 21:09:33 +0000 (21:09 +0000)] 
lei_mirror: use write_file to append configs

It saves us a few lines of code and reduces unnecessary
error checking since the close done by write_file aleady
does autodie for error checking.

4 months agomsgmap: use v5.12
Eric Wong [Mon, 10 Feb 2025 21:09:32 +0000 (21:09 +0000)] 
msgmap: use v5.12

No unicode_string dependencies, here, so we can use v5.12 and
perhaps to speed up loading one day if everything drops `strict'
in favor of v5.12 (or higher).

4 months agoinit: reduce git-config execve for idempotent cases
Eric Wong [Mon, 10 Feb 2025 21:09:31 +0000 (21:09 +0000)] 
init: reduce git-config execve for idempotent cases

This probably saves us a few cycles in some cases since spawning
new processes is expensive.

4 months agomulti_git: remove redundant ops
Eric Wong [Mon, 10 Feb 2025 21:09:30 +0000 (21:09 +0000)] 
multi_git: remove redundant ops

read_all already returns an array in array context so the
`split' op is unnecessary, and we don't need to use
`join("", ...)' on arrays that are destined for `print'
or `say'.

4 months agodevel/sysdefs-list: explain why we don't autodie, here
Eric Wong [Mon, 10 Feb 2025 21:09:29 +0000 (21:09 +0000)] 
devel/sysdefs-list: explain why we don't autodie, here

Some distros split out core modules like autodie, and
sysdefs-list has higher portability requirements than the rest
of our code since it's used for uncommon platforms.

4 months agoconfig: handle limiter `max' knob in ->setup_limiter
Eric Wong [Sat, 8 Feb 2025 03:26:39 +0000 (03:26 +0000)] 
config: handle limiter `max' knob in ->setup_limiter

Handling all config knobs in the same sub is more logical and
reduces the size of the frequently-loaded PublicInbox::Config
package.

4 months agoqspawn: drop redundant check against limiter->{max}
Eric Wong [Sat, 8 Feb 2025 03:26:38 +0000 (03:26 +0000)] 
qspawn: drop redundant check against limiter->{max}

We only need to guard against excessive parallelism on entry
in one place during Qspawn->start.  Redundant guards are
confusing and make bugs harder-to-spot.

4 months agoviewvcs: -codeblob limiter w/ depth for solver
Eric Wong [Sat, 8 Feb 2025 03:26:37 +0000 (03:26 +0000)] 
viewvcs: -codeblob limiter w/ depth for solver

By adding the `publicinboxlimiter.<name>.depth' parameter for
all limiters, we can have queue overflow detection and reject
requests with HTTP 503 error codes for all limiter uses, not
just the custom queue in ViewVCS as before.  Then we can just
use a normal limiter in ViewVCS and get rid of the old custom
queue.

4 months agoqspawn: use limiter->new default
Eric Wong [Sat, 8 Feb 2025 03:26:36 +0000 (03:26 +0000)] 
qspawn: use limiter->new default

No need to hardcode `32' here since PublicInbox::Limiter already
uses that value already (and includes a helpful comment about
using the same value as git-daemon).

4 months agodaemon: check connections before solving codeblobs
Eric Wong [Sat, 8 Feb 2025 03:26:35 +0000 (03:26 +0000)] 
daemon: check connections before solving codeblobs

The /$MSGID/s/ codeblob reconstruction endpoint is extremely
expensive and one of the few which do a large amount of upfront
processing before being able to write to an HTTP client and
detect if they're still alive (unlike other expensive
endpoints).

For TCP connections, use the TCP_INFO via getsockopt(2) if
supported or attempt to use poll(2) + the FIONREAD ioctl(2) to
check the connection before possibly invoking
git-(apply|ls-files|update-index) dozens or even hundreds of
times to reconstruct a blob.

4 months agogit_http_backend: fix 32 default connection limit
Eric Wong [Sat, 8 Feb 2025 03:26:34 +0000 (03:26 +0000)] 
git_http_backend: fix 32 default connection limit

Using any configurable limiter actually defaults to `1', which
isn't intended for `-httpbackend'.  So add a $max_default
parameter to ensure we can set a per-limiter default value for
default limiters such as `-httpbackend'.

Fixes: 0a083241 (git_http_backend: use default limiter from Qspawn, 2025-01-28)
4 months agosyscall: `use bytes' throughout
Eric Wong [Sat, 8 Feb 2025 03:26:33 +0000 (03:26 +0000)] 
syscall: `use bytes' throughout

All *nix syscalls operate on the byte-level with no knowledge of
encodings, so ensure all `substr' and `length' calculations use
the bytes(3perl) pragma.

5 months agoRevert "daemon: check connections WIP"
Eric Wong [Tue, 4 Feb 2025 20:33:50 +0000 (20:33 +0000)] 
Revert "daemon: check connections WIP"

Accidentally pushed out a WIP change since I couldn't access my
normal VM for pushing :x
This reverts commit 10394e508575fd7cc78d0255d39b04c833efcdbb.

5 months agogit_http_backend: use default limiter from Qspawn
Eric Wong [Tue, 28 Jan 2025 08:31:13 +0000 (08:31 +0000)] 
git_http_backend: use default limiter from Qspawn

There's no reason for us to have another default limiter object
when the one in Qspawn already exists.

5 months agowww: configurable "-httpbackend" named limiter
Eric Wong [Tue, 28 Jan 2025 08:31:12 +0000 (08:31 +0000)] 
www: configurable "-httpbackend" named limiter

The default limiter being hard-coded to 32 is excessive for
smaller systems.  With dozens or hundreds of inboxes,
configuring all inboxes to point to a user-defined limiter
is tedious, as well.  So give WWW admins the opportunity
to configure the limiter for all git-http-backend(1) processes
with one `publicinbox.-httpbackend.max' knob.

5 months agolimiter: ignore unparseable rlimit
Eric Wong [Tue, 28 Jan 2025 08:31:11 +0000 (08:31 +0000)] 
limiter: ignore unparseable rlimit

Instead of blindly accepting whatever bogus values the config
gave us, ignore it and use the `W:' prefix to be consistent
with the rest of our codebase.

5 months agodaemon: check connections WIP
Eric Wong [Tue, 28 Jan 2025 08:10:06 +0000 (08:10 +0000)] 
daemon: check connections WIP

5 months agoviewvcs: handle exceptions in on_destroy cb
Eric Wong [Fri, 24 Jan 2025 09:18:56 +0000 (09:18 +0000)] 
viewvcs: handle exceptions in on_destroy cb

We need to account for File::Temp->newdir or autodie::open
croaking on us in case the system is out of space or memory.
These resource errors are already handled in our normal code
path.  However, this on_destroy codepath can be triggered
when the solver request needs to be queued due to business.
Thus we explicitly call the {-wcb} to ensure the socket
isn't forgotten.

5 months agoemergency: remove needless $! clobber
Eric Wong [Sat, 18 Jan 2025 01:26:16 +0000 (01:26 +0000)] 
emergency: remove needless $! clobber

We don't check $! unless `sysopen' fails, and `sysopen' will set
$! on failure so there's no need to undef $! ourselves.

5 months agomda: use read_all for error handling
Eric Wong [Sat, 18 Jan 2025 01:26:15 +0000 (01:26 +0000)] 
mda: use read_all for error handling

We already imported PublicInbox::IO for this script and read_all
provides more safety in case default PerlIO semantics change for
read-in-full behavior.

5 months agov2writable: remove outdated FIXME comment and assertions
Eric Wong [Sat, 18 Jan 2025 01:26:14 +0000 (01:26 +0000)] 
v2writable: remove outdated FIXME comment and assertions

The lei/store bug was resolved and thus the comments and
assertions are no longer necessary.

Followup-to: 99fc3d76 (v2writable: done: force synchronous awaitpid, 2024-11-19)
5 months agoconfig: config_fh_parse: hardcode FS/RS
Eric Wong [Sat, 18 Jan 2025 01:26:13 +0000 (01:26 +0000)] 
config: config_fh_parse: hardcode FS/RS

We no longer need the flexibility to handle non-NUL-delimited
output from `git config -l', so simplify callers and allow
a theoretically sufficiently-advanced Perl implementation
optimize more easily.

Followup-to: 21146412 (config: drop scalar ref support from internal API, 2023-09-24)
5 months agogit: rely on autodie for sysseek/sysread/truncate
Eric Wong [Sat, 18 Jan 2025 01:26:12 +0000 (01:26 +0000)] 
git: rely on autodie for sysseek/sysread/truncate

Error messages are more consistent with autodie.  We'll also
drop autodie::read since it's no longer in use for this package.

5 months agotreewide: use autodie for seek+sysseek
Eric Wong [Sat, 18 Jan 2025 01:26:11 +0000 (01:26 +0000)] 
treewide: use autodie for seek+sysseek

The underlying lseek(2) syscall won't fail due to HW errors,
only due to usage errors (ESPIPE, EINVAL); so don't waste
code on error checking ourselves and just let autodie check
things during development.

5 months agolei_xsearch: use autodie for `pipe' ops
Eric Wong [Sat, 18 Jan 2025 01:26:10 +0000 (01:26 +0000)] 
lei_xsearch: use autodie for `pipe' ops

We already use autodie in this module and we can have more
consistent error messages this way.

5 months agotreewide: replace redundant `;;' with `;'
Eric Wong [Sat, 18 Jan 2025 01:26:09 +0000 (01:26 +0000)] 
treewide: replace redundant `;;' with `;'

Wonky USB keyboard adapter or too much coffee sometimes makes me
type too many semi-colons (and other characters) :x

5 months agolei_saved_search: drop needless comparisons and `next'
Eric Wong [Sat, 18 Jan 2025 01:26:08 +0000 (01:26 +0000)] 
lei_saved_search: drop needless comparisons and `next'

The grep(!/\A\.\.?\z/, ...) op already filters out the `.' and
`..' entries from `readdir'.