/*
** CHUNKED TRANSFER CODING
**
** (c) COPYRIGHT MIT 1995.
** Please first read the full copyright statement in the file COPYRIGH.
** @(#) $Id: HTTChunk.c,v 2.11 1998/01/21 17:24:43 frystyk Exp $
**
** This stream parses a chunked transfer encoding using multiple chunks.
**
** Authors
** HF Henrik Frystyk <frystyk@w3.org>
**
** History:
** Apr 96 Written from scratch
**
*/
/* Library include files */
#include "sysdep.h"
#include "WWWUtil.h"
#include "WWWCore.h"
#include "HTTChunk.h" /* Implemented here */
#define PUTBLOCK(b, l) (*me->target->isa->put_block)(me->target, b, l)
#define PUTC(c) (*me->target->isa->put_character)(me->target, c)
struct _HTStream {
const HTStreamClass * isa;
HTEncoding coding;
HTStream * target;
HTRequest * request;
char * param; /* Extra parameters for encoding */
long left; /* Remaining bytes in this chunk */
long total; /* Full length */
BOOL lastchunk; /* Is this the last chunk? */
BOOL trailer; /* Do we have a trailer? */
HTEOLState state;
HTChunk * buf;
int status; /* return code from down stream */
};
/* ------------------------------------------------------------------------- */
/*
** Chunked Decoder stream
*/
PRIVATE BOOL HTChunkDecode_header (HTStream * me)
{
char * line = HTChunk_data(me->buf);
if (line) {
char *errstr = NULL;
me->left = strtol(line, &errstr, 16); /* hex! */
if (STREAM_TRACE) HTTrace("Chunked..... `%s\' chunk size: %X\n", line, me->left);
if (errstr == line) {
HTTrace("Chunked..... Received illigal chunk size: `%s\'\n", line);
return NO;
}
if (me->left > 0) {
me->total += me->left;
/* Look for arguments */
HTChunk_clear(me->buf);
} else if (me->left == 0) /* Last chunk */
me->lastchunk = YES;
else if (me->left < 0)
return NO;
return YES;
}
return NO;
}
PRIVATE int HTChunkDecode_block (HTStream * me, const char * b, int l)
{
while (l > 0) {
int length = l;
if (me->left <= 0 && !me->trailer) {
while (l > 0) {
if (me->state == EOL_FLF) {
if (HTChunkDecode_header(me) == NO) return HT_ERROR;
if (me->lastchunk) if (*b != CR && *b != LF) me->trailer = YES;
me->state = EOL_DOT;
break;
} else if (me->state == EOL_SLF) {
if (me->lastchunk) break;
me->state = EOL_BEGIN;
HTChunk_putc(me->buf, *b);
} else if (*b == CR) {
me->state = me->state == EOL_DOT ? EOL_SCR : EOL_FCR;
} else if (*b == LF) {
me->state = me->state == EOL_SCR ? EOL_SLF : EOL_FLF;
} else
HTChunk_putc(me->buf, *b);
b++, l--;
}
}
/*
** Account for the parts we read in the chunk header +
** the chunk that we are reading.
*/
if (length != l)
HTHost_setConsumed(HTNet_host(HTRequest_net(me->request)), length - l);
/*
** If we have to read trailers. Otherwise we are done.
*/
if (me->trailer) {
me->target = HTStreamStack(WWW_MIME_FOOT, WWW_SOURCE,
me->target, me->request, NO);
} else if (me->state == EOL_SLF) {
if (me->lastchunk) return HT_LOADED;
me->state = EOL_BEGIN;
}
/*
** Handle the rest of the data including trailers
*/
if (l > 0 && me->left) {
int bytes = HTMIN(l, me->left);
int status = (*me->target->isa->put_block)(me->target, b, bytes);
if (status != HT_OK) return status;
HTHost_setConsumed(HTNet_host(HTRequest_net(me->request)), bytes);
me->left -= bytes;
l -= bytes, b+= bytes;
}
}
return HT_OK;
}
PRIVATE int HTChunkDecode_string (HTStream * me, const char * s)
{
return HTChunkDecode_block(me, s, (int) strlen(s));
}
PRIVATE int HTChunkDecode_character (HTStream * me, char c)
{
return HTChunkDecode_block(me, &c, 1);
}
PRIVATE int HTChunkDecode_flush (HTStream * me)
{
return (*me->target->isa->flush)(me->target);
}
PRIVATE int HTChunkDecode_free (HTStream * me)
{
int status = HT_OK;
HTParentAnchor * anchor = HTRequest_anchor(me->request);
/*
** Update the amount of data that we read in all.
*/
HTAnchor_setLength(anchor, me->total);
if (me->target) {
if ((status = (*me->target->isa->_free)(me->target)) == HT_WOULD_BLOCK)
return HT_WOULD_BLOCK;
}
if (PROT_TRACE) HTTrace("Chunked..... FREEING....\n");
HTChunk_delete(me->buf);
HT_FREE(me);
return status;
}
PRIVATE int HTChunkDecode_abort (HTStream * me, HTList * e)
{
int status = HT_ERROR;
if (me->target) status = (*me->target->isa->abort)(me->target, e);
if (PROT_TRACE) HTTrace("Chunked..... ABORTING...\n");
HTChunk_delete(me->buf);
HT_FREE(me);
return status;
}
PRIVATE const HTStreamClass HTChunkDecodeClass =
{
"ChunkDecoder",
HTChunkDecode_flush,
HTChunkDecode_free,
HTChunkDecode_abort,
HTChunkDecode_character,
HTChunkDecode_string,
HTChunkDecode_block
};
PUBLIC HTStream * HTChunkedDecoder (HTRequest * request,
void * param,
HTEncoding coding,
HTStream * target)
{
HTStream * me;
HTParentAnchor * anchor = HTRequest_anchor(request);
if ((me = (HTStream *) HT_CALLOC(1, sizeof(HTStream))) == NULL)
HT_OUTOFMEM("HTChunkDecoder");
me->isa = &HTChunkDecodeClass;
me->coding = coding;
me->target = target;
me->request = request;
me->state = EOL_BEGIN;
me->buf = HTChunk_new(64);
me->status = HT_ERROR;
/* Adjust information in anchor */
HTAnchor_setLength(anchor, -1);
HTAnchor_setTransfer(anchor, NULL);
if (STREAM_TRACE) HTTrace("Chunked..... Decoder stream created\n");
return me;
}
/*
** Chunked Encoder Stream
*/
PRIVATE int HTChunkEncode_block (HTStream * me, const char * b, int l)
{
char * chunky = HTChunk_data(me->buf);
if (me->lastchunk) return HT_LOADED;
if (me->param) {
if (me->total)
sprintf(chunky, "%c%c%x %s %c%c", CR, LF, l, me->param, CR, LF);
else
sprintf(chunky, "%x %s %c%c", l, me->param, CR, LF);
} else {
if (me->total)
sprintf(chunky, "%c%c%x%c%c", CR, LF, l, CR, LF);
else
sprintf(chunky, "%x%c%c", l, CR, LF);
}
me->total += l;
PUTBLOCK(chunky, (int) strlen(chunky));
if (STREAM_TRACE) HTTrace("Chunked..... chunk size 0x%X\n", l);
if (l > 0) return PUTBLOCK(b, l);
/* Here we should provide a footer */
PUTC(CR);
PUTC(LF);
me->lastchunk = YES;
(*me->target->isa->flush)(me->target);
return HT_LOADED;
}
PRIVATE int HTChunkEncode_string (HTStream * me, const char * s)
{
return HTChunkEncode_block(me, s, (int) strlen(s));
}
PRIVATE int HTChunkEncode_character (HTStream * me, char c)
{
return HTChunkEncode_block(me, &c, 1);
}
PRIVATE int HTChunkEncode_flush (HTStream * me)
{
return (*me->target->isa->flush)(me->target);
}
PRIVATE int HTChunkEncode_free (HTStream * me)
{
#if 0
int status = HTChunkEncode_block(me, NULL, 0);
if (status != HT_WOULD_BLOCK) {
if ((status = (*me->target->isa->_free)(me->target)) == HT_WOULD_BLOCK)
return HT_WOULD_BLOCK;
HT_FREE(me);
}
return status;
#else
int status = me->target ? (*me->target->isa->_free)(me->target) : HT_OK;
HT_FREE(me);
return status;
#endif
}
PRIVATE int HTChunkEncode_abort (HTStream * me, HTList * e)
{
int status = HT_ERROR;
if (me->target) status = (*me->target->isa->_free)(me->target);
if (PROT_TRACE) HTTrace("Chunked..... ABORTING...\n");
HT_FREE(me);
return status;
}
PRIVATE const HTStreamClass HTChunkEncoderClass =
{
"ChunkEncoder",
HTChunkEncode_flush,
HTChunkEncode_free,
HTChunkEncode_abort,
HTChunkEncode_character,
HTChunkEncode_string,
HTChunkEncode_block
};
PUBLIC HTStream * HTChunkedEncoder (HTRequest * request,
void * param,
HTEncoding coding,
HTStream * target)
{
HTStream * me;
HTParentAnchor * anchor = HTRequest_anchor(request);
if ((me = (HTStream *) HT_CALLOC(1, sizeof(HTStream))) == NULL)
HT_OUTOFMEM("HTChunkEncoder");
me->isa = &HTChunkEncoderClass;
me->coding = coding;
me->target = target;
me->request = request;
me->param = (char *) param;
me->state = EOL_BEGIN;
me->status = HT_ERROR;
{
int length = me->param ? strlen(me->param)+20 : 20;
me->buf = HTChunk_new(length);
HTChunk_ensure(me->buf, length);
}
/* Adjust information in anchor */
HTAnchor_setTransfer(anchor, NULL);
if (STREAM_TRACE) HTTrace("Chunked..... Encoder stream created\n");
return me;
}
Webmaster