> From: Evgeny Shvidky [mailto:evg...@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.

Reply via email to