blob: a92245246586ed9c627d845d9d3b8536d5ae04a3 [file] [log] [blame]
/*
This file is part of libmicrohttpd
Copyright (C) 2007-2018 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 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
*/
/**
* @file lib/connection_cleanup.c
* @brief function to clean up completed connections
* @author Christian Grothoff
*/
#include "internal.h"
#include "connection_cleanup.h"
#include "daemon_ip_limit.h"
#ifdef UPGRADE_SUPPORT
/**
* Finally cleanup upgrade-related resources. It should
* be called when TLS buffers have been drained and
* application signaled MHD by #MHD_UPGRADE_ACTION_CLOSE.
*
* @param connection handle to the upgraded connection to clean
*/
static void
connection_cleanup_upgraded (struct MHD_Connection *connection)
{
struct MHD_UpgradeResponseHandle *urh = connection->request.urh;
if (NULL == urh)
return;
#ifdef HTTPS_SUPPORT
/* Signal remote client the end of TLS connection by
* gracefully closing TLS session. */
{
struct MHD_TLS_Plugin *tls;
if (NULL != (tls = connection->daemon->tls_api))
(void) tls->shutdown_connection (tls->cls,
connection->tls_cs);
}
if (MHD_INVALID_SOCKET != urh->mhd.socket)
MHD_socket_close_chk_ (urh->mhd.socket);
if (MHD_INVALID_SOCKET != urh->app.socket)
MHD_socket_close_chk_ (urh->app.socket);
#endif /* HTTPS_SUPPORT */
connection->request.urh = NULL;
free (urh);
}
#endif /* UPGRADE_SUPPORT */
/**
* Free resources associated with all closed connections. (destroy
* responses, free buffers, etc.). All closed connections are kept in
* the "cleanup" doubly-linked list.
*
* @remark To be called only from thread that process daemon's
* select()/poll()/etc.
*
* @param daemon daemon to clean up
*/
void
MHD_connection_cleanup_ (struct MHD_Daemon *daemon)
{
struct MHD_Connection *pos;
MHD_mutex_lock_chk_ (&daemon->cleanup_connection_mutex);
while (NULL != (pos = daemon->cleanup_tail))
{
DLL_remove (daemon->cleanup_head,
daemon->cleanup_tail,
pos);
MHD_mutex_unlock_chk_ (&daemon->cleanup_connection_mutex);
if ( (MHD_TM_THREAD_PER_CONNECTION == daemon->threading_mode) &&
(! pos->thread_joined) &&
(! MHD_join_thread_ (pos->pid.handle)) )
MHD_PANIC (_ ("Failed to join a thread.\n"));
#ifdef UPGRADE_SUPPORT
connection_cleanup_upgraded (pos);
#endif /* UPGRADE_SUPPORT */
MHD_pool_destroy (pos->pool);
#ifdef HTTPS_SUPPORT
{
struct MHD_TLS_Plugin *tls;
if (NULL != (tls = daemon->tls_api))
tls->teardown_connection (tls->cls,
pos->tls_cs);
}
#endif /* HTTPS_SUPPORT */
/* clean up the connection */
if (NULL != daemon->notify_connection_cb)
daemon->notify_connection_cb (daemon->notify_connection_cb_cls,
pos,
MHD_CONNECTION_NOTIFY_CLOSED);
MHD_ip_limit_del (daemon,
(const struct sockaddr *) &pos->addr,
pos->addr_len);
#ifdef EPOLL_SUPPORT
if (MHD_ELS_EPOLL == daemon->event_loop_syscall)
{
if (0 != (pos->epoll_state & MHD_EPOLL_STATE_IN_EREADY_EDLL))
{
EDLL_remove (daemon->eready_head,
daemon->eready_tail,
pos);
pos->epoll_state &= ~MHD_EPOLL_STATE_IN_EREADY_EDLL;
}
if ( (-1 != daemon->epoll_fd) &&
(0 != (pos->epoll_state & MHD_EPOLL_STATE_IN_EPOLL_SET)) )
{
/* epoll documentation suggests that closing a FD
automatically removes it from the epoll set; however,
this is not true as if we fail to do manually remove it,
we are still seeing an event for this fd in epoll,
causing grief (use-after-free...) --- at least on my
system. */if (0 != epoll_ctl (daemon->epoll_fd,
EPOLL_CTL_DEL,
pos->socket_fd,
NULL))
MHD_PANIC (_ ("Failed to remove FD from epoll set.\n"));
pos->epoll_state &= ~MHD_EPOLL_STATE_IN_EPOLL_SET;
}
}
#endif
if (NULL != pos->request.response)
{
MHD_response_queue_for_destroy (pos->request.response);
pos->request.response = NULL;
}
if (MHD_INVALID_SOCKET != pos->socket_fd)
MHD_socket_close_chk_ (pos->socket_fd);
free (pos);
MHD_mutex_lock_chk_ (&daemon->cleanup_connection_mutex);
daemon->connections--;
daemon->at_limit = false;
}
MHD_mutex_unlock_chk_ (&daemon->cleanup_connection_mutex);
}
/* end of connection_cleanup.c */