web822.cc #1

  • //
  • guest/
  • perforce_software/
  • p4/
  • 2014-1/
  • web/
  • web822.cc
  • View
  • Commits
  • Open Download .zip Download (6 KB)
/*
 * Copyright 1999, 2000 Perforce Software.  All rights reserved.
 *
 * This file is part of Perforce - the FAST SCM System.
 */

# include <stdhdrs.h>
# include <strbuf.h>
# include <strops.h>
# include <strdict.h>
# include <strtable.h>
# include <error.h>

# include <keepalive.h>
# include "netportparser.h"
# include <netconnect.h>
# include <netbuffer.h>
# include "web822.h"

/*
 * Web822.cc -- read and set RFC822 style headers
 *
 * LoadHeaders() uses a simple state machine to parse headers, reading
 * from the underlying NetBuffer (a buffered NetTransport) one byte at
 * a time.
 *
 * The following deficiencies come to mind:
 *
 *	It currently accepts lines without the required :, since the
 *	operation request in HTTP doesn't have it.  This should be
 *	special cased for the first line.
 *
 *	It reads a byte at a time from its source.  Ouch!
 *
 *	It uses the new, wildly inefficient StrBufDict to store the
 *	values.
 *
 *	Decent error messages aren't set on parse error.
 *
 * NetBuffer normally requires flushing, even before closing.
 * But Web822::LoadHeaders() flushes any previous Sends(), and
 * ~Web822() also flushes, so our caller shouldn't worry.
 */

/*
 * Michael's 2 second description of RFC822 style headers.
 * Note state numbers ^x:
 *
 *	Header <HS*> :  <HS*> value* <VS>
 *	^1     ^2    ^3 ^4    ^5     ^6
 *	<HS+> value* <VS>
 *	^7    ^5     ^6
 *
 * HS is space or tab
 * VS is CR, LF, CRLF, LFCR, or CRLF NULL
 */

enum CharClass {
	C_CHAR,		// non-white character
	C_COLON,	// the : char
	C_HS,		// horizontal space (tab, space)
	C_VS,		// lf, cr, crlf, lfcr, crlf\0
	C_LAST
} ;

enum ParseState {
	S_0, S_1, S_2, S_3, 
	S_4, S_5, S_6, S_7, 
	S_ERR, S_END, S_LAST
} ;

ParseState StateTable[ S_LAST ][ C_LAST ] = {

/*		CHAR	COLON	HS	VS */
/* 0 */ {	S_1,	S_ERR,	S_ERR,	S_END	},
/* 1 */ {	S_1,	S_3,	S_2,	S_ERR	},
/* 2 */	{	S_5,	S_3,	S_2,	S_ERR	},
/* 3 */	{	S_5,	S_5,	S_4,	S_ERR	},
/* 4 */	{	S_5,	S_5,	S_4,	S_ERR	},
/* 5 */	{	S_5,	S_5,	S_5,	S_6	},
/* 6 */ {	S_1,	S_ERR,	S_7,	S_END	},
/* 7 */ {	S_5,	S_5,	S_7,	S_ERR	}

} ;

int
Web822::LoadHeader()
{
	// Loading the headers clears both the incoming and the
	// outgoing, and flushes any data sent on previous request.

	transport.Flush( &e );
	recvHeaders.Clear();
	sendHeaders.Clear();
	recvBody.Clear();
	haveReadBody = 0;
	
	/*
	 * We need to handle the crazy vertical space definition,
	 * accepting CR, LF, CRLF, or LFCR.  To do so, we remember
	 * if the previous character was a CR or LF, and soak up a
	 * LF or CR from the input stream.
	 */

	enum Vs {
		V_OK,		// not in vertical space
		V_LF,		// just saw LF, soak a CR
		V_CR		// just saw CR, soak a LF
	} ;

	ParseState state = S_0;
	Vs vs = V_OK;
	StrBuf var, val;
	char c;
	
	while( Receive( &c, 1 ) == 1 )
	{
	    // Save old state, vertical spacing

	    ParseState oldState = state;
	    Vs oldVs = vs;
	    vs = V_OK;

	    // Get the character class, handling vertical spacing

	    CharClass cc;

	    switch( c )
	    {
	    // Null - skip always

	    case 0: continue;

	    // The easy ones

	    default: cc = C_CHAR; break;
	    case ':': cc = C_COLON; break;
	    case ' ': cc = C_HS; break;
	    case '\t': cc = C_HS; break;

	    // Soak up CR if just saw an LF

	    case '\r': 
		if( oldVs == V_LF )
		    continue;
		cc = C_VS;
		vs = V_CR;
		break;

	    // Soak up LF if just saw a CR

	    case '\n': 
		if( oldVs == V_CR )
		    continue;
		cc = C_VS;
		vs = V_LF;
		break;
	    }

	    // Jump to new state, given current state and input

	    state = StateTable[ state ][ cc ];

	    // If we've finished the vertical spacing after
	    // the value, and aren't seeing indenting on the next
	    // line, it is time to save the var/value pair.

	    if( oldState == S_6 && state != S_7 )
	    {
		// save the result
		var.Terminate();
		val.Terminate();
		StrOps::Lower( var );
		recvHeaders.SetVar( var, val );
		var.Clear();
		val.Clear();
	    }

	    // S_1/S_5 -- save var/value
	    // S_END/S_ERR -- return success or failure

	    switch( state )
	    {
	    case S_1: var.Extend( c ); break;
	    case S_5: val.Extend( c ); break;
	    case S_END: return 1;
	    case S_ERR: return 0;
	    }
	}

	// sudden EOF? bail.

	return 0;
}

