Author: covener
Date: Fri Aug 7 14:20:48 2026
New Revision: 1936971
Log:
Merge r1936966, r1936967, r1936970 from apr trunk:
fix memcache version processing
copy sigpipe stuff from other memcache mock test
silently ended ./testall in backport
use sub-pools for memcache tests
so they can be cleaned up while sigpipe is still
ignored (quit on closed socket?)
Modified:
apr/apr-util/branches/1.7.x/ (props changed)
apr/apr-util/branches/1.7.x/memcache/apr_memcache.c
apr/apr-util/branches/1.7.x/test/memcachedmock.c
apr/apr-util/branches/1.7.x/test/testmemcache.c
Modified: apr/apr-util/branches/1.7.x/memcache/apr_memcache.c
==============================================================================
--- apr/apr-util/branches/1.7.x/memcache/apr_memcache.c Fri Aug 7 14:16:46 2026 (r1936970)
+++ apr/apr-util/branches/1.7.x/memcache/apr_memcache.c Fri Aug 7 14:20:48 2026 (r1936971)
@@ -1171,12 +1171,12 @@ apr_memcache_version(apr_memcache_server
}
if (strncmp(MS_VERSION, conn->buffer, MS_VERSION_LEN) == 0) {
- if (conn->blen < MS_VERSION_LEN + 2) {
+ if (conn->blen < MS_VERSION_LEN + 4) {
rv = APR_EGENERAL;
}
else {
*baton = apr_pstrmemdup(p, conn->buffer+MS_VERSION_LEN+1,
- conn->blen - MS_VERSION_LEN - 2);
+ conn->blen - MS_VERSION_LEN - 3);
rv = APR_SUCCESS;
}
}
Modified: apr/apr-util/branches/1.7.x/test/memcachedmock.c
==============================================================================
--- apr/apr-util/branches/1.7.x/test/memcachedmock.c Fri Aug 7 14:16:46 2026 (r1936970)
+++ apr/apr-util/branches/1.7.x/test/memcachedmock.c Fri Aug 7 14:20:48 2026 (r1936971)
@@ -20,7 +20,15 @@
#include "apr_pools.h"
#include "testmemcache.h"
-#define MOCK_REPLY "VERSION 1.5.22\r\n"
+#define MOCK_REPLY_DEFAULT "VERSION 1.5.22\r\n"
+
+/* Number of connections to serve before exiting.
+ * The test_connection_validation test needs 2 (one intentional, one
+ * after the server closes the socket). The version-response tests
+ * need only 1 each. Pass MOCK_NCONN on the command line after the
+ * reply string to override; default is 2 for backward compatibility.
+ */
+#define MOCK_NCONN_DEFAULT 2
int main(int argc, char *argv[])
{
@@ -30,7 +38,11 @@ int main(int argc, char *argv[])
apr_socket_t *server_connection;
apr_status_t rv;
apr_size_t length;
- int i;
+ int i, nconn, conn;
+ const char *reply;
+
+ reply = (argc >= 2) ? argv[1] : MOCK_REPLY_DEFAULT;
+ nconn = (argc >= 3) ? atoi(argv[2]) : MOCK_NCONN_DEFAULT;
apr_initialize();
atexit(apr_terminate);
@@ -48,37 +60,23 @@ int main(int argc, char *argv[])
apr_socket_listen(server, 5);
- /* Do spin instead of a proper poll for sake of simplicity */
- for (i = 0; i < 4; i++) {
+ for (conn = 0; conn < nconn; conn++) {
+ /* Do spin instead of a proper poll for sake of simplicity */
+ for (i = 0; i < 4; i++) {
+
+ rv = apr_socket_accept(&server_connection, server, p);
+ if (rv == APR_SUCCESS) {
+ break;
+ }
- rv = apr_socket_accept(&server_connection, server, p);
- if (rv == APR_SUCCESS) {
- break;
+ apr_sleep(apr_time_from_sec(1));
}
- apr_sleep(apr_time_from_sec(1));
- }
-
- length = strlen(MOCK_REPLY);
- apr_socket_send(server_connection, MOCK_REPLY, &length);
-
- apr_socket_close(server_connection);
+ length = strlen(reply);
+ apr_socket_send(server_connection, reply, &length);
- /* Do spin instead of a proper poll for sake of simplicity */
- for (i = 0; i < 4; i++) {
-
- rv = apr_socket_accept(&server_connection, server, p);
- if (rv == APR_SUCCESS) {
- break;
- }
-
- apr_sleep(apr_time_from_sec(1));
+ apr_socket_close(server_connection);
}
- length = strlen(MOCK_REPLY);
- apr_socket_send(server_connection, MOCK_REPLY, &length);
-
- apr_socket_close(server_connection);
-
exit(0);
}
Modified: apr/apr-util/branches/1.7.x/test/testmemcache.c
==============================================================================
--- apr/apr-util/branches/1.7.x/test/testmemcache.c Fri Aug 7 14:16:46 2026 (r1936970)
+++ apr/apr-util/branches/1.7.x/test/testmemcache.c Fri Aug 7 14:20:48 2026 (r1936971)
@@ -674,6 +674,7 @@ static void test_connection_validation(a
apr_status_t rv;
apr_memcache_t *memcache;
apr_memcache_server_t *memserver;
+ apr_pool_t *mc_pool;
char *result;
apr_procattr_t *procattr;
apr_proc_t proc;
@@ -719,10 +720,18 @@ static void test_connection_validation(a
/* Wait for the mock memcached to start */
apr_sleep(apr_time_from_sec(2));
- rv = apr_memcache_create(p, 1, 0, &memcache);
+ /*
+ * Use a sub-pool for the memcache objects so the reslist (and its
+ * mc_conn_destruct callbacks, which write to the now-dead socket) is
+ * torn down before we restore the SIGPIPE handler.
+ */
+ rv = apr_pool_create(&mc_pool, p);
+ ABTS_ASSERT(tc, "pool create failed", rv == APR_SUCCESS);
+
+ rv = apr_memcache_create(mc_pool, 1, 0, &memcache);
ABTS_ASSERT(tc, "memcache create failed", rv == APR_SUCCESS);
- rv = apr_memcache_server_create(p, MOCK_HOST, MOCK_PORT, 0, 1, 1,
+ rv = apr_memcache_server_create(mc_pool, MOCK_HOST, MOCK_PORT, 0, 1, 1,
apr_time_from_sec(60), &memserver);
ABTS_ASSERT(tc, "server create failed", rv == APR_SUCCESS);
@@ -738,6 +747,12 @@ static void test_connection_validation(a
rv = apr_memcache_version(memserver, p, &result);
ABTS_ASSERT(tc, "Couldn't get version after connection shutdown", rv == APR_SUCCESS);
+ /*
+ * Destroy the pool while SIGPIPE is still ignored: mc_conn_destruct sends
+ * "quit\r\n" to the dead socket which would otherwise raise SIGPIPE.
+ */
+ apr_pool_destroy(mc_pool);
+
#ifdef SIGPIPE
/* Restore old SIGPIPE handler */
apr_signal(SIGPIPE, old_action);
@@ -746,6 +761,135 @@ static void test_connection_validation(a
apr_proc_wait(&proc, &exitcode, &why, APR_WAIT);
}
+/*
+ * Helper: spawn memcachedmock with a specific reply string (and nconn=1),
+ * call apr_memcache_version(), return the status and (on success) the
+ * version string. Waits for the mock to finish before returning.
+ */
+static apr_status_t run_mock_version(abts_case *tc, const char *reply,
+ char **result_out)
+{
+ apr_procattr_t *procattr;
+ apr_proc_t proc;
+ apr_status_t rv;
+ apr_memcache_t *memcache;
+ apr_memcache_server_t *memserver;
+ apr_pool_t *mc_pool;
+ char *result = NULL;
+ const char *args[4];
+ int exitcode;
+ apr_exit_why_e why;
+
+ rv = apr_procattr_create(&procattr, p);
+ ABTS_ASSERT(tc, "Couldn't create procattr", rv == APR_SUCCESS);
+ rv = apr_procattr_io_set(procattr, APR_NO_PIPE, APR_NO_PIPE, APR_NO_PIPE);
+ ABTS_ASSERT(tc, "Couldn't set io in procattr", rv == APR_SUCCESS);
+ rv = apr_procattr_error_check_set(procattr, 1);
+ ABTS_ASSERT(tc, "Couldn't set error check in procattr", rv == APR_SUCCESS);
+ rv = apr_procattr_cmdtype_set(procattr, APR_PROGRAM_ENV);
+ ABTS_ASSERT(tc, "Couldn't set copy environment", rv == APR_SUCCESS);
+
+ /* argv: memcachedmock <reply> 1 (serve exactly one connection) */
+ args[0] = "memcachedmock" EXTENSION;
+ args[1] = reply;
+ args[2] = "1";
+ args[3] = NULL;
+ rv = apr_proc_create(&proc, TESTBINPATH "memcachedmock" EXTENSION,
+ args, NULL, procattr, p);
+ if (APR_SUCCESS != rv) {
+ return APR_ENOTIMPL; /* signal skip to caller */
+ }
+
+ apr_sleep(apr_time_from_sec(2));
+
+ /*
+ * Use a sub-pool for the memcache objects so the reslist (and its
+ * mc_conn_destruct callbacks, which write to the now-dead socket) is
+ * torn down before the caller restores the SIGPIPE handler.
+ */
+ rv = apr_pool_create(&mc_pool, p);
+ ABTS_ASSERT(tc, "pool create failed", rv == APR_SUCCESS);
+
+ rv = apr_memcache_create(mc_pool, 1, 0, &memcache);
+ ABTS_ASSERT(tc, "memcache create failed", rv == APR_SUCCESS);
+ rv = apr_memcache_server_create(mc_pool, MOCK_HOST, MOCK_PORT, 0, 1, 1,
+ apr_time_from_sec(60), &memserver);
+ ABTS_ASSERT(tc, "server create failed", rv == APR_SUCCESS);
+ rv = apr_memcache_add_server(memcache, memserver);
+ ABTS_ASSERT(tc, "server add failed", rv == APR_SUCCESS);
+
+ rv = apr_memcache_version(memserver, p, &result);
+
+ /*
+ * Destroy the sub-pool before waiting for the process: this tears down
+ * the reslist and its mc_conn_destruct callbacks while SIGPIPE is still
+ * ignored by the caller (test_version_responses).
+ */
+ apr_pool_destroy(mc_pool);
+
+ apr_proc_wait(&proc, &exitcode, &why, APR_WAIT);
+
+ if (result_out) {
+ *result_out = result;
+ }
+ return rv;
+}
+
+/*
+ * Test how apr_memcache_version() handles well-formed and malformed
+ * VERSION responses from the server.
+ */
+static void test_version_responses(abts_case *tc, void *data)
+{
+ apr_status_t rv;
+ char *result;
+#ifdef SIGPIPE
+ apr_sigfunc_t *old_action;
+
+ old_action = apr_signal(SIGPIPE, SIG_IGN);
+#endif
+
+ /* --- good response ------------------------------------------------ */
+ abts_log_message("version test: sending 'VERSION 1.5.22\\r\\n', expecting APR_SUCCESS");
+ rv = run_mock_version(tc, "VERSION 1.5.22\r\n", &result);
+ if (rv == APR_ENOTIMPL) {
+ ABTS_SKIP(tc, data, TESTBINPATH "memcachedmock" EXTENSION " could not be executed, skipped");
+ return;
+ }
+ abts_log_message("version test: rv=%d result='%s'", rv, result ? result : "(null)");
+ ABTS_ASSERT(tc, "good VERSION should succeed", rv == APR_SUCCESS);
+ ABTS_STR_EQUAL(tc, "1.5.22", result);
+
+ /* --- empty version string: "VERSION \r\n" ------------------------- */
+ abts_log_message("version test: sending 'VERSION \\r\\n' (empty version), expecting EGENERAL");
+ rv = run_mock_version(tc, "VERSION \r\n", &result);
+ abts_log_message("version test: rv=%d (expected non-zero)", rv);
+ ABTS_ASSERT(tc, "empty version string should fail", rv != APR_SUCCESS);
+
+ /* --- no space after VERSION: "VERSION\r\n" ------------------------ */
+ abts_log_message("version test: sending 'VERSION\\r\\n' (no space), expecting EGENERAL");
+ rv = run_mock_version(tc, "VERSION\r\n", &result);
+ abts_log_message("version test: rv=%d (expected non-zero)", rv);
+ ABTS_ASSERT(tc, "missing space should fail", rv != APR_SUCCESS);
+
+ /* --- completely wrong prefix -------------------------------------- */
+ abts_log_message("version test: sending 'ERROR\\r\\n' (wrong prefix), expecting EGENERAL");
+ rv = run_mock_version(tc, "ERROR\r\n", &result);
+ abts_log_message("version test: rv=%d (expected non-zero)", rv);
+ ABTS_ASSERT(tc, "wrong prefix should fail", rv != APR_SUCCESS);
+
+ /* --- single-character version: shortest valid response ------------ */
+ abts_log_message("version test: sending 'VERSION 1\\r\\n' (single char version), expecting APR_SUCCESS");
+ rv = run_mock_version(tc, "VERSION 1\r\n", &result);
+ abts_log_message("version test: rv=%d result='%s'", rv, result ? result : "(null)");
+ ABTS_ASSERT(tc, "single-char VERSION should succeed", rv == APR_SUCCESS);
+ ABTS_STR_EQUAL(tc, "1", result);
+
+#ifdef SIGPIPE
+ apr_signal(SIGPIPE, old_action);
+#endif
+}
+
abts_suite *testmemcache(abts_suite * suite)
{
suite = ADD_SUITE(suite);
@@ -757,6 +901,7 @@ abts_suite *testmemcache(abts_suite * su
abts_run_test(suite, test_memcache_addreplace, NULL);
abts_run_test(suite, test_memcache_incrdecr, NULL);
abts_run_test(suite, test_connection_validation, NULL);
+ abts_run_test(suite, test_version_responses, NULL);
return suite;
}
lmpx.com only provides a reader for public news (NNTP) servers. It is not
affiliated with the servers or forums shown here and is not responsible for
the content of articles, which is written by their respective authors.