httpd-cvs mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From dgau...@hyperreal.org
Subject cvs commit: apache-2.0/mpm/src/modules/mpm/prefork prefork.c
Date Thu, 24 Jun 1999 07:29:34 GMT
dgaudet     99/06/24 00:29:33

  Modified:    mpm/src/include buff.h
               mpm/src/main buff.c http_protocol.c iol_unix.c
               mpm/src/modules/mpm/prefork prefork.c
  Log:
  new-fangled BUFF... this could easily be broken, but hey, the one that
  was in here before was broken in different ways... I've served up a few
  pages with this one.
  
  Revision  Changes    Path
  1.4       +38 -6     apache-2.0/mpm/src/include/buff.h
  
  Index: buff.h
  ===================================================================
  RCS file: /home/cvs/apache-2.0/mpm/src/include/buff.h,v
  retrieving revision 1.3
  retrieving revision 1.4
  diff -u -r1.3 -r1.4
  --- buff.h	1999/06/18 23:34:58	1.3
  +++ buff.h	1999/06/24 07:29:29	1.4
  @@ -65,6 +65,37 @@
   #include <stdarg.h>
   #include "ap_iol.h"
   
  +/*
  +    A BUFF is an i/o object which can be used in any of the following
  +    output modes:
  +
  +    blocking, buffered
  +    blocking, buffered, HTTP-chunked
  +    blocking, unbuffered
  +    blocking, unbuffered, HTTP-chunked
  +    non-blocking, unbuffered
  +    non-blocking, unbuffered, HTTP-chunked
  +
  +    In all the blocking modes, a bwrite(fb, buf, len) will return less
  +    than len only in an error state.  The error may be deferred until
  +    the next use of fb.
  +
  +    In the non-blocking, chunked mode, the caller of bwrite() makes a
  +    guarantee that if a partial write occurs, they will call back later
  +    with at least as many bytes to write -- prior to disabling chunking.
  +    This is a protocol correctness requirement -- the chunk length may
  +    already have hit the wire, and is in essence "committed".
  +
  +    bputc, bputs, bvputs, and bprintf are supported only in the buffered
  +    modes.
  +
  +    The following input modes are supported:
  +
  +    blocking, buffered
  +    blocking, unbuffered
  +    non-blocking, unbuffered
  +*/
  +
   /* Reading is buffered */
   #define B_RD     (1)
   /* Writing is buffered */
  @@ -82,16 +113,12 @@
   #undef B_ERROR
   #endif
   #define B_ERROR (48)
  -/* TODO: implement chunked encoding as a layer */
  +/* Use chunked writing */
  +#define B_CHUNK (64)
   /* bflush() if a read would block */
   /* TODO: #define B_SAFEREAD (128) */
  -/* buffer is a socket */
  -#define B_SOCKET (256)
  -
   /* caller expects non-blocking behaviour */
   #define B_NONBLOCK (512)
  -/* non-blocking bit set on fd */
  -#define B_NONBLOCK_SET (1024)
   
   /* TODO: implement a ebcdic/ascii conversion layers */
   
  @@ -103,10 +130,15 @@
       unsigned char *inptr;	/* pointer to next location to read */
       int incnt;			/* number of bytes left to read from input buffer;
   				 * always 0 if had a read error  */
  +    int outchunk;               /* location of chunk header when chunking */
       int outcnt;			/* number of byte put in output buffer */
       unsigned char *inbase;
       unsigned char *outbase;
       int bufsiz;
  +    int chunk_overcommit;	/* when we start a chunk and get a partial write we
  +				 * keep track of the #remaining bytes in the chunk
  +				 * here
  +				 */
       void (*error) (BUFF *fb, int op, void *data);
       void *error_data;
       long int bytes_sent;	/* number of bytes actually written */
  
  
  
  1.5       +266 -143  apache-2.0/mpm/src/main/buff.c
  
  Index: buff.c
  ===================================================================
  RCS file: /home/cvs/apache-2.0/mpm/src/main/buff.c,v
  retrieving revision 1.4
  retrieving revision 1.5
  diff -u -r1.4 -r1.5
  --- buff.c	1999/06/20 12:25:54	1.4
  +++ buff.c	1999/06/24 07:29:31	1.5
  @@ -76,6 +76,17 @@
   #define DEFAULT_BUFSIZE (4096)
   #endif
   
  +/* the maximum size of any chunk */
  +#ifndef MAX_CHUNK_SIZE
  +#define MAX_CHUNK_SIZE (0x8000)
  +#endif
  +
  +/* This must be enough to represent MAX_CHUNK_SIZE in hex,
  + * plus two extra characters.
  + */
  +#ifndef CHUNK_HEADER_SIZE
  +#define CHUNK_HEADER_SIZE (6)
  +#endif
   
   /* bwrite()s of greater than this size can result in a large_write() call,
    * which can result in a writev().  It's a little more work to set up the
  @@ -88,7 +99,6 @@
   #define LARGE_WRITE_THRESHOLD 31
   #endif
   
  -
   /*
    * Buffered I/O routines.
    * These are a replacement for the stdio routines.
  @@ -136,15 +146,18 @@
       fb = ap_palloc(p, sizeof(BUFF));
       fb->pool = p;
       fb->bufsiz = DEFAULT_BUFSIZE;
  -    fb->flags = flags & (B_RDWR | B_SOCKET);
  +    fb->flags = flags & B_RDWR;
   
       if (flags & B_RD)
   	fb->inbase = ap_palloc(p, fb->bufsiz);
       else
   	fb->inbase = NULL;
   
  +    /* overallocate so that we can put a chunk trailer of CRLF into this
  +     * buffer... and possibly the beginning of a new chunk
  +     */
       if (flags & B_WR)
  -	fb->outbase = ap_palloc(p, fb->bufsiz);
  +	fb->outbase = ap_palloc(p, fb->bufsiz + 2 + CHUNK_HEADER_SIZE + 1);
       else
   	fb->outbase = NULL;
   
  @@ -152,8 +165,10 @@
   
       fb->incnt = 0;
       fb->outcnt = 0;
  +    fb->outchunk = -1;
       fb->error = NULL;
       fb->bytes_sent = 0;
  +    fb->chunk_overcommit = 0;
   
       return fb;
   }
  @@ -183,6 +198,11 @@
   	return 0;
   
       case BO_TIMEOUT:
  +	fb->flags &= ~B_NONBLOCK;
  +	if (optval == 0) {
  +	    fb->flags |= B_NONBLOCK;
  +	    /* XXX: should remove B_WR now... */
  +	}
   	return iol_setopt(&fb->iol, AP_IOL_TIMEOUT, optval);
       }
       errno = EINVAL;
  @@ -207,19 +227,82 @@
       errno = EINVAL;
       return -1;
   }
  +
  +static void start_chunk(BUFF *fb)
  +{
  +    fb->outchunk = fb->outcnt;
  +    fb->outcnt += CHUNK_HEADER_SIZE;
  +}
  +
  +static int end_chunk(BUFF *fb, int extra)
  +{
  +    int i;
  +    unsigned char *strp;
  +    int chunk_size;
  +
  +    chunk_size = fb->outcnt - fb->outchunk - CHUNK_HEADER_SIZE + extra;
  +    if (chunk_size == 0) {
  +        /* nothing was written into this chunk, and we can't write a 0 size
  +         * chunk because that signifies EOF, so just erase it
  +         */
  +        fb->outcnt = fb->outchunk;
  +        fb->outchunk = -1;
  +        return 0;
  +    }
  +
  +    if (chunk_size > MAX_CHUNK_SIZE) {
  +	extra -= chunk_size - MAX_CHUNK_SIZE;
  +	chunk_size = MAX_CHUNK_SIZE;
  +    }
  +
  +    /* we know this will fit because of how we wrote it in start_chunk() */
  +    i = ap_snprintf((char *) &fb->outbase[fb->outchunk], CHUNK_HEADER_SIZE,
  +                "%x", chunk_size);
  +
  +    /* we may have to tack some trailing spaces onto the number we just wrote
  +     * in case it was smaller than our estimated size.  We've also written
  +     * a \0 into the buffer with ap_snprintf so we might have to put a
  +     * \r back in.
  +     */
  +    strp = &fb->outbase[fb->outchunk + i];
  +    while (i < CHUNK_HEADER_SIZE - 2) {
  +        *strp++ = ' ';
  +        ++i;
  +    }
  +    *strp++ = '\015';
  +    *strp = '\012';
  +
  +    /* tack on the trailing CRLF, we've reserved room for this */
  +    fb->outbase[fb->outcnt++] = '\015';
  +    fb->outbase[fb->outcnt++] = '\012';
  +
  +    fb->outchunk = -1;
  +    
  +    return extra;
  +}
   
  -static int bflush_core(BUFF *fb);
   
  +
   /*
    * Set a flag on (1) or off (0).
    */
   API_EXPORT(int) ap_bsetflag(BUFF *fb, int flag, int value)
   {
  +    int old_flags = fb->flags;
  +
       if (value) {
   	fb->flags |= flag;
  +	/* start chunking if we haven't already */
  +        if ((flag ^ old_flags) & B_CHUNK) {
  +            start_chunk(fb);
  +        }
       }
       else {
   	fb->flags &= ~flag;
  +	/* stop chunking if we haven't already */
  +        if ((flag ^ old_flags) & B_CHUNK) {
  +            end_chunk(fb, 0);
  +        }
       }
       return value;
   }
  @@ -236,7 +319,7 @@
       }
       else if (rv == -1) {
   	fb->saved_errno = errno;
  -	if (errno != EWOULDBLOCK) {
  +	if (errno != EAGAIN) {
   	    doerror(fb, B_RD);
   	}
       }
  @@ -443,14 +526,14 @@
   	return buf[0];
   }
   
  -/* A wrapper for write which deals with error conditions and
  +/* A wrapper for writev which deals with error conditions and
    * bytes_sent.
    */
  -static int write_with_errors(BUFF *fb, const void *buf, int nbyte)
  +static int writev_with_errors(BUFF *fb, const struct iovec *vec, int nvec)
   {
       int rv;
   
  -    rv = iol_write(&fb->iol, buf, nbyte);
  +    rv = iol_writev(&fb->iol, vec, nvec);
       if (rv == -1) {
   	fb->saved_errno = errno;
   	if (errno != EAGAIN) {
  @@ -458,22 +541,144 @@
   	}
   	return -1;
       }
  -    else if (rv == 0) {
  -	errno = EAGAIN;
  -	return -1;
  -    }
       fb->bytes_sent += rv;
       return rv;
   }
   
  -/* A wrapper for writev which deals with error conditions and
  +
  +static int writev_it_all(BUFF *fb, struct iovec *vec, int nvec)
  +{
  +    int i;
  +    int rv;
  +    int total;
  +
  +    i = 0;
  +    total = 0;
  +    while (i < nvec) {
  +	rv = writev_with_errors(fb, vec, 2);
  +	if (rv < 0) {
  +	    return total ? -1 : total;
  +	}
  +	total += rv;
  +	if (fb->flags & B_NONBLOCK) {
  +	    return total;
  +	}
  +	/* recalculate to deal with partial writes */
  +	while (rv > 0) {
  +            if (rv < vec[i].iov_len) {
  +                vec[i].iov_base = (char *) vec[i].iov_base + rv;
  +                vec[i].iov_len -= rv;
  +                break;
  +            }
  +            else {
  +                rv -= vec[i].iov_len;
  +                ++i;
  +            }
  +        }
  +    }
  +    return total;
  +}
  +
  +
  +/* write the contents of fb->outbase, and buf,
  +   stop at first partial write for a non-blocking buff
  +
  +   return number of bytes of buf which were written
  +   -1 for errors
  +*/
  +static int large_write(BUFF *fb, const char *buf, int nbyte)
  +{
  +    struct iovec vec[2];
  +    int nvec;
  +    int rv;
  +
  +    ap_assert(nbyte > 0);
  +    if (fb->outcnt) {
  +	vec[0].iov_base = fb->outbase;
  +	vec[0].iov_len = fb->outcnt;
  +	vec[1].iov_base = (void *)buf;
  +	vec[1].iov_len = nbyte;
  +	nvec = 2;
  +    }
  +    else {
  +	vec[0].iov_base = (void *)buf;
  +	vec[0].iov_len = nbyte;
  +	nvec = 1;
  +    }
  +    rv = writev_it_all(fb, vec, nvec);
  +    if (rv <= 0) {
  +	return rv;
  +    }
  +    if (rv < fb->outcnt) {
  +	/* shift bytes forward in buffer */
  +	memmove(fb->outbase, fb->outbase + rv, fb->outcnt - rv);
  +	fb->outcnt -= rv;
  +	return 0;
  +    }
  +    rv -= fb->outcnt;
  +    fb->outcnt = 0;
  +    return rv;
  +}
  +
  +
  +static int large_write_chunk(BUFF *fb, const char *buf, int nbyte)
  +{
  +    int rv;
  +    int amt;
  +    int total;
  +
  +    ap_assert(nbyte > 0);
  +    if (fb->chunk_overcommit) {
  +	amt = nbyte > fb->chunk_overcommit ? fb->chunk_overcommit : nbyte;
  +	rv = large_write(fb, buf, amt);
  +	if (rv <= 0) {
  +	    return rv;
  +	}
  +	fb->chunk_overcommit -= rv;
  +	if (fb->chunk_overcommit == 0) {
  +	    fb->outbase[0] = '\015';
  +	    fb->outbase[1] = '\012';
  +	    fb->outcnt = 2;
  +	    start_chunk(fb);
  +	}
  +	if (rv < amt || amt == nbyte) {
  +	    return rv;
  +	}
  +	nbyte -= rv;
  +	buf += rv;
  +    }
  +    ap_assert(fb->chunk_overcommit == 0 && fb->outchunk != -1);
  +    total = 0;
  +    do {
  +	amt = end_chunk(fb, nbyte);
  +	rv = large_write(fb, buf, amt);
  +	if (rv < amt) {
  +	    if (rv < 0) {
  +		fb->chunk_overcommit = amt;
  +		return total ? total : -1;
  +	    }
  +	    fb->chunk_overcommit = amt - rv;
  +	    return total + rv;
  +	}
  +	fb->outbase[0] = '\015';
  +	fb->outbase[1] = '\012';
  +	fb->outcnt = 2;
  +	start_chunk(fb);
  +	nbyte -= amt;
  +	buf += amt;
  +	total += amt;
  +    } while (nbyte);
  +    return total;
  +}
  +
  +/* A wrapper for write which deals with error conditions and
    * bytes_sent.
    */
  -static int writev_with_errors(BUFF *fb, const struct iovec *vec, int nvec)
  +static int write_with_errors(BUFF *fb, const void *buf, int nbyte)
   {
       int rv;
   
  -    rv = iol_writev(&fb->iol, vec, nvec);
  +    rv = iol_write(&fb->iol, buf, nbyte);
       if (rv == -1) {
   	fb->saved_errno = errno;
   	if (errno != EAGAIN) {
  @@ -481,42 +686,36 @@
   	}
   	return -1;
       }
  -    else if (rv == 0) {
  -	errno = EAGAIN;
  -	return -1;
  -    }
       fb->bytes_sent += rv;
       return rv;
   }
   
   
  -/*
  - * Used to combine the contents of the fb buffer, and a large buffer
  - * passed in.  The return code is how many bytes of buf were written,
  - * or -1.
  - */
  -static int large_write(BUFF *fb, const void *buf, int nbyte)
  +static int bflush_core(BUFF *fb)
   {
  -    struct iovec vec[2];
  +    int total;
       int rv;
   
  -    vec[0].iov_base = (void *) fb->outbase;
  -    vec[0].iov_len = fb->outcnt;
  -    vec[1].iov_base = (void *) buf;
  -    vec[1].iov_len = nbyte;
  -    rv = writev_with_errors(fb, vec, 2);
  -    if (rv >= fb->outcnt) {
  -	rv -= fb->outcnt;
  -	fb->outcnt = 0;
  -	return rv;
  +    if (fb->flags & B_CHUNK) {
  +	end_chunk(fb, 0);
       }
  -    else if (rv > 0) {
  -	/* shift bytes forward in buffer */
  -	memmove(fb->outbase, fb->outbase + rv, fb->outcnt - rv);
  +    total = 0;
  +    while (fb->outcnt > 0) {
  +	rv = write_with_errors(fb, fb->outbase + total, fb->outcnt);
  +	if (rv <= 0) {
  +	    if (total) {
  +		memmove(fb->outbase, fb->outbase + total, fb->outcnt);
  +		return total;
  +	    }
  +	    return -1;
  +	}
   	fb->outcnt -= rv;
  -	return 0;
  +	total += rv;
       }
  -    return rv;
  +    if (fb->flags & B_CHUNK) {
  +	start_chunk(fb);
  +    }
  +    return total;
   }
   
   
  @@ -529,7 +728,8 @@
    */
   API_EXPORT(int) ap_bwrite(BUFF *fb, const void *buf, int nbyte)
   {
  -    int i, nwr;
  +    int amt;
  +    int total;
   
       if (fb->flags & (B_WRERR | B_EOUT)) {
   	errno = fb->saved_errno;
  @@ -538,10 +738,6 @@
       if (nbyte == 0)
   	return 0;
   
  -    if (!(fb->flags & B_WR)) {
  -	return write_with_errors(fb, buf, nbyte);
  -    }
  -
   /*
    * Detect case where we're asked to write a large buffer, and combine our
    * current buffer with it in a single writev().  Note we don't consider
  @@ -549,104 +745,31 @@
    * us to use writev() too frequently.  In those cases we really should just
    * start a new buffer.
    */
  -    if (fb->outcnt > 0 && nbyte > LARGE_WRITE_THRESHOLD
  -	&& nbyte + fb->outcnt >= fb->bufsiz) {
  -	int n=large_write(fb, buf, nbyte);
  -	if (n == nbyte)
  -	    return nbyte;
  -	buf+=n;
  -	nbyte-=n;
  -    }
  -
  -/*
  - * Whilst there is data in the buffer, keep on adding to it and writing it
  - * out
  - */
  -    nwr = 0;
  -    while (fb->outcnt > 0) {
  -/* can we accept some data? */
  -	i = fb->bufsiz - fb->outcnt;
  -	if (i > 0) {
  -	    if (i > nbyte)
  -		i = nbyte;
  -	    memcpy(fb->outbase + fb->outcnt, buf, i);
  -	    fb->outcnt += i;
  -	    nbyte -= i;
  -	    buf = i + (const char *) buf;
  -	    nwr += i;
  -	    if (nbyte == 0)
  -		return nwr;	/* return if none left */
  -	}
  -
  -/* the buffer must be full */
  -	i = write_with_errors(fb, fb->outbase, fb->outcnt);
  -	if (i <= 0) {
  -	    return nwr ? nwr : -1;
  -	}
  -
  -	/* deal with a partial write */
  -	if (i < fb->outcnt) {
  -	    memmove(fb->outbase, fb->outbase + i, fb->outcnt - i);
  -	    fb->outcnt -= i;
  -	}
  -	else
  -	    fb->outcnt = 0;
  -
  -	if (fb->flags & B_EOUT)
  -	    return -1;
  -    }
  -/* we have emptied the file buffer. Now try to write the data from the
  - * original buffer until there is less than bufsiz left.
  - */
  -    while (nbyte >= fb->bufsiz) {
  -	i = write_with_errors(fb, buf, nbyte);
  -	if (i <= 0) {
  -	    return nwr ? nwr : -1;
  -	}
  -
  -	buf = i + (const char *) buf;
  -	nwr += i;
  -	nbyte -= i;
  -
  -	if (fb->flags & B_EOUT)
  -	    return -1;
  -    }
  -/* copy what's left to the file buffer */
  -    /* assert(fb->outcnt == 0); */
  -    if (nbyte > 0)
  -	memcpy(fb->outbase, buf, nbyte);
  -    fb->outcnt = nbyte;
  -    nwr += nbyte;
  -    return nwr;
  -}
  -
  -
  -static int bflush_core(BUFF *fb)
  -{
  -    int i;
  -
  -    while (fb->outcnt > 0) {
  -	i = write_with_errors(fb, fb->outbase, fb->outcnt);
  -	if (i <= 0)
  -	    return -1;
  -
  -	/*
  -	 * We should have written all the data, but if the fd was in a
  -	 * strange (non-blocking) mode, then we might not have done so.
  -	 */
  -	if (i < fb->outcnt) {
  -	    memmove(fb->outbase, fb->outbase + i, fb->outcnt - i);
  -	}
  -	fb->outcnt -= i;
  -
  -	/* If a soft timeout occurs while flushing, the handler should
  -	 * have set the buffer flag B_EOUT.
  -	 */
  -	if (fb->flags & B_EOUT)
  -	    return -1;
  -    }
  -
  -    return 0;
  +    if (!(fb->flags & B_WR)
  +	|| (nbyte > LARGE_WRITE_THRESHOLD && nbyte + fb->outcnt >= fb->bufsiz))
{
  +	if (fb->flags & B_CHUNK) {
  +	    return large_write_chunk(fb, buf, nbyte);
  +	}
  +	return large_write(fb, buf, nbyte);
  +    }
  +
  +    /* at this point we know that nbyte < fb->bufsize */
  +    amt = fb->bufsiz - fb->outcnt;
  +    total = 0;
  +    if (nbyte > amt) {
  +	memcpy(fb->outbase + fb->outcnt, buf, amt);
  +	fb->outcnt += amt;
  +	buf = (const char *) buf + amt;
  +	nbyte -= amt;
  +	if (bflush_core(fb) < amt) {
  +	    return amt;
  +	}
  +	total = amt;
  +    }
  +    /* now we know that nbyte < fb->bufsiz */
  +    memcpy(fb->outbase + fb->outcnt, buf, nbyte);
  +    fb->outcnt += nbyte;
  +    return total + nbyte;
   }
   
   /*
  
  
  
  1.6       +0 -4      apache-2.0/mpm/src/main/http_protocol.c
  
  Index: http_protocol.c
  ===================================================================
  RCS file: /home/cvs/apache-2.0/mpm/src/main/http_protocol.c,v
  retrieving revision 1.5
  retrieving revision 1.6
  diff -u -r1.5 -r1.6
  --- http_protocol.c	1999/06/20 11:19:46	1.5
  +++ http_protocol.c	1999/06/24 07:29:31	1.6
  @@ -1551,10 +1551,8 @@
       r->sent_bodyct = 1;         /* Whatever follows is real body stuff... */
   
       /* Set buffer flags for the body */
  -#if 0	/* TODO: implemented chunked layer */
       if (r->chunked)
           ap_bsetflag(r->connection->client, B_CHUNK, 1);
  -#endif
   #ifdef CHARSET_EBCDIC
       if (!convert)
           ap_bsetflag(r->connection->client, B_EBCDIC2ASCII, convert);
  @@ -1573,9 +1571,7 @@
            * Turn off chunked encoding --- we can only do this once.
            */
           r->chunked = 0;
  -#if 0	/* TODO: implemented chunked layer */
           ap_bsetflag(r->connection->client, B_CHUNK, 0);
  -#endif
   
           ap_rputs("0\015\012", r);
           /* If we had footer "headers", we'd send them now */
  
  
  
  1.5       +1 -1      apache-2.0/mpm/src/main/iol_unix.c
  
  Index: iol_unix.c
  ===================================================================
  RCS file: /home/cvs/apache-2.0/mpm/src/main/iol_unix.c,v
  retrieving revision 1.4
  retrieving revision 1.5
  diff -u -r1.4 -r1.5
  --- iol_unix.c	1999/06/19 20:40:17	1.4
  +++ iol_unix.c	1999/06/24 07:29:31	1.5
  @@ -164,7 +164,7 @@
   	    if (set_nonblock(fd->fd)) { \
   		return -1; \
   	    } \
  -	    fd->flags |= B_NONBLOCK_SET; \
  +	    fd->flags |= FD_NONBLOCKING_SET; \
   	} \
    \
   	/* try writing, ignoring EINTR, the upper layer has to handle \
  
  
  
  1.2       +1 -1      apache-2.0/mpm/src/modules/mpm/prefork/prefork.c
  
  Index: prefork.c
  ===================================================================
  RCS file: /home/cvs/apache-2.0/mpm/src/modules/mpm/prefork/prefork.c,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- prefork.c	1999/06/24 01:57:58	1.1
  +++ prefork.c	1999/06/24 07:29:32	1.2
  @@ -2630,7 +2630,7 @@
   	(void) ap_update_child_status(my_child_num, SERVER_BUSY_READ,
   				   (request_rec *) NULL);
   
  -	conn_io = ap_bcreate(ptrans, B_RDWR | B_SOCKET);
  +	conn_io = ap_bcreate(ptrans, B_RDWR);
   
   #ifdef B_SFIO
   	(void) sfdisc(conn_io->sf_in, SF_POPDISC);
  
  
  

Mime
View raw message