File:  [Public] / libwww / Library / src / HTTPReq.c
Revision 2.7: download - view: text, annotated - select for diffs
Tue Jun 27 20:21:31 1995 UTC (28 years, 11 months ago) by frystyk
Branches: MAIN
CVS tags: v3/1pre3, v3/1pre2, v3/1, NT1, NT, NNTP, HEAD
most of 3.1pre2

/*								      HTTPReq.c
**	MULTITHREADED IMPLEMENTATION OF HTTP CLIENT
**
**	This module implements the output stream for HTTP used for sending
**	requests with or without a entity body.
**
** History:
**	Jan 95 HFN	Written from scratch
**
*/

/* Library Includes */
#include "tcp.h"
#include "HTUtils.h"
#include "HTString.h"
#include "HTParse.h"
#include "HTFormat.h"
#include "HTThread.h"
#include "HTTCP.h"
#include "HTWriter.h"
#include "HTChunk.h"
#include "HTTPReq.h"					       /* Implements */

/* Type definitions and global variables etc. local to this module */ 
extern char * HTAppName;		  /* Application name: please supply */
extern char * HTAppVersion;	       /* Application version: please supply */
PUBLIC char * HTProxyHeaders = NULL;		    /* Headers to pass as-is */

/* Macros and other defines */
#define HTTP_VERSION	"HTTP/1.0"
#define MIME_VERSION	"MIME/1.0"
#define PUTC(c)		(*me->target->isa->put_character)(me->target, c)
#define PUTS(s)		(*me->target->isa->put_string)(me->target, s)
#define PUTBLOCK(b, l)	(*me->target->isa->put_block)(me->target, b, l)
#define FREE_TARGET	
#define ABORT_TARGET	(*me->target->isa->abort)(me->target, e)

/* Type definitions and global variables etc. local to this module */

PRIVATE char linebuf[256];	/* @@@ */

struct _HTStream {
    CONST HTStreamClass *	isa;
    HTStream *		  	target;
    HTRequest *			request;
    SOCKFD			sockfd;
    HTChunk *  			buffer;
    BOOL			transparent;
};

/* ------------------------------------------------------------------------- */
/* 			    HTTP Output Request Stream			     */
/* ------------------------------------------------------------------------- */

