/* ====================================================================
* Copyright (c) 1996-1999 The Apache Group. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. All advertising materials mentioning features or use of this
* software must display the following acknowledgment:
* "This product includes software developed by the Apache Group
* for use in the Apache HTTP server project (http://www.apache.org/)."
*
* 4. The names "Apache Server" and "Apache Group" must not be used to
* endorse or promote products derived from this software without
* prior written permission. For written permission, please contact
* apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache"
* nor may "Apache" appear in their names without prior written
* permission of the Apache Group.
*
* 6. Redistributions of any form whatsoever must retain the following
* acknowledgment:
* "This product includes software developed by the Apache Group
* for use in the Apache HTTP server project (http://www.apache.org/)."
*
* THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
* EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Group and was originally based
* on public domain software written at the National Center for
* Supercomputing Applications, University of Illinois, Urbana-Champaign.
* For more information on the Apache Group and the Apache HTTP server
* project, please see .
*
*/
#include "httpd.h"
#include "http_main.h"
#include "http_log.h"
#include
#include
#include
#include
#ifndef NO_WRITEV
#include
#include
#endif
#ifdef HAVE_BSTRING_H
#include /* for IRIX, FD_SET calls bzero() */
#endif
#ifndef DEFAULT_BUFSIZE
#define DEFAULT_BUFSIZE (4096)
#endif
/* This must be enough to represent (DEFAULT_BUFSIZE - 3) in hex,
* plus two extra characters.
*/
#ifndef CHUNK_HEADER_SIZE
#define CHUNK_HEADER_SIZE (5)
#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
* writev() rather than copy bytes into the buffer, so we don't do it for small
* writes. This is especially important when chunking (which is a very likely
* source of small writes if it's a module using ap_bputc/ap_bputs)...because we
* have the expense of actually building two chunks for each writev().
*/
#ifndef LARGE_WRITE_THRESHOLD
#define LARGE_WRITE_THRESHOLD 31
#endif
/*
* Buffered I/O routines.
* These are a replacement for the stdio routines.
* Advantages:
* Known semantics for handling of file-descriptors (on close etc.)
* No problems reading and writing simultanously to the same descriptor
* No limits on the number of open file handles.
* Only uses memory resources; no need to ensure the close routine
* is called.
* Extra code could be inserted between the buffered and un-buffered routines.
* Timeouts could be handled by using select or poll before read or write.
* Extra error handling could be introduced; e.g.
* keep an address to which we should longjump(), or
* keep a stack of routines to call on error.
*/
/* Notes:
* On reading EOF, EOF will set in the flags and no further Input will
* be done.
*
* On an error except for EAGAIN, ERROR will be set in the flags and no
* futher I/O will be done
*/
#ifdef WIN32
/*
select() sometimes returns 1 even though the write will block. We must work around this.
*/
int sendwithtimeout(int sock, const char *buf, int len, int flags)
{
int iostate = 1;
fd_set fdset;
struct timeval tv;
int err = WSAEWOULDBLOCK;
int rv;
int retry;
if (!(tv.tv_sec = ap_check_alarm()))
return (send(sock, buf, len, flags));
rv = ioctlsocket(sock, FIONBIO, &iostate);
iostate = 0;
if (rv) {
err = WSAGetLastError();
ap_assert(0);
}
rv = send(sock, buf, len, flags);
if (rv == SOCKET_ERROR) {
err = WSAGetLastError();
if (err == WSAEWOULDBLOCK)
do {
retry=0;
FD_ZERO(&fdset);
FD_SET(sock, &fdset);
tv.tv_usec = 0;
rv = select(FD_SETSIZE, NULL, &fdset, NULL, &tv);
if (rv == SOCKET_ERROR)
err = WSAGetLastError();
else if (rv == 0) {
ioctlsocket(sock, FIONBIO, &iostate);
if(ap_check_alarm() < 0) {
WSASetLastError(EINTR); /* Simulate an alarm() */
return (SOCKET_ERROR);
}
}
else {
rv = send(sock, buf, len, flags);
if (rv == SOCKET_ERROR) {
err = WSAGetLastError();
if(err == WSAEWOULDBLOCK) {
ap_log_error(APLOG_MARK,APLOG_DEBUG,NULL,
"select claimed we could write, but in fact we couldn't. This is a bug in Windows.");
retry=1;
Sleep(100);
}
}
}
} while(retry);
}
ioctlsocket(sock, FIONBIO, &iostate);
if (rv == SOCKET_ERROR)
WSASetLastError(err);
return (rv);
}
int recvwithtimeout(int sock, char *buf, int len, int flags)
{
int iostate = 1;
fd_set fdset;
struct timeval tv;
int err = WSAEWOULDBLOCK;
int rv;
if (!(tv.tv_sec = ap_check_alarm()))
return (recv(sock, buf, len, flags));
rv = ioctlsocket(sock, FIONBIO, &iostate);
iostate = 0;
ap_assert(!rv);
rv = recv(sock, buf, len, flags);
if (rv == SOCKET_ERROR) {
err = WSAGetLastError();
if (err == WSAEWOULDBLOCK) {
FD_ZERO(&fdset);
FD_SET(sock, &fdset);
tv.tv_usec = 0;
rv = select(FD_SETSIZE, &fdset, NULL, NULL, &tv);
if (rv == SOCKET_ERROR)
err = WSAGetLastError();
else if (rv == 0) {
ioctlsocket(sock, FIONBIO, &iostate);
ap_check_alarm();
WSASetLastError(WSAEWOULDBLOCK);
return (SOCKET_ERROR);
}
else {
rv = recv(sock, buf, len, flags);
if (rv == SOCKET_ERROR)
err = WSAGetLastError();
}
}
}
ioctlsocket(sock, FIONBIO, &iostate);
if (rv == SOCKET_ERROR)
WSASetLastError(err);
return (rv);
}
#endif /* WIN32 */
/* the lowest level reading primitive */
static int ap_read(BUFF *fb, void *buf, int nbyte)
{
int rv;
#ifdef WIN32
if (fb->hFH != INVALID_HANDLE_VALUE) {
if (!ReadFile(fb->hFH,buf,nbyte,&rv,NULL))
rv = -1;
}
else
#endif
rv = read(fb->fd_in, buf, nbyte);
return rv;
}
static ap_inline int buff_read(BUFF *fb, void *buf, int nbyte)
{
int rv;
#if defined (WIN32)
if (fb->flags & B_SOCKET) {
rv = recvwithtimeout(fb->fd_in, buf, nbyte, 0);
if (rv == SOCKET_ERROR)
errno = WSAGetLastError();
}
else
rv = ap_read(fb, buf, nbyte);
#elif defined (BEOS)
if (fb->flags & B_SOCKET) {
rv = recv(fb->fd_in, buf, nbyte, 0);
} else
rv = ap_read(fb,buf,nbyte);
#elif defined(TPF)
fd_set fds;
struct timeval tv;
ap_check_signals();
if (fb->flags & B_SOCKET) {
alarm(rv = alarm(0));
FD_ZERO(&fds);
FD_SET(fb->fd_in, &fds);
tv.tv_sec = rv+1;
tv.tv_usec = 0;
rv = ap_select(fb->fd_in + 1, &fds, NULL, NULL, &tv);
if (rv > 0)
rv = ap_read(fb, buf, nbyte);
}
else
rv = ap_read(fb, buf, nbyte);
#else
rv = ap_read(fb, buf, nbyte);
#endif /* WIN32 */
return rv;
}
/* the lowest level writing primitive */
static int ap_write(BUFF *fb, const void *buf, int nbyte)
{
int rv;
#ifdef WIN32
if (fb->hFH != INVALID_HANDLE_VALUE) {
if (!WriteFile(fb->hFH,buf,nbyte,&rv,NULL))
rv = -1;
}
else
#endif
#if defined (B_SFIO)
rv = sfwrite(fb->sf_out, buf, nbyte);
#else
rv = write(fb->fd, buf, nbyte);
#endif
return rv;
}
static ap_inline int buff_write(BUFF *fb, const void *buf, int nbyte)
{
int rv;
#if defined(WIN32)
if (fb->flags & B_SOCKET) {
rv = sendwithtimeout(fb->fd, buf, nbyte, 0);
if (rv == SOCKET_ERROR)
errno = WSAGetLastError();
}
else
rv = ap_write(fb, buf, nbyte);
#elif defined(BEOS)
if(fb->flags & B_SOCKET) {
rv = send(fb->fd, buf, nbyte, 0);
} else
rv = ap_write(fb, buf,nbyte);
#else
rv = ap_write(fb, buf, nbyte);
#endif /* WIN32 */
return rv;
}
static void doerror(BUFF *fb, int direction)
{
int errsave = errno; /* Save errno to prevent overwriting it below */
fb->flags |= (direction == B_RD ? B_RDERR : B_WRERR);
if (fb->error != NULL)
(*fb->error) (fb, direction, fb->error_data);
errno = errsave;
}
/* Buffering routines */
/*
* Create a new buffered stream
*/
API_EXPORT(BUFF *) ap_bcreate(pool *p, int flags)
{
BUFF *fb;
fb = ap_palloc(p, sizeof(BUFF));
fb->pool = p;
fb->bufsiz = DEFAULT_BUFSIZE;
fb->flags = flags & (B_RDWR | B_SOCKET);
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 */
if (flags & B_WR)
fb->outbase = ap_palloc(p, fb->bufsiz + 2);
else
fb->outbase = NULL;
#ifdef CHARSET_EBCDIC
fb->flags |= (flags & B_SOCKET) ? (B_EBCDIC2ASCII | B_ASCII2EBCDIC) : 0;
#endif /*CHARSET_EBCDIC*/
fb->inptr = fb->inbase;
fb->incnt = 0;
fb->outcnt = 0;
fb->outchunk = -1;
fb->error = NULL;
fb->bytes_sent = 0L;
fb->fd = -1;
fb->fd_in = -1;
#ifdef WIN32
fb->hFH = INVALID_HANDLE_VALUE;
#endif
#ifdef B_SFIO
fb->sf_in = NULL;
fb->sf_out = NULL;
fb->sf_in = sfnew(fb->sf_in, NIL(Void_t *),
(size_t) SF_UNBOUND, 0, SF_READ);
fb->sf_out = sfnew(fb->sf_out, NIL(Void_t *),
(size_t) SF_UNBOUND, 1, SF_WRITE);
#endif
return fb;
}
/*
* Push some I/O file descriptors onto the stream
*/
API_EXPORT(void) ap_bpushfd(BUFF *fb, int fd_in, int fd_out)
{
fb->fd = fd_out;
fb->fd_in = fd_in;
}
#ifdef WIN32
/*
* Push some Win32 handles onto the stream.
*/
API_EXPORT(void) ap_bpushh(BUFF *fb, HANDLE hFH)
{
fb->hFH = hFH;
}
#endif
API_EXPORT(int) ap_bsetopt(BUFF *fb, int optname, const void *optval)
{
if (optname == BO_BYTECT) {
fb->bytes_sent = *(const long int *) optval - (long int) fb->outcnt;;
return 0;
}
else {
errno = EINVAL;
return -1;
}
}
API_EXPORT(int) ap_bgetopt(BUFF *fb, int optname, void *optval)
{
if (optname == BO_BYTECT) {
long int bs = fb->bytes_sent + fb->outcnt;
if (bs < 0L)
bs = 0L;
*(long int *) optval = bs;
return 0;
}
else {
errno = EINVAL;
return -1;
}
}
static int bflush_core(BUFF *fb);
/*
* Start chunked encoding.
*
* Note that in order for ap_bputc() to be an efficient macro we have to guarantee
* that start_chunk() has always been called on the buffer before we leave any
* routine in this file. Said another way, if a routine here uses end_chunk()
* and writes something on the wire, then it has to call start_chunk() or set
* an error condition before returning.
*/
static void start_chunk(BUFF *fb)
{
if (fb->outchunk != -1) {
/* already chunking */
return;
}
if ((fb->flags & (B_WRERR | B_EOUT | B_WR)) != B_WR) {
/* unbuffered writes */
return;
}
/* we need at least the header_len + at least 1 data byte
* remember that we've overallocated fb->outbase so that we can always
* fit the two byte CRLF trailer
*/
if (fb->bufsiz - fb->outcnt < CHUNK_HEADER_SIZE + 1) {
bflush_core(fb);
}
fb->outchunk = fb->outcnt;
fb->outcnt += CHUNK_HEADER_SIZE;
}
/*
* end a chunk -- tweak the chunk_header from start_chunk, and add a trailer
*/
static void end_chunk(BUFF *fb)
{
int i;
unsigned char *strp;
if (fb->outchunk == -1) {
/* not chunking */
return;
}
if (fb->outchunk + CHUNK_HEADER_SIZE == fb->outcnt) {
/* 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;
}
/* 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", fb->outcnt - fb->outchunk - CHUNK_HEADER_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';
#ifdef CHARSET_EBCDIC
/* Chunks are an HTTP/1.1 Protocol feature. They must ALWAYS be in ASCII */
ebcdic2ascii(&fb->outbase[fb->outchunk], &fb->outbase[fb->outchunk], CHUNK_HEADER_SIZE);
#endif /*CHARSET_EBCDIC*/
/* tack on the trailing CRLF, we've reserved room for this */
fb->outbase[fb->outcnt++] = '\015';
fb->outbase[fb->outcnt++] = '\012';
fb->outchunk = -1;
}
/*
* Set a flag on (1) or off (0).
*/
API_EXPORT(int) ap_bsetflag(BUFF *fb, int flag, int value)
{
if (value) {
fb->flags |= flag;
if (flag & B_CHUNK) {
start_chunk(fb);
}
}
else {
fb->flags &= ~flag;
if (flag & B_CHUNK) {
end_chunk(fb);
}
}
return value;
}
API_EXPORT(int) ap_bnonblock(BUFF *fb, int direction)
{
int fd;
fd = (direction == B_RD) ? fb->fd_in : fb->fd;
#if defined(O_NONBLOCK)
return fcntl(fd, F_SETFL, O_NONBLOCK);
#elif defined(O_NDELAY)
return fcntl(fd, F_SETFL, O_NDELAY);
#elif defined(FNDELAY)
return fcntl(fd, F_SETFL, FNDELAY);
#else
/* XXXX: this breaks things, but an alternative isn't obvious...*/
return 0;
#endif
}
API_EXPORT(int) ap_bfileno(BUFF *fb, int direction)
{
return (direction == B_RD) ? fb->fd_in : fb->fd;
}
/*
* This is called instead of read() everywhere in here. It implements
* the B_SAFEREAD functionality -- which is to force a flush() if a read()
* would block. It also deals with the EINTR errno result from read().
* return code is like read() except EINTR is eliminated.
*/
#if !defined (B_SFIO) || defined (WIN32)
#define saferead saferead_guts
#else
static int saferead(BUFF *fb, char *buf, int nbyte)
{
return sfread(fb->sf_in, buf, nbyte);
}
#endif
/* Test the descriptor and flush the output buffer if it looks like
* we will block on the next read.
*
* Note we assume the caller has ensured that fb->fd_in <= FD_SETSIZE
*/
API_EXPORT(void) ap_bhalfduplex(BUFF *fb)
{
int rv;
fd_set fds;
struct timeval tv;
/* We don't need to do anything if the connection has been closed
* or there is something readable in the incoming buffer
* or there is nothing flushable in the output buffer.
*/
if (fb == NULL || fb->fd_in < 0 || fb->incnt > 0 || fb->outcnt == 0) {
return;
}
/* test for a block */
do {
FD_ZERO(&fds);
FD_SET(fb->fd_in, &fds);
tv.tv_sec = 0;
tv.tv_usec = 0;
rv = ap_select(fb->fd_in + 1, &fds, NULL, NULL, &tv);
} while (rv < 0 && errno == EINTR && !(fb->flags & B_EOUT));
/* treat any error as if it would block as well */
if (rv != 1) {
ap_bflush(fb);
}
}
static ap_inline int saferead_guts(BUFF *fb, void *buf, int nbyte)
{
int rv;
if (fb->flags & B_SAFEREAD) {
ap_bhalfduplex(fb);
}
do {
rv = buff_read(fb, buf, nbyte);
} while (rv == -1 && errno == EINTR && !(fb->flags & B_EOUT));
return (rv);
}
#ifdef B_SFIO
int bsfio_read(Sfio_t * f, char *buf, int nbyte, apache_sfio *disc)
{
int rv;
BUFF *fb = disc->buff;
rv = saferead_guts(fb, buf, nbyte);
buf[rv] = '\0';
f->next = 0;
return (rv);
}
int bsfio_write(Sfio_t * f, char *buf, int nbyte, apache_sfio *disc)
{
return ap_write(disc->buff, buf, nbyte);
}
Sfdisc_t *bsfio_new(pool *p, BUFF *b)
{
apache_sfio *disc;
if (!(disc = (apache_sfio *) ap_palloc(p, sizeof(apache_sfio))))
return (Sfdisc_t *) disc;
disc->disc.readf = (Sfread_f) bsfio_read;
disc->disc.writef = (Sfwrite_f) bsfio_write;
disc->disc.seekf = (Sfseek_f) NULL;
disc->disc.exceptf = (Sfexcept_f) NULL;
disc->buff = b;
return (Sfdisc_t *) disc;
}
#endif
/* A wrapper around saferead which does error checking and EOF checking
* yeah, it's confusing, this calls saferead, which calls buff_read...
* and then there's the SFIO case. Note that saferead takes care
* of EINTR.
*/
static int read_with_errors(BUFF *fb, void *buf, int nbyte)
{
int rv;
rv = saferead(fb, buf, nbyte);
if (rv == 0) {
fb->flags |= B_EOF;
}
else if (rv == -1 && errno != EAGAIN) {
doerror(fb, B_RD);
}
return rv;
}
/*
* Read up to nbyte bytes into buf.
* If fewer than byte bytes are currently available, then return those.
* Returns 0 for EOF, -1 for error.
* NOTE EBCDIC: The readahead buffer _always_ contains *unconverted* data.
* Only when the caller retrieves data from the buffer (calls bread)
* is a conversion done, if the conversion flag is set at that time.
*/
API_EXPORT(int) ap_bread(BUFF *fb, void *buf, int nbyte)
{
int i, nrd;
if (fb->flags & B_RDERR)
return -1;
if (nbyte == 0)
return 0;
if (!(fb->flags & B_RD)) {
/* Unbuffered reading. First check if there was something in the
* buffer from before we went unbuffered. */
if (fb->incnt) {
i = (fb->incnt > nbyte) ? nbyte : fb->incnt;
#ifdef CHARSET_EBCDIC
if (fb->flags & B_ASCII2EBCDIC)
ascii2ebcdic(buf, fb->inptr, i);
else
#endif /*CHARSET_EBCDIC*/
memcpy(buf, fb->inptr, i);
fb->incnt -= i;
fb->inptr += i;
return i;
}
i = read_with_errors(fb, buf, nbyte);
#ifdef CHARSET_EBCDIC
if (i > 0 && ap_bgetflag(fb, B_ASCII2EBCDIC))
ascii2ebcdic(buf, buf, i);
#endif /*CHARSET_EBCDIC*/
return i;
}
nrd = fb->incnt;
/* can we fill the buffer */
if (nrd >= nbyte) {
#ifdef CHARSET_EBCDIC
if (fb->flags & B_ASCII2EBCDIC)
ascii2ebcdic(buf, fb->inptr, nbyte);
else
#endif /*CHARSET_EBCDIC*/
memcpy(buf, fb->inptr, nbyte);
fb->incnt = nrd - nbyte;
fb->inptr += nbyte;
return nbyte;
}
if (nrd > 0) {
#ifdef CHARSET_EBCDIC
if (fb->flags & B_ASCII2EBCDIC)
ascii2ebcdic(buf, fb->inptr, nrd);
else
#endif /*CHARSET_EBCDIC*/
memcpy(buf, fb->inptr, nrd);
nbyte -= nrd;
buf = nrd + (char *) buf;
fb->incnt = 0;
}
if (fb->flags & B_EOF)
return nrd;
/* do a single read */
if (nbyte >= fb->bufsiz) {
/* read directly into caller's buffer */
i = read_with_errors(fb, buf, nbyte);
#ifdef CHARSET_EBCDIC
if (i > 0 && ap_bgetflag(fb, B_ASCII2EBCDIC))
ascii2ebcdic(buf, buf, i);
#endif /*CHARSET_EBCDIC*/
if (i == -1) {
return nrd ? nrd : -1;
}
}
else {
/* read into hold buffer, then memcpy */
fb->inptr = fb->inbase;
i = read_with_errors(fb, fb->inptr, fb->bufsiz);
if (i == -1) {
return nrd ? nrd : -1;
}
fb->incnt = i;
if (i > nbyte)
i = nbyte;
#ifdef CHARSET_EBCDIC
if (fb->flags & B_ASCII2EBCDIC)
ascii2ebcdic(buf, fb->inptr, i);
else
#endif /*CHARSET_EBCDIC*/
memcpy(buf, fb->inptr, i);
fb->incnt -= i;
fb->inptr += i;
}
return nrd + i;
}
/*
* Reads from the stream into the array pointed to by buff, until
* a (CR)LF sequence is read, or end-of-file condition is encountered
* or until n-1 bytes have been stored in buff. If a CRLF sequence is
* read, it is replaced by a newline character. The string is then
* terminated with a null character.
*
* Returns the number of bytes stored in buff, or zero on end of
* transmission, or -1 on an error.
*
* Notes:
* If null characters are expected in the data stream, then
* buff should not be treated as a null terminated C string; instead
* the returned count should be used to determine the length of the
* string.
* CR characters in the byte stream not immediately followed by a LF
* will be preserved.
*/
API_EXPORT(int) ap_bgets(char *buff, int n, BUFF *fb)
{
int i, ch, ct;
/* Can't do bgets on an unbuffered stream */
if (!(fb->flags & B_RD)) {
errno = EINVAL;
return -1;
}
if (fb->flags & B_RDERR)
return -1;
ct = 0;
i = 0;
for (;;) {
if (i == fb->incnt) {
/* no characters left */
fb->inptr = fb->inbase;
fb->incnt = 0;
if (fb->flags & B_EOF)
break;
i = read_with_errors(fb, fb->inptr, fb->bufsiz);
if (i == -1) {
buff[ct] = '\0';
return ct ? ct : -1;
}
fb->incnt = i;
if (i == 0)
break; /* EOF */
i = 0;
continue; /* restart with the new data */
}
ch = fb->inptr[i++];
#ifndef CHARSET_EBCDIC
if (ch == '\012') { /* got LF */
if (ct == 0)
buff[ct++] = '\n';
/* if just preceeded by CR, replace CR with LF */
else if (buff[ct - 1] == '\015')
buff[ct - 1] = '\n';
else if (ct < n - 1)
buff[ct++] = '\n';
else
i--; /* no room for LF */
break;
}
#else /* an EBCDIC machine: do the same, but convert to EBCDIC on the fly: */
if (fb->flags & B_ASCII2EBCDIC)
ch = os_toebcdic[(unsigned char)ch];
if (ch == os_toebcdic['\012']) { /* got LF */
if (ct == 0)
buff[ct++] = '\n';
/* if just preceeded by CR, replace CR with LF */
else if (buff[ct - 1] == os_toebcdic['\015'])
buff[ct - 1] = '\n';
else if (ct < n - 1)
buff[ct++] = '\n';
else
i--; /* no room for LF */
break;
}
#endif
if (ct == n - 1) {
i--; /* push back ch */
break;
}
buff[ct++] = ch;
}
fb->incnt -= i;
fb->inptr += i;
buff[ct] = '\0';
return ct;
}
/*
* Looks at the stream fb and places the first character into buff
* without removing it from the stream buffer.
*
* Returns 1 on success, zero on end of transmission, or -1 on an error.
*
*/
API_EXPORT(int) ap_blookc(char *buff, BUFF *fb)
{
int i;
*buff = '\0';
if (!(fb->flags & B_RD)) { /* Can't do blookc on an unbuffered stream */
errno = EINVAL;
return -1;
}
if (fb->flags & B_RDERR)
return -1;
if (fb->incnt == 0) { /* no characters left in stream buffer */
fb->inptr = fb->inbase;
if (fb->flags & B_EOF)
return 0;
i = read_with_errors(fb, fb->inptr, fb->bufsiz);
if (i <= 0) {
return i;
}
fb->incnt = i;
}
#ifndef CHARSET_EBCDIC
*buff = fb->inptr[0];
#else /*CHARSET_EBCDIC*/
*buff = (fb->flags & B_ASCII2EBCDIC)
? os_toebcdic[(unsigned char)fb->inptr[0]]
: fb->inptr[0];
#endif /*CHARSET_EBCDIC*/
return 1;
}
/*
* Skip data until a linefeed character is read
* Returns 1 on success, 0 if no LF found, or -1 on error
*/
API_EXPORT(int) ap_bskiplf(BUFF *fb)
{
unsigned char *x;
int i;
/* Can't do bskiplf on an unbuffered stream */
if (!(fb->flags & B_RD)) {
errno = EINVAL;
return -1;
}
if (fb->flags & B_RDERR)
return -1;
for (;;) {
x = (unsigned char *) memchr(fb->inptr, '\012', fb->incnt);
if (x != NULL) {
x++;
fb->incnt -= x - fb->inptr;
fb->inptr = x;
return 1;
}
fb->inptr = fb->inbase;
fb->incnt = 0;
if (fb->flags & B_EOF)
return 0;
i = read_with_errors(fb, fb->inptr, fb->bufsiz);
if (i <= 0)
return i;
fb->incnt = i;
}
}
/*
* output a single character. Used by ap_bputs when the buffer
* is full... and so it'll cause the buffer to be flushed first.
*/
API_EXPORT(int) ap_bflsbuf(int c, BUFF *fb)
{
char ss[1];
ss[0] = c;
return ap_bwrite(fb, ss, 1);
}
/*
* Fill the buffer and read a character from it
*/
API_EXPORT(int) ap_bfilbuf(BUFF *fb)
{
int i;
char buf[1];
i = ap_bread(fb, buf, 1);
if (i == 0)
errno = 0; /* no error; EOF */
if (i != 1)
return EOF;
else
return buf[0];
}
/*
* When doing chunked encodings we really have to write everything in the
* chunk before proceeding onto anything else. This routine either writes
* nbytes and returns 0 or returns -1 indicating a failure.
*
* This is *seriously broken* if used on a non-blocking fd. It will poll.
*
* Deals with calling doerror and setting bytes_sent.
*/
static int write_it_all(BUFF *fb, const void *buf, int nbyte)
{
int i;
if (fb->flags & (B_WRERR | B_EOUT))
return -1;
while (nbyte > 0) {
i = buff_write(fb, buf, nbyte);
if (i < 0) {
if (errno != EAGAIN && errno != EINTR) {
doerror(fb, B_WR);
return -1;
}
}
else {
nbyte -= i;
buf = i + (const char *) buf;
fb->bytes_sent += i;
}
if (fb->flags & B_EOUT)
return -1;
}
return 0;
}
#ifndef NO_WRITEV
/* Similar to previous, but uses writev. Note that it modifies vec.
* return 0 if successful, -1 otherwise.
*
* Deals with doerror() and bytes_sent.
*/
static int writev_it_all(BUFF *fb, struct iovec *vec, int nvec)
{
int i, rv;
/* while it's nice an easy to build the vector and crud, it's painful
* to deal with a partial writev()
*/
i = 0;
while (i < nvec) {
do
rv = writev(fb->fd, &vec[i], nvec - i);
while (rv == -1 && (errno == EINTR || errno == EAGAIN)
&& !(fb->flags & B_EOUT));
if (rv == -1) {
if (errno != EINTR && errno != EAGAIN) {
doerror(fb, B_WR);
}
return -1;
}
fb->bytes_sent += rv;
/* recalculate vec 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;
rv = 0;
}
else {
rv -= vec[i].iov_len;
++i;
}
}
if (fb->flags & B_EOUT)
return -1;
}
/* if we got here, we wrote it all */
return 0;
}
#endif
/* A wrapper for buff_write which deals with error conditions and
* bytes_sent. Also handles non-blocking writes.
*/
static int write_with_errors(BUFF *fb, const void *buf, int nbyte)
{
int rv;
do
rv = buff_write(fb, buf, nbyte);
while (rv == -1 && errno == EINTR && !(fb->flags & B_EOUT));
if (rv == -1) {
if (errno != EAGAIN) {
doerror(fb, B_WR);
}
return -1;
}
else if (rv == 0) {
errno = EAGAIN;
return -1;
}
fb->bytes_sent += rv;
return rv;
}
/*
* A hook to write() that deals with chunking. This is really a protocol-
* level issue, but we deal with it here because it's simpler; this is
* an interim solution pending a complete rewrite of all this stuff in
* 2.0, using something like sfio stacked disciplines or BSD's funopen().
*
* Can be used on non-blocking descriptors, but only if they're not chunked.
* Deals with doerror() and bytes_sent.
*/
static int bcwrite(BUFF *fb, const void *buf, int nbyte)
{
char chunksize[16]; /* Big enough for practically anything */
#ifndef NO_WRITEV
struct iovec vec[3];
#endif
if (fb->flags & (B_WRERR | B_EOUT))
return -1;
if (!(fb->flags & B_CHUNK)) {
return write_with_errors(fb, buf, nbyte);
}
#ifdef NO_WRITEV
/* without writev() this has poor performance, too bad */
ap_snprintf(chunksize, sizeof(chunksize), "%x\015\012", nbyte);
#ifdef CHARSET_EBCDIC
/* Chunks are an HTTP/1.1 Protocol feature. They must ALWAYS be in ASCII */
ebcdic2ascii(chunksize, chunksize, strlen(chunksize));
#endif /*CHARSET_EBCDIC*/
if (write_it_all(fb, chunksize, strlen(chunksize)) == -1)
return -1;
if (write_it_all(fb, buf, nbyte) == -1)
return -1;
if (write_it_all(fb, "\015\012", 2) == -1)
return -1;
return nbyte;
#else
vec[0].iov_base = chunksize;
vec[0].iov_len = ap_snprintf(chunksize, sizeof(chunksize), "%x\015\012",
nbyte);
#ifdef CHARSET_EBCDIC
/* Chunks are an HTTP/1.1 Protocol feature. They must ALWAYS be in ASCII */
ebcdic2ascii(chunksize, chunksize, strlen(chunksize));
#endif /*CHARSET_EBCDIC*/
vec[1].iov_base = (void *) buf; /* cast is to avoid const warning */
vec[1].iov_len = nbyte;
vec[2].iov_base = "\015\012";
vec[2].iov_len = 2;
return writev_it_all(fb, vec, (sizeof(vec) / sizeof(vec[0]))) ? -1 : nbyte;
#endif
}
#ifndef NO_WRITEV
/*
* Used to combine the contents of the fb buffer, and a large buffer
* passed in.
*/
static int large_write(BUFF *fb, const void *buf, int nbyte)
{
struct iovec vec[4];
int nvec;
char chunksize[16];
/* it's easiest to end the current chunk */
if (fb->flags & B_CHUNK) {
end_chunk(fb);
}
nvec = 0;
if (fb->outcnt > 0) {
vec[nvec].iov_base = (void *) fb->outbase;
vec[nvec].iov_len = fb->outcnt;
++nvec;
}
if (fb->flags & B_CHUNK) {
vec[nvec].iov_base = chunksize;
vec[nvec].iov_len = ap_snprintf(chunksize, sizeof(chunksize),
"%x\015\012", nbyte);
#ifdef CHARSET_EBCDIC
/* Chunks are an HTTP/1.1 Protocol feature. They must ALWAYS be in ASCII */
ebcdic2ascii(chunksize, chunksize, strlen(chunksize));
#endif /*CHARSET_EBCDIC*/
++nvec;
vec[nvec].iov_base = (void *) buf;
vec[nvec].iov_len = nbyte;
++nvec;
vec[nvec].iov_base = "\015\012";
vec[nvec].iov_len = 2;
++nvec;
}
else {
vec[nvec].iov_base = (void *) buf;
vec[nvec].iov_len = nbyte;
++nvec;
}
fb->outcnt = 0;
if (writev_it_all(fb, vec, nvec)) {
return -1;
}
else if (fb->flags & B_CHUNK) {
start_chunk(fb);
}
return nbyte;
}
#endif
/*
* Write nbyte bytes.
* Only returns fewer than nbyte if an error ocurred.
* Returns -1 if no bytes were written before the error ocurred.
* It is worth noting that if an error occurs, the buffer is in an unknown
* state.
*/
API_EXPORT(int) ap_bwrite(BUFF *fb, const void *buf, int nbyte)
{
int i, nwr, useable_bufsiz;
#ifdef CHARSET_EBCDIC
static char *cbuf = NULL;
static int csize = 0;
#endif /*CHARSET_EBCDIC*/
if (fb->flags & (B_WRERR | B_EOUT))
return -1;
if (nbyte == 0)
return 0;
#ifdef CHARSET_EBCDIC
if (ap_bgetflag(fb, B_EBCDIC2ASCII)) {
if (nbyte > csize) {
if (cbuf != NULL)
free(cbuf);
cbuf = malloc(csize = nbyte+HUGE_STRING_LEN);
if (cbuf == NULL) {
fprintf(stderr, "Ouch! Out of memory in ap_bwrite()!\n");
csize = 0;
}
}
ebcdic2ascii((cbuf) ? cbuf : (void*)buf, buf, nbyte);
buf = (cbuf) ? cbuf : buf;
}
#endif /*CHARSET_EBCDIC*/
if (!(fb->flags & B_WR)) {
/* unbuffered write -- have to use bcwrite since we aren't taking care
* of chunking any other way */
return bcwrite(fb, buf, nbyte);
}
#ifndef NO_WRITEV
/*
* 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
* the case nbyte == 1 because modules which use rputc() loops will cause
* 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) {
return large_write(fb, buf, nbyte);
}
#endif
/*
* 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 */
if (fb->flags & B_CHUNK) {
end_chunk(fb);
/* it is just too painful to try to re-cram the buffer while
* chunking
*/
if (write_it_all(fb, fb->outbase, fb->outcnt) == -1) {
/* we cannot continue after a chunked error */
return -1;
}
fb->outcnt = 0;
break;
}
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) {
int j, n = fb->outcnt;
unsigned char *x = fb->outbase;
for (j = i; j < n; j++)
x[j - i] = x[j];
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. Note that we
* use bcwrite() to do this for us, it will do the chunking so that
* we don't have to dink around building a chunk in our own buffer.
*
* Note also that bcwrite never does a partial write if we're chunking,
* so we're guaranteed to either end in an error state, or make it
* out of this loop and call start_chunk() below.
*
* Remember we may not be able to use the entire buffer if we're
* chunking.
*/
useable_bufsiz = fb->bufsiz;
if (fb->flags & B_CHUNK) useable_bufsiz -= CHUNK_HEADER_SIZE;
while (nbyte >= useable_bufsiz) {
i = bcwrite(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 */
fb->outcnt = 0;
if (fb->flags & B_CHUNK)
start_chunk(fb);
if (nbyte > 0)
memcpy(fb->outbase + fb->outcnt, 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) {
int j, n = fb->outcnt;
unsigned char *x = fb->outbase;
for (j = i; j < n; j++)
x[j - i] = x[j];
}
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;
}
/*
* Flushes the buffered stream.
* Returns 0 on success or -1 on error
*/
API_EXPORT(int) ap_bflush(BUFF *fb)
{
int ret;
if ((fb->flags & (B_WRERR | B_EOUT | B_WR)) != B_WR)
return -1;
if (fb->flags & B_CHUNK)
end_chunk(fb);
ret = bflush_core(fb);
if (ret == 0 && (fb->flags & B_CHUNK)) {
start_chunk(fb);
}
return ret;
}
/*
* Flushes and closes the file, even if an error occurred.
* Discards an data that was not read, or not written by bflush()
* Sets the EOF flag to indicate no futher data can be read,
* and the EOUT flag to indicate no further data can be written.
*/
API_EXPORT(int) ap_bclose(BUFF *fb)
{
int rc1, rc2, rc3;
if (fb->flags & B_WR)
rc1 = ap_bflush(fb);
else
rc1 = 0;
#ifdef WIN32
if (fb->flags & B_SOCKET) {
rc2 = ap_pclosesocket(fb->pool, fb->fd);
if (fb->fd_in != fb->fd) {
rc3 = ap_pclosesocket(fb->pool, fb->fd_in);
}
else {
rc3 = 0;
}
}
else if (fb->hFH != INVALID_HANDLE_VALUE) {
rc2 = ap_pcloseh(fb->pool, fb->hFH);
rc3 = 0;
}
else {
#elif defined(BEOS)
if (fb->flags & B_SOCKET) {
rc2 = ap_pclosesocket(fb->pool, fb->fd);
if (fb->fd_in != fb->fd) {
rc3 = ap_pclosesocket(fb->pool, fb->fd_in);
}
else {
rc3 = 0;
}
} else {
#endif
rc2 = ap_pclosef(fb->pool, fb->fd);
if (fb->fd_in != fb->fd) {
rc3 = ap_pclosef(fb->pool, fb->fd_in);
}
else {
rc3 = 0;
}
#if defined(WIN32) || defined (BEOS)
}
#endif
fb->inptr = fb->inbase;
fb->incnt = 0;
fb->outcnt = 0;
fb->flags |= B_EOF | B_EOUT;
fb->fd = -1;
fb->fd_in = -1;
#ifdef B_SFIO
sfclose(fb->sf_in);
sfclose(fb->sf_out);
#endif
if (rc1 != 0)
return rc1;
else if (rc2 != 0)
return rc2;
else
return rc3;
}
/*
* returns the number of bytes written or -1 on error
*/
API_EXPORT(int) ap_bputs(const char *x, BUFF *fb)
{
int i, j = strlen(x);
i = ap_bwrite(fb, x, j);
if (i != j)
return -1;
else
return j;
}
/*
* returns the number of bytes written or -1 on error
*/
API_EXPORT_NONSTD(int) ap_bvputs(BUFF *fb,...)
{
int i, j, k;
va_list v;
const char *x;
va_start(v, fb);
for (k = 0;;) {
x = va_arg(v, const char *);
if (x == NULL)
break;
j = strlen(x);
i = ap_bwrite(fb, x, j);
if (i != j) {
va_end(v);
return -1;
}
k += i;
}
va_end(v);
return k;
}
API_EXPORT(void) ap_bonerror(BUFF *fb, void (*error) (BUFF *, int, void *),
void *data)
{
fb->error = error;
fb->error_data = data;
}
struct bprintf_data {
ap_vformatter_buff vbuff;
BUFF *fb;
};
static int bprintf_flush(ap_vformatter_buff *vbuff)
{
struct bprintf_data *b = (struct bprintf_data *)vbuff;
BUFF *fb = b->fb;
#ifdef CHARSET_EBCDIC
/* Characters were pushed into the buffer without conversion. Do it now */
if (fb->flags & B_EBCDIC2ASCII)
ebcdic2ascii(&fb->outbase[fb->outcnt],
&fb->outbase[fb->outcnt],
b->vbuff.curpos - (char *)&fb->outbase[fb->outcnt]);
#endif /*CHARSET_EBCDIC*/
fb->outcnt += b->vbuff.curpos - (char *)&fb->outbase[fb->outcnt];
if (fb->outcnt == fb->bufsiz) {
if (ap_bflush(fb)) {
return -1;
}
}
vbuff->curpos = (char *)&fb->outbase[fb->outcnt];
vbuff->endpos = (char *)&fb->outbase[fb->bufsiz];
return 0;
}
API_EXPORT_NONSTD(int) ap_bprintf(BUFF *fb, const char *fmt, ...)
{
va_list ap;
int res;
struct bprintf_data b;
/* XXX: only works with buffered writes */
if ((fb->flags & (B_WRERR | B_EOUT | B_WR)) != B_WR)
return -1;
b.vbuff.curpos = (char *)&fb->outbase[fb->outcnt];
b.vbuff.endpos = (char *)&fb->outbase[fb->bufsiz];
b.fb = fb;
va_start(ap, fmt);
res = ap_vformatter(bprintf_flush, &b.vbuff, fmt, ap);
va_end(ap);
if (res != -1) {
#ifdef CHARSET_EBCDIC
/* Characters were pushed into the buffer without conversion. Do it now */
if (fb->flags & B_EBCDIC2ASCII)
ebcdic2ascii(&fb->outbase[fb->outcnt],
&fb->outbase[fb->outcnt],
b.vbuff.curpos - (char *)&fb->outbase[fb->outcnt]);
#endif /*CHARSET_EBCDIC*/
fb->outcnt += b.vbuff.curpos - (char *)&fb->outbase[fb->outcnt];
}
return res;
}
API_EXPORT(int) ap_vbprintf(BUFF *fb, const char *fmt, va_list ap)
{
struct bprintf_data b;
int res;
/* XXX: only works with buffered writes */
if ((fb->flags & (B_WRERR | B_EOUT | B_WR)) != B_WR)
return -1;
b.vbuff.curpos = (char *)&fb->outbase[fb->outcnt];
b.vbuff.endpos = (char *)&fb->outbase[fb->bufsiz];
b.fb = fb;
res = ap_vformatter(bprintf_flush, &b.vbuff, fmt, ap);
if (res != -1) {
#ifdef CHARSET_EBCDIC
/* Characters were pushed into the buffer without conversion. Do it now */
if (fb->flags & B_EBCDIC2ASCII)
ebcdic2ascii(&fb->outbase[fb->outcnt],
&fb->outbase[fb->outcnt],
b.vbuff.curpos - (char *)&fb->outbase[fb->outcnt]);
#endif /*CHARSET_EBCDIC*/
fb->outcnt += b.vbuff.curpos - (char *)&fb->outbase[fb->outcnt];
}
return res;
}