int
Web822::LoadBody()
{
	// Loading the body flushes any data sent on previous request.
	
	transport.Flush( &e );
	recvBody.Clear();
	haveReadBody = 1;

	// Determine how many bytes to read

	StrPtr * contentlength = GetVar( StrRef ( "content-length" ) );

	if ( contentlength == NULL )
		return 0;

	int nbytes = atoi( contentlength->Value() );
	int nread = 0;
	int currRead = 0;
	char c;

	// Read number of bytes specified in Content-length variable
	// and save result, stripping out leading \n

	while ( nread < nbytes )
	{
	    currRead = Receive( &c, 1 );

		if (!currRead)
			break;		// bail

		if( nread == 0 && c == '\n' )
	    	continue;

	    nread += currRead;

	    *recvBody.Alloc(1) = c;
	}

	recvBody.Terminate();

	return nread;
}

void
Web822::GetRecvHeaders(StrBuf *headers)
{
	StrRef var, val;
	
	for( int i = 0; recvHeaders.GetVar( i, var, val ); i++ )
		*headers << var << ": " << val << "\r\n";
}

char *
Web822::GetBodyData()
{
	// If body data hasn't been read, do it now
	if( !haveReadBody )
	   (void)LoadBody();
	   
	if( recvBody.Length() == 0 )
	    return NULL;
	    
	return recvBody.Text(); 
}

int
Web822::GetBodyLgth()
{
	// If body data hasn't been read, do it now
	if( !haveReadBody )
	   (void)LoadBody();
	   
	return recvBody.Length(); 
}

void
Web822::SendHeader( const StrPtr *response )
{
	// send the http 1.0 response

	*this << "HTTP/1.0 ";
	if( response ) *this << *response;
	*this << "\r\n";

	// Step through the headers

	StrRef var, val;

	for( int i = 0; sendHeaders.GetVar( i, var, val ); i++ )
		*this << var << ": " << val << "\r\n";

	*this << "\r\n";
}

void
Web822::SendRecvHeaders()
{
	StrBuf	headers;
	StrRef var, val;
	
	headers << "Receive headers:\r\n";

	for( int i = 0; recvHeaders.GetVar( i, var, val ); i++ )
		headers << var << ": " << val << "\r\n";

	headers << "End Receive headers\r\n";
	
	Send (headers.Text(), headers.Length() );
}

void
Web822::SendSendHeaders()
{
	StrBuf	headers; 
	StrRef var, val;
	
	headers << "Send headers:\r\n";

	for( int i = 0; sendHeaders.GetVar( i, var, val ); i++ )
	    headers << var << ": " << val << "\r\n";

	headers << "End Send headers\r\n";
	
	Send (headers.Text(), headers.Length() );
}

StrPtr *
Web822::GetAddress( int raf_flags )
{
	return transport.GetAddress( raf_flags );
}

StrPtr *
Web822::GetPeerAddress( int raf_flags )
{
	return transport.GetPeerAddress( raf_flags );
}
# Change User Description Committed
#1 15902 Matt Attaway A second renaming that I will not obliterate as a badge of shame
//guest/perforce_software/p4/2014_1/web/web822.cc
#1 15901 Matt Attaway Clean up code to fit modern Workshop naming standards
//guest/perforce_software/p4/2014.1/web/web822.cc
#1 12188 Matt Attaway Move 'main' p4 into a release specific directory in prep for new releases
//guest/perforce_software/p4/web/web822.cc
#1 9129 Matt Attaway Initial commit of the 2014.1 p4/p4api source code