gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[GNUnet-SVN] r8332 - in libmicrohttpd/src: daemon include testcurl


From: gnunet
Subject: [GNUnet-SVN] r8332 - in libmicrohttpd/src: daemon include testcurl
Date: Mon, 16 Mar 2009 16:33:31 -0600

Author: grothoff
Date: 2009-03-16 16:33:31 -0600 (Mon, 16 Mar 2009)
New Revision: 8332

Added:
   libmicrohttpd/src/testcurl/daemontest_iplimit.c
Modified:
   libmicrohttpd/src/daemon/daemon.c
   libmicrohttpd/src/daemon/internal.h
   libmicrohttpd/src/include/platform.h
   libmicrohttpd/src/testcurl/Makefile.am
Log:
second half of Richard Alimi's patches

Modified: libmicrohttpd/src/daemon/daemon.c
===================================================================
--- libmicrohttpd/src/daemon/daemon.c   2009-03-16 22:23:33 UTC (rev 8331)
+++ libmicrohttpd/src/daemon/daemon.c   2009-03-16 22:33:31 UTC (rev 8332)
@@ -64,6 +64,213 @@
 #endif
 #endif
 
+/**
+ * Maintain connection count for single address.
+ */
+struct MHD_IPCount
+{
+  int family;
+  union
+  {
+    struct in_addr ipv4;
+#if HAVE_IPV6
+    struct in6_addr ipv6;
+#endif
+  } addr;
+  unsigned int count;
+};
+
+/**
+ * Lock shared structure for IP connection counts
+ */
+static void
+MHD_ip_count_lock(struct MHD_Daemon *daemon)
+{
+  if (0 != pthread_mutex_lock(&daemon->per_ip_connection_mutex))
+    {
+#if HAVE_MESSAGES
+      MHD_DLOG (daemon, "Failed to acquire IP connection limit mutex\n");
+#endif
+      abort();
+    }
+}
+
+/**
+ * Unlock shared structure for IP connection counts
+ */
+static void
+MHD_ip_count_unlock(struct MHD_Daemon *daemon)
+{
+  if (0 != pthread_mutex_unlock(&daemon->per_ip_connection_mutex))
+    {
+#if HAVE_MESSAGES
+      MHD_DLOG (daemon, "Failed to release IP connection limit mutex\n");
+#endif
+      abort();
+    }
+}
+
+/**
+ * Tree comparison function for IP addresses (supplied to tsearch() family).
+ * We compare everything in the struct up through the beginning of the
+ * 'count' field.
+ */
+static int
+MHD_ip_addr_compare(const void *a1, const void *a2)
+{
+  return memcmp (a1, a2, offsetof(struct MHD_IPCount, count));
+}
+
+/**
+ * Parse address and initialize 'key' using the address. Returns MHD_YES
+ * on success and MHD_NO otherwise (e.g., invalid address type).
+ */
+static int
+MHD_ip_addr_to_key(struct sockaddr *addr, socklen_t addrlen,
+                   struct MHD_IPCount *key)
+{
+  memset(key, 0, sizeof(*key));
+
+  /* IPv4 addresses */
+  if (addrlen == sizeof(struct sockaddr_in))
+    {
+      const struct sockaddr_in *addr4 = (const struct sockaddr_in*)addr;
+      key->family = AF_INET;
+      memcpy (&key->addr.ipv4, &addr4->sin_addr, sizeof(addr4->sin_addr));
+      return MHD_YES;
+    }
+
+#if HAVE_IPV6
+  /* IPv6 addresses */
+  if (addrlen == sizeof (struct sockaddr_in6))
+    {
+      const struct sockaddr_in6 *addr6 = (const struct sockaddr_in6*)addr;
+      key->family = AF_INET6;
+      memcpy (&key->addr.ipv6, &addr6->sin6_addr, sizeof(addr6->sin6_addr));
+      return MHD_YES;
+    }
+#endif
+
+  /* Some other address */
+  return MHD_NO;
+}
+
+/**
+ * Check if IP address is over its limit.
+ *
+ * @return Return MHD_YES if IP below limit, MHD_NO if IP has surpassed limit.
+ *   Also returns MHD_NO if fails to allocate memory.
+ */
+static int
+MHD_ip_limit_add(struct MHD_Daemon *daemon,
+                 struct sockaddr *addr, socklen_t addrlen)
+{
+  struct MHD_IPCount *key;
+  void *node;
+  int result;
+
+  /* Ignore if no connection limit assigned */
+  if (daemon->per_ip_connection_limit == 0)
+    return MHD_YES;
+
+  key = (struct MHD_IPCount*) malloc (sizeof(*key));
+  if (!key)
+    return MHD_NO;
+
+  /* Initialize key */
+  if (MHD_NO == MHD_ip_addr_to_key (addr, addrlen, key))
+    {
+      /* Allow unhandled address types through */
+      free (key);
+      return MHD_YES;
+    }
+
+  MHD_ip_count_lock (daemon);
+
+  /* Search for the IP address */
+  node = tsearch (key, &daemon->per_ip_connection_count, MHD_ip_addr_compare);
+  if (!node)
+    {
+#if HAVE_MESSAGES
+      MHD_DLOG(daemon,
+               "Failed to add IP connection count node\n");
+#endif
+      MHD_ip_count_unlock (daemon);
+      return MHD_NO;
+    }
+  node = *(void**)node;
+
+  /* If we got an existing node back, free the one we created */
+  if (node != key)
+    free(key);
+
+  /* Test if there is room for another connection; if so,
+   * increment count */
+  result = (key->count < daemon->per_ip_connection_limit);
+  if (result == MHD_YES)
+    ++key->count;
+
+  MHD_ip_count_unlock (daemon);
+  return result;
+}
+
+/**
+ * Decrement connection count for IP address, removing from table
+ * count reaches 0
+ */
+static void
+MHD_ip_limit_del(struct MHD_Daemon *daemon,
+                 struct sockaddr *addr, socklen_t addrlen)
+{
+  struct MHD_IPCount search_key;
+  struct MHD_IPCount *found_key;
+  void *node;
+
+  /* Ignore if no connection limit assigned */
+  if (daemon->per_ip_connection_limit == 0)
+    return;
+
+  /* Initialize search key */
+  if (MHD_NO == MHD_ip_addr_to_key (addr, addrlen, &search_key))
+    return;
+
+  MHD_ip_count_lock (daemon);
+
+  /* Search for the IP address */
+  node = tfind (&search_key, &daemon->per_ip_connection_count, 
MHD_ip_addr_compare);
+
+  /* Something's wrong if we couldn't find an IP address
+   * that was previously added */
+  if (!node)
+    {
+#if HAVE_MESSAGES
+      MHD_DLOG (daemon,
+                "Failed to find previously-added IP address\n");
+#endif
+      abort();
+    }
+  found_key = (struct MHD_IPCount*)*(void**)node;
+
+  /* Validate existing count for IP address */
+  if (found_key->count == 0)
+    {
+#if HAVE_MESSAGES
+      MHD_DLOG (daemon,
+                "Previously-added IP address had 0 count\n");
+#endif
+      abort();
+    }
+
+  /* Remove the node entirely if count reduces to 0 */
+  if (--found_key->count == 0)
+    {
+      tdelete (found_key, &daemon->per_ip_connection_count, 
MHD_ip_addr_compare);
+      free (found_key);
+    }
+
+  MHD_ip_count_unlock (daemon);
+}
+
 #if HTTPS_SUPPORT
 pthread_mutex_t MHD_gnutls_init_mutex;
 