/*                                                              HTTPMakeRequest
**
**	This function composes the HTTP request header.
*/
PRIVATE void HTTPMakeRequest ARGS2(HTStream *, me, HTRequest *, request)
{
    HTChunk *header = me->buffer;
    HTParentAnchor *entity =
	(request->source && request->source->anchor) ?
	    request->source->anchor : request->anchor;

    /* Generate the HTTP/1.0 RequestLine */
    if (request->method != METHOD_INVALID) {
	HTChunkPuts(header, HTMethod_name(request->method));
	HTChunkPutc(header, ' ');
    } else
	HTChunkPuts(header, "GET ");

    /* If we are using a proxy then only take the `path' info in the URL */
    {
	char *addr = HTAnchor_physical(request->anchor);
	char *fullurl = HTParse(addr, "", PARSE_PATH|PARSE_PUNCTUATION);
	if (request->using_proxy) {
	    HTChunkPuts(header, fullurl+1);
	} else {
	    HTChunkPuts(header, fullurl);
	}
	free(fullurl);
    }
    HTChunkPutc(header, ' ');
    HTChunkPuts(header, HTTP_VERSION);
    HTChunkPutc(header, CR);
    HTChunkPutc(header, LF);

    /* General Headers */
    if (request->GenMask & HT_DATE) {
	time_t local = time(NULL);
	sprintf(linebuf, "Date: %s%c%c", HTDateTimeStr(&local, NO), CR,LF);
	HTChunkPuts(header, linebuf);
    }
    if (request->GenMask & HT_FORWARDED) {		/* @@@@@@ */
    }
    if (request->GenMask & HT_MESSAGE_ID) {
	CONST char *msgid = HTMessageIdStr();
	if (msgid) {
	    sprintf(linebuf, "Message-ID: %s%c%c", msgid, CR, LF);
	    HTChunkPuts(header, linebuf);
	}
    }
    if (request->GenMask & HT_MIME) {
	sprintf(linebuf, "MIME-Version: %s%c%c", MIME_VERSION, CR, LF);
	HTChunkPuts(header, linebuf);
    }

    /* Request Headers */
    if (request->RequestMask & HT_ACCEPT_TYPE) {
	int list;
	HTList *cur;
	for (list=0; list<2; list++) {
	    if ((!list && ((cur=HTConversions) != NULL)) ||
		(list && ((cur=request->conversions) != NULL))) {
		HTPresentation  *pres;
		while ((pres =(HTPresentation *) HTList_nextObject(cur))) {
		    if (pres->rep_out == WWW_PRESENT) {
			if (pres->quality != 1.0) {
			    sprintf(linebuf, "Accept: %s; q=%1.1f%c%c",
				    HTAtom_name(pres->rep),
				    pres->quality, CR, LF);
			} else {
			    sprintf(linebuf, "Accept: %s%c%c",
				    HTAtom_name(pres->rep), CR, LF);
			}
			HTChunkPuts(header, linebuf);
		    }
		}
	    }
	}
    }
    if (request->RequestMask & HT_ACCEPT_CHAR) {
	BOOL first=YES;
	int list;
	HTList *cur;
	for (list=0; list<2; list++) {
	    if ((!list && ((cur=HTCharsets) != NULL)) ||
		(list && ((cur=request->charsets) != NULL))) {
		HTAcceptNode *pres;
		while ((pres = (HTAcceptNode *) HTList_nextObject(cur))) {
		    if (first) {
			HTChunkPuts(header, "Accept-Charset: ");
			first=NO;
		    }
		    if (cur->next)
			sprintf(linebuf, "%s,", HTAtom_name(pres->atom));
		    else
			sprintf(linebuf, "%s%c%c", HTAtom_name(pres->atom),
				CR, LF);
		    HTChunkPuts(header, linebuf);
		}
	    }
	}
    }
    if (request->RequestMask & HT_ACCEPT_ENC) {
	BOOL first=YES;
	int list;
	HTList *cur;
	for (list=0; list<2; list++) {
	    if ((!list && ((cur=HTEncodings) != NULL)) ||
		(list && ((cur=request->encodings) != NULL))) {
		HTAcceptNode *pres;
		while ((pres = (HTAcceptNode *) HTList_nextObject(cur))) {
		    if (first) {
			HTChunkPuts(header, "Accept-Encoding: ");
			first=NO;
		    }
		    if (cur->next)
			sprintf(linebuf, "%s,", HTAtom_name(pres->atom));
		    else
			sprintf(linebuf, "%s%c%c", HTAtom_name(pres->atom),
				CR, LF);
		    HTChunkPuts(header, linebuf);
		}
	    }
	}
    }
    if (request->RequestMask & HT_ACCEPT_LAN) {
	BOOL first=YES;
	int list;
	HTList *cur;
	for (list=0; list<2; list++) {
	    if ((!list && ((cur=HTLanguages) != NULL)) ||
		(list && ((cur=request->languages) != NULL))) {
		HTAcceptNode *pres;
		while ((pres = (HTAcceptNode *) HTList_nextObject(cur))) {
		    if (first) {
			HTChunkPuts(header, "Accept-Language: ");
			first=NO;
		    }
		    if (cur->next)
			sprintf(linebuf, "%s,", HTAtom_name(pres->atom));
		    else
			sprintf(linebuf, "%s%c%c", HTAtom_name(pres->atom),
				CR, LF);
		    HTChunkPuts(header, linebuf);
		}
	    }
	}
    }
    if (request->authorization != NULL) {	    /* Put out authorization */
	sprintf(linebuf, "Authorization: %s%c%c", request->authorization,
		CR, LF);
	HTChunkPuts(header, linebuf);
    }
    if (request->RequestMask & HT_FROM) {
	CONST char *mailaddress = HTGetMailAddress();
	if (mailaddress) {
	    sprintf(linebuf, "From: %s%c%c", mailaddress, CR, LF);
	    HTChunkPuts(header, linebuf);
	}
    }
    if (request->RequestMask & HT_PRAGMA) {
	sprintf(linebuf, "Pragma: %s%c%c", "no-cache", CR, LF);
	HTChunkPuts(header, linebuf);
    }
    if (request->RequestMask & HT_REFERER && request->parentAnchor) {
	char *act = HTAnchor_address((HTAnchor *) request->anchor);
	char *parent = HTAnchor_address((HTAnchor *) request->parentAnchor);
	char *relative = HTParse(parent, act,
				 PARSE_ACCESS|PARSE_HOST|PARSE_PATH|PARSE_PUNCTUATION);
	if (relative && *relative) {
	    sprintf(linebuf, "Referer: %s%c%c", parent, CR, LF);
	    HTChunkPuts(header, linebuf);
	}
	free(act);
	free(parent);
	    free(relative);
    }
    if (request->RequestMask & HT_USER_AGENT) {
	sprintf(linebuf, "User-Agent: %s/%s libwww/%s%c%c",
		HTAppName ? HTAppName : "unknown",
		HTAppVersion ? HTAppVersion : "0.0",
		HTLibraryVersion, CR, LF);
	HTChunkPuts(header, linebuf);
    }

    /* Now put out entity headers if we are using PUT or POST. If we have a
    ** PostAnchor then we take the information from this and uses the
    ** destination anchor to contain the reply. Otherwise, we have created an
    ** anchor (using internal editing etc) and we can use the destination
    ** anchor directly.
    */
    if (request->method==METHOD_PUT || request->method==METHOD_POST) {
	if (request->EntityMask & HT_ALLOW) {		/* @@@@@@@@@@ */

	}
	if (request->EntityMask & HT_CONTENT_ENCODING &&
	    entity->content_encoding) {
	    sprintf(linebuf, "Content-Encoding: %s%c%c",
		    HTAtom_name(entity->content_encoding), CR, LF);
	    HTChunkPuts(header, linebuf);
	}

	/* @@@ SHOULD BE A LIST @@@ */
	if (request->EntityMask & HT_CONTENT_LANGUAGE &&
	    entity->content_language) {
	    sprintf(linebuf, "Content-Language: %s%c%c",
		    HTAtom_name(entity->content_language), CR, LF);
	    HTChunkPuts(header, linebuf);
	}
	if (request->EntityMask & HT_CONTENT_LENGTH) {   /* Must be there!!! */
	    sprintf(linebuf, "Content-Length: %ld%c%c",
		    entity->content_length, CR, LF);
	    HTChunkPuts(header, linebuf);	
	}
	if (request->EntityMask & HT_CTE && entity->cte) {
	    sprintf(linebuf, "Content-Transfer-Encoding: %s%c%c",
		    HTAtom_name(entity->cte), CR, LF);
	    HTChunkPuts(header, linebuf);
	}
	if (request->EntityMask & HT_CONTENT_TYPE && entity->content_type) {
	    sprintf(linebuf, "Content-Type: %s",
		    HTAtom_name(entity->content_type));
	    if (entity->charset) {
		strcat(linebuf, "; charset=");
		strcat(linebuf, HTAtom_name(entity->charset));
	    }
	    if (entity->level) {
		strcat(linebuf, "; level=");
		strcat(linebuf, HTAtom_name(entity->level));
	    }
	    HTChunkPuts(header, linebuf);
	    HTChunkPutc(header, CR);
	    HTChunkPutc(header, LF);
	}
	if (request->EntityMask & HT_DERIVED_FROM && entity->derived_from) {
	    sprintf(linebuf, "Derived-From: %s%c%c", entity->derived_from,
		    CR, LF);
	    HTChunkPuts(header, linebuf);
	}
	if (request->EntityMask & HT_EXPIRES) {		/* @@@@@@@@@@ */

	}
	if (request->EntityMask & HT_LAST_MODIFIED) {	/* @@@@@@@@@@ */

	}
	if (request->EntityMask & HT_LINK) {		/* @@@@@@@@@@ */

	}
	if (request->EntityMask & HT_TITLE) {		/* @@@@@@@@@@ */

	}
	if (request->EntityMask & HT_URI) {		/* @@@@@@@@@@ */

	}
	if (request->EntityMask & HT_VERSION && entity->version) {
	    sprintf(linebuf, "Version: %s%c%c", entity->version, CR, LF);
	    HTChunkPuts(header, linebuf);
	}
    }

    /* Put out extra information if any */
    if (request->ExtraHeaders)
	HTChunkPuts(header, request->ExtraHeaders);
    
    HTChunkPutc(header, CR);			   /* Blank line means "end" */
    HTChunkPutc(header, LF);
    HTChunkTerminate(header);
    if (PROT_TRACE)
	fprintf(TDEST, "HTTP Tx..... %s", header->data);
}

