mirror of
https://git.gnunet.org/libmicrohttpd.git
synced 2026-09-25 04:09:31 +03:00
added :
* configure.ac support for --with-gnutls=PFX * TLS connection handler in daemon.c * simple TLS echo client server example
This commit is contained in:
@@ -141,6 +141,37 @@ AC_CHECK_PROG([HAVE_SOCAT],[socat], 1, 0)
|
||||
AM_CONDITIONAL(HAVE_ZZUF, test 0 != $HAVE_ZZUF)
|
||||
AM_CONDITIONAL(HAVE_SOCAT, test 0 != $HAVE_SOCAT)
|
||||
|
||||
# GNUtls linkage
|
||||
AC_ARG_WITH(gnutls,
|
||||
[ --with-gnutls=PFX Base of GNU TLS installation],
|
||||
[AC_MSG_RESULT("$with_gnutls")
|
||||
case $with_gnutls in
|
||||
no)
|
||||
;;
|
||||
yes)
|
||||
AC_CHECK_HEADERS(gnutls.h,gnutls=true)
|
||||
;;
|
||||
*)
|
||||
LDFLAGS="-L$with_gnutls/lib $LDFLAGS"
|
||||
CPPFLAGS="-I$with_gnutls/include $CPPFLAGS"
|
||||
AC_CHECK_HEADERS(gnutls/gnutls.h,
|
||||
# check for 'gnutls_global_init' in gnutls.so
|
||||
AC_CHECK_LIB(gnutls,gnutls_global_init,
|
||||
GNUTLS_LIB_PATH="$with_gnutls/lib"
|
||||
GNUTLS_LDFLAGS="-L$with_gnutls/lib"
|
||||
GNUTLS_CPPFLAGS="-I$with_gnutls/include"
|
||||
gnutls=true))
|
||||
LDFLAGS=$SAVE_LDFLAGS
|
||||
CPPFLAGS=$SAVE_CPPFLAGS
|
||||
;;
|
||||
esac
|
||||
],
|
||||
[AC_MSG_RESULT([--with-gnutls not specified])])
|
||||
|
||||
AC_SUBST(GNUTLS_LIB_PATH)
|
||||
AC_SUBST(GNUTLS_LDFLAGS)
|
||||
AC_SUBST(GNUTLS_CPPFLAGS)
|
||||
|
||||
AC_SUBST(CPPFLAGS)
|
||||
AC_SUBST(LIBS)
|
||||
AC_SUBST(LDFLAGS)
|
||||
|
||||
+606
-576
@@ -1,22 +1,22 @@
|
||||
/*
|
||||
This file is part of libmicrohttpd
|
||||
(C) 2007 Daniel Pittman and Christian Grothoff
|
||||
This file is part of libmicrohttpd
|
||||
(C) 2007 Daniel Pittman and Christian Grothoff
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library 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
|
||||
Lesser General Public License for more details.
|
||||
This library 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
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file daemon.c
|
||||
@@ -52,7 +52,6 @@
|
||||
*/
|
||||
#define DEBUG_CONNECT MHD_NO
|
||||
|
||||
|
||||
/**
|
||||
* Obtain the select sets for this daemon.
|
||||
*
|
||||
@@ -60,307 +59,331 @@
|
||||
* daemon was not started with the right
|
||||
* options for this call.
|
||||
*/
|
||||
int
|
||||
MHD_get_fdset (struct MHD_Daemon *daemon,
|
||||
fd_set * read_fd_set,
|
||||
fd_set * write_fd_set, fd_set * except_fd_set, int *max_fd)
|
||||
{
|
||||
struct MHD_Connection *pos;
|
||||
int fd;
|
||||
int MHD_get_fdset(struct MHD_Daemon *daemon,
|
||||
fd_set * read_fd_set,
|
||||
fd_set * write_fd_set,
|
||||
fd_set * except_fd_set,
|
||||
int *max_fd)
|
||||
{
|
||||
struct MHD_Connection *con_itr;
|
||||
int fd;
|
||||
|
||||
if ((daemon == NULL) ||
|
||||
(read_fd_set == NULL) ||
|
||||
(write_fd_set == NULL) ||
|
||||
(except_fd_set == NULL) ||
|
||||
(max_fd == NULL) ||
|
||||
(-1 == (fd = daemon->socket_fd)) ||
|
||||
(daemon->shutdown == MHD_YES) ||
|
||||
((daemon->options & MHD_USE_THREAD_PER_CONNECTION) != 0))
|
||||
return MHD_NO;
|
||||
FD_SET (fd, read_fd_set);
|
||||
if ((*max_fd) < fd)
|
||||
*max_fd = fd;
|
||||
pos = daemon->connections;
|
||||
while (pos != NULL)
|
||||
{
|
||||
if (MHD_YES != MHD_connection_get_fdset (pos,
|
||||
read_fd_set,
|
||||
write_fd_set,
|
||||
except_fd_set, max_fd))
|
||||
return MHD_NO;
|
||||
pos = pos->next;
|
||||
}
|
||||
if ((daemon == NULL) || (read_fd_set == NULL) || (write_fd_set == NULL)
|
||||
|| (except_fd_set == NULL) || (max_fd == NULL) || (-1 == (fd = daemon->
|
||||
socket_fd)) || (daemon->shutdown == MHD_YES) || ((daemon->options
|
||||
& MHD_USE_THREAD_PER_CONNECTION) != 0))
|
||||
return MHD_NO;
|
||||
|
||||
__FD_SET (fd, read_fd_set);
|
||||
|
||||
/* update max file descriptor */
|
||||
if ((*max_fd) < fd)
|
||||
*max_fd = fd;
|
||||
|
||||
con_itr = daemon->connections;
|
||||
while (con_itr != NULL)
|
||||
{
|
||||
if (MHD_YES != MHD_connection_get_fdset (con_itr,
|
||||
read_fd_set,
|
||||
write_fd_set,
|
||||
except_fd_set, max_fd))
|
||||
return MHD_NO;
|
||||
con_itr = con_itr->next;
|
||||
}
|
||||
#if DEBUG_CONNECT
|
||||
MHD_DLOG (daemon, "Maximum socket in select set: %d\n", *max_fd);
|
||||
MHD_DLOG (daemon, "Maximum socket in select set: %d\n", *max_fd);
|
||||
#endif
|
||||
return MHD_YES;
|
||||
}
|
||||
return MHD_YES;
|
||||
}
|
||||
|
||||
/**
|
||||
* Main function of the thread that handles an individual
|
||||
* connection.
|
||||
*/
|
||||
static void *
|
||||
MHD_handle_connection (void *data)
|
||||
{
|
||||
struct MHD_Connection *con = data;
|
||||
int num_ready;
|
||||
fd_set rs;
|
||||
fd_set ws;
|
||||
fd_set es;
|
||||
int max;
|
||||
struct timeval tv;
|
||||
unsigned int timeout;
|
||||
unsigned int now;
|
||||
static void * MHD_handle_connection(void *data)
|
||||
{
|
||||
struct MHD_Connection *con = data;
|
||||
int num_ready;
|
||||
fd_set rs;
|
||||
fd_set ws;
|
||||
fd_set es;
|
||||
int max;
|
||||
struct timeval tv;
|
||||
unsigned int timeout;
|
||||
unsigned int now;
|
||||
|
||||
if (con == NULL)
|
||||
abort ();
|
||||
timeout = con->daemon->connection_timeout;
|
||||
while ((!con->daemon->shutdown) && (con->socket_fd != -1))
|
||||
{
|
||||
FD_ZERO (&rs);
|
||||
FD_ZERO (&ws);
|
||||
FD_ZERO (&es);
|
||||
max = 0;
|
||||
MHD_connection_get_fdset (con, &rs, &ws, &es, &max);
|
||||
now = time (NULL);
|
||||
tv.tv_usec = 0;
|
||||
if (timeout > (now - con->last_activity))
|
||||
tv.tv_sec = timeout - (now - con->last_activity);
|
||||
else
|
||||
tv.tv_sec = 0;
|
||||
num_ready = SELECT (max + 1,
|
||||
&rs, &ws, &es, (timeout != 0) ? &tv : NULL);
|
||||
if (num_ready < 0)
|
||||
{
|
||||
if (errno == EINTR)
|
||||
continue;
|
||||
if (con == NULL)
|
||||
abort();
|
||||
timeout = con->daemon->connection_timeout;
|
||||
while ((!con->daemon->shutdown) && (con->socket_fd != -1))
|
||||
{
|
||||
FD_ZERO (&rs);
|
||||
FD_ZERO (&ws);
|
||||
FD_ZERO (&es);
|
||||
max = 0;
|
||||
MHD_connection_get_fdset(con, &rs, &ws, &es, &max);
|
||||
now = time(NULL);
|
||||
tv.tv_usec = 0;
|
||||
if (timeout > (now - con->last_activity))
|
||||
tv.tv_sec = timeout - (now - con->last_activity);
|
||||
else
|
||||
tv.tv_sec = 0;
|
||||
num_ready = SELECT (max + 1,
|
||||
&rs, &ws, &es, (timeout != 0) ? &tv : NULL);
|
||||
if (num_ready < 0)
|
||||
{
|
||||
if (errno == EINTR)
|
||||
continue;
|
||||
#if HAVE_MESSAGES
|
||||
MHD_DLOG (con->daemon, "Error during select (%d): `%s'\n",
|
||||
max, STRERROR (errno));
|
||||
MHD_DLOG(con->daemon, "Error during select (%d): `%s'\n", max,
|
||||
STRERROR (errno));
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
if (FD_ISSET (con->socket_fd, &rs))
|
||||
MHD_connection_handle_read (con);
|
||||
if ((con->socket_fd != -1) && (FD_ISSET (con->socket_fd, &ws)))
|
||||
MHD_connection_handle_write (con);
|
||||
if (con->socket_fd != -1)
|
||||
MHD_connection_handle_idle (con);
|
||||
}
|
||||
if (con->socket_fd != -1)
|
||||
{
|
||||
break;
|
||||
}
|
||||
if (FD_ISSET (con->socket_fd, &rs))
|
||||
MHD_connection_handle_read(con);
|
||||
if ((con->socket_fd != -1) && (FD_ISSET (con->socket_fd, &ws)))
|
||||
MHD_connection_handle_write(con);
|
||||
if (con->socket_fd != -1)
|
||||
MHD_connection_handle_idle(con);
|
||||
}
|
||||
if (con->socket_fd != -1)
|
||||
{
|
||||
#if DEBUG_CLOSE
|
||||
#if HAVE_MESSAGES
|
||||
MHD_DLOG (con->daemon,
|
||||
"Processing thread terminating, closing connection\n");
|
||||
MHD_DLOG (con->daemon,
|
||||
"Processing thread terminating, closing connection\n");
|
||||
#endif
|
||||
#endif
|
||||
SHUTDOWN (con->socket_fd, SHUT_RDWR);
|
||||
CLOSE (con->socket_fd);
|
||||
con->socket_fd = -1;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
SHUTDOWN (con->socket_fd, SHUT_RDWR);
|
||||
CLOSE (con->socket_fd);
|
||||
con->socket_fd = -1;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle an individual TLS connection.
|
||||
*/
|
||||
static void * MHDS_handle_connection(void *data)
|
||||
{
|
||||
struct MHD_Connection *con = data;
|
||||
|
||||
if (con == NULL)
|
||||
abort();
|
||||
|
||||
/* forward call to handler */
|
||||
con->daemon->default_handler(NULL, con, NULL, NULL, NULL, NULL, NULL,
|
||||
NULL);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Accept an incoming connection and create the MHD_Connection object for
|
||||
* it. This function also enforces policy by way of checking with the
|
||||
* accept policy callback.
|
||||
*/
|
||||
static int
|
||||
MHD_accept_connection (struct MHD_Daemon *daemon)
|
||||
{
|
||||
struct MHD_Connection *pos;
|
||||
struct MHD_Connection *connection;
|
||||
struct sockaddr_in6 addr6;
|
||||
struct sockaddr *addr = (struct sockaddr *) &addr6;
|
||||
socklen_t addrlen;
|
||||
unsigned int have;
|
||||
int s;
|
||||
static int MHD_accept_connection(struct MHD_Daemon *daemon)
|
||||
{
|
||||
struct MHD_Connection *pos;
|
||||
struct MHD_Connection *connection;
|
||||
struct sockaddr_in6 addr6;
|
||||
struct sockaddr *addr = (struct sockaddr *) &addr6;
|
||||
socklen_t addrlen;
|
||||
unsigned int have;
|
||||
int s, res_thread_create;
|
||||
#if OSX
|
||||
static int on = 1;
|
||||
static int on = 1;
|
||||
#endif
|
||||
|
||||
if (sizeof(struct sockaddr) > sizeof(struct sockaddr_in6))
|
||||
abort(); /* fatal, serious error */
|
||||
addrlen = sizeof(struct sockaddr_in6);
|
||||
memset(addr, 0, sizeof(struct sockaddr_in6));
|
||||
|
||||
if (sizeof (struct sockaddr) > sizeof (struct sockaddr_in6))
|
||||
abort (); /* fatal, serious error */
|
||||
addrlen = sizeof (struct sockaddr_in6);
|
||||
memset (addr, 0, sizeof (struct sockaddr_in6));
|
||||
s = ACCEPT (daemon->socket_fd, addr, &addrlen);
|
||||
if ((s < 0) || (addrlen <= 0))
|
||||
{
|
||||
s = ACCEPT (daemon->socket_fd, addr, &addrlen);
|
||||
|
||||
if ((s < 0) || (addrlen <= 0))
|
||||
{
|
||||
#if HAVE_MESSAGES
|
||||
MHD_DLOG (daemon, "Error accepting connection: %s\n", STRERROR (errno));
|
||||
MHD_DLOG(daemon, "Error accepting connection: %s\n", STRERROR (errno));
|
||||
#endif
|
||||
if (s != -1)
|
||||
{
|
||||
SHUTDOWN (s, SHUT_RDWR);
|
||||
CLOSE (s); /* just in case */
|
||||
}
|
||||
return MHD_NO;
|
||||
}
|
||||
if (s != -1)
|
||||
{
|
||||
SHUTDOWN (s, SHUT_RDWR);
|
||||
CLOSE (s);
|
||||
/* just in case */
|
||||
}
|
||||
return MHD_NO;
|
||||
}
|
||||
#if DEBUG_CONNECT
|
||||
MHD_DLOG (daemon, "Accepted connection on socket %d\n", s);
|
||||
MHD_DLOG (daemon, "Accepted connection on socket %d\n", s);
|
||||
#endif
|
||||
have = 0;
|
||||
if ((daemon->per_ip_connection_limit != 0) && (daemon->max_connections > 0))
|
||||
{
|
||||
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 (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++;
|
||||
}
|
||||
}
|
||||
pos = pos->next;
|
||||
}
|
||||
}
|
||||
have = 0;
|
||||
if ((daemon->per_ip_connection_limit != 0) && (daemon->max_connections > 0))
|
||||
{
|
||||
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 (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++;
|
||||
}
|
||||
}
|
||||
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 ((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,
|
||||
"Server reached connection limit (closing inbound connection)\n");
|
||||
MHD_DLOG(daemon,
|
||||
"Server reached connection limit (closing inbound connection)\n");
|
||||
#endif
|
||||
SHUTDOWN (s, SHUT_RDWR);
|
||||
CLOSE (s);
|
||||
return MHD_NO;
|
||||
}
|
||||
if ((daemon->apc != NULL) &&
|
||||
(MHD_NO == daemon->apc (daemon->apc_cls, addr, addrlen)))
|
||||
{
|
||||
SHUTDOWN (s, SHUT_RDWR);
|
||||
CLOSE (s);
|
||||
return MHD_NO;
|
||||
}
|
||||
if ((daemon->apc != NULL) && (MHD_NO == daemon->apc (daemon->apc_cls, addr, addrlen)))
|
||||
{
|
||||
#if DEBUG_CLOSE
|
||||
#if HAVE_MESSAGES
|
||||
MHD_DLOG (daemon, "Connection rejected, closing connection\n");
|
||||
MHD_DLOG (daemon, "Connection rejected, closing connection\n");
|
||||
#endif
|
||||
#endif
|
||||
SHUTDOWN (s, SHUT_RDWR);
|
||||
CLOSE (s);
|
||||
return MHD_YES;
|
||||
}
|
||||
SHUTDOWN (s, SHUT_RDWR);
|
||||
CLOSE (s);
|
||||
return MHD_YES;
|
||||
}
|
||||
#if OSX
|
||||
#ifdef SOL_SOCKET
|
||||
#ifdef SO_NOSIGPIPE
|
||||
setsockopt (s, SOL_SOCKET, SO_NOSIGPIPE, &on, sizeof (on));
|
||||
setsockopt (s, SOL_SOCKET, SO_NOSIGPIPE, &on, sizeof (on));
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
connection = malloc (sizeof (struct MHD_Connection));
|
||||
if (connection == NULL)
|
||||
{
|
||||
connection = malloc(sizeof(struct MHD_Connection));
|
||||
if (connection == NULL)
|
||||
{
|
||||
#if HAVE_MESSAGES
|
||||
MHD_DLOG (daemon, "Error allocating memory: %s\n", STRERROR (errno));
|
||||
MHD_DLOG(daemon, "Error allocating memory: %s\n", STRERROR (errno));
|
||||
#endif
|
||||
SHUTDOWN (s, SHUT_RDWR);
|
||||
CLOSE (s);
|
||||
return MHD_NO;
|
||||
}
|
||||
memset (connection, 0, sizeof (struct MHD_Connection));
|
||||
connection->pool = NULL;
|
||||
connection->addr = malloc (addrlen);
|
||||
if (connection->addr == NULL)
|
||||
{
|
||||
SHUTDOWN (s, SHUT_RDWR);
|
||||
CLOSE (s);
|
||||
return MHD_NO;
|
||||
}
|
||||
memset(connection, 0, sizeof(struct MHD_Connection));
|
||||
connection->pool = NULL;
|
||||
connection->addr = malloc(addrlen);
|
||||
if (connection->addr == NULL)
|
||||
{
|
||||
#if HAVE_MESSAGES
|
||||
MHD_DLOG (daemon, "Error allocating memory: %s\n", STRERROR (errno));
|
||||
MHD_DLOG(daemon, "Error allocating memory: %s\n", STRERROR (errno));
|
||||
#endif
|
||||
SHUTDOWN (s, SHUT_RDWR);
|
||||
CLOSE (s);
|
||||
free (connection);
|
||||
return MHD_NO;
|
||||
}
|
||||
memcpy (connection->addr, addr, addrlen);
|
||||
connection->addr_len = addrlen;
|
||||
connection->socket_fd = s;
|
||||
connection->daemon = daemon;
|
||||
if ((0 != (daemon->options & MHD_USE_THREAD_PER_CONNECTION)) &&
|
||||
(0 != pthread_create (&connection->pid,
|
||||
NULL, &MHD_handle_connection, connection)))
|
||||
{
|
||||
#if HAVE_MESSAGES
|
||||
MHD_DLOG (daemon, "Failed to create a thread: %s\n", STRERROR (errno));
|
||||
#endif
|
||||
SHUTDOWN (s, SHUT_RDWR);
|
||||
CLOSE (s);
|
||||
free (connection->addr);
|
||||
free (connection);
|
||||
return MHD_NO;
|
||||
}
|
||||
connection->last_activity = time (NULL);
|
||||
connection->next = daemon->connections;
|
||||
daemon->connections = connection;
|
||||
daemon->max_connections--;
|
||||
return MHD_YES;
|
||||
}
|
||||
SHUTDOWN (s, SHUT_RDWR);
|
||||
CLOSE (s);
|
||||
free(connection);
|
||||
return MHD_NO;
|
||||
}
|
||||
memcpy(connection->addr, addr, addrlen);
|
||||
connection->addr_len = addrlen;
|
||||
connection->socket_fd = s;
|
||||
connection->daemon = daemon;
|
||||
|
||||
/* attempt to create handler thread */
|
||||
if (0 != (daemon->options & MHD_USE_THREAD_PER_CONNECTION))
|
||||
{
|
||||
if (daemon->options & MHD_USE_SSL)
|
||||
res_thread_create= pthread_create(&connection->pid, NULL,
|
||||
&MHDS_handle_connection, connection);
|
||||
else
|
||||
{
|
||||
res_thread_create= pthread_create(&connection->pid, NULL,
|
||||
&MHD_handle_connection, connection);
|
||||
}
|
||||
if (res_thread_create != 0)
|
||||
{
|
||||
#if HAVE_MESSAGES
|
||||
MHD_DLOG(daemon, "Failed to create a thread: %s\n", STRERROR (errno));
|
||||
#endif
|
||||
SHUTDOWN (s, SHUT_RDWR);
|
||||
CLOSE (s);
|
||||
free(connection->addr);
|
||||
free(connection);
|
||||
return MHD_NO;
|
||||
}
|
||||
}
|
||||
|
||||
connection->last_activity = time(NULL);
|
||||
connection->next = daemon->connections;
|
||||
daemon->connections = connection;
|
||||
daemon->max_connections--;
|
||||
return MHD_YES;
|
||||
}
|
||||
|
||||
/**
|
||||
* Free resources associated with all closed connections.
|
||||
* (destroy responses, free buffers, etc.). A connection
|
||||
* is known to be closed if the socket_fd is -1.
|
||||
*/
|
||||
static void
|
||||
MHD_cleanup_connections (struct MHD_Daemon *daemon)
|
||||
{
|
||||
struct MHD_Connection *pos;
|
||||
struct MHD_Connection *prev;
|
||||
void *unused;
|
||||
static void MHD_cleanup_connections(struct MHD_Daemon *daemon)
|
||||
{
|
||||
struct MHD_Connection *pos;
|
||||
struct MHD_Connection *prev;
|
||||
void *unused;
|
||||
|
||||
pos = daemon->connections;
|
||||
prev = NULL;
|
||||
while (pos != NULL)
|
||||
{
|
||||
if (pos->socket_fd == -1)
|
||||
{
|
||||
if (prev == NULL)
|
||||
daemon->connections = pos->next;
|
||||
else
|
||||
prev->next = pos->next;
|
||||
if (0 != (daemon->options & MHD_USE_THREAD_PER_CONNECTION))
|
||||
{
|
||||
pthread_kill (pos->pid, SIGALRM);
|
||||
pthread_join (pos->pid, &unused);
|
||||
}
|
||||
MHD_destroy_response (pos->response);
|
||||
MHD_pool_destroy (pos->pool);
|
||||
free (pos->addr);
|
||||
free (pos);
|
||||
daemon->max_connections++;
|
||||
if (prev == NULL)
|
||||
pos = daemon->connections;
|
||||
else
|
||||
pos = prev->next;
|
||||
continue;
|
||||
}
|
||||
prev = pos;
|
||||
pos = pos->next;
|
||||
}
|
||||
}
|
||||
pos = daemon->connections;
|
||||
prev = NULL;
|
||||
while (pos != NULL)
|
||||
{
|
||||
if (pos->socket_fd == -1)
|
||||
{
|
||||
if (prev == NULL)
|
||||
daemon->connections = pos->next;
|
||||
else
|
||||
prev->next = pos->next;
|
||||
if (0 != (daemon->options & MHD_USE_THREAD_PER_CONNECTION))
|
||||
{
|
||||
pthread_kill(pos->pid, SIGALRM);
|
||||
pthread_join(pos->pid, &unused);
|
||||
}
|
||||
MHD_destroy_response(pos->response);
|
||||
MHD_pool_destroy(pos->pool);
|
||||
free(pos->addr);
|
||||
free(pos);
|
||||
daemon->max_connections++;
|
||||
if (prev == NULL)
|
||||
pos = daemon->connections;
|
||||
else
|
||||
pos = prev->next;
|
||||
continue;
|
||||
}
|
||||
prev = pos;
|
||||
pos = pos->next;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain timeout value for select for this daemon
|
||||
@@ -373,35 +396,34 @@ MHD_cleanup_connections (struct MHD_Daemon *daemon)
|
||||
* not used (or no connections exist that would
|
||||
* necessiate the use of a timeout right now).
|
||||
*/
|
||||
int
|
||||
MHD_get_timeout (struct MHD_Daemon *daemon, unsigned long long *timeout)
|
||||
{
|
||||
time_t earliest_deadline;
|
||||
time_t now;
|
||||
struct MHD_Connection *pos;
|
||||
unsigned int dto;
|
||||
int MHD_get_timeout(struct MHD_Daemon *daemon, unsigned long long *timeout)
|
||||
{
|
||||
time_t earliest_deadline;
|
||||
time_t now;
|
||||
struct MHD_Connection *pos;
|
||||
unsigned int dto;
|
||||
|
||||
dto = daemon->connection_timeout;
|
||||
if (0 == dto)
|
||||
return MHD_NO;
|
||||
pos = daemon->connections;
|
||||
if (pos == NULL)
|
||||
return MHD_NO; /* no connections */
|
||||
now = time (NULL);
|
||||
/* start with conservative estimate */
|
||||
earliest_deadline = now + dto;
|
||||
while (pos != NULL)
|
||||
{
|
||||
if (earliest_deadline > pos->last_activity + dto)
|
||||
earliest_deadline = pos->last_activity + dto;
|
||||
pos = pos->next;
|
||||
}
|
||||
if (earliest_deadline < now)
|
||||
*timeout = 0;
|
||||
else
|
||||
*timeout = (earliest_deadline - now);
|
||||
return MHD_YES;
|
||||
}
|
||||
dto = daemon->connection_timeout;
|
||||
if (0 == dto)
|
||||
return MHD_NO;
|
||||
pos = daemon->connections;
|
||||
if (pos == NULL)
|
||||
return MHD_NO; /* no connections */
|
||||
now = time(NULL);
|
||||
/* start with conservative estimate */
|
||||
earliest_deadline = now + dto;
|
||||
while (pos != NULL)
|
||||
{
|
||||
if (earliest_deadline > pos->last_activity + dto)
|
||||
earliest_deadline = pos->last_activity + dto;
|
||||
pos = pos->next;
|
||||
}
|
||||
if (earliest_deadline < now)
|
||||
*timeout = 0;
|
||||
else
|
||||
*timeout = (earliest_deadline - now);
|
||||
return MHD_YES;
|
||||
}
|
||||
|
||||
/**
|
||||
* Main select call.
|
||||
@@ -409,101 +431,102 @@ MHD_get_timeout (struct MHD_Daemon *daemon, unsigned long long *timeout)
|
||||
* @param may_block YES if blocking, NO if non-blocking
|
||||
* @return MHD_NO on serious errors, MHD_YES on success
|
||||
*/
|
||||
static int
|
||||
MHD_select (struct MHD_Daemon *daemon, int may_block)
|
||||
{
|
||||
struct MHD_Connection *pos;
|
||||
int num_ready;
|
||||
fd_set rs;
|
||||
fd_set ws;
|
||||
fd_set es;
|
||||
int max;
|
||||
struct timeval timeout;
|
||||
unsigned long long ltimeout;
|
||||
int ds;
|
||||
time_t now;
|
||||
static int MHD_select(struct MHD_Daemon *daemon, int may_block)
|
||||
{
|
||||
struct MHD_Connection *pos;
|
||||
int num_ready;
|
||||
fd_set rs;
|
||||
fd_set ws;
|
||||
fd_set es;
|
||||
int max;
|
||||
struct timeval timeout;
|
||||
unsigned long long ltimeout;
|
||||
int ds;
|
||||
time_t now;
|
||||
|
||||
timeout.tv_sec = 0;
|
||||
timeout.tv_usec = 0;
|
||||
if (daemon == NULL)
|
||||
abort ();
|
||||
if (daemon->shutdown == MHD_YES)
|
||||
return MHD_NO;
|
||||
FD_ZERO (&rs);
|
||||
FD_ZERO (&ws);
|
||||
FD_ZERO (&es);
|
||||
max = 0;
|
||||
|
||||
if (0 == (daemon->options & MHD_USE_THREAD_PER_CONNECTION))
|
||||
{
|
||||
/* single-threaded, go over everything */
|
||||
if (MHD_NO == MHD_get_fdset (daemon, &rs, &ws, &es, &max))
|
||||
return MHD_NO;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* accept only, have one thread per connection */
|
||||
max = daemon->socket_fd;
|
||||
if (max == -1)
|
||||
return MHD_NO;
|
||||
FD_SET (max, &rs);
|
||||
}
|
||||
if (may_block == MHD_NO)
|
||||
{
|
||||
timeout.tv_usec = 0;
|
||||
timeout.tv_sec = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* ltimeout is in ms */
|
||||
if (MHD_YES == MHD_get_timeout (daemon, <imeout))
|
||||
{
|
||||
timeout.tv_usec = (ltimeout % 1000) * 1000;
|
||||
timeout.tv_sec = ltimeout / 1000;
|
||||
may_block = MHD_NO;
|
||||
}
|
||||
}
|
||||
num_ready = SELECT (max + 1,
|
||||
&rs, &ws, &es, may_block == MHD_NO ? &timeout : NULL);
|
||||
if (daemon->shutdown == MHD_YES)
|
||||
return MHD_NO;
|
||||
if (num_ready < 0)
|
||||
{
|
||||
if (errno == EINTR)
|
||||
return MHD_YES;
|
||||
#if HAVE_MESSAGES
|
||||
MHD_DLOG (daemon, "Select failed: %s\n", STRERROR (errno));
|
||||
#endif
|
||||
timeout.tv_sec = 0;
|
||||
timeout.tv_usec = 0;
|
||||
if (daemon == NULL)
|
||||
abort();
|
||||
if (daemon->shutdown == MHD_YES)
|
||||
return MHD_NO;
|
||||
}
|
||||
ds = daemon->socket_fd;
|
||||
if (ds == -1)
|
||||
return MHD_YES;
|
||||
if (FD_ISSET (ds, &rs))
|
||||
MHD_accept_connection (daemon);
|
||||
if (0 == (daemon->options & MHD_USE_THREAD_PER_CONNECTION))
|
||||
{
|
||||
/* do not have a thread per connection, process all connections now */
|
||||
now = time (NULL);
|
||||
pos = daemon->connections;
|
||||
while (pos != NULL)
|
||||
{
|
||||
ds = pos->socket_fd;
|
||||
if (ds != -1)
|
||||
{
|
||||
if (FD_ISSET (ds, &rs))
|
||||
MHD_connection_handle_read (pos);
|
||||
if ((pos->socket_fd != -1) && (FD_ISSET (ds, &ws)))
|
||||
MHD_connection_handle_write (pos);
|
||||
if (pos->socket_fd != -1)
|
||||
MHD_connection_handle_idle (pos);
|
||||
}
|
||||
pos = pos->next;
|
||||
}
|
||||
}
|
||||
return MHD_YES;
|
||||
}
|
||||
FD_ZERO (&rs);
|
||||
FD_ZERO (&ws);
|
||||
FD_ZERO (&es);
|
||||
max = 0;
|
||||
|
||||
if (0 == (daemon->options & MHD_USE_THREAD_PER_CONNECTION))
|
||||
{
|
||||
/* single-threaded, go over everything */
|
||||
if (MHD_NO == MHD_get_fdset (daemon, &rs, &ws, &es, &max))
|
||||
return MHD_NO;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* accept only, have one thread per connection */
|
||||
max = daemon->socket_fd;
|
||||
if (max == -1)
|
||||
return MHD_NO;
|
||||
FD_SET (max, &rs);
|
||||
}
|
||||
|
||||
if (may_block == MHD_NO)
|
||||
{
|
||||
timeout.tv_usec = 0;
|
||||
timeout.tv_sec = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* ltimeout is in ms */
|
||||
if (MHD_YES == MHD_get_timeout (daemon, <imeout))
|
||||
{
|
||||
timeout.tv_usec = (ltimeout % 1000) * 1000;
|
||||
timeout.tv_sec = ltimeout / 1000;
|
||||
may_block = MHD_NO;
|
||||
}
|
||||
}
|
||||
|
||||
num_ready = select(max + 1, &rs, &ws, &es, may_block == MHD_NO ? &timeout
|
||||
: NULL);
|
||||
|
||||
if (daemon->shutdown == MHD_YES)
|
||||
return MHD_NO;
|
||||
if (num_ready < 0)
|
||||
{
|
||||
if (errno == EINTR)
|
||||
return MHD_YES;
|
||||
#if HAVE_MESSAGES
|
||||
MHD_DLOG(daemon, "Select failed: %s\n", STRERROR (errno));
|
||||
#endif
|
||||
return MHD_NO;
|
||||
}
|
||||
ds = daemon->socket_fd;
|
||||
if (ds == -1)
|
||||
return MHD_YES;
|
||||
if (__FD_ISSET (ds, &rs))
|
||||
MHD_accept_connection(daemon);
|
||||
if (0 == (daemon->options & MHD_USE_THREAD_PER_CONNECTION))
|
||||
{
|
||||
/* do not have a thread per connection, process all connections now */
|
||||
now = time(NULL);
|
||||
pos = daemon->connections;
|
||||
while (pos != NULL)
|
||||
{
|
||||
ds = pos->socket_fd;
|
||||
if (ds != -1)
|
||||
{
|
||||
if (FD_ISSET (ds, &rs))
|
||||
MHD_connection_handle_read(pos);
|
||||
if ((pos->socket_fd != -1) && (FD_ISSET (ds, &ws)))
|
||||
MHD_connection_handle_write(pos);
|
||||
if (pos->socket_fd != -1)
|
||||
MHD_connection_handle_idle(pos);
|
||||
}
|
||||
pos = pos->next;
|
||||
}
|
||||
}
|
||||
return MHD_YES;
|
||||
}
|
||||
|
||||
/**
|
||||
* Run webserver operations (without blocking unless
|
||||
@@ -515,35 +538,31 @@ MHD_select (struct MHD_Daemon *daemon, int may_block)
|
||||
* daemon was not started with the right
|
||||
* options for this call.
|
||||
*/
|
||||
int
|
||||
MHD_run (struct MHD_Daemon *daemon)
|
||||
{
|
||||
if ((daemon->shutdown != MHD_NO) ||
|
||||
(0 != (daemon->options & MHD_USE_THREAD_PER_CONNECTION)) ||
|
||||
(0 != (daemon->options & MHD_USE_SELECT_INTERNALLY)))
|
||||
return MHD_NO;
|
||||
MHD_select (daemon, MHD_NO);
|
||||
MHD_cleanup_connections (daemon);
|
||||
return MHD_YES;
|
||||
}
|
||||
|
||||
int MHD_run(struct MHD_Daemon *daemon)
|
||||
{
|
||||
if ((daemon->shutdown != MHD_NO) || (0 != (daemon->options
|
||||
& MHD_USE_THREAD_PER_CONNECTION)) || (0 != (daemon->options
|
||||
& MHD_USE_SELECT_INTERNALLY)))
|
||||
return MHD_NO;
|
||||
MHD_select(daemon, MHD_NO);
|
||||
MHD_cleanup_connections(daemon);
|
||||
return MHD_YES;
|
||||
}
|
||||
|
||||
/**
|
||||
* Thread that runs the select loop until the daemon
|
||||
* is explicitly shut down.
|
||||
*/
|
||||
static void *
|
||||
MHD_select_thread (void *cls)
|
||||
{
|
||||
struct MHD_Daemon *daemon = cls;
|
||||
while (daemon->shutdown == MHD_NO)
|
||||
{
|
||||
MHD_select (daemon, MHD_YES);
|
||||
MHD_cleanup_connections (daemon);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void * MHD_select_thread(void *cls)
|
||||
{
|
||||
struct MHD_Daemon *daemon = cls;
|
||||
while (daemon->shutdown == MHD_NO)
|
||||
{
|
||||
MHD_select(daemon, MHD_YES);
|
||||
MHD_cleanup_connections(daemon);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Start a webserver on the given port.
|
||||
@@ -556,106 +575,119 @@ MHD_select_thread (void *cls)
|
||||
* @param dh_cls extra argument to dh
|
||||
* @return NULL on error, handle to daemon on success
|
||||
*/
|
||||
struct MHD_Daemon *
|
||||
MHD_start_daemon (unsigned int options,
|
||||
unsigned short port,
|
||||
MHD_AcceptPolicyCallback apc,
|
||||
void *apc_cls,
|
||||
MHD_AccessHandlerCallback dh, void *dh_cls, ...)
|
||||
{
|
||||
const int on = 1;
|
||||
struct MHD_Daemon *retVal;
|
||||
int socket_fd;
|
||||
struct sockaddr_in servaddr4;
|
||||
struct sockaddr_in6 servaddr6;
|
||||
const struct sockaddr *servaddr;
|
||||
socklen_t addrlen;
|
||||
va_list ap;
|
||||
enum MHD_OPTION opt;
|
||||
struct MHD_Daemon * MHD_start_daemon(unsigned int options,
|
||||
unsigned short port,
|
||||
MHD_AcceptPolicyCallback apc,
|
||||
void *apc_cls,
|
||||
MHD_AccessHandlerCallback dh,
|
||||
void *dh_cls,
|
||||
...)
|
||||
{
|
||||
const int on = 1;
|
||||
struct MHD_Daemon *retVal;
|
||||
|
||||
if ((options & MHD_USE_SSL) != 0)
|
||||
return NULL;
|
||||
if ((port == 0) || (dh == NULL))
|
||||
return NULL;
|
||||
if ((options & MHD_USE_IPv6) != 0)
|
||||
socket_fd = SOCKET (PF_INET6, SOCK_STREAM, 0);
|
||||
else
|
||||
socket_fd = SOCKET (PF_INET, SOCK_STREAM, 0);
|
||||
if (socket_fd < 0)
|
||||
{
|
||||
#if HAVE_MESSAGES
|
||||
if ((options & MHD_USE_DEBUG) != 0)
|
||||
fprintf (stderr, "Call to socket failed: %s\n", STRERROR (errno));
|
||||
#endif
|
||||
/* listeningss sockets used by the daemon */
|
||||
int socket_fd;
|
||||
|
||||
struct sockaddr_in servaddr4;
|
||||
struct sockaddr_in6 servaddr6;
|
||||
const struct sockaddr *servaddr;
|
||||
socklen_t addrlen;
|
||||
va_list ap;
|
||||
enum MHD_OPTION opt;
|
||||
|
||||
if ((port == 0) || (dh == NULL))
|
||||
return NULL;
|
||||
}
|
||||
if ((SETSOCKOPT (socket_fd,
|
||||
SOL_SOCKET,
|
||||
SO_REUSEADDR,
|
||||
&on, sizeof (on)) < 0) && (options & MHD_USE_DEBUG) != 0)
|
||||
{
|
||||
if ((options & MHD_USE_IPv6) != 0)
|
||||
socket_fd = SOCKET (PF_INET6, SOCK_STREAM, 0);
|
||||
else
|
||||
socket_fd = SOCKET (PF_INET, SOCK_STREAM, 0);
|
||||
if (socket_fd < 0)
|
||||
{
|
||||
#if HAVE_MESSAGES
|
||||
fprintf (stderr, "setsockopt failed: %s\n", STRERROR (errno));
|
||||
if ((options & MHD_USE_DEBUG) != 0)
|
||||
fprintf(stderr, "Call to socket failed: %s\n", STRERROR (errno));
|
||||
#endif
|
||||
}
|
||||
if ((options & MHD_USE_IPv6) != 0)
|
||||
{
|
||||
memset (&servaddr6, 0, sizeof (struct sockaddr_in6));
|
||||
servaddr6.sin6_family = AF_INET6;
|
||||
servaddr6.sin6_port = htons (port);
|
||||
servaddr = (struct sockaddr *) &servaddr6;
|
||||
addrlen = sizeof (struct sockaddr_in6);
|
||||
}
|
||||
else
|
||||
{
|
||||
memset (&servaddr4, 0, sizeof (struct sockaddr_in));
|
||||
servaddr4.sin_family = AF_INET;
|
||||
servaddr4.sin_port = htons (port);
|
||||
servaddr = (struct sockaddr *) &servaddr4;
|
||||
addrlen = sizeof (struct sockaddr_in);
|
||||
}
|
||||
if (BIND (socket_fd, servaddr, addrlen) < 0)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
if ((SETSOCKOPT (socket_fd,
|
||||
SOL_SOCKET,
|
||||
SO_REUSEADDR,
|
||||
&on, sizeof (on)) < 0) && (options & MHD_USE_DEBUG) != 0)
|
||||
{
|
||||
#if HAVE_MESSAGES
|
||||
if ((options & MHD_USE_DEBUG) != 0)
|
||||
fprintf (stderr,
|
||||
"Failed to bind to port %u: %s\n", port, STRERROR (errno));
|
||||
fprintf(stderr, "setsockopt failed: %s\n", STRERROR (errno));
|
||||
#endif
|
||||
CLOSE (socket_fd);
|
||||
return NULL;
|
||||
}
|
||||
if (LISTEN (socket_fd, 20) < 0)
|
||||
{
|
||||
}
|
||||
if ((options & MHD_USE_IPv6) != 0)
|
||||
{
|
||||
memset(&servaddr6, 0, sizeof(struct sockaddr_in6));
|
||||
servaddr6.sin6_family = AF_INET6;
|
||||
servaddr6.sin6_port = htons(port);
|
||||
servaddr = (struct sockaddr *) &servaddr6;
|
||||
addrlen = sizeof(struct sockaddr_in6);
|
||||
}
|
||||
else
|
||||
{
|
||||
memset(&servaddr4, 0, sizeof(struct sockaddr_in));
|
||||
servaddr4.sin_family = AF_INET;
|
||||
servaddr4.sin_port = htons(port);
|
||||
servaddr = (struct sockaddr *) &servaddr4;
|
||||
addrlen = sizeof(struct sockaddr_in);
|
||||
}
|
||||
if (BIND (socket_fd, servaddr, addrlen) < 0)
|
||||
{
|
||||
#if HAVE_MESSAGES
|
||||
if ((options & MHD_USE_DEBUG) != 0)
|
||||
fprintf (stderr,
|
||||
"Failed to listen for connections: %s\n", STRERROR (errno));
|
||||
if ((options & MHD_USE_DEBUG) != 0)
|
||||
fprintf(stderr,
|
||||
"Failed to bind to port %u: %s\n", port, STRERROR (errno));
|
||||
#endif
|
||||
CLOSE (socket_fd);
|
||||
return NULL;
|
||||
}
|
||||
retVal = malloc (sizeof (struct MHD_Daemon));
|
||||
if (retVal == NULL)
|
||||
{
|
||||
CLOSE (socket_fd);
|
||||
return NULL;
|
||||
}
|
||||
memset (retVal, 0, sizeof (struct MHD_Daemon));
|
||||
retVal->options = options;
|
||||
retVal->port = port;
|
||||
retVal->apc = apc;
|
||||
retVal->apc_cls = apc_cls;
|
||||
retVal->socket_fd = socket_fd;
|
||||
retVal->default_handler = dh;
|
||||
retVal->default_handler_cls = dh_cls;
|
||||
retVal->max_connections = MHD_MAX_CONNECTIONS_DEFAULT;
|
||||
retVal->pool_size = MHD_POOL_SIZE_DEFAULT;
|
||||
retVal->connection_timeout = 0; /* no timeout */
|
||||
va_start (ap, dh_cls);
|
||||
while (MHD_OPTION_END != (opt = va_arg (ap, enum MHD_OPTION)))
|
||||
{
|
||||
switch (opt)
|
||||
{
|
||||
CLOSE (socket_fd);
|
||||
return NULL;
|
||||
}
|
||||
if (LISTEN (socket_fd, 20) < 0)
|
||||
{
|
||||
#if HAVE_MESSAGES
|
||||
if ((options & MHD_USE_DEBUG) != 0)
|
||||
fprintf(stderr,
|
||||
"Failed to listen for connections: %s\n", STRERROR (errno));
|
||||
#endif
|
||||
CLOSE (socket_fd);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* allocate the mhd daemon */
|
||||
|
||||
retVal = malloc(sizeof(struct MHD_Daemon));
|
||||
|
||||
if (retVal == NULL)
|
||||
{
|
||||
CLOSE (socket_fd);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memset(retVal, 0, sizeof(struct MHD_Daemon));
|
||||
retVal->options = options;
|
||||
retVal->port = port;
|
||||
retVal->apc = apc;
|
||||
retVal->apc_cls = apc_cls;
|
||||
retVal->socket_fd = socket_fd;
|
||||
retVal->default_handler = dh;
|
||||
retVal->default_handler_cls = dh_cls;
|
||||
retVal->max_connections = MHD_MAX_CONNECTIONS_DEFAULT;
|
||||
retVal->pool_size = MHD_POOL_SIZE_DEFAULT;
|
||||
retVal->connection_timeout = 0; /* no timeout */
|
||||
|
||||
/* initializes the argument pointer variable */
|
||||
va_start (ap, dh_cls);
|
||||
|
||||
/*
|
||||
* loop through daemon options
|
||||
*/
|
||||
while (MHD_OPTION_END != (opt = va_arg (ap, enum MHD_OPTION)))
|
||||
{
|
||||
switch (opt)
|
||||
{
|
||||
case MHD_OPTION_CONNECTION_MEMORY_LIMIT:
|
||||
retVal->pool_size = va_arg (ap, unsigned int);
|
||||
break;
|
||||
@@ -666,8 +698,7 @@ MHD_start_daemon (unsigned int options,
|
||||
retVal->connection_timeout = va_arg (ap, unsigned int);
|
||||
break;
|
||||
case MHD_OPTION_NOTIFY_COMPLETED:
|
||||
retVal->notify_completed =
|
||||
va_arg (ap, MHD_RequestCompletedCallback);
|
||||
retVal->notify_completed = va_arg (ap, MHD_RequestCompletedCallback);
|
||||
retVal->notify_completed_cls = va_arg (ap, void *);
|
||||
break;
|
||||
case MHD_OPTION_PER_IP_CONNECTION_LIMIT:
|
||||
@@ -675,76 +706,76 @@ MHD_start_daemon (unsigned int options,
|
||||
break;
|
||||
default:
|
||||
#if HAVE_MESSAGES
|
||||
fprintf (stderr,
|
||||
"Invalid MHD_OPTION argument! (Did you terminate the list with MHD_OPTION_END?)\n");
|
||||
fprintf(stderr,
|
||||
"Invalid MHD_OPTION argument! (Did you terminate the list with MHD_OPTION_END?)\n");
|
||||
#endif
|
||||
abort ();
|
||||
}
|
||||
}
|
||||
va_end (ap);
|
||||
if (((0 != (options & MHD_USE_THREAD_PER_CONNECTION)) ||
|
||||
(0 != (options & MHD_USE_SELECT_INTERNALLY))) &&
|
||||
(0 != pthread_create (&retVal->pid, NULL, &MHD_select_thread, retVal)))
|
||||
{
|
||||
abort();
|
||||
}
|
||||
}
|
||||
va_end (ap);
|
||||
if (((0 != (options & MHD_USE_THREAD_PER_CONNECTION)) || (0 != (options
|
||||
& MHD_USE_SELECT_INTERNALLY))) && (0 != pthread_create(&retVal->pid,
|
||||
NULL, &MHD_select_thread, retVal)))
|
||||
{
|
||||
#if HAVE_MESSAGES
|
||||
MHD_DLOG (retVal,
|
||||
"Failed to create listen thread: %s\n", STRERROR (errno));
|
||||
MHD_DLOG(retVal, "Failed to create listen thread: %s\n",
|
||||
STRERROR (errno));
|
||||
#endif
|
||||
free (retVal);
|
||||
CLOSE (socket_fd);
|
||||
return NULL;
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
free(retVal);
|
||||
CLOSE (socket_fd);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Shutdown an http daemon.
|
||||
*/
|
||||
void
|
||||
MHD_stop_daemon (struct MHD_Daemon *daemon)
|
||||
{
|
||||
void *unused;
|
||||
int fd;
|
||||
void MHD_stop_daemon(struct MHD_Daemon *daemon)
|
||||
{
|
||||
void *unused;
|
||||
int fd;
|
||||
|
||||
if (daemon == NULL)
|
||||
return;
|
||||
daemon->shutdown = MHD_YES;
|
||||
fd = daemon->socket_fd;
|
||||
daemon->socket_fd = -1;
|
||||
if (daemon == NULL)
|
||||
return;
|
||||
daemon->shutdown = MHD_YES;
|
||||
fd = daemon->socket_fd;
|
||||
daemon->socket_fd = -1;
|
||||
#if DEBUG_CLOSE
|
||||
#if HAVE_MESSAGES
|
||||
MHD_DLOG (daemon, "MHD shutdown, closing listen socket\n");
|
||||
MHD_DLOG (daemon, "MHD shutdown, closing listen socket\n");
|
||||
#endif
|
||||
#endif
|
||||
CLOSE (fd);
|
||||
if ((0 != (daemon->options & MHD_USE_THREAD_PER_CONNECTION)) ||
|
||||
(0 != (daemon->options & MHD_USE_SELECT_INTERNALLY)))
|
||||
{
|
||||
pthread_kill (daemon->pid, SIGALRM);
|
||||
pthread_join (daemon->pid, &unused);
|
||||
}
|
||||
while (daemon->connections != NULL)
|
||||
{
|
||||
if (-1 != daemon->connections->socket_fd)
|
||||
{
|
||||
CLOSE (fd);
|
||||
if ((0 != (daemon->options & MHD_USE_THREAD_PER_CONNECTION)) || (0
|
||||
!= (daemon->
|
||||
options & MHD_USE_SELECT_INTERNALLY)))
|
||||
{
|
||||
pthread_kill(daemon->pid, SIGALRM);
|
||||
pthread_join(daemon->pid, &unused);
|
||||
}
|
||||
while (daemon->connections != NULL)
|
||||
{
|
||||
if (-1 != daemon->connections->socket_fd)
|
||||
{
|
||||
#if DEBUG_CLOSE
|
||||
#if HAVE_MESSAGES
|
||||
MHD_DLOG (daemon, "MHD shutdown, closing active connections\n");
|
||||
MHD_DLOG (daemon, "MHD shutdown, closing active connections\n");
|
||||
#endif
|
||||
#endif
|
||||
if (daemon->notify_completed != NULL)
|
||||
daemon->notify_completed (daemon->notify_completed_cls,
|
||||
daemon->connections,
|
||||
&daemon->connections->client_context,
|
||||
MHD_REQUEST_TERMINATED_DAEMON_SHUTDOWN);
|
||||
SHUTDOWN (daemon->connections->socket_fd, SHUT_RDWR);
|
||||
CLOSE (daemon->connections->socket_fd);
|
||||
daemon->connections->socket_fd = -1;
|
||||
}
|
||||
MHD_cleanup_connections (daemon);
|
||||
}
|
||||
free (daemon);
|
||||
}
|
||||
if (daemon->notify_completed != NULL)
|
||||
daemon->notify_completed(daemon->notify_completed_cls,
|
||||
daemon->connections, &daemon->connections->client_context,
|
||||
MHD_REQUEST_TERMINATED_DAEMON_SHUTDOWN);
|
||||
SHUTDOWN (daemon->connections->socket_fd, SHUT_RDWR);
|
||||
CLOSE (daemon->connections->socket_fd);
|
||||
daemon->connections->socket_fd = -1;
|
||||
}
|
||||
MHD_cleanup_connections(daemon);
|
||||
}
|
||||
free(daemon);
|
||||
}
|
||||
|
||||
#ifndef WINDOWS
|
||||
|
||||
@@ -752,38 +783,37 @@ static struct sigaction sig;
|
||||
|
||||
static struct sigaction old;
|
||||
|
||||
static void
|
||||
sigalrmHandler (int sig)
|
||||
{
|
||||
}
|
||||
static void sigalrmHandler(int sig)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the signal handler for SIGALRM.
|
||||
*/
|
||||
void __attribute__ ((constructor)) MHD_pthread_handlers_ltdl_init ()
|
||||
{
|
||||
/* make sure SIGALRM does not kill us */
|
||||
memset (&sig, 0, sizeof (struct sigaction));
|
||||
memset (&old, 0, sizeof (struct sigaction));
|
||||
sig.sa_flags = SA_NODEFER;
|
||||
sig.sa_handler = &sigalrmHandler;
|
||||
sigaction (SIGALRM, &sig, &old);
|
||||
}
|
||||
void __attribute__ ((constructor)) MHD_pthread_handlers_ltdl_init()
|
||||
{
|
||||
/* make sure SIGALRM does not kill us */
|
||||
memset(&sig, 0, sizeof(struct sigaction));
|
||||
memset(&old, 0, sizeof(struct sigaction));
|
||||
sig.sa_flags = SA_NODEFER;
|
||||
sig.sa_handler = &sigalrmHandler;
|
||||
sigaction(SIGALRM, &sig, &old);
|
||||
}
|
||||
|
||||
void __attribute__ ((destructor)) MHD_pthread_handlers_ltdl_fini ()
|
||||
{
|
||||
sigaction (SIGALRM, &old, &sig);
|
||||
}
|
||||
void __attribute__ ((destructor)) MHD_pthread_handlers_ltdl_fini()
|
||||
{
|
||||
sigaction(SIGALRM, &old, &sig);
|
||||
}
|
||||
#else
|
||||
void __attribute__ ((constructor)) MHD_win_ltdl_init ()
|
||||
{
|
||||
plibc_init ("CRISP", "libmicrohttpd");
|
||||
}
|
||||
{
|
||||
plibc_init ("CRISP", "libmicrohttpd");
|
||||
}
|
||||
|
||||
void __attribute__ ((destructor)) MHD_win_ltdl_fini ()
|
||||
{
|
||||
plibc_shutdown ();
|
||||
}
|
||||
{
|
||||
plibc_shutdown ();
|
||||
}
|
||||
#endif
|
||||
|
||||
/* end of daemon.c */
|
||||
|
||||
+432
-414
@@ -1,21 +1,21 @@
|
||||
/*
|
||||
This file is part of libmicrohttpd
|
||||
(C) 2007 Daniel Pittman and Christian Grothoff
|
||||
This file is part of libmicrohttpd
|
||||
(C) 2007 Daniel Pittman and Christian Grothoff
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library 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
|
||||
Lesser General Public License for more details.
|
||||
This library 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
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file internal.h
|
||||
@@ -27,7 +27,6 @@
|
||||
#ifndef INTERNAL_H
|
||||
#define INTERNAL_H
|
||||
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
@@ -64,101 +63,101 @@
|
||||
* fprintf-like helper function for logging debug
|
||||
* messages.
|
||||
*/
|
||||
void MHD_DLOG (const struct MHD_Daemon *daemon, const char *format, ...);
|
||||
void MHD_DLOG(const struct MHD_Daemon *daemon, const char *format, ...);
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Process escape sequences ('+'=space, %HH).
|
||||
* Updates val in place.
|
||||
*/
|
||||
void MHD_http_unescape (char *val);
|
||||
void MHD_http_unescape(char *val);
|
||||
|
||||
/**
|
||||
* Header or cookie in HTTP request or response.
|
||||
*/
|
||||
struct MHD_HTTP_Header
|
||||
{
|
||||
struct MHD_HTTP_Header *next;
|
||||
{
|
||||
struct MHD_HTTP_Header *next;
|
||||
|
||||
char *header;
|
||||
char *header;
|
||||
|
||||
char *value;
|
||||
char *value;
|
||||
|
||||
enum MHD_ValueKind kind;
|
||||
enum MHD_ValueKind kind;
|
||||
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Representation of a response.
|
||||
*/
|
||||
struct MHD_Response
|
||||
{
|
||||
{
|
||||
|
||||
/**
|
||||
* Headers to send for the response. Initially
|
||||
* the linked list is created in inverse order;
|
||||
* the order should be inverted before sending!
|
||||
*/
|
||||
struct MHD_HTTP_Header *first_header;
|
||||
/**
|
||||
* Headers to send for the response. Initially
|
||||
* the linked list is created in inverse order;
|
||||
* the order should be inverted before sending!
|
||||
*/
|
||||
struct MHD_HTTP_Header *first_header;
|
||||
|
||||
/**
|
||||
* Buffer pointing to data that we are supposed
|
||||
* to send as a response.
|
||||
*/
|
||||
char *data;
|
||||
/**
|
||||
* Buffer pointing to data that we are supposed
|
||||
* to send as a response.
|
||||
*/
|
||||
char *data;
|
||||
|
||||
/**
|
||||
* Closure to give to the content reader
|
||||
* free callback.
|
||||
*/
|
||||
void *crc_cls;
|
||||
/**
|
||||
* Closure to give to the content reader
|
||||
* free callback.
|
||||
*/
|
||||
void *crc_cls;
|
||||
|
||||
/**
|
||||
* How do we get more data? NULL if we are
|
||||
* given all of the data up front.
|
||||
*/
|
||||
MHD_ContentReaderCallback crc;
|
||||
/**
|
||||
* How do we get more data? NULL if we are
|
||||
* given all of the data up front.
|
||||
*/
|
||||
MHD_ContentReaderCallback crc;
|
||||
|
||||
/**
|
||||
* NULL if data must not be freed, otherwise
|
||||
* either user-specified callback or "&free".
|
||||
*/
|
||||
MHD_ContentReaderFreeCallback crfc;
|
||||
/**
|
||||
* NULL if data must not be freed, otherwise
|
||||
* either user-specified callback or "&free".
|
||||
*/
|
||||
MHD_ContentReaderFreeCallback crfc;
|
||||
|
||||
/**
|
||||
* Mutex to synchronize access to data/size and
|
||||
* reference counts.
|
||||
*/
|
||||
pthread_mutex_t mutex;
|
||||
/**
|
||||
* Mutex to synchronize access to data/size and
|
||||
* reference counts.
|
||||
*/
|
||||
pthread_mutex_t mutex;
|
||||
|
||||
/**
|
||||
* Reference count for this response. Free
|
||||
* once the counter hits zero.
|
||||
*/
|
||||
unsigned int reference_count;
|
||||
/**
|
||||
* Reference count for this response. Free
|
||||
* once the counter hits zero.
|
||||
*/
|
||||
unsigned int reference_count;
|
||||
|
||||
/**
|
||||
* Set to -1 if size is not known.
|
||||
*/
|
||||
size_t total_size;
|
||||
/**
|
||||
* Set to -1 if size is not known.
|
||||
*/
|
||||
size_t total_size;
|
||||
|
||||
/**
|
||||
* Size of data.
|
||||
*/
|
||||
size_t data_size;
|
||||
/**
|
||||
* Size of data.
|
||||
*/
|
||||
size_t data_size;
|
||||
|
||||
/**
|
||||
* Size of the data buffer.
|
||||
*/
|
||||
size_t data_buffer_size;
|
||||
/**
|
||||
* Size of the data buffer.
|
||||
*/
|
||||
size_t data_buffer_size;
|
||||
|
||||
/**
|
||||
* At what offset in the stream is the
|
||||
* beginning of data located?
|
||||
*/
|
||||
size_t data_start;
|
||||
/**
|
||||
* At what offset in the stream is the
|
||||
* beginning of data located?
|
||||
*/
|
||||
size_t data_start;
|
||||
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* States in a state machine for a connection.
|
||||
@@ -175,419 +174,438 @@ struct MHD_Response
|
||||
* requires the write to be complete.
|
||||
*/
|
||||
enum MHD_CONNECTION_STATE
|
||||
{
|
||||
/**
|
||||
* Connection just started (no headers received).
|
||||
* Waiting for the line with the request type, URL and version.
|
||||
*/
|
||||
MHD_CONNECTION_INIT = 0,
|
||||
{
|
||||
/**
|
||||
* Connection just started (no headers received).
|
||||
* Waiting for the line with the request type, URL and version.
|
||||
*/
|
||||
MHD_CONNECTION_INIT = 0,
|
||||
|
||||
/**
|
||||
* 1: We got the URL (and request type and version). Wait for a header line.
|
||||
*/
|
||||
MHD_CONNECTION_URL_RECEIVED = MHD_CONNECTION_INIT + 1,
|
||||
/**
|
||||
* 1: We got the URL (and request type and version). Wait for a header line.
|
||||
*/
|
||||
MHD_CONNECTION_URL_RECEIVED = MHD_CONNECTION_INIT + 1,
|
||||
|
||||
/**
|
||||
* 2: We got part of a multi-line request header. Wait for the rest.
|
||||
*/
|
||||
MHD_CONNECTION_HEADER_PART_RECEIVED = MHD_CONNECTION_URL_RECEIVED + 1,
|
||||
/**
|
||||
* 2: We got part of a multi-line request header. Wait for the rest.
|
||||
*/
|
||||
MHD_CONNECTION_HEADER_PART_RECEIVED = MHD_CONNECTION_URL_RECEIVED + 1,
|
||||
|
||||
/**
|
||||
* 3: We got the request headers. Process them.
|
||||
*/
|
||||
MHD_CONNECTION_HEADERS_RECEIVED = MHD_CONNECTION_HEADER_PART_RECEIVED + 1,
|
||||
/**
|
||||
* 3: We got the request headers. Process them.
|
||||
*/
|
||||
MHD_CONNECTION_HEADERS_RECEIVED = MHD_CONNECTION_HEADER_PART_RECEIVED + 1,
|
||||
|
||||
/**
|
||||
* 4: We have processed the request headers. Send 100 continue.
|
||||
*/
|
||||
MHD_CONNECTION_HEADERS_PROCESSED = MHD_CONNECTION_HEADERS_RECEIVED + 1,
|
||||
/**
|
||||
* 4: We have processed the request headers. Send 100 continue.
|
||||
*/
|
||||
MHD_CONNECTION_HEADERS_PROCESSED = MHD_CONNECTION_HEADERS_RECEIVED + 1,
|
||||
|
||||
/**
|
||||
* 5: We have processed the headers and need to send 100 CONTINUE.
|
||||
*/
|
||||
MHD_CONNECTION_CONTINUE_SENDING = MHD_CONNECTION_HEADERS_PROCESSED + 1,
|
||||
/**
|
||||
* 5: We have processed the headers and need to send 100 CONTINUE.
|
||||
*/
|
||||
MHD_CONNECTION_CONTINUE_SENDING = MHD_CONNECTION_HEADERS_PROCESSED + 1,
|
||||
|
||||
/**
|
||||
* 6: We have sent 100 CONTINUE (or do not need to). Read the message body.
|
||||
*/
|
||||
MHD_CONNECTION_CONTINUE_SENT = MHD_CONNECTION_CONTINUE_SENDING + 1,
|
||||
/**
|
||||
* 6: We have sent 100 CONTINUE (or do not need to). Read the message body.
|
||||
*/
|
||||
MHD_CONNECTION_CONTINUE_SENT = MHD_CONNECTION_CONTINUE_SENDING + 1,
|
||||
|
||||
/**
|
||||
* 7: We got the request body. Wait for a line of the footer.
|
||||
*/
|
||||
MHD_CONNECTION_BODY_RECEIVED = MHD_CONNECTION_CONTINUE_SENT + 1,
|
||||
/**
|
||||
* 7: We got the request body. Wait for a line of the footer.
|
||||
*/
|
||||
MHD_CONNECTION_BODY_RECEIVED = MHD_CONNECTION_CONTINUE_SENT + 1,
|
||||
|
||||
/**
|
||||
* 8: We got part of a line of the footer. Wait for the
|
||||
* rest.
|
||||
*/
|
||||
MHD_CONNECTION_FOOTER_PART_RECEIVED = MHD_CONNECTION_BODY_RECEIVED + 1,
|
||||
/**
|
||||
* 8: We got part of a line of the footer. Wait for the
|
||||
* rest.
|
||||
*/
|
||||
MHD_CONNECTION_FOOTER_PART_RECEIVED = MHD_CONNECTION_BODY_RECEIVED + 1,
|
||||
|
||||
/**
|
||||
* 9: We received the entire footer. Wait for a response to be queued
|
||||
* and prepare the response headers.
|
||||
*/
|
||||
MHD_CONNECTION_FOOTERS_RECEIVED = MHD_CONNECTION_FOOTER_PART_RECEIVED + 1,
|
||||
/**
|
||||
* 9: We received the entire footer. Wait for a response to be queued
|
||||
* and prepare the response headers.
|
||||
*/
|
||||
MHD_CONNECTION_FOOTERS_RECEIVED = MHD_CONNECTION_FOOTER_PART_RECEIVED + 1,
|
||||
|
||||
/**
|
||||
* 10: We have prepared the response headers in the writ buffer.
|
||||
* Send the response headers.
|
||||
*/
|
||||
MHD_CONNECTION_HEADERS_SENDING = MHD_CONNECTION_FOOTERS_RECEIVED + 1,
|
||||
/**
|
||||
* 10: We have prepared the response headers in the writ buffer.
|
||||
* Send the response headers.
|
||||
*/
|
||||
MHD_CONNECTION_HEADERS_SENDING = MHD_CONNECTION_FOOTERS_RECEIVED + 1,
|
||||
|
||||
/**
|
||||
* 11: We have sent the response headers. Get ready to send the body.
|
||||
*/
|
||||
MHD_CONNECTION_HEADERS_SENT = MHD_CONNECTION_HEADERS_SENDING + 1,
|
||||
/**
|
||||
* 11: We have sent the response headers. Get ready to send the body.
|
||||
*/
|
||||
MHD_CONNECTION_HEADERS_SENT = MHD_CONNECTION_HEADERS_SENDING + 1,
|
||||
|
||||
/**
|
||||
* 12: We are ready to send a part of a non-chunked body. Send it.
|
||||
*/
|
||||
MHD_CONNECTION_NORMAL_BODY_READY = MHD_CONNECTION_HEADERS_SENT + 1,
|
||||
/**
|
||||
* 12: We are ready to send a part of a non-chunked body. Send it.
|
||||
*/
|
||||
MHD_CONNECTION_NORMAL_BODY_READY = MHD_CONNECTION_HEADERS_SENT + 1,
|
||||
|
||||
/**
|
||||
* 13: We are waiting for the client to provide more
|
||||
* data of a non-chunked body.
|
||||
*/
|
||||
MHD_CONNECTION_NORMAL_BODY_UNREADY = MHD_CONNECTION_NORMAL_BODY_READY + 1,
|
||||
/**
|
||||
* 13: We are waiting for the client to provide more
|
||||
* data of a non-chunked body.
|
||||
*/
|
||||
MHD_CONNECTION_NORMAL_BODY_UNREADY = MHD_CONNECTION_NORMAL_BODY_READY + 1,
|
||||
|
||||
/**
|
||||
* 14: We are ready to send a chunk.
|
||||
*/
|
||||
MHD_CONNECTION_CHUNKED_BODY_READY = MHD_CONNECTION_NORMAL_BODY_UNREADY + 1,
|
||||
/**
|
||||
* 14: We are ready to send a chunk.
|
||||
*/
|
||||
MHD_CONNECTION_CHUNKED_BODY_READY = MHD_CONNECTION_NORMAL_BODY_UNREADY + 1,
|
||||
|
||||
/**
|
||||
* 15: We are waiting for the client to provide a chunk of the body.
|
||||
*/
|
||||
MHD_CONNECTION_CHUNKED_BODY_UNREADY = MHD_CONNECTION_CHUNKED_BODY_READY + 1,
|
||||
/**
|
||||
* 15: We are waiting for the client to provide a chunk of the body.
|
||||
*/
|
||||
MHD_CONNECTION_CHUNKED_BODY_UNREADY = MHD_CONNECTION_CHUNKED_BODY_READY + 1,
|
||||
|
||||
/**
|
||||
* 16: We have sent the response body. Prepare the footers.
|
||||
*/
|
||||
MHD_CONNECTION_BODY_SENT = MHD_CONNECTION_CHUNKED_BODY_UNREADY + 1,
|
||||
/**
|
||||
* 16: We have sent the response body. Prepare the footers.
|
||||
*/
|
||||
MHD_CONNECTION_BODY_SENT = MHD_CONNECTION_CHUNKED_BODY_UNREADY + 1,
|
||||
|
||||
/**
|
||||
* 17: We have prepared the response footer. Send it.
|
||||
*/
|
||||
MHD_CONNECTION_FOOTERS_SENDING = MHD_CONNECTION_BODY_SENT + 1,
|
||||
/**
|
||||
* 17: We have prepared the response footer. Send it.
|
||||
*/
|
||||
MHD_CONNECTION_FOOTERS_SENDING = MHD_CONNECTION_BODY_SENT + 1,
|
||||
|
||||
/**
|
||||
* 18: We have sent the response footer. Shutdown or restart.
|
||||
*/
|
||||
MHD_CONNECTION_FOOTERS_SENT = MHD_CONNECTION_FOOTERS_SENDING + 1,
|
||||
/**
|
||||
* 18: We have sent the response footer. Shutdown or restart.
|
||||
*/
|
||||
MHD_CONNECTION_FOOTERS_SENT = MHD_CONNECTION_FOOTERS_SENDING + 1,
|
||||
|
||||
/**
|
||||
* 19: This connection is closed (no more activity
|
||||
* allowed).
|
||||
*/
|
||||
MHD_CONNECTION_CLOSED = MHD_CONNECTION_FOOTERS_SENT + 1,
|
||||
/**
|
||||
* 19: This connection is closed (no more activity
|
||||
* allowed).
|
||||
*/
|
||||
MHD_CONNECTION_CLOSED = MHD_CONNECTION_FOOTERS_SENT + 1,
|
||||
|
||||
};
|
||||
};
|
||||
|
||||
enum MHDS_CONNECTION_STATE
|
||||
{
|
||||
MHDS_CONNECTION_INIT = 0,
|
||||
|
||||
/**
|
||||
* 1: We got the URL (and request type and version). Wait for a header line.
|
||||
*/
|
||||
MHDS_HANDSHAKE_COMPLETE = MHDS_CONNECTION_INIT + 1,
|
||||
|
||||
MHDS_CONNECTION_CONTINUE_SENDING = MHDS_HANDSHAKE_COMPLETE + 1,
|
||||
|
||||
MHDS_CONNECTION_CLOSED = MHDS_CONNECTION_CONTINUE_SENDING + 1
|
||||
};
|
||||
|
||||
struct MHD_Connection
|
||||
{
|
||||
{
|
||||
|
||||
/**
|
||||
* This is a linked list.
|
||||
*/
|
||||
struct MHD_Connection *next;
|
||||
/**
|
||||
* This is a linked list.
|
||||
*/
|
||||
struct MHD_Connection *next;
|
||||
|
||||
/**
|
||||
* Reference to the MHD_Daemon struct.
|
||||
*/
|
||||
struct MHD_Daemon *daemon;
|
||||
/**
|
||||
* Reference to the MHD_Daemon struct.
|
||||
*/
|
||||
struct MHD_Daemon *daemon;
|
||||
|
||||
/**
|
||||
* Linked list of parsed headers.
|
||||
*/
|
||||
struct MHD_HTTP_Header *headers_received;
|
||||
/**
|
||||
* Linked list of parsed headers.
|
||||
*/
|
||||
struct MHD_HTTP_Header *headers_received;
|
||||
|
||||
/**
|
||||
* Response to transmit (initially NULL).
|
||||
*/
|
||||
struct MHD_Response *response;
|
||||
/**
|
||||
* Response to transmit (initially NULL).
|
||||
*/
|
||||
struct MHD_Response *response;
|
||||
|
||||
/**
|
||||
* The memory pool is created whenever we first read
|
||||
* from the TCP stream and destroyed at the end of
|
||||
* each request (and re-created for the next request).
|
||||
* In the meantime, this pointer is NULL. The
|
||||
* pool is used for all connection-related data
|
||||
* except for the response (which maybe shared between
|
||||
* connections) and the IP address (which persists
|
||||
* across individual requests).
|
||||
*/
|
||||
struct MemoryPool *pool;
|
||||
/**
|
||||
* The memory pool is created whenever we first read
|
||||
* from the TCP stream and destroyed at the end of
|
||||
* each request (and re-created for the next request).
|
||||
* In the meantime, this pointer is NULL. The
|
||||
* pool is used for all connection-related data
|
||||
* except for the response (which maybe shared between
|
||||
* connections) and the IP address (which persists
|
||||
* across individual requests).
|
||||
*/
|
||||
struct MemoryPool *pool;
|
||||
|
||||
/**
|
||||
* We allow the main application to associate some
|
||||
* pointer with the connection. Here is where we
|
||||
* store it. (MHD does not know or care what it
|
||||
* is).
|
||||
*/
|
||||
void *client_context;
|
||||
/**
|
||||
* We allow the main application to associate some
|
||||
* pointer with the connection. Here is where we
|
||||
* store it. (MHD does not know or care what it
|
||||
* is).
|
||||
*/
|
||||
void *client_context;
|
||||
|
||||
/**
|
||||
* Request method. Should be GET/POST/etc. Allocated
|
||||
* in pool.
|
||||
*/
|
||||
char *method;
|
||||
/**
|
||||
* Request method. Should be GET/POST/etc. Allocated
|
||||
* in pool.
|
||||
*/
|
||||
char *method;
|
||||
|
||||
/**
|
||||
* Requested URL (everything after "GET" only). Allocated
|
||||
* in pool.
|
||||
*/
|
||||
char *url;
|
||||
/**
|
||||
* Requested URL (everything after "GET" only). Allocated
|
||||
* in pool.
|
||||
*/
|
||||
char *url;
|
||||
|
||||
/**
|
||||
* HTTP version string (i.e. http/1.1). Allocated
|
||||
* in pool.
|
||||
*/
|
||||
char *version;
|
||||
/**
|
||||
* HTTP version string (i.e. http/1.1). Allocated
|
||||
* in pool.
|
||||
*/
|
||||
char *version;
|
||||
|
||||
/**
|
||||
* Buffer for reading requests. Allocated
|
||||
* in pool. Actually one byte larger than
|
||||
* read_buffer_size (if non-NULL) to allow for
|
||||
* 0-termination.
|
||||
*/
|
||||
char *read_buffer;
|
||||
/**
|
||||
* Buffer for reading requests. Allocated
|
||||
* in pool. Actually one byte larger than
|
||||
* read_buffer_size (if non-NULL) to allow for
|
||||
* 0-termination.
|
||||
*/
|
||||
char *read_buffer;
|
||||
|
||||
/**
|
||||
* Buffer for writing response (headers only). Allocated
|
||||
* in pool.
|
||||
*/
|
||||
char *write_buffer;
|
||||
/**
|
||||
* Buffer for writing response (headers only). Allocated
|
||||
* in pool.
|
||||
*/
|
||||
char *write_buffer;
|
||||
|
||||
/**
|
||||
* Last incomplete header line during parsing of headers.
|
||||
* Allocated in pool. Only valid if state is
|
||||
* either HEADER_PART_RECEIVED or FOOTER_PART_RECEIVED.
|
||||
*/
|
||||
char *last;
|
||||
/**
|
||||
* Last incomplete header line during parsing of headers.
|
||||
* Allocated in pool. Only valid if state is
|
||||
* either HEADER_PART_RECEIVED or FOOTER_PART_RECEIVED.
|
||||
*/
|
||||
char *last;
|
||||
|
||||
/**
|
||||
* Position after the colon on the last incomplete header
|
||||
* line during parsing of headers.
|
||||
* Allocated in pool. Only valid if state is
|
||||
* either HEADER_PART_RECEIVED or FOOTER_PART_RECEIVED.
|
||||
*/
|
||||
char *colon;
|
||||
/**
|
||||
* Position after the colon on the last incomplete header
|
||||
* line during parsing of headers.
|
||||
* Allocated in pool. Only valid if state is
|
||||
* either HEADER_PART_RECEIVED or FOOTER_PART_RECEIVED.
|
||||
*/
|
||||
char *colon;
|
||||
|
||||
/**
|
||||
* Foreign address (of length addr_len). MALLOCED (not
|
||||
* in pool!).
|
||||
*/
|
||||
struct sockaddr_in *addr;
|
||||
/**
|
||||
* Foreign address (of length addr_len). MALLOCED (not
|
||||
* in pool!).
|
||||
*/
|
||||
struct sockaddr_in *addr;
|
||||
|
||||
/**
|
||||
* Thread for this connection (if we are using
|
||||
* one thread per connection).
|
||||
*/
|
||||
pthread_t pid;
|
||||
/**
|
||||
* Thread for this connection (if we are using
|
||||
* one thread per connection).
|
||||
*/
|
||||
pthread_t pid;
|
||||
|
||||
/**
|
||||
* Size of read_buffer (in bytes). This value indicates
|
||||
* how many bytes we're willing to read into the buffer;
|
||||
* the real buffer is one byte longer to allow for
|
||||
* adding zero-termination (when needed).
|
||||
*/
|
||||
size_t read_buffer_size;
|
||||
/**
|
||||
* Size of read_buffer (in bytes). This value indicates
|
||||
* how many bytes we're willing to read into the buffer;
|
||||
* the real buffer is one byte longer to allow for
|
||||
* adding zero-termination (when needed).
|
||||
*/
|
||||
size_t read_buffer_size;
|
||||
|
||||
/**
|
||||
* Position where we currently append data in
|
||||
* read_buffer (last valid position).
|
||||
*/
|
||||
size_t read_buffer_offset;
|
||||
/**
|
||||
* Position where we currently append data in
|
||||
* read_buffer (last valid position).
|
||||
*/
|
||||
size_t read_buffer_offset;
|
||||
|
||||
/**
|
||||
* Size of write_buffer (in bytes).
|
||||
*/
|
||||
size_t write_buffer_size;
|
||||
/**
|
||||
* Size of write_buffer (in bytes).
|
||||
*/
|
||||
size_t write_buffer_size;
|
||||
|
||||
/**
|
||||
* Offset where we are with sending from write_buffer.
|
||||
*/
|
||||
size_t write_buffer_send_offset;
|
||||
/**
|
||||
* Offset where we are with sending from write_buffer.
|
||||
*/
|
||||
size_t write_buffer_send_offset;
|
||||
|
||||
/**
|
||||
* Last valid location in write_buffer (where do we
|
||||
* append and up to where is it safe to send?)
|
||||
*/
|
||||
size_t write_buffer_append_offset;
|
||||
/**
|
||||
* Last valid location in write_buffer (where do we
|
||||
* append and up to where is it safe to send?)
|
||||
*/
|
||||
size_t write_buffer_append_offset;
|
||||
|
||||
/**
|
||||
* How many more bytes of the body do we expect
|
||||
* to read? "-1" for unknown.
|
||||
*/
|
||||
size_t remaining_upload_size;
|
||||
/**
|
||||
* How many more bytes of the body do we expect
|
||||
* to read? "-1" for unknown.
|
||||
*/
|
||||
size_t remaining_upload_size;
|
||||
|
||||
/**
|
||||
* Current write position in the actual response
|
||||
* (excluding headers, content only; should be 0
|
||||
* while sending headers).
|
||||
*/
|
||||
size_t response_write_position;
|
||||
/**
|
||||
* Current write position in the actual response
|
||||
* (excluding headers, content only; should be 0
|
||||
* while sending headers).
|
||||
*/
|
||||
size_t response_write_position;
|
||||
|
||||
/**
|
||||
* Position in the 100 CONTINUE message that
|
||||
* we need to send when receiving http 1.1 requests.
|
||||
*/
|
||||
size_t continue_message_write_offset;
|
||||
/**
|
||||
* Position in the 100 CONTINUE message that
|
||||
* we need to send when receiving http 1.1 requests.
|
||||
*/
|
||||
size_t continue_message_write_offset;
|
||||
|
||||
/**
|
||||
* Length of the foreign address.
|
||||
*/
|
||||
socklen_t addr_len;
|
||||
/**
|
||||
* Length of the foreign address.
|
||||
*/
|
||||
socklen_t addr_len;
|
||||
|
||||
/**
|
||||
* Last time this connection had any activity
|
||||
* (reading or writing).
|
||||
*/
|
||||
time_t last_activity;
|
||||
/**
|
||||
* Last time this connection had any activity
|
||||
* (reading or writing).
|
||||
*/
|
||||
time_t last_activity;
|
||||
|
||||
/**
|
||||
* Socket for this connection. Set to -1 if
|
||||
* this connection has died (daemon should clean
|
||||
* up in that case).
|
||||
*/
|
||||
int socket_fd;
|
||||
/**
|
||||
* Socket for this connection. Set to -1 if
|
||||
* this connection has died (daemon should clean
|
||||
* up in that case).
|
||||
*/
|
||||
int socket_fd;
|
||||
|
||||
/**
|
||||
* Has this socket been closed for reading (i.e.
|
||||
* other side closed the connection)? If so,
|
||||
* we must completely close the connection once
|
||||
* we are done sending our response (and stop
|
||||
* trying to read from this socket).
|
||||
*/
|
||||
int read_closed;
|
||||
/**
|
||||
* Has this socket been closed for reading (i.e.
|
||||
* other side closed the connection)? If so,
|
||||
* we must completely close the connection once
|
||||
* we are done sending our response (and stop
|
||||
* trying to read from this socket).
|
||||
*/
|
||||
int read_closed;
|
||||
|
||||
/**
|
||||
* State in the FSM for this connection.
|
||||
*/
|
||||
enum MHD_CONNECTION_STATE state;
|
||||
/**
|
||||
* State in the FSM for this connection.
|
||||
*/
|
||||
enum MHD_CONNECTION_STATE state;
|
||||
|
||||
/**
|
||||
* HTTP response code. Only valid if response object
|
||||
* is already set.
|
||||
*/
|
||||
unsigned int responseCode;
|
||||
/**
|
||||
* HTTP response code. Only valid if response object
|
||||
* is already set.
|
||||
*/
|
||||
unsigned int responseCode;
|
||||
|
||||
/**
|
||||
* Set to MHD_YES if the response's content reader
|
||||
* callback failed to provide data the last time
|
||||
* we tried to read from it. In that case, the
|
||||
* write socket should be marked as unready until
|
||||
* the CRC call succeeds.
|
||||
*/
|
||||
int response_unready;
|
||||
/**
|
||||
* Set to MHD_YES if the response's content reader
|
||||
* callback failed to provide data the last time
|
||||
* we tried to read from it. In that case, the
|
||||
* write socket should be marked as unready until
|
||||
* the CRC call succeeds.
|
||||
*/
|
||||
int response_unready;
|
||||
|
||||
/**
|
||||
* Are we sending with chunked encoding?
|
||||
*/
|
||||
int have_chunked_response;
|
||||
/**
|
||||
* Are we sending with chunked encoding?
|
||||
*/
|
||||
int have_chunked_response;
|
||||
|
||||
/**
|
||||
* Are we receiving with chunked encoding? This will be set to
|
||||
* MHD_YES after we parse the headers and are processing the body
|
||||
* with chunks. After we are done with the body and we are
|
||||
* processing the footers; once the footers are also done, this will
|
||||
* be set to MHD_NO again (before the final call to the handler).
|
||||
*/
|
||||
int have_chunked_upload;
|
||||
/**
|
||||
* Are we receiving with chunked encoding? This will be set to
|
||||
* MHD_YES after we parse the headers and are processing the body
|
||||
* with chunks. After we are done with the body and we are
|
||||
* processing the footers; once the footers are also done, this will
|
||||
* be set to MHD_NO again (before the final call to the handler).
|
||||
*/
|
||||
int have_chunked_upload;
|
||||
|
||||
/**
|
||||
* If we are receiving with chunked encoding, where are we right
|
||||
* now? Set to 0 if we are waiting to receive the chunk size;
|
||||
* otherwise, this is the size of the current chunk. A value of
|
||||
* zero is also used when we're at the end of the chunks.
|
||||
*/
|
||||
unsigned int current_chunk_size;
|
||||
/**
|
||||
* If we are receiving with chunked encoding, where are we right
|
||||
* now? Set to 0 if we are waiting to receive the chunk size;
|
||||
* otherwise, this is the size of the current chunk. A value of
|
||||
* zero is also used when we're at the end of the chunks.
|
||||
*/
|
||||
unsigned int current_chunk_size;
|
||||
|
||||
/**
|
||||
* If we are receiving with chunked encoding, where are we currently
|
||||
* with respect to the current chunk (at what offset / position)?
|
||||
*/
|
||||
unsigned int current_chunk_offset;
|
||||
|
||||
};
|
||||
/**
|
||||
* If we are receiving with chunked encoding, where are we currently
|
||||
* with respect to the current chunk (at what offset / position)?
|
||||
*/
|
||||
unsigned int current_chunk_offset;
|
||||
|
||||
};
|
||||
|
||||
typedef struct MHD_Connection MHD_Connection_t;
|
||||
|
||||
struct MHD_Daemon
|
||||
{
|
||||
{
|
||||
|
||||
/**
|
||||
* Callback function for all requests.
|
||||
*/
|
||||
MHD_AccessHandlerCallback default_handler;
|
||||
/**
|
||||
* Callback function for all requests.
|
||||
*/
|
||||
MHD_AccessHandlerCallback default_handler;
|
||||
|
||||
/**
|
||||
* Closure argument to default_handler.
|
||||
*/
|
||||
void *default_handler_cls;
|
||||
/**
|
||||
* Closure argument to default_handler.
|
||||
*/
|
||||
void *default_handler_cls;
|
||||
|
||||
/**
|
||||
* Linked list of our current connections.
|
||||
*/
|
||||
struct MHD_Connection *connections;
|
||||
/**
|
||||
* Linked list of our current connections.
|
||||
*/
|
||||
struct MHD_Connection *connections;
|
||||
|
||||
MHD_AcceptPolicyCallback apc;
|
||||
/**
|
||||
* Linked list of our current connections.
|
||||
*/
|
||||
// TODO switch to a dedicated tls connection struct
|
||||
struct MHD_Connection *tls_connections;
|
||||
|
||||
void *apc_cls;
|
||||
MHD_AcceptPolicyCallback apc;
|
||||
|
||||
MHD_RequestCompletedCallback notify_completed;
|
||||
void *apc_cls;
|
||||
|
||||
void *notify_completed_cls;
|
||||
MHD_RequestCompletedCallback notify_completed;
|
||||
|
||||
/**
|
||||
* PID of the select thread (if we have internal select)
|
||||
*/
|
||||
pthread_t pid;
|
||||
void *notify_completed_cls;
|
||||
|
||||
/**
|
||||
* Listen socket.
|
||||
*/
|
||||
int socket_fd;
|
||||
/**
|
||||
* PID of the select thread (if we have internal select)
|
||||
*/
|
||||
pthread_t pid;
|
||||
|
||||
/**
|
||||
* Are we shutting down?
|
||||
*/
|
||||
int shutdown;
|
||||
/**
|
||||
* Listen socket.
|
||||
*/
|
||||
int socket_fd;
|
||||
|
||||
/**
|
||||
* Size of the per-connection memory pools.
|
||||
*/
|
||||
unsigned int pool_size;
|
||||
/**
|
||||
* Are we shutting down?
|
||||
*/
|
||||
int shutdown;
|
||||
|
||||
/**
|
||||
* Limit on the number of parallel connections.
|
||||
*/
|
||||
unsigned int max_connections;
|
||||
/**
|
||||
* Size of the per-connection memory pools.
|
||||
*/
|
||||
unsigned int pool_size;
|
||||
|
||||
/**
|
||||
* After how many seconds of inactivity should
|
||||
* connections time out? Zero for no timeout.
|
||||
*/
|
||||
unsigned int connection_timeout;
|
||||
/**
|
||||
* Limit on the number of parallel connections.
|
||||
*/
|
||||
unsigned int max_connections;
|
||||
|
||||
/**
|
||||
* Maximum number of connections per IP, or 0 for
|
||||
* unlimited.
|
||||
*/
|
||||
unsigned int per_ip_connection_limit;
|
||||
/**
|
||||
* After how many seconds of inactivity should
|
||||
* connections time out? Zero for no timeout.
|
||||
*/
|
||||
unsigned int connection_timeout;
|
||||
|
||||
/**
|
||||
* Daemon's options.
|
||||
*/
|
||||
enum MHD_OPTION options;
|
||||
/**
|
||||
* Maximum number of connections per IP, or 0 for
|
||||
* unlimited.
|
||||
*/
|
||||
unsigned int per_ip_connection_limit;
|
||||
|
||||
/**
|
||||
* Listen port.
|
||||
*/
|
||||
unsigned short port;
|
||||
/**
|
||||
* Daemon's options.
|
||||
*/
|
||||
enum MHD_OPTION options;
|
||||
|
||||
};
|
||||
/**
|
||||
* Listen port.
|
||||
*/
|
||||
unsigned short port;
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,10 +1,16 @@
|
||||
SUBDIRS = .
|
||||
|
||||
INCLUDES = -I$(top_srcdir)/src/include
|
||||
AM_CPPFLAGS = -I$(top_srcdir)/src/include
|
||||
|
||||
# example programs
|
||||
|
||||
noinst_PROGRAMS = minimal_example querystring_example fileserver_example fileserver_example_external_select
|
||||
noinst_PROGRAMS = \
|
||||
https_server_example \
|
||||
https_echo_client_example \
|
||||
minimal_example \
|
||||
querystring_example \
|
||||
fileserver_example \
|
||||
fileserver_example_external_select
|
||||
|
||||
minimal_example_SOURCES = \
|
||||
minimal_example.c
|
||||
@@ -21,9 +27,29 @@ fileserver_example_SOURCES = \
|
||||
fileserver_example_LDADD = \
|
||||
$(top_builddir)/src/daemon/libmicrohttpd.la
|
||||
|
||||
|
||||
fileserver_example_external_select_SOURCES = \
|
||||
fileserver_example_external_select.c
|
||||
fileserver_example_external_select_LDADD = \
|
||||
$(top_builddir)/src/daemon/libmicrohttpd.la
|
||||
|
||||
https_server_example_CPPFLAGS = \
|
||||
$(GNUTLS_CPPFLAGS) \
|
||||
-I$(top_srcdir)/src/daemon
|
||||
https_server_example_SOURCES = \
|
||||
https_server_example.c
|
||||
https_server_example_LDADD = \
|
||||
$(top_builddir)/src/daemon/libmicrohttpd.la
|
||||
https_server_example_LDFLAGS = \
|
||||
-L$(GNUTLS_LIB_PATH) \
|
||||
-lgnutls
|
||||
|
||||
https_echo_client_example_CPPFLAGS = \
|
||||
$(GNUTLS_CPPFLAGS) \
|
||||
-I$(top_srcdir)/src/daemon
|
||||
https_echo_client_example_SOURCES = \
|
||||
https_echo_client_example.c
|
||||
https_echo_client_example_LDADD = \
|
||||
$(top_builddir)/src/daemon/libmicrohttpd.la
|
||||
https_echo_client_example_LDFLAGS = \
|
||||
-L$(GNUTLS_LIB_PATH) \
|
||||
-lgnutls
|
||||
|
||||
@@ -0,0 +1,153 @@
|
||||
/*
|
||||
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 https_echo_client.c
|
||||
* @brief a simple echo client to use in conjuction with the echo TLS server.
|
||||
* @author LV-426
|
||||
*/
|
||||
|
||||
#if HAVE_CONFIG_H
|
||||
# include <config.h>
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <unistd.h>
|
||||
#include <gnutls/gnutls.h>
|
||||
|
||||
#define MAX_BUF 1024
|
||||
#define SA struct sockaddr
|
||||
#define MSG "GET / HTTP/1.0\r\n\r\n"
|
||||
|
||||
extern int tcp_connect (void);
|
||||
extern void tcp_close (int sd);
|
||||
|
||||
int
|
||||
main (int argc, char **argv)
|
||||
{
|
||||
int ret, sd, ii, err;
|
||||
gnutls_session_t session;
|
||||
char buffer[MAX_BUF + 1];
|
||||
gnutls_anon_client_credentials_t anoncred;
|
||||
|
||||
struct sockaddr_in servaddr4;
|
||||
const struct sockaddr *servaddr;
|
||||
struct sockaddr_in sa;
|
||||
socklen_t addrlen;
|
||||
|
||||
if (argc < 2)
|
||||
{
|
||||
printf ("Usage : %s SERVER-PORT\n", argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
gnutls_global_init ();
|
||||
|
||||
gnutls_anon_allocate_client_credentials (&anoncred);
|
||||
|
||||
/* Initialize TLS session */
|
||||
gnutls_init (&session, GNUTLS_CLIENT);
|
||||
|
||||
/* Use default priorities */
|
||||
gnutls_priority_set_direct (session, "PERFORMANCE:+ANON-DH:!ARCFOUR-128",
|
||||
NULL);
|
||||
|
||||
/* put the anonymous credentials to the current session */
|
||||
gnutls_credentials_set (session, GNUTLS_CRD_ANON, anoncred);
|
||||
|
||||
sd = socket (AF_INET, SOCK_STREAM, 0);
|
||||
memset (&sa, '\0', sizeof (sa));
|
||||
sa.sin_family = AF_INET;
|
||||
sa.sin_port = htons (atoi (argv[1]));
|
||||
inet_pton (AF_INET, "127.0.0.1", &sa.sin_addr);
|
||||
|
||||
/* connect to the peer */
|
||||
err = connect (sd, (struct sockaddr *) &sa, sizeof (sa));
|
||||
if (err < 0)
|
||||
{
|
||||
fprintf (stderr, "Connect error\n");
|
||||
exit (1);
|
||||
}
|
||||
|
||||
gnutls_transport_set_ptr (session, (gnutls_transport_ptr_t) sd);
|
||||
|
||||
/* Perform the TLS handshake */
|
||||
ret = gnutls_handshake (session);
|
||||
|
||||
if (ret < 0)
|
||||
{
|
||||
fprintf (stderr, "*** Handshake failed\n");
|
||||
gnutls_perror (ret);
|
||||
goto end;
|
||||
}
|
||||
else
|
||||
{
|
||||
printf ("- Handshake was completed\n");
|
||||
}
|
||||
|
||||
for (;;)
|
||||
{
|
||||
/**/ scanf ("%s", buffer);
|
||||
|
||||
if (strcmp (buffer, "exit") == 0)
|
||||
{
|
||||
gnutls_record_send (session, buffer, strlen (MSG));
|
||||
break;
|
||||
}
|
||||
gnutls_record_send (session, buffer, strlen (MSG));
|
||||
|
||||
ret = gnutls_record_recv (session, buffer, MAX_BUF);
|
||||
if (ret == 0)
|
||||
{
|
||||
printf ("- Peer has closed the TLS connection\n");
|
||||
goto end;
|
||||
}
|
||||
else if (ret < 0)
|
||||
{
|
||||
fprintf (stderr, "*** Error: %s\n", gnutls_strerror (ret));
|
||||
break;
|
||||
}
|
||||
|
||||
printf ("- Received %d bytes: ", ret);
|
||||
for (ii = 0; ii < ret; ii++)
|
||||
{
|
||||
fputc (buffer[ii], stdout);
|
||||
}
|
||||
fputs ("\n", stdout);
|
||||
}
|
||||
|
||||
end:
|
||||
|
||||
shutdown (sd, SHUT_RDWR);
|
||||
close (sd);
|
||||
|
||||
gnutls_deinit (session);
|
||||
|
||||
gnutls_anon_free_client_credentials (anoncred);
|
||||
|
||||
gnutls_global_deinit ();
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,182 @@
|
||||
/*
|
||||
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 https_server_example.c
|
||||
* @brief a simple echo server using TLS. echo input from client until 'exit' message is received.
|
||||
* @author LV-426
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include <microhttpd.h>
|
||||
#include "internal.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#ifndef MINGW
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <gnutls/gnutls.h>
|
||||
|
||||
#define DH_BITS 1024
|
||||
#define MAX_BUF 1024
|
||||
/* server credintials */
|
||||
gnutls_anon_server_credentials_t anoncred;
|
||||
|
||||
/* server Diffie-Hellman parameters */
|
||||
static gnutls_dh_params_t dh_params;
|
||||
|
||||
|
||||
/* Generate Diffie Hellman parameters - for use with DHE kx algorithms. */
|
||||
static int
|
||||
generate_dh_params (void)
|
||||
{
|
||||
|
||||
gnutls_dh_params_init (&dh_params);
|
||||
gnutls_dh_params_generate2 (dh_params, DH_BITS);
|
||||
return 0;
|
||||
}
|
||||
|
||||
gnutls_session_t
|
||||
initialize_tls_session (void)
|
||||
{
|
||||
gnutls_session_t session;
|
||||
|
||||
gnutls_init (&session, GNUTLS_SERVER);
|
||||
|
||||
gnutls_priority_set_direct (session, "NORMAL:+ANON-DH", NULL);
|
||||
|
||||
gnutls_credentials_set (session, GNUTLS_CRD_ANON, anoncred);
|
||||
|
||||
gnutls_dh_set_prime_bits (session, DH_BITS);
|
||||
|
||||
return session;
|
||||
}
|
||||
|
||||
/* Accept Policy Callback */
|
||||
static int
|
||||
TLS_echo (void *cls,
|
||||
struct MHD_Connection *connection,
|
||||
const char *url,
|
||||
const char *method,
|
||||
const char *upload_data,
|
||||
const char *version, unsigned int *upload_data_size, void **ptr)
|
||||
{
|
||||
gnutls_session_t session;
|
||||
static int aptr;
|
||||
struct MHD_Response *response;
|
||||
char buffer[MAX_BUF + 1];
|
||||
int ret;
|
||||
|
||||
printf ("accepted connection from %d\n", connection->addr->sin_addr);
|
||||
|
||||
session = initialize_tls_session ();
|
||||
|
||||
gnutls_transport_set_ptr (session, connection->socket_fd);
|
||||
|
||||
ret = gnutls_handshake (session);
|
||||
if (ret < 0)
|
||||
{
|
||||
/* set connection as closed */
|
||||
connection->socket_fd = 1;
|
||||
gnutls_deinit (session);
|
||||
fprintf (stderr, "*** Handshake has failed (%s)\n\n",
|
||||
gnutls_strerror (ret));
|
||||
return MHD_NO;
|
||||
}
|
||||
|
||||
printf ("TLS Handshake completed\n");
|
||||
connection->state = MHDS_HANDSHAKE_COMPLETE;
|
||||
|
||||
/* simple echo loop. message encryption/decryption is acheived through 'gnutls_record_send'
|
||||
* & gnutls_record_recv calls. */
|
||||
for (;;)
|
||||
{
|
||||
memset (buffer, 0, MAX_BUF + 1);
|
||||
ret = gnutls_record_recv (session, buffer, MAX_BUF);
|
||||
|
||||
if (ret < 0)
|
||||
{
|
||||
fprintf (stderr, "\n*** Received corrupted "
|
||||
"data(%d). Closing the connection.\n\n", ret);
|
||||
break;
|
||||
}
|
||||
else if (ret >= 0)
|
||||
{
|
||||
if (strcmp (buffer, "exit") == 0)
|
||||
{
|
||||
printf ("\n- Peer has closed the GNUTLS connection\n");
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* echo data back to the client */
|
||||
gnutls_record_send (session, buffer, strlen (buffer));
|
||||
}
|
||||
}
|
||||
}
|
||||
printf ("\n");
|
||||
|
||||
/* mark connection as closed */
|
||||
connection->socket_fd = -1;
|
||||
|
||||
gnutls_deinit (session);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, char *const *argv)
|
||||
{
|
||||
struct MHD_Daemon *daemon;
|
||||
struct MHD_Daemon *TLS_daemon;
|
||||
|
||||
/* look for HTTPS port argument */
|
||||
if (argc < 4)
|
||||
{
|
||||
printf ("Usage : %s HTTP-PORT SECONDS-TO-RUN HTTPS-PORT\n", argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
gnutls_global_init ();
|
||||
|
||||
gnutls_anon_allocate_server_credentials (&anoncred);
|
||||
|
||||
generate_dh_params ();
|
||||
|
||||
gnutls_anon_set_server_dh_params (anoncred, dh_params);
|
||||
|
||||
TLS_daemon = MHD_start_daemon (MHD_USE_THREAD_PER_CONNECTION
|
||||
| MHD_USE_DEBUG | MHD_USE_SSL,
|
||||
atoi (argv[3]), NULL, NULL, &TLS_echo, NULL,
|
||||
MHD_OPTION_END);
|
||||
|
||||
if (TLS_daemon == NULL)
|
||||
return 1;
|
||||
sleep (atoi (argv[2]));
|
||||
|
||||
MHD_stop_daemon (daemon);
|
||||
|
||||
gnutls_anon_free_server_credentials (anoncred);
|
||||
|
||||
gnutls_global_deinit ();
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user