fix #76: get returns NOTFOUND on TIMEOUT
authorMichael Wallner <mike@php.net>
Fri, 10 Sep 2021 10:19:15 +0000 (12:19 +0200)
committerMichael Wallner <mike@php.net>
Fri, 10 Sep 2021 10:19:15 +0000 (12:19 +0200)
src/libmemcached/fetch.cc
test/CMakeLists.txt
test/tests/memcached/regression/lp_001-540-680.cpp [new file with mode: 0644]
test/timeout.c [new file with mode: 0644]

index d7feaca7c5c7245eeb11a2a01a4200898157541f..fee6a10d219d12cd36ef0523311803cb1f93696c 100644 (file)
@@ -141,6 +141,7 @@ memcached_result_st *memcached_fetch_result(memcached_st *ptr, memcached_result_
   memcached_instance_st *server;
   memcached_return_t read_ret = MEMCACHED_SUCCESS;
   bool connection_failures = false;
+  bool timeouts = false;
   while ((server = memcached_io_get_readable_server(ptr, read_ret))) {
     char buffer[MEMCACHED_DEFAULT_COMMAND_SIZE];
     *error = memcached_response(server, buffer, sizeof(buffer), result);
@@ -150,6 +151,8 @@ memcached_result_st *memcached_fetch_result(memcached_st *ptr, memcached_result_
     } else if (*error == MEMCACHED_CONNECTION_FAILURE) {
       connection_failures = true;
       continue;
+    } else if (*error == MEMCACHED_TIMEOUT) {
+      timeouts = true;
     } else if (*error == MEMCACHED_SUCCESS) {
       result->count++;
       return result;
@@ -175,6 +178,8 @@ memcached_result_st *memcached_fetch_result(memcached_st *ptr, memcached_result_
         that.
         */
     *error = MEMCACHED_CONNECTION_FAILURE;
+  } else if (timeouts) {
+    *error = MEMCACHED_TIMEOUT;
   } else if (*error == MEMCACHED_SUCCESS) {
     *error = MEMCACHED_END;
   } else if (result->count == 0) {
index 6e64777a5a80083c29a720ed42e76b499ae20731..523db5be19219081582570fdddbbc472ce4913d0 100644 (file)
@@ -45,6 +45,8 @@ if(NOT MEMCACHED_BINARY)
     set(ENV{INVALID_CONFIGURATION} 1)
 endif()
 
+add_executable(timeout timeout.c)
+
 file(GLOB_RECURSE TESTING_SRC RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} *.cpp)
 set(TESTING_ROOT "${CMAKE_CURRENT_BINARY_DIR}")
 set(SOURCES_ROOT "${CMAKE_SOURCE_DIR}")
@@ -66,7 +68,7 @@ if(NOT (thread IN_LIST ENABLE_SANITIZERS))
     endif()
 endif()
 
-add_dependencies(runtests ${CLIENTS})
+add_dependencies(runtests ${CLIENTS} timeout)
 if(TARGET memaslap)
     configure_set(HAVE_MEMASLAP 1)
     add_dependencies(runtests memaslap)
diff --git a/test/tests/memcached/regression/lp_001-540-680.cpp b/test/tests/memcached/regression/lp_001-540-680.cpp
new file mode 100644 (file)
index 0000000..85cd70a
--- /dev/null
@@ -0,0 +1,20 @@
+#include "test/lib/common.hpp"
+#include "test/lib/random.hpp"
+#include "test/lib/Server.hpp"
+#include "test/lib/ReturnMatcher.hpp"
+
+TEST_CASE("memcached_regression_lp1540680") {
+  Server timeout{TESTING_ROOT "/timeout", {"-p", random_port_string}};
+  MemcachedPtr memc;
+  LoneReturnMatcher test{*memc};
+
+  REQUIRE(timeout.start());
+  this_thread::sleep_for(500ms);
+
+  REQUIRE_SUCCESS(memcached_server_add(*memc, "localhost", get<int>(timeout.getSocketOrPort())));
+
+  memcached_return_t rc;
+  Malloced val(memcached_get(*memc, S("not-found"), nullptr, nullptr, &rc));
+  REQUIRE_RC(MEMCACHED_TIMEOUT, rc);
+  REQUIRE_FALSE(*val);
+}
diff --git a/test/timeout.c b/test/timeout.c
new file mode 100644 (file)
index 0000000..e3c59a3
--- /dev/null
@@ -0,0 +1,27 @@
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <memory.h>
+#include <arpa/inet.h>
+#include <sys/socket.h>
+
+// $0 -u nobody -p <port>
+int main(int argc, char **argv) {
+  short port = argc == 5 ? atoi(argv[4]) : 11211;
+  struct sockaddr_in servaddr;
+  memset(&servaddr, 0, sizeof(struct sockaddr_in));
+
+  servaddr.sin_family = AF_INET;
+  servaddr.sin_addr.s_addr = htons(INADDR_ANY);
+  servaddr.sin_port = htons(port);
+
+  int listen_fd = socket(AF_INET, SOCK_STREAM, 0);
+  bind(listen_fd, (struct sockaddr *) &servaddr, sizeof(servaddr));
+  listen(listen_fd, 10);
+  printf("Listening (%d) on port %d\n", listen_fd, port);
+
+  int comm_fd = accept(listen_fd, NULL, NULL);
+  printf("Connection (%d) accepted, now do nothing...\n", comm_fd);
+
+  pause();
+}