PRIVATE int HTTPRequest_put_character ARGS2(HTStream *, me, char, c)
{
    if (!me->target) {
	return HT_WOULD_BLOCK;
    } else if (me->transparent)
	return PUTC(c);
    else {
	int status;
	HTTPMakeRequest(me, me->request);		  /* Generate header */
	if ((status=PUTBLOCK(me->buffer->data, me->buffer->size-1)) == HT_OK) {
	    me->transparent = YES;
	    return PUTC(c);
	}
	return status;
    }
}

PRIVATE int HTTPRequest_put_string ARGS2(HTStream *, me, CONST char*, s)
{
    if (!me->target) {
	return HT_WOULD_BLOCK;
    } else if (me->transparent)
	return PUTS(s);
    else {
	int status;
	HTTPMakeRequest(me, me->request);		  /* Generate header */
	if ((status=PUTBLOCK(me->buffer->data, me->buffer->size-1)) == HT_OK) {
	    me->transparent = YES;
	    return PUTS(s);
	}
	return status;
    }
}

PRIVATE int HTTPRequest_put_block ARGS3(HTStream *, me, CONST char*, b, int, l)
{
    if (!me->target) {
	return HT_WOULD_BLOCK;
    } else if (me->transparent)
	return PUTBLOCK(b, l);
    else {
	int status;
	HTTPMakeRequest(me, me->request);		  /* Generate header */
	if ((status=PUTBLOCK(me->buffer->data, me->buffer->size-1)) == HT_OK) {
	    me->transparent = YES;
	    return PUTBLOCK(b, l);
	}
	return status;
    }
}