@@ -335,7 +542,6 @@
 #endif
   struct sockaddr *addr = (struct sockaddr *) &addrstorage;
   socklen_t addrlen;
-  unsigned int have;
   int s, res_thread_create;
 #if OSX
   static int on = 1;
@@ -368,46 +574,9 @@
   MHD_DLOG (daemon, "Accepted connection on socket %d\n", s);
 #endif
 #endif
-  have = 0;
-  if ((daemon->per_ip_connection_limit != 0) && (daemon->max_connections > 0))
+  if ((daemon->max_connections == 0)
+      || MHD_ip_limit_add (daemon, addr, addrlen) == MHD_NO))
     {
-      pos = daemon->connections;
-      while (pos != NULL)
-        {
-          if ((pos->addr != NULL) && (pos->addr_len == addrlen))
-            {
-              if (addrlen == sizeof (struct sockaddr_in))
-                {
-                  const struct sockaddr_in *a1 =
-                    (const struct sockaddr_in *) addr;
-                  const struct sockaddr_in *a2 =
-                    (const struct sockaddr_in *) pos->addr;
-                  if (0 == memcmp (&a1->sin_addr, &a2->sin_addr,
-                                   sizeof (struct in_addr)))
-                    have++;
-                }
-#if HAVE_INET6
-              if (addrlen == sizeof (struct sockaddr_in6))
-                {
-                  const struct sockaddr_in6 *a1 =
-                    (const struct sockaddr_in6 *) addr;
-                  const struct sockaddr_in6 *a2 =
-                    (const struct sockaddr_in6 *) pos->addr;
-                  if (0 == memcmp (&a1->sin6_addr, &a2->sin6_addr,
-                                   sizeof (struct in6_addr)))
-                    have++;
-                }
-#endif
-            }
-          pos = pos->next;
-        }
-    }
-
-  if ((daemon->max_connections == 0) || ((daemon->per_ip_connection_limit
-                                          != 0)
-                                         && (daemon->per_ip_connection_limit
-                                             <= have)))
-    {
       /* above connection limit - reject */
 #if HAVE_MESSAGES
       MHD_DLOG (daemon,
@@ -429,6 +598,7 @@
 #endif
       SHUTDOWN (s, SHUT_RDWR);
       CLOSE (s);
+      MHD_ip_limit_del (daemon, addr, addrlen);
       return MHD_YES;
     }
 #if OSX
@@ -446,6 +616,7 @@
 #endif
       SHUTDOWN (s, SHUT_RDWR);
       CLOSE (s);
+      MHD_ip_limit_del (daemon, addr, addrlen);
       return MHD_NO;
     }
   memset (connection, 0, sizeof (struct MHD_Connection));
