Commit a48b7584 authored by Jeff Trawick's avatar Jeff Trawick
Browse files

Get lingering_close() working again.


git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@84951 13f79535-47bb-0310-9956-ffa450edef68
parent dd5c40b0
Loading
Loading
Loading
Loading
+48 −57
Changes for server/connection.c: 48 added lines, 57 removed lines.
Original line number Diff line number Diff line
@@ -75,9 +75,6 @@ HOOK_STRUCT(
IMPLEMENT_HOOK_VOID(pre_connection,(conn_rec *c),(c))
IMPLEMENT_HOOK_RUN_FIRST(int,process_connection,(conn_rec *c),(c),DECLINED)

/* TODO: reimplement the lingering close stuff */
#define NO_LINGCLOSE

/*
 * More machine-dependent networking gooo... on some systems,
 * you've got to be *really* sure that all the packets are acknowledged
@@ -126,70 +123,65 @@ static void sock_enable_linger(int s)

#ifndef NO_LINGCLOSE

/* Since many clients will abort a connection instead of closing it,
 * attempting to log an error message from this routine will only
 * confuse the webmaster.  There doesn't seem to be any portable way to
 * distinguish between a dropped connection and something that might be
 * worth logging.
/* we now proceed to read from the client until we get EOF, or until
 * MAX_SECS_TO_LINGER has passed.  the reasons for doing this are
 * documented in a draft:
 *
 * http://www.ics.uci.edu/pub/ietf/http/draft-ietf-http-connection-00.txt
 *
 * in a nutshell -- if we don't make this effort we risk causing
 * TCP RST packets to be sent which can tear down a connection before
 * all the response data has been sent to the client.
 */
static void lingering_close(request_rec *r)     

static void lingering_close(conn_rec *c)
{
  /*TODO remove the hardwired 512. This is an IO Buffer Size */
    char dummybuf[512];
    struct pollfd pd;
    int lsd;
    int max_wait;

    /* Prevent a slow-drip client from holding us here indefinitely */

    max_wait = 30;
    ap_bsetopt(r->connection->client, BO_TIMEOUT, &max_wait);
    ap_time_t start;
    ap_ssize_t nbytes;
    ap_status_t rc;
    int timeout;

    /* Send any leftover data to the client, but never try to again */

    if (ap_bflush(r->connection->client) != APR_SUCCESS) {
	ap_bclose(r->connection->client);
    if (ap_bflush(c->client) != APR_SUCCESS) {
        ap_bclose(c->client);
        return;
    }
    ap_bsetflag(r->connection->client, B_EOUT, 1);

    /* Close our half of the connection --- send the client a FIN */

    lsd = r->connection->client->fd;
    /* Shut down the socket for write, which will send a FIN
     * to the peer.
     */
    
    if ((shutdown(lsd, 1) != 0)  
        || ap_is_aborted(r->connection)) {
	ap_bclose(r->connection->client);
    if (ap_bshutdown(c->client, 1) != APR_SUCCESS
        || ap_is_aborted(c)) {
        ap_bclose(c->client);
        return;
    }

    /* Set up to wait for readable data on socket... */
    pd.fd = lsd;
    pd.events = POLLIN;

    /* Wait for readable data or error condition on socket;
     * slurp up any data that arrives...  We exit when we go for an
     * interval of tv length without getting any more data, get an error
     * from poll(), get an error or EOF on a read, or the timer expires.
     */
    /* We use a 2 second timeout because current (Feb 97) browsers
     * fail to close a connection after the server closes it.  Thus,
     * to avoid keeping the child busy, we are only lingering long enough
     * for a client that is actively sending data on a connection.
     * This should be sufficient unless the connection is massively
     * losing packets, in which case we might have missed the RST anyway.
     * These parameters are reset on each pass, since they might be
     * changed by poll.
    /* Read all data from the peer until we reach "end-of-file" (FIN
     * from peer) or we've exceeded our overall timeout.
     */
    do {
        pd.revents = 0;
    } while ((poll(&pd, 1, 2) == 1)   
             && read(lsd, dummybuf, sizeof(dummybuf)));
      /* && (time() = epoch) < max_wait); */    

    /* Should now have seen final ack.  Safe to finally kill socket */
    ap_bclose(r->connection->client);
    
    start = ap_now();
    timeout = MAX_SECS_TO_LINGER;
    for (;;) {
        ap_bsetopt(c->client, BO_TIMEOUT, &timeout);
        rc = ap_bread(c->client, dummybuf, sizeof(dummybuf),
                      &nbytes);
        if (rc != APR_SUCCESS || nbytes == 0) break;

        /* how much time has elapsed? */
        timeout = (int)((ap_now() - start) / AP_USEC_PER_SEC);
        if (timeout >= MAX_SECS_TO_LINGER) break;

        /* figure out the new timeout */
        timeout = MAX_SECS_TO_LINGER - timeout;
    }

    ap_bclose(c->client);
}

#endif /* ndef NO_LINGCLOSE */

CORE_EXPORT(void) ap_process_connection(conn_rec *c)
@@ -209,12 +201,11 @@ CORE_EXPORT(void) ap_process_connection(conn_rec *c)
#ifdef NO_LINGCLOSE
    ap_bclose(c->client);	/* just close it */
#else
    if (r && r->connection
	&& !r->connection->aborted
	&& r->connection->client
	&& (r->connection->client->fd >= 0)) {
    if (!c->aborted
	&& c->client
	/* && (c->client->fd >= 0) */ ) {

	lingering_close(r);
	lingering_close(c);
    }
    else {
	ap_bsetflag(c->client, B_EOUT, 1);
+28 −0
Changes for server/mpm/winnt/mpm_winnt.c: 28 added lines, 0 removed lines.
Original line number Diff line number Diff line
@@ -886,6 +886,20 @@ static int create_acceptex_context(ap_context_t *_pconf, ap_listen_rec *lr)
                         "create_acceptex_context: AcceptEx failed. Process will exit.");
            return -1;
        }
        
        /* SO_UPDATE_ACCEPT_CONTEXT is a Microsoft-ism which is required
         * if you want to use more than several key socket calls on a
         * socket initialized via AcceptEx().  In particular, it is
         * required for shutdown() to work.
         */

        if (setsockopt(context->accept_socket, SOL_SOCKET,
                       SO_UPDATE_ACCEPT_CONTEXT, (char *)&nsd,
                       sizeof(nsd))) {
            ap_log_error(APLOG_MARK, APLOG_ERR, WSAGetLastError(), server_conf,
                         "setsockopt(SO_UPDATE_ACCEPT_CONTEXT) failed.");
        }

    }
    lr->count++;

@@ -926,6 +940,20 @@ static ap_inline int reset_acceptex_context(PCOMP_CONTEXT context)
                         "reset_acceptex_context: AcceptEx failed. Leaving the process running.");
            return -1;
        }
        
        /* SO_UPDATE_ACCEPT_CONTEXT is a Microsoft-ism which is required
         * if you want to use more than several key socket calls on a
         * socket initialized via AcceptEx().  In particular, it is
         * required for shutdown() to work.
         */

        if (setsockopt(context->accept_socket, SOL_SOCKET,
                       SO_UPDATE_ACCEPT_CONTEXT, (char *)&nsd,
                       sizeof(nsd))) {
            ap_log_error(APLOG_MARK, APLOG_WARNING, WSAGetLastError(),
                         server_conf,
                         "setsockopt(SO_UPDATE_ACCEPT_CONTEXT) failed.");
        }
    }

    return 0;