> From: Evgeny Shvidky [mailto:[email protected]]
> Sent: Wednesday, 17 October 2012 12:50 AM
> To: [email protected]
> 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.