@@ -458,6 +629,7 @@
 #endif
       SHUTDOWN (s, SHUT_RDWR);
       CLOSE (s);
+      MHD_ip_limit_del (daemon, addr, addrlen);
       free (connection);
       return MHD_NO;
     }
@@ -521,6 +693,7 @@
 #endif
           SHUTDOWN (s, SHUT_RDWR);
           CLOSE (s);
+          MHD_ip_limit_del (daemon, addr, addrlen);
           free (connection->addr);
           free (connection);
           return MHD_NO;
@@ -567,6 +740,7 @@
           if (pos->tls_session != NULL)
             MHD__gnutls_deinit (pos->tls_session);
 #endif
+          MHD_ip_limit_del (daemon, (struct sockaddr*)pos->addr, 
pos->addr_len);
           free (pos->addr);
           free (pos);
           daemon->max_connections++;
@@ -1020,6 +1194,17 @@
       return NULL;
     }
 
+  if (0 != pthread_mutex_init (&retVal->per_ip_connection_mutex, NULL))
+    {
+#if HAVE_MESSAGES
+      MHD_DLOG (retVal,
+               "MHD failed to initialize IP connection limit mutex\n");
+#endif
+      CLOSE (socket_fd);
+      free (retVal);
+      return NULL;
+    }
+
 #if HTTPS_SUPPORT
   /* initialize HTTPS daemon certificate aspects & send / recv functions */
   if ((0 != (options & MHD_USE_SSL)) && (0 != MHD_TLS_init (retVal)))
@@ -1028,6 +1213,7 @@
       MHD_DLOG (retVal, "Failed to initialize TLS support\n");
 #endif
       CLOSE (socket_fd);
+      pthread_mutex_destroy (&retVal->per_ip_connection_mutex);
       free (retVal);
       return NULL;
     }
@@ -1041,6 +1227,7 @@
       MHD_DLOG (retVal,
                 "Failed to create listen thread: %s\n", STRERROR (errno));
 #endif
+      pthread_mutex_destroy (&retVal->per_ip_connection_mutex);
       free (retVal);
       CLOSE (socket_fd);
       return NULL;
@@ -1111,6 +1298,7 @@
       pthread_mutex_unlock (&MHD_gnutls_init_mutex);
     }
 #endif
