[Top][All Lists]
[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, ©Buffer);
+ 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 */
+}
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- [GNUnet-SVN] r8332 - in libmicrohttpd/src: daemon include testcurl,
gnunet <=