/*
**	Flushes data but doesn't free stream object
*/
PRIVATE int HTTPRequest_flush ARGS1(HTStream *, me)
{
    if (!me->target) {
	return HT_WOULD_BLOCK;
    } else if (!me->transparent) {
	int status;
	HTTPMakeRequest(me, me->request);		  /* Generate header */
	if ((status=PUTBLOCK(me->buffer->data, me->buffer->size-1)) == HT_OK)
	    me->transparent = YES;
	else
	    return status;
    }
    return HT_OK;
}

/*
**	Flushes data and frees stream object
*/
PRIVATE int HTTPRequest_free ARGS1(HTStream *, me)
{
    int status;
    if (!me->target) {
	return HT_WOULD_BLOCK;
    } else if (!me->transparent) {
	HTTPMakeRequest(me, me->request);		  /* Generate header */
	if ((status = PUTBLOCK(me->buffer->data, me->buffer->size-1)) == HT_OK)
	    me->transparent = YES;
	else
	    return status;
    }
    if ((status = (*me->target->isa->_free)(me->target)) == HT_WOULD_BLOCK)
	return HT_WOULD_BLOCK;
    HTChunkFree(me->buffer);
    free(me);
    return status;
}

PRIVATE int HTTPRequest_abort ARGS2(HTStream *, me, HTError, e)
{
    if (me->target)
	ABORT_TARGET;
    HTChunkFree(me->buffer);
    free(me);
    if (PROT_TRACE)
	fprintf(TDEST, "HTTPRequest. ABORTING...\n");
    return HT_ERROR;
}

/*	HTTPRequest Stream
**	-----------------
*/
PRIVATE CONST HTStreamClass HTTPRequestClass =
{		
    "HTTPRequest",
    HTTPRequest_flush,
    HTTPRequest_free,
    HTTPRequest_abort,
    HTTPRequest_put_character,
    HTTPRequest_put_string,
    HTTPRequest_put_block
};

PUBLIC HTStream * HTTPRequest_new ARGS2(HTRequest *,	request,
					HTStream *,	target)
{
    HTStream * me = (HTStream *) calloc(1, sizeof(HTStream));
    if (!me) outofmem(__FILE__, "HTTPRequest_new");
    me->isa = &HTTPRequestClass;
    me->target = target;
    me->request = request;
    me->buffer = HTChunkCreate(512);
    me->transparent = NO;
    return me;
}



Webmaster