+  pthread_mutex_destroy (&daemon->per_ip_connection_mutex);
   free (daemon);
 }
 

Modified: libmicrohttpd/src/daemon/internal.h
===================================================================
--- libmicrohttpd/src/daemon/internal.h 2009-03-16 22:23:33 UTC (rev 8331)
+++ libmicrohttpd/src/daemon/internal.h 2009-03-16 22:33:31 UTC (rev 8332)
@@ -719,6 +719,16 @@
   unsigned int per_ip_connection_limit;
 
   /**
+   * Table storing number of connections per IP
+   */
+  void *per_ip_connection_count;
+
+  /**
+   * Mutex for per-IP connection counts
+   */
+  pthread_mutex_t per_ip_connection_mutex;
+
+  /**
    * Daemon's options.
    */
   enum MHD_OPTION options;

Modified: libmicrohttpd/src/include/platform.h
===================================================================
--- libmicrohttpd/src/include/platform.h        2009-03-16 22:23:33 UTC (rev 
8331)
+++ libmicrohttpd/src/include/platform.h        2009-03-16 22:33:31 UTC (rev 
8332)
@@ -53,6 +53,8 @@
 #include <errno.h>
 #include <fcntl.h>
 #include <signal.h>
+#include <search.h>
+#include <stddef.h>
 #undef HAVE_CONFIG_H
 #include <pthread.h>
 #define HAVE_CONFIG_H 1

Modified: libmicrohttpd/src/testcurl/Makefile.am
===================================================================
--- libmicrohttpd/src/testcurl/Makefile.am      2009-03-16 22:23:33 UTC (rev 
8331)
+++ libmicrohttpd/src/testcurl/Makefile.am      2009-03-16 22:33:31 UTC (rev 
8332)
@@ -35,7 +35,8 @@
   daemontest_long_header \
   daemontest_long_header11 \
   daemontest_get_chunked \
-  daemontest_put_chunked
+  daemontest_put_chunked \
+  daemontest_iplimit11
 noinst_PROGRAMS = \
   daemon_options_test
 
@@ -165,3 +166,9 @@
   $(top_builddir)/src/daemon/libmicrohttpd.la \
   @LIBCURL@ 
 
+daemontest_iplimit11_SOURCES = \
+  daemontest_iplimit.c
+daemontest_iplimit11_LDADD = \
+  $(top_builddir)/src/daemon/libmicrohttpd.la \
+  @LIBCURL@ 
+

Added: libmicrohttpd/src/testcurl/daemontest_iplimit.c
===================================================================
--- libmicrohttpd/src/testcurl/daemontest_iplimit.c                             
(rev 0)
+++ libmicrohttpd/src/testcurl/daemontest_iplimit.c     2009-03-16 22:33:31 UTC 
(rev 8332)
@@ -0,0 +1,202 @@
+/*
+     This file is part of libmicrohttpd
+     (C) 2007 Christian Grothoff
+
+     libmicrohttpd is free software; you can redistribute it and/or modify
+     it under the terms of the GNU General Public License as published
+     by the Free Software Foundation; either version 2, or (at your
+     option) any later version.
+
+     libmicrohttpd is distributed in the hope that it will be useful, but
+     WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     General Public License for more details.
+
+     You should have received a copy of the GNU General Public License
+     along with libmicrohttpd; see the file COPYING.  If not, write to the
+     Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+     Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * @file daemontest_get.c
+ * @brief  Testcase for libmicrohttpd GET operations
+ *         TODO: test parsing of query
+ * @author Christian Grothoff
+ */
+
+#include "MHD_config.h"
+#include "platform.h"
+#include <curl/curl.h>
+#include <microhttpd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#ifndef WINDOWS
+#include <unistd.h>
+#endif
+
+static int oneone;
+
+struct CBC
+{
+  char *buf;
+  size_t pos;
+  size_t size;
+};
+
+static size_t
+copyBuffer (void *ptr, size_t size, size_t nmemb, void *ctx)
+{
+  struct CBC *cbc = ctx;
+
+  if (cbc->pos + size * nmemb > cbc->size)
+    return 0;                   /* overflow */
+  memcpy (&cbc->buf[cbc->pos], ptr, size * nmemb);
+  cbc->pos += size * nmemb;
+  return size * nmemb;
+}
+
+static int
+ahc_echo (void *cls,
+          struct MHD_Connection *connection,
+          const char *url,
+          const char *method,
+          const char *version,
+          const char *upload_data, size_t *upload_data_size,
+          void **unused)
+{
+  static int ptr;
+  const char *me = cls;
+  struct MHD_Response *response;
+  int ret;
+
+  if (0 != strcmp (me, method))
+    return MHD_NO;              /* unexpected method */
+  if (&ptr != *unused)
+    {
+      *unused = &ptr;
+      return MHD_YES;
+    }
+  *unused = NULL;
+  response = MHD_create_response_from_data (strlen (url),
+                                            (void *) url, MHD_NO, MHD_YES);
+  ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
+  MHD_destroy_response (response);
+  if (ret == MHD_NO)
+    abort ();
+  return ret;
+}
+
+static int
+testMultithreadedPoolGet ()
+{
+  struct MHD_Daemon *d;
+  char buf[2048];
+  int k;
+
+  /* Test only valid for HTTP/1.1 (uses persistent connections) */
+  if (!oneone)
+    return 0;
+
+  d = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_DEBUG,
+                        1081, NULL, NULL, &ahc_echo, "GET",
+                        MHD_OPTION_PER_IP_CONNECTION_LIMIT, 2,
+                        MHD_OPTION_END);
+  if (d == NULL)
+    return 16;
+
+  for (k = 0; k < 3; ++k)
+    {
+      struct CBC cbc[3];
+      CURL *cenv[3];
+      int i;
+
+      for (i = 0; i < 3; ++i)
+        {
+          CURL *c;
+          CURLcode errornum;
+ 
+          cenv[i] = c = curl_easy_init ();
+          cbc[i].buf = buf;
+          cbc[i].size = 2048;
+          cbc[i].pos = 0;
+
+          curl_easy_setopt (c, CURLOPT_URL, 
"http://localhost:1081/hello_world";);
+          curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
+          curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc[i]);
+          curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
+          curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
+          curl_easy_setopt (c, CURLOPT_FORBID_REUSE, 0L);
+          curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
+          curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 15L);
+          // NOTE: use of CONNECTTIMEOUT without also
+          //   setting NOSIGNAL results in really weird
+          //   crashes on my system!
+          curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
+
+          errornum = curl_easy_perform (c);
+          if (CURLE_OK != errornum && i < 2
+              || CURLE_OK == errornum && i == 2)
+            {
+              int j;
+
+              /* First 2 should succeed */
+              if (i < 2)
+                fprintf (stderr,
+                         "curl_easy_perform failed: `%s'\n",
+                         curl_easy_strerror (errornum));
+
+              /* Last request should have failed */
+              else
+                fprintf (stderr,
+                         "No error on IP address over limit\n");
+
+              for (j = 0; j < i; ++j)
+                curl_easy_cleanup (cenv[j]);
+              MHD_stop_daemon (d);
+              return 32;
+            }
+        }
+
+      /* Cleanup the environments */
+      for (i = 0; i < 3; ++i)
+        curl_easy_cleanup (cenv[i]);
+
+      sleep(2);
+
+      for (i = 0; i < 2; ++i)
+        {
+          if (cbc[i].pos != strlen ("/hello_world"))
+            {
+              MHD_stop_daemon (d);
+              return 64;
+            }
+          if (0 != strncmp ("/hello_world", cbc[i].buf, strlen 
("/hello_world")))
+            {
+              MHD_stop_daemon (d);
+              return 128;
+            }
+        }
+
+
+    }
+  MHD_stop_daemon (d);
+  return 0;
+}
+
+int
+main (int argc, char *const *argv)
+{
+  unsigned int errorCount = 0;
+
+  oneone = NULL != strstr (argv[0], "11");
+  if (0 != curl_global_init (CURL_GLOBAL_WIN32))
+    return 2;
+  errorCount += testMultithreadedPoolGet ();
+  if (errorCount != 0)
+    fprintf (stderr, "Error (code: %u)\n", errorCount);
+  curl_global_cleanup ();
+  return errorCount != 0;       /* 0 == pass */
+}





reply via email to

[Prev in Thread] Current Thread [Next in Thread]