httpd-modules-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Tony Abo <t...@hitech.com>
Subject RE: "Close" HTTP connection callback/hook
Date Sun, 21 Oct 2012 23:47:43 GMT
> From: Evgeny Shvidky [mailto:evgeny@skyfence.com]
> Sent: Wednesday, 17 October 2012 12:50 AM
> To: modules-dev@httpd.apache.org
> Subject: "Close" HTTP connection callback/hook
> 
> Hi,
> 
> I am implementing a new module on C.
> I need to perform some functionality when a user closes a HTTP
> connection before he received any response for his request.
> How can I know when a HTTP user request state has been changed/closed?
> Is there any callback/hook for this functionality I can register?
> 
> Thanks,
> Evgeny

Hi, I am usually the one asking questions, but I think I have had and solved a similar problem
to yours.

I have a module with a handler that sometimes does a lot of processing before sending a response.
It needs to check periodically whether the browser connection is still there so that it can
stop the processing (and cleanup a bunch of resources) if the user gives up. 

The problem I found is that tcp/ip sockets are not particularly proactive in telling you that
the other end has closed the connection. I found that the only reliable way to test a socket
is to attempt to read from it. (Yes, I have found that send() can appear to succeed even though
the remote end has already closed the socket.) So in fact, once apache has read the request
successfully, it will not even know if the connection has been closed because it will not
be trying to read the socket anymore (or in fact do anything with the socket until the handler
begins to send a response).

What I found useful is doing a recv() with the MSG_PEEK flag on the socket. That seems to
always detect the closed socket and return an error. It is good because it does not do anything
to change the state of the socket if it is still open.

The next problem is finding the actual socket to test. By the time my handler is running,
the socket is buried in apache's data structures where it is difficult to find reliably. The
solution (suggested by the gurus on this list) was to use the pre_connection hook to get the
socket of each new connection and store in a place that the handler can find it. So basically:

ap_hook_pre_connection(PreConnection, NULL, NULL, APR_HOOK_MIDDLE);

...

static int PreConnection(conn_rec *c, void *csd)
{
   apr_os_sock_t *p_os_fd = apr_palloc( c->pool, sizeof(apr_os_sock_t) );

   /* Retrieve the new connection's socket number */
   apr_os_sock_get(p_os_fd, (apr_socket_t *)csd);

   /* Store it for CheckConnected */
   ap_set_module_config( c->conn_config, &my_module, p_os_fd );

   return OK;
}

/* This is called by the handler periodically to see of the browser is still waiting */
Static int CheckConnected( request_rec *r )
{
   int *pnSocket;
   char acBuffer[1];
   int nResult;

   /* connected socket was conveniently stored away in out conn_configuration in pre_connection
*/
   pnSocket = ap_get_module_config( r->connection->conn_config, &my_module );

   nResult = recv( *pnSocket, acBuffer, 1, MSG_PEEK );
   return nResult > 0 || (nResult == -1 && errno == EAGAIN);
}

So, if the socket is still connected, recv() will return either some number of bytes that
can be read, or an EAGAIN error indicating that no data is available. Any other error implies
that there was a problem reading the socket, which I construe as meaning that the socket is
closed. This now seems to be working reliably on several Unix and Linux platforms, with Apache
2.0.x and 2.2.x servers.

I hope that you find this helpful.


Mime
View raw message