(file) Return to HTTPConnection.cpp CVS log (file) (dir) Up to [Pegasus] / pegasus / src / Pegasus / Common

File: [Pegasus] / pegasus / src / Pegasus / Common / HTTPConnection.cpp (download)
Revision: 1.74, Wed Jun 30 17:20:14 2004 UTC (20 years ago) by h.sterling
Branch: MAIN
CVS Tags: RELEASE_2_4_FC_CANDIDATE_1
Changes since 1.73: +4 -2 lines
PEP#: 165
TITLE: SSL client verification
USER: Heather Sterling
MAIL: hsterl@us.ibm.com

DESCRIPTION

Ballot 60 AuthenticationInfo changes

//%2003////////////////////////////////////////////////////////////////////////
//
// Copyright (c) 2000, 2001, 2002  BMC Software, Hewlett-Packard Development
// Company, L. P., IBM Corp., The Open Group, Tivoli Systems.
// Copyright (c) 2003 BMC Software; Hewlett-Packard Development Company, L. P.;
// IBM Corp.; EMC Corporation, The Open Group.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// 
// THE ABOVE COPYRIGHT NOTICE AND THIS PERMISSION NOTICE SHALL BE INCLUDED IN
// ALL COPIES OR SUBSTANTIAL PORTIONS OF THE SOFTWARE. THE SOFTWARE IS PROVIDED
// "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
// LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
//==============================================================================
//
// Author: Mike Brasher (mbrasher@bmc.com)
//
// Modified By:
//         Nag Boranna, Hewlett-Packard Company(nagaraja_boranna@hp.com)
//         Jenny Yu, Hewlett-Packard Company (jenny_yu@hp.com)
//         Dave Rosckes (rosckes@us.ibm.com)
//         Amit Arora, IBM (amita@in.ibm.com)
//         Heather Sterling, IBM (hsterl@us.ibm.com)
//         Brian G. Campbell, EMC (campbell_brian@emc.com) - PEP140/phase1
//
//%/////////////////////////////////////////////////////////////////////////////

#include <Pegasus/Common/Config.h>
#include <Pegasus/Common/Constants.h>

#include <iostream>
#include <cctype>
#include <cstdlib>
#include "Socket.h"
#include "TLS.h"
#include "HTTPConnection.h"
#include "MessageQueue.h"
#include "Monitor.h"
#include "HTTPMessage.h"
#include "Signal.h"
#include "Tracer.h"

#ifdef PEGASUS_KERBEROS_AUTHENTICATION
#include <Pegasus/Common/CIMKerberosSecurityAssociation.h>
#endif
#include <Pegasus/Common/XmlWriter.h>

PEGASUS_USING_STD;

PEGASUS_NAMESPACE_BEGIN

// initialize the request count

AtomicInt HTTPConnection::_requestCount = 0;

////////////////////////////////////////////////////////////////////////////////
//
// Local routines:
//
////////////////////////////////////////////////////////////////////////////////

/*
 * string and number constants for HTTP sending/receiving
 */

// buffer size for sending receiving
static const Uint32 httpTcpBufferSize = 8192;

// string constants for HTTP header. "Name" represents strings on the left 
// side of headerNameTerminator and "Value" represents strings on the right 
// side of headerNameTerminator

#define headerNameTrailer "Trailer"
#undef CRLF
#define CRLF "\r\n"

static const char headerNameTransferTE[] = "TE";
static const char headerNameTransferEncoding[] = "transfer-encoding";
static const char headerNameContentLength[] = "content-length";
static const char headerValueTransferEncodingChunked[] = "chunked";
static const char headerValueTransferEncodingIdentity[] = "identity";
static const char headerValueTEchunked[] = "chunked";
static const char headerValueTEtrailers[] = "trailers";
static const char headerNameError[] = "CIMError";
static const char headerNameCode[] = "CIMStatusCode";
static const char headerNameDescription[] = "CIMStatusCodeDescription";
static const char headerNameOperation[] = "CIMOperation";

// the names comes from the HTTP specification on chunked transfer encoding

static const char headerNameTerminator[] = ": ";
static const char headerValueSeparator[] = ", ";
static const char headerLineTerminator[] = CRLF;
static const char headerTerminator[] = CRLF CRLF;
static const Sint8 chunkLineTerminator[] = CRLF;
static const Sint8 chunkTerminator[] = CRLF;
static const Sint8 chunkBodyTerminator[] = CRLF;
static const Sint8 trailerTerminator[] = CRLF CRLF;
static const Sint8 chunkExtensionTerminator[] = ";";

// string sizes

static const Uint32 headerNameContentLengthLength = sizeof(headerNameContentLength)-1;
static const Uint32 headerValueTransferEncodingChunkedLength = sizeof(headerValueTransferEncodingChunked)-1;
static const Uint32 headerNameTransferEncodingLength = sizeof(headerNameTransferEncoding)-1;
static const Uint32 headerNameTerminatorLength =sizeof(headerNameTerminator)-1;
static const Uint32 headerLineTerminatorLength =sizeof(headerLineTerminator)-1;
static const Uint32 chunkLineTerminatorLength = sizeof(chunkLineTerminator)-1;
static const Uint32 chunkTerminatorLength = sizeof(chunkTerminator)-1;
static const Uint32 chunkBodyTerminatorLength = sizeof(chunkBodyTerminator)-1;
static const Uint32 trailerTerminatorLength = sizeof(trailerTerminator)-1;
static const Uint32 chunkExtensionTerminatorLength = sizeof(chunkExtensionTerminator)-1;

// the number of bytes it takes to place a Uint32 into a string (minus null)
static const Uint32 numberAsStringLength = 10;

/*
 * given an HTTP status code, return the description. not all codes are listed
 * here. Unmapped codes result in the internal error string.
 * Add any required future codes here.
 */

static const String httpDetailDelimiter = headerValueSeparator;
static const String httpStatusInternal = HTTP_STATUS_INTERNALSERVERERROR;

/*
 * throw given http code with detail, file, line
 * This is shared client/server code. The caller will decide what to do
 * with the thrown message
 */

static void _throwEventFailure(const String &status, const String &detail,
															 const char *func, 
															 const char *file , Uint32 line)
{ 
	String message = status + httpDetailDelimiter + detail;
  Tracer::trace(file, line, TRC_HTTP, Tracer::LEVEL2, message);
	if (status == httpStatusInternal)
		throw AssertionFailureException(file, line, message);
  else throw Exception(message);
}

// throw a http exception. This is used for both client and server common code.
// The macro allows is used for file, line inclusion for debugging

#define _throwEventFailure(status, detail) \
  _throwEventFailure(status, String(detail), func, __FILE__, __LINE__)

static inline Uint32 _Min(Uint32 x, Uint32 y) 
{
    return x < y ? x : y; 
}

static char* _FindSeparator(const char* data, Uint32 size)
{
    const char* p = data;
    const char* end = p + size;

    while (p != end)
    {
	if (*p == '\r')
	{
	    Uint32 n = end - p;

	    if (n >= 2 && p[1] == '\n')
		return (char*)p;
	}
	else if (*p == '\n')
	    return (char*)p;

	p++;
    }

    return 0;
}

// Used to test signal handling
void * sigabrt_generator(void * parm)
{
    abort();
    return 0;
}


////////////////////////////////////////////////////////////////////////////////
//
// HTTPConnection
//
////////////////////////////////////////////////////////////////////////////////

HTTPConnection::HTTPConnection(
    Monitor* monitor,
		AutoPtr<MP_Socket>& socket,
    MessageQueue* ownerMessageQueue,
    MessageQueue* outputMessageQueue,
		Boolean exportConnection)
    : 
   Base(PEGASUS_QUEUENAME_HTTPCONNECTION), 
   _monitor(monitor),
   _socket(socket.get()), 
   _ownerMessageQueue(ownerMessageQueue),
   _outputMessageQueue(outputMessageQueue),
   _contentOffset(-1),
   _contentLength(-1),
   _connectionClosePending(false)
{
   PEG_METHOD_ENTER(TRC_HTTP, "HTTPConnection::HTTPConnection");

   //Socket::disableBlocking(_socket);
   _socket->disableBlocking();
   _authInfo = new AuthenticationInfo(true);

   // Add SSL verification information to the authentication information
   if (_socket->isSecure()) 
   {
       //
       // Set the flag to indicate that the request was received on
       // export Connection
       //
       if (exportConnection)
       {
          _authInfo->setExportConnection(exportConnection);
       }

       if (_socket->isPeerVerificationEnabled() && _socket->isCertificateVerified()) 
       {
               _authInfo->setAuthStatus(AuthenticationInfoRep::AUTHENTICATED);
			   _authInfo->setAuthType(AuthenticationInfoRep::AUTH_TYPE_SSL);
       }
    }

   _responsePending = false;
   _connectionRequestCount = 0;
   _transferEncodingChunkOffset = 0;

   PEG_METHOD_EXIT();
}

HTTPConnection::~HTTPConnection()
{
   PEG_METHOD_ENTER(TRC_HTTP, "HTTPConnection::~HTTPConnection");

    _socket->close();
    //delete _socket;
    delete _authInfo;

   PEG_METHOD_EXIT();
}


void HTTPConnection::handleEnqueue(Message *message)
{
   PEG_METHOD_ENTER(TRC_HTTP, "HTTPConnection::handleEnqueue");

   if( ! message )
   {
      PEG_METHOD_EXIT();
      return;
   }

   Boolean LockAcquired = false;
	 
	 if (pegasus_thread_self() != _connection_mut.get_owner())
	 {
     _connection_mut.lock(pegasus_thread_self());  // Use lock_connection() ?
     LockAcquired = true;
	 }

   switch (message->getType())
   {
		 case SOCKET_MESSAGE:
		 {
			 Tracer::trace(TRC_HTTP, Tracer::LEVEL4,
										 "HTTPConnection::handleEnqueue - SOCKET_MESSAGE");
			 SocketMessage* socketMessage = (SocketMessage*)message;
			 if (socketMessage->events & SocketMessage::READ)
				 _handleReadEvent();
			 break;
		 }

		 case HTTP_MESSAGE:
		 {
			 Tracer::trace(TRC_HTTP, Tracer::LEVEL4,
										 "HTTPConnection::handleEnqueue - HTTP_MESSAGE");

			 _handleWriteEvent(*message);
			 break;
		 }
		 
     default:
			 // ATTN: need unexpected message error!
			 break;
   } // switch
	 
   delete message;
   
   if (LockAcquired)
   {
     _connection_mut.unlock();  // Use unlock_connection() ?
   }
   PEG_METHOD_EXIT();
}

/*
 * handle the message coming down from the above. This is shared client and 
 * server code. If the message is coming in chunks, then validate the chunk 
 * sequence number. If the message is being processed on the server side,
 * make sure the client has requested transfer encodings and/or trailers before
 * sending them. If not, the message must be queued up until a complete flag
 * has arrived.
 */

Boolean HTTPConnection::_handleWriteEvent(Message &message)
{
  static const char func[] = "HTTPConnection::_handleWriteEvent";
	String httpStatus;
	HTTPMessage& httpMessage = *(HTTPMessage*)&message;
	Array<Sint8>& buffer = httpMessage.message;
	Boolean isFirst = message.isFirst();
	Boolean isLast = message.isComplete();
	Sint32 totalBytesWritten = 0;
	Uint32 messageLength = buffer.size();

	try
	{

#ifdef PEGASUS_KERBEROS_AUTHENTICATION

		// The following is processing to wrap (encrypt) the response from the
		// server when using kerberos authentications.
		// If the security association does not exist then kerberos authentication
		// is not being used.
		CIMKerberosSecurityAssociation *sa = _authInfo->getSecurityAssociation();

		if (sa)
		{
			// The message needs to be parsed in order to distinguish between the
			// headers and content. When parsing, the code breaks out
			// of the loop as soon as it finds the double separator that terminates
			// the headers so the headers and content can be easily separated.
			// Parse the HTTP message:
			String startLine;
			Array<HTTPHeader> headers;
			Uint32 contentLength = 0;
			httpMessage.parse(startLine, headers, contentLength);
			Boolean authrecExists = false;
			String authorization = String::EMPTY;
			if (HTTPMessage::lookupHeader(headers, "WWW-Authenticate", authorization, false))
			{
				authrecExists = true;
			}
			
			// The following is processing to wrap (encrypt) the response from the
			// server when using kerberos authentications.
			sa->wrapResponseMessage(buffer, contentLength, authrecExists);
			messageLength = buffer.size();
		}
#endif
		
		// ATTN: convert over to asynchronous write scheme:
		// Send response message to the client (use synchronous I/O for now:

		_socket->enableBlocking();
		SignalHandler::ignore(PEGASUS_SIGPIPE);
		
		// use the next four lines to test the SIGABRT handler
		//getSigHandle()->registerHandler(PEGASUS_SIGABRT, sig_act);
		//getSigHandle()->activate(PEGASUS_SIGABRT);
		//Thread t(sigabrt_generator, NULL, false);
		//t.run();
		
		// delivery behavior:
		// 1 handler.processing() : header + optional body?
		// 2 handler.deliver()    : 1+ fully XML encoded object(s)
		// 3 handler.complete()   : deliver() + isLast = true
		
		Uint32 bytesRemaining = messageLength;
		Sint8 *messageStart = (Sint8 *)buffer.getData();
		Uint32 bytesToWrite = httpTcpBufferSize;
		Uint32 messageIndex = message.getIndex();
		Boolean isChunkResponse = false;
		Boolean isChunkRequest = false;

		if (_isClient() == false)
		{
			// for null termination
			buffer.reserveCapacity(messageLength + 1);
			messageStart = (Sint8 *)buffer.getData();
			messageStart[messageLength] = 0;

			if (isFirst == true)
			{
				_incomingBuffer.clear();
				// tracks the message coming from above
				_transferEncodingChunkOffset = 0;
				_mpostPrefix.clear();
			}
			else
			{
				// this is coming from our own internal code, therefore it is an
				// internal error. somehow the chunks came out of order.
				if (_transferEncodingChunkOffset+1 != messageIndex)
					_throwEventFailure(httpStatusInternal, "chunk sequence mismatch");
				_transferEncodingChunkOffset++;
			}

			// check to see if the client requested chunking OR trailers. trailers
			// are tightly integrated with chunking, so it can also be used.

			if (_transferEncodingTEValues.size() > 0 && 
					(Contains(_transferEncodingTEValues, String(headerValueTEchunked)) ||
					 Contains(_transferEncodingTEValues, String(headerValueTEtrailers))))
			{
				isChunkRequest = true;
			}
		
			// We now need to adjust the contentLength line.
			// If chunking was requested and this is the first chunk, then we need
			// to enter this block so we can adjust the header and send to the client
			// the first set of bytes right away.
			// If chunking was NOT requested, we have to wait for the last chunk of
			// the message to get (and set) the size of the content because we are 
			// going to send it the traditional (i.e non-chunked) way

			if (isChunkRequest == true && isFirst == true ||
					isChunkRequest == false && isLast == true)
			{
				// need to find the end of the header
				String startLine;
				Array<HTTPHeader> headers;
				Uint32 contentLength = 0;
				
				// Note: this gets the content length from subtracting the header 
				// length from the messageLength, not by parsing the content length
				// header field
				
				httpMessage.parse(startLine, headers, contentLength);
				Uint32 httpStatusCode = 0;
				String httpVersion;
				String reasonPhrase;
				Boolean isValid = httpMessage.
					parseStatusLine(startLine, httpVersion, httpStatusCode,reasonPhrase);
				Uint32 headerLength = messageLength - contentLength;
				Sint8 save = messageStart[headerLength];
				messageStart[headerLength] = 0;
			
				Sint8 *contentLengthStart = 
					strstr(messageStart, headerNameContentLength);

				Sint8 *contentLengthEnd = contentLengthStart ? 
					strstr(contentLengthStart, headerLineTerminator) : 0;
				
				messageStart[headerLength] = save;

				// the message may or may not have the content length specified 
				// depending on the type of request it is

				if (contentLengthStart)
				{
					// the message has the content length specified.
					// If we are NOT sending a chunked response, then we need to overlay 
					// the contentLength number to reflect the actual byte count of the
					// content (i.e message body). If we ARE sending a chunked response,
					// then we will overlay the transferEncoding keyword name and value
					// on top of the contentLength keyword and value.

					// Important note:
					// for performance reasons, the contentLength and/or transferEncoding
					// strings are being overlayed DIRECTLY inside the message buffer 
					// WITHOUT changing the actual length in bytes of the message. 
					// The XmlWriter has been modified to pad out the maximum number in 
					// zeros to accomodate any number. The maximum contentLength name and
					// value is identical to the transferEncoding name and value and can
					// be easily interchanged. By doing this, we do not have to piece 
					// together the header (and more importantly, the lengthy body)
					// all over again!
					// This is why the http line lengths are validated below

					Uint32 transferEncodingLineLengthExpected = 
						headerNameTransferEncodingLength +
						headerNameTerminatorLength +
						headerValueTransferEncodingChunkedLength;
					
					Uint32 contentLengthLineLengthExpected = 
						headerNameContentLengthLength + 
						headerNameTerminatorLength + numberAsStringLength;
					
					Uint32 contentLengthLineLengthFound = 
						contentLengthEnd - contentLengthStart;

					if (isValid == false || ! contentLengthEnd ||
							contentLengthLineLengthFound !=
							transferEncodingLineLengthExpected ||
							transferEncodingLineLengthExpected !=
							contentLengthLineLengthExpected)
					{
						// these should match up since this is coming directly from our
						// code in XmlWriter! If not,some code changes have got out of sync

						_throwEventFailure(httpStatusInternal, 
															 "content length was incorrectly formatted");
					}
					
					// we will be sending a chunk response if:
					// 1. chunking has been requested AND
					// 2. contentLength has been set 
					//    (meaning entire message has come in) OR
					// 3. this is not the last message 
					//  (meaning the data is coming in pieces and we should send chunked)

					if (isChunkRequest == true &&	(contentLength > 0 || isLast == false))
						isChunkResponse = true;
				
					save = contentLengthStart[contentLengthLineLengthExpected];
					contentLengthStart[contentLengthLineLengthExpected] = 0;

					// overlay the contentLength value
					if (isChunkResponse == false)
					{
						// overwrite the content length number with the actual byte count
						Sint8 *contentLengthNumberStart = contentLengthStart + 
							headerNameContentLengthLength + headerNameTerminatorLength;
						char format[6];
						sprintf (format, "%%.%uu", numberAsStringLength);
						// overwrite the bytes in buffer with the content encoding length
						sprintf(contentLengthNumberStart, format, contentLength);
						contentLengthStart[contentLengthLineLengthExpected] = save;
					}
					else
					{
						// overlay the contentLength name and value with the
						// transferEncoding name and value

						sprintf(contentLengthStart, "%s%s%s",headerNameTransferEncoding,
										headerNameTerminator,headerValueTransferEncodingChunked);
						bytesToWrite = messageLength - contentLength;

						contentLengthStart[contentLengthLineLengthExpected] = save;
						String operationName = headerNameOperation;
						// look for 2-digit prefix (if mpost was use)
						HTTPMessage::lookupHeaderPrefix(headers, operationName,
																						_mpostPrefix);
					} // else chunk response is true

				} // if content length was found
			} // if this is the first chunk containing the header
			else
			{
				// if chunking was requested, then subsequent messages that are 
				// received need to turn response chunking on
				
				if (isChunkRequest == true && messageIndex > 0)
				{
					isChunkResponse = true;
					bytesToWrite = messageLength;
				}
			}
			
			if (isChunkResponse == false)
			{
				// we are not sending chunks in the response because either
				// the client did not request it or there is no body to send
				// because of bodyless message or an error

				if (isLast == false)
				{
					// this tells the send loop (below) to NOT send any of the 
					// data collected yet
					bytesRemaining = 0;
					
					Uint32 capacity = _incomingBuffer.size() + messageLength + 1;
					_incomingBuffer.reserveCapacity(capacity);
					_incomingBuffer.appendArray(buffer);
					if (_incomingBuffer.size() > 0)
					{
						Sint8 *data = (Sint8 *) _incomingBuffer.getData();
						data[_incomingBuffer.size()] = 0;
					}

					messageLength = 0;
					messageStart = 0;
				}
				else
				{
					// performance: if this was the last of at least 2 chunks, get it
					// from the incoming buffer which has been collecting them
					if (isFirst == false)
					{
						messageLength = _incomingBuffer.size();
						messageStart = (Sint8 *)_incomingBuffer.getData();
					}
					// ... else get it from the single buffer
					else
					{
						messageLength = buffer.size();
						messageStart = (Sint8 *)buffer.getData();
					}
					bytesRemaining = messageLength;
				}
			} // if not sending chunks
		} // if not a client

		static const char errorSocket[] = "socket write error";
		Sint8 *sendStart = messageStart;
		Sint32 bytesWritten = 0;
		
		if (isFirst == true && isChunkResponse == true)
		{
			// send the header first for chunked reponses.

			// dont include header terminator yet
			Uint32 headerLength = bytesToWrite;
			bytesToWrite -= headerLineTerminatorLength;

			bytesWritten = _socket->write(sendStart, bytesToWrite);
			if (bytesWritten < 0)
				_throwEventFailure(httpStatusInternal, errorSocket);
			totalBytesWritten += bytesWritten;
			bytesRemaining -= bytesWritten;

			// put in trailer header.

			Array<Sint8> trailer;
			trailer << headerNameTrailer << headerNameTerminator << 
				_mpostPrefix << headerNameCode <<	headerValueSeparator << 
				_mpostPrefix << headerNameDescription << headerLineTerminator;
			sendStart = (Sint8 *)trailer.getData();
			bytesToWrite = trailer.size();
			bytesWritten = _socket->write(sendStart, bytesToWrite);

			if (bytesWritten < 0)
				_throwEventFailure(httpStatusInternal, errorSocket);
			totalBytesWritten += bytesWritten;
			// the trailer is outside the header buffer, so dont include in
			// tracking variables

			// now send header terminator
			bytesToWrite = headerLineTerminatorLength;
			sendStart = messageStart + headerLength - bytesToWrite;
			bytesWritten = _socket->write(sendStart, bytesToWrite);
			if (bytesWritten < 0)
				_throwEventFailure(httpStatusInternal, errorSocket);
			totalBytesWritten += bytesWritten;
			bytesRemaining -= bytesWritten;

			messageStart += headerLength;
			messageLength -= headerLength;
			sendStart = messageStart;
			bytesWritten = 0;
			bytesToWrite = bytesRemaining;
		} // if first chunk of chunked response
		
		// room enough for hex string representing chunk length and terminator
		char chunkLine[sizeof(Uint32)*2 + chunkLineTerminatorLength+1];
		
		for (; bytesRemaining > 0; )
		{
			if (isChunkResponse == true)
			{
				// send chunk line containing hex string and chunk line terminator
				sprintf(chunkLine, "%x%s", bytesToWrite, chunkLineTerminator);
				sendStart = chunkLine;
				Sint32 chunkBytesToWrite = strlen(sendStart);
				bytesWritten = _socket->write(sendStart, chunkBytesToWrite);
				if (bytesWritten < 0)
					_throwEventFailure(httpStatusInternal, errorSocket);
				totalBytesWritten += bytesWritten;
			}
			
			// for chunking, we will send the entire chunk data in one send, but
			// for non-chunking, we will send incrementally
			else bytesToWrite = _Min(bytesRemaining, bytesToWrite);

			// send non-chunked data
			sendStart = messageStart + messageLength - bytesRemaining;
			bytesWritten = _socket->write(sendStart, bytesToWrite);
			if (bytesWritten < 0)
				_throwEventFailure(httpStatusInternal, errorSocket);
			totalBytesWritten += bytesWritten;
			bytesRemaining -= bytesWritten;
			
			if (isChunkResponse == true)
			{
				// send chunk terminator, on the last chunk, it is the chunk body
				// terminator
				Array<Sint8> trailer;
				trailer << chunkLineTerminator;

				// on the last chunk, attach the last chunk termination sequence:
				// 0 + last chunk terminator + optional trailer + chunkBodyTerminator

				if (isLast == true)
				{
					if (bytesRemaining > 0)
						_throwEventFailure(httpStatusInternal, 
															 "more bytes after indicated last chunk");
					trailer << "0" << chunkLineTerminator;
					Uint32 httpStatus = httpMessage.cimException.getCode();

					if (httpStatus != 0)
					{
						char httpStatusP[11];
						sprintf(httpStatusP, "%u",httpStatus);

						trailer << _mpostPrefix << headerNameCode << headerNameTerminator 
										<< httpStatusP << headerLineTerminator;
						const String& httpDescription = httpMessage.cimException.getMessage();
						if (httpDescription.size() != 0)
							trailer << _mpostPrefix << headerNameDescription << 
								headerNameTerminator << httpDescription << headerLineTerminator;

						// terminate the header
						trailer << headerLineTerminator;
					}

					// now add chunkBodyTerminator
					trailer << chunkBodyTerminator;
				} // if isLast

				sendStart = (Sint8 *)trailer.getData();
				Sint32 chunkBytesToWrite = (Sint32) trailer.size();
				bytesWritten = _socket->write(sendStart, chunkBytesToWrite);
				if (bytesWritten < 0)
					_throwEventFailure(httpStatusInternal, errorSocket);
				totalBytesWritten += bytesWritten;
			} // isChunkResponse == true

		} // for all bytes in message
		
	} // try
	
	catch (Exception &e)
	{
		httpStatus = e.getMessage();
	}
	catch (...)
	{
		httpStatus = HTTP_STATUS_INTERNALSERVERERROR;
		String message("Unknown internal error");
		Tracer::trace(__FILE__, __LINE__, TRC_HTTP, Tracer::LEVEL2, message);
	}

	if (httpStatus.size() > 0)
		isLast = true;

	if (isLast == true)
	{
		_incomingBuffer.clear();
		_transferEncodingTEValues.clear();

     //
     // handle automatic truststore update, if enabled
     //
     if (_socket->isSecure() && _socket->isPeerVerificationEnabled())
     {
			 Tracer::trace(TRC_HTTP, Tracer::LEVEL3,
										 "Authenticated = %d; Username = %s",
										 _authInfo->isAuthenticated(), 
										 (const char*)_authInfo->getAuthenticatedUser().getCString());
			 
			 // If the client sent an untrusted certificate along with valid credentials
			 // for the SSL truststore, add the certificate to the server's truststore.
			 // This will fail in the addTrustedClient function if enableSSLTrustStoreAutoUpdate is 
			 // not enabled.
			 if (_authInfo->isAuthenticated() &&
				 _socket->getPeerCertificate() && 
				 _socket->getPeerCertificate()->getErrorCode() != (Uint32) SSLCertificateInfo::V_OK)
			 {
				 _socket->addTrustedClient(_authInfo->getAuthenticatedUser().getCString());
			 }
     }

		//
		// decrement request count
		//
		
		_requestCount--;
		_socket->disableBlocking();
		_responsePending = false;

		if (httpStatus.size() == 0)
		{
			static const char msg[] = 
				"A response has been sent (%d of %d bytes have been written).\n"
				"There are %d requests pending within the CIM Server.\n" 
				"A total of %d requests have been processed on this connection.";
			
			Tracer::trace(TRC_HTTP, Tracer::LEVEL4, msg, totalBytesWritten,
										messageLength, _requestCount.value(), _connectionRequestCount);
		}
		
	}
	
	return httpStatus.size() == 0 ? false : true;

}

void HTTPConnection::handleEnqueue()
{
   Message* message = dequeue();

    if (!message)
        return;
    handleEnqueue(message);
}

Boolean _IsBodylessMessage(const char* line)
{
    //ATTN: Make sure this is the right place to check for HTTP/1.1 and
    //      HTTP/1.0 that is part of the authentication challenge header.
    // ATTN-RK-P2-20020305: How do we make sure we have the complete list?
    const char* METHOD_NAMES[] =
    {
        "GET",
        "HTTP/1.1 400",
        "HTTP/1.0 400",
        "HTTP/1.1 401",
        "HTTP/1.0 401",
        "HTTP/1.1 413",
        "HTTP/1.0 413",
        "HTTP/1.1 500",
        "HTTP/1.0 500",
        "HTTP/1.1 501",
        "HTTP/1.0 501",
        "HTTP/1.1 503",
        "HTTP/1.0 503"
    };

    const Uint32 METHOD_NAMES_SIZE = sizeof(METHOD_NAMES) / sizeof(char*);

    for (Uint32 i = 0; i < METHOD_NAMES_SIZE; i++)
    {
	Uint32 n = strlen(METHOD_NAMES[i]);

	if (strncmp(line, METHOD_NAMES[i], n) == 0 && isspace(line[n]))
	    return true;
    }

    return false;
}

void HTTPConnection::_getContentLengthAndContentOffset() throw (Exception)
{
  static const char func[] = 
    "HTTPConnection::_getContentLengthAndContentOffset";
    Uint32 size = _incomingBuffer.size();
		if (size == 0)
			return;
    char* data = (char*)_incomingBuffer.getData();
    char* line = (char*)data;
    char* sep;
    Uint32 lineNum = 0;
    Boolean bodylessMessage = false;

    while ((sep = _FindSeparator(line, size - (line - data))))
    {
	char save = *sep;
	*sep = '\0';

	// Did we find the double separator which terminates the headers?

	if (line == sep)
	{
	    *sep = save;
	    line = sep + ((save == '\r') ? 2 : 1);
	    _contentOffset = line - _incomingBuffer.getData();

	    // reserve space for entire non-chunked message
	    if (_contentLength > 0)
	    {
	      Uint32 capacity = (Uint32)(_contentLength + _contentOffset + 1);
	      _incomingBuffer.reserveCapacity(capacity);
        data = (char *)_incomingBuffer.getData();
        data[capacity-1] = 0;
	    }

	    break;
	}

	// If this is one of the bodyless methods, then we can assume the
	// message is complete when the "\r\n\r\n" is encountered.

	if (lineNum == 0 && _IsBodylessMessage(line))
	    bodylessMessage = true;

	// Look for the content-length if not already found:

	char* colon = strchr(line, ':');

	if (colon)
	{
	    *colon  = '\0';
			
			// remove whitespace after colon before value
			char *valueStart = colon + 1;
			while(*valueStart == ' ' || *valueStart == '\t')
				valueStart++;

			// we found some non-whitespace token
			if (valueStart != sep)
			{
				char *valueEnd = sep - 1;

				// now remove whitespace from end of line back to last byte of value
				while(*valueEnd == ' ' || *valueEnd == '\t')
					valueEnd--;

				char valueSave = *(valueEnd+1);

				if (System::strcasecmp(line, headerNameContentLength) == 0)
				{
					if (_transferEncodingValues.size() == 0)
						_contentLength = atoi(valueStart);
					else _contentLength = -1;
				}
				else if (System::strcasecmp(line, headerNameTransferEncoding) == 0)
				{
					_transferEncodingValues.clear();

					if (strcmp(valueStart,headerValueTransferEncodingChunked) == 0)
						_transferEncodingValues.append(headerValueTransferEncodingChunked);
					else if (strcmp(valueStart,headerValueTransferEncodingIdentity) == 0)
						; // do nothing
					else _throwEventFailure(HTTP_STATUS_NOTIMPLEMENTED, 
																	"unimplemented transfer-encoding value");
					_contentLength = -1;
				}
				else if (System::strcasecmp(line, headerNameTransferTE) == 0)
				{
					_transferEncodingTEValues.clear();
					static const char valueDelimiter = ',';
					char *valuesStart = valueStart;

					// now tokenize the values
					while (*valuesStart)
					{
						// strip off whitepsace from the front
						while(*valuesStart == ' ' || *valuesStart == '\t')
							valuesStart++;

						if (valuesStart == valueEnd)
							break;

						char *v = strchr(valuesStart, valueDelimiter);
						if (v)
						{
							if (v == valuesStart)
							{
								valuesStart++;
								continue;
							}
							v--;
							// strip off whitespace from the end
							while(*v == ' ' || *v == '\t')
								v--;
							v++;
							*v = 0;
						}

						_transferEncodingTEValues.append(valuesStart);

						if (v)
						{
							*v = valueDelimiter;
							valuesStart = v+1;
						}
						else break;
					}
				}
				
				*(valueEnd+1) = valueSave;
			} // if some value tokens

	    *colon = ':';
	}

	*sep = save;
	line = sep + ((save == '\r') ? 2 : 1);
	lineNum++;
    }

    if (_contentOffset != -1 && bodylessMessage)
	_contentLength = 0;
}

void HTTPConnection::_clearIncoming()
{
    _contentOffset = -1;
    _contentLength = -1;
    _incomingBuffer.clear();
		_mpostPrefix.clear();
}

void HTTPConnection::_closeConnection()
{
   // return - don't send the close connection message. 
   // let the monitor dispatch function do the cleanup. 
   PEG_METHOD_ENTER(TRC_HTTP, "HTTPConnection::_closeConnection");
   _connectionClosePending = true; 

   if (_responsePending)
   {
       Tracer::trace(TRC_DISCARDED_DATA, Tracer::LEVEL2,
         "HTTPConnection::_closeConnection - Connection being closed with response still pending.");
   }

   if (_connectionRequestCount == 0)
   {
       Tracer::trace(TRC_HTTP, Tracer::LEVEL4,
         "HTTPConnection::_closeConnection - Connection being closed without receiving any requests.");
   }

   PEG_METHOD_EXIT();

//     Message* message= new CloseConnectionMessage(_socket->getSocket));
//     message->dest = _ownerMessageQueue->getQueueId();
//    SendForget(message);
//    _ownerMessageQueue->enqueue(message);
}

// determine if the current code being executed is on the client side

Boolean HTTPConnection::_isClient()
{
	return strcmp(get_owner().getQueueName(), 
								PEGASUS_QUEUENAME_HTTPCONNECTOR) == 0 ? true : false;
}

/*
 * determine if the data read in should be treated as transfer encoded data.
 * If so, proceed to strip out the transfer encoded meta data within the body
 * but the headers relating to transfer encoding will remain unchanged. 
 * One should refer to the transfer encoding section of the HTTP protocol 
 * specification to understand the parsing semantics below.
 * NOTE: this function is coded as a syncronous read! The entire message will
 * be read in before the message leaves this class and is passed up to the
 * client application.
 */

void HTTPConnection::_handleReadEventTransferEncoding() throw (Exception)
{
  static const char func[] = "HTTPConnection::_handleReadEventTransferEncoding";
  PEG_METHOD_ENTER(TRC_HTTP, func);
  Uint32 messageLength = _incomingBuffer.size();
  Uint32 headerLength = (Uint32) _contentOffset;

  // return immediately under these conditions:

  // - Header terminator has not been reached yet (_contentOffset < 0)
  // - This is a non-transfer encoded message because the content length
	//   has been set from the given header value (_contentLength > 0)
  //   (_contentLength == 0 means bodyless, so return too - section 4.3)
  // - The message read in so far is <= to the header length
  // - No transfer encoding has been declared in the header.

  if (_contentOffset < 0 || _contentLength >= 0 || 
			messageLength <= headerLength || _transferEncodingValues.size() == 0)
  {
    PEG_METHOD_EXIT();
    return;
  }

	// on the first chunk in the message, set the encoding offset to the content
	// offset

  if (_transferEncodingChunkOffset == 0)
    _transferEncodingChunkOffset = (Uint32) _contentOffset;

  Sint8 *headerStart = (Sint8 *) _incomingBuffer.getData();
  Sint8 *messageStart = headerStart;

  // loop thru the received data (so far) and strip out all chunked meta data.
	// this logic assumes that the data read in may be only partial at any point
	// during the parsing. the variable _transferEncodingChunkOffset represents
	// the byte offset (from the start of the message) of the last NON completed
	// chunk parsed within the message. Remember that the tcp reader has padded
	// the buffer with a terminating null for easy string parsing.

  for (;;)
  {
    // we have parsed the length, but not all bytes of chunk have been read
		// in yet
    if (_transferEncodingChunkOffset >= messageLength)
      break;

    // this is the length from _transferEncodingChunkOffset to the end
    // of the message (so far). It represents the bytes that have not been
    // processed yet
      
    Uint32 remainderLength = messageLength - _transferEncodingChunkOffset;

		// the start of the first fully non-parsed chunk of this interation
    Sint8 *chunkLineStart = messageStart + _transferEncodingChunkOffset;
    Sint8 *chunkLineEnd = chunkLineStart;

    // Find the end of the hex string representing the data portion length of 
		// the current chunk. Note that we must hit at least one non-hexdigit 
		// (except null) to know we have read in the complete number

    while (isxdigit(*chunkLineEnd))
      chunkLineEnd++;
    
    if (! *chunkLineEnd)
      break;

    // This is the parsed chunk length in hex. From here on, this many bytes
		// plus the chunk terminator (AFTER this chunk line is done) must be
		// read in to constitute a complete chunk in which 
		// _transferEncodingChunkOffset can be incremented to the next chunk

    Uint32 chunkLengthParsed = 
      (Uint32) strtoul((const char *)chunkLineStart, 0, 16);

		// this also covers strings stated even larger
    if (chunkLengthParsed == PEG_NOT_FOUND)
      _throwEventFailure(HTTP_STATUS_REQUEST_TOO_LARGE,
												 "stated chunk length too large");

		Sint8 *chunkExtensionStart = chunkLineEnd;
    chunkLineEnd = strstr(chunkLineEnd, chunkLineTerminator);

    // If we have not received the chunk line terminator yet, then return and
    // wait for the next iteration. This is done because the hex length given
		// only represents the non-meta data, not the chunk line itself.

    if (!chunkLineEnd)
      break;

		// the token after the hex digit must be either the chunk line terminator
		// or the chunk extension terminator. If not, the sender has sent an 
		// illegal chunked encoding syntax.

		if (strncmp(chunkExtensionStart, chunkExtensionTerminator, 
								chunkExtensionTerminatorLength) != 0 &&
				strncmp(chunkExtensionStart, chunkLineTerminator, 
								chunkLineTerminatorLength) != 0)
			_throwEventFailure(HTTP_STATUS_BADREQUEST, "missing chunk extension");

    chunkLineEnd += chunkLineTerminatorLength;
    Uint32 chunkLineLength = chunkLineEnd - chunkLineStart;
    Uint32 chunkMetaLength = chunkLineLength;
    if (chunkLengthParsed > 0)
      chunkMetaLength += chunkTerminatorLength;
    Uint32 chunkTerminatorOffset = _transferEncodingChunkOffset + 
      chunkLineLength + chunkLengthParsed;

    // The parsed length represents the non-meta data bytes which starts
    // after the chunk line terminator has been received. 
    // If we dont have enough remainder bytes to process from the length parsed
    // then return and wait for the next iteration.

    if (chunkLengthParsed + chunkMetaLength > remainderLength)
      break;

		// at this point we have a complete chunk. proceed and strip out meta-data
		// NOTE: any time "remove" is called on the buffer, many variables must be
		// recomputed to reflect the data removed.

    // remove the chunk length line
    _incomingBuffer.remove(_transferEncodingChunkOffset, chunkLineLength);
    messageLength = _incomingBuffer.size();
		// always keep the byte after the last data byte null for easy string 
		// processing.
    messageStart[messageLength] = 0;

    // recalculate since we just removed the chunk length line
    chunkTerminatorOffset -= chunkLineLength;

		// is this the last chunk ?
    if (chunkLengthParsed == 0)
    {
      // We are at the last chunk. The only remaining data should be:
      // 1. optional trailer first
      // 2. message terminator (will remain on incoming buffer and passed up)

      remainderLength -= chunkLineLength;

			CIMStatusCode cimStatusCode = CIM_ERR_SUCCESS;
			Uint32 httpStatusCode = HTTP_STATUSCODE_OK;
			String httpStatus;
			String cimErrorValue;

			// is there an optional trailer ?
      if (remainderLength > chunkBodyTerminatorLength)
      {
        // must be a trailer here. The way we are going to look for the
        // end of the trailer is to go to the end of the message and back up
        // chunkBodyTerminatorLength bytes. This should put us right at the
        // trailer terminator start. There must be at least the trailer
				// terminator plus the chunk body terminator.

        if (remainderLength <
            chunkBodyTerminatorLength + trailerTerminatorLength)
          _throwEventFailure(HTTP_STATUS_BADREQUEST, 
														 "missing bytes in transfer encoding");

        Uint32 trailerLength =  remainderLength - chunkBodyTerminatorLength;
        Uint32 trailerOffset = _transferEncodingChunkOffset;
        Sint8 *trailerStart = messageStart + trailerOffset; 
        Sint8 *trailerTerminatorStart = trailerStart + trailerLength - 
          trailerTerminatorLength;

        // no trailer terminator before end of chunk body ?
        if (strncmp(trailerTerminatorStart, trailerTerminator, 
										trailerTerminatorLength) != 0)
          _throwEventFailure(HTTP_STATUS_BADREQUEST, 
														 "No chunk trailer terminator received");

				Array<Sint8> trailer;
				// add a dummy startLine so that the parser works
				trailer << " " << headerLineTerminator;

				char save = trailerStart[trailerLength];
				trailerStart[trailerLength] = 0;
				trailer << trailerStart;
				trailerStart[trailerLength] = save;

        _incomingBuffer.remove(trailerOffset, trailerLength);
        messageLength = _incomingBuffer.size();
        messageStart[messageLength] = 0;
        remainderLength -= trailerLength;

				// parse the trailer looking for the code and description
				String startLine;
				Array<HTTPHeader> headers;
				Uint32 contentLength = 0;
				HTTPMessage httpTrailer(trailer);
				httpTrailer.parse(startLine, headers, contentLength);

				String cimErrorName = headerNameError;
				// first look for cim error. this is an http level error
				Boolean found = false;

				found = httpTrailer.lookupHeader(headers, cimErrorName, cimErrorValue,
																				 true);

				if (found == true)
				{
					// we have a cim error. parse the header to get the original http 
					// level error if any, otherwise, we have to make one up.

					Array<Sint8> header(messageStart, headerLength);
					String startLine;
					Array<HTTPHeader> headers;
					Uint32 contentLength = 0;
					HTTPMessage httpHeader(header);
					httpHeader.parse(startLine, headers, contentLength);
					String httpVersion;
					Boolean isValid = httpHeader.
						parseStatusLine(startLine, httpVersion, httpStatusCode,httpStatus);
					if (isValid == false || httpStatusCode == 0 || 
							httpStatusCode == HTTP_STATUSCODE_OK)
					{
						// ??? ATTN: make up our own http code if not given ?
						httpStatusCode = (Uint32) HTTP_STATUSCODE_BADREQUEST;
						httpStatus = HTTP_STATUS_BADREQUEST;
					}
				}
				else
				{
					String codeName = headerNameCode;
					String codeValue;
					found = httpTrailer.lookupHeader(headers, codeName, codeValue,
																					 true);
					if (found == true && codeValue.size() > 0 &&
							(cimStatusCode = (CIMStatusCode)atoi(codeValue.getCString()))>0)
					{
						HTTPMessage::lookupHeaderPrefix(headers, codeName, _mpostPrefix);
						httpStatus = _mpostPrefix + codeName + headerNameTerminator + 
							codeValue + headerLineTerminator;

						// look for cim status description
						String descriptionName = headerNameDescription;
						String descriptionValue;
						found = httpTrailer.lookupHeader(headers, descriptionName, 
																						 descriptionValue, 
																						 true);
						if (descriptionValue.size() == 0)
							descriptionValue = cimStatusCodeToString(cimStatusCode);

						httpStatus = httpStatus + _mpostPrefix + descriptionName + 
							headerNameTerminator + descriptionValue + headerLineTerminator;

					} // if found a cim status code
				} // else not a cim error
			} // if optional trailer present
      
      Sint8 *chunkBodyTerminatorStart = 
        messageStart + _transferEncodingChunkOffset;

			// look for chunk body terminator
      if (remainderLength != chunkBodyTerminatorLength ||
          strncmp(chunkBodyTerminatorStart, chunkBodyTerminator,
									chunkBodyTerminatorLength) != 0)
        _throwEventFailure(HTTP_STATUS_BADREQUEST,
													 "No chunk body terminator received");

      // else the remainder is just the terminator, which we will leave
      // on the incoming buffer and pass up 
			// (as if a non-transfer message arrived)

      _transferEncodingChunkOffset = 0;
      _contentLength = messageLength - headerLength;

			if (httpStatusCode != HTTP_STATUSCODE_OK)
			{
				_handleReadEventFailure(httpStatus, cimErrorValue);
			}
			else if (cimStatusCode != CIM_ERR_SUCCESS)
			{
				// discard the XML payload data (body) according to cim operations spec
				// and add code and description to the header so the next layer can
				// interpret the error correctly

				_incomingBuffer.remove(headerLength, _contentLength);
				// remove the header line terminator
				_incomingBuffer.remove(headerLength - headerLineTerminatorLength, 
															 headerLineTerminatorLength);
				// append new status
				_incomingBuffer.append(httpStatus.getCString(), httpStatus.size());

				_incomingBuffer.append(headerLineTerminator, headerLineTerminatorLength);
				// null terminate - the buffer is at least as long after removing
				Sint8 *data = (Sint8 *)_incomingBuffer.getData();
				data[_incomingBuffer.size()] = 0;
				_contentLength = 0;
				_contentOffset = 0;
			}

      break;
    } // if last chunk 

    // we are NOT on the last chunk! validate that the offset where the chunk 
		// terminator was found matches what the parsed chunk length claimed.

    if (strncmp(messageStart + chunkTerminatorOffset, chunkTerminator, 
                chunkTerminatorLength) != 0)
      _throwEventFailure(HTTP_STATUS_BADREQUEST, "Bad chunk terminator");

    // now remove the chunk terminator
    _incomingBuffer.remove(chunkTerminatorOffset, chunkTerminatorLength);
    messageLength = _incomingBuffer.size();
    messageStart[messageLength] = 0;

    // jump to the start of the next chunk (which may not have been read yet)
    _transferEncodingChunkOffset = chunkTerminatorOffset;
  } // for all remaining bytes containing chunks

  PEG_METHOD_EXIT();
}

/*
 * Handle a failure on the read or an HTTP error. This is NOT meant for 
 * errors found in the cim response or the trailer.
 * The http status MAY have the detailed message attached to it using the
 * detail delimiter.
 */

void HTTPConnection::_handleReadEventFailure(String &httpStatusWithDetail, 
																						 String cimError)
{
	Uint32 delimiterFound = httpStatusWithDetail.find(httpDetailDelimiter);
	String httpDetail;
	String httpStatus;

	if (delimiterFound != PEG_NOT_FOUND)
	{
		httpDetail = httpStatus.subString(delimiterFound+1);
		httpStatus = httpStatus.subString(0, delimiterFound);
	}
	
	String combined = httpStatus + httpDetailDelimiter + httpDetail + 
		httpDetailDelimiter + cimError;

  Tracer::trace(__FILE__, __LINE__, TRC_HTTP, Tracer::LEVEL2, combined);
  _requestCount++;
  Array<Sint8> message;
  message = XmlWriter::formatHttpErrorRspMessage(httpStatus, cimError, 
																								 httpDetail);
  HTTPMessage* httpMessage = new HTTPMessage(message);
  Tracer::traceBuffer(TRC_XML_IO, Tracer::LEVEL2,
                      httpMessage->message.getData(),
                      httpMessage->message.size());

	// this is common error code. If we are the server side, we want to send
	// back the error to the client, but if we are the client side, then we
	// simply want to queue up this error locally so the client app can receive
	// the error. The client side's own message queue name will be the same
	// as the connector name (the server would be acceptor)

	if (_isClient() == true)
	{
		httpMessage->dest = _outputMessageQueue->getQueueId();

#ifndef LOCK_CONNECTION_ENABLED
	_outputMessageQueue->enqueue(httpMessage);
#endif

	_clearIncoming();

#ifdef LOCK_CONNECTION_ENABLED
	unlock_connection();
	_outputMessageQueue->enqueue(httpMessage);
  _clearIncoming();
#endif

	}
	else
	{
		// else server side processing error - send back to client
		handleEnqueue(httpMessage);

#ifdef LOCK_CONNECTION_ENABLED
		unlock_connection();
#endif

	}
  _closeConnection(); 
}

void HTTPConnection::_handleReadEvent()
{
    static const char func[] = "HTTPConnection::_handleReadEvent()";
    PEG_METHOD_ENTER(TRC_HTTP, func);

    // -- Append all data waiting on socket to incoming buffer:

#ifdef LOCK_CONNECTION_ENABLED
    lock_connection();
#endif

    String httpStatus;
    Sint32 bytesRead = 0;
    Boolean incompleteSecureReadOccurred = false;

    for (;;)
    {
      // save one for null
      char buffer[httpTcpBufferSize+1];
      buffer[sizeof(buffer)-1] = 0;

      Sint32 n = _socket->read(buffer, sizeof(buffer)-1);
    
      if (n <= 0)
      {
        if (_socket->isSecure() && bytesRead == 0)
        {
          // It is possible that SSL_read was not able to 
          // read the entire SSL record.  This could happen
          // if the record was send in multiple packets
          // over the network and only some of the packets
          // are available.  Since SSL requires the entire
          // record to successfully decrypt, the SSL_read
          // operation will return "0 bytes" read.
          // Once all the bytes of the SSL record have been read,
          // SSL_read will return the entire record.
          // The following test was added to allow
          // handleReadEvent to distinguish between a 
          // disconnect and partial read of an SSL record.
          //
          incompleteSecureReadOccurred = !_socket->incompleteReadOccurred(n);

        }
        break;
      }
        
      try
      {
        buffer[n] = 0;
				// important: always keep message buffer null terminated for easy 
				// string parsing!
        Uint32 size = _incomingBuffer.size() + n;
        _incomingBuffer.reserveCapacity(size + 1);
        _incomingBuffer.append(buffer, n);
        // put a null on it. This is safe sice we have reserved an extra byte
        Sint8 *data = (Sint8 *)_incomingBuffer.getData();
        data[size] = 0;
      }

      catch(...)
      {
				static const char detailP[] = 
					"Unable to append the request to the input buffer";
				httpStatus = 
					HTTP_STATUS_REQUEST_TOO_LARGE + httpDetailDelimiter + detailP;
        _handleReadEventFailure(httpStatus);
        PEG_METHOD_EXIT();
        return;
      }
      
      bytesRead += n;
    }

    Tracer::trace(TRC_HTTP, Tracer::LEVEL4,
     "Total bytesRead = %d; Bytes read this iteration = %d", 
                _incomingBuffer.size(), bytesRead);

    try
    {
      if (_contentOffset == -1)
        _getContentLengthAndContentOffset();
      _handleReadEventTransferEncoding();
    }
    catch(Exception &e)
    {
      httpStatus = e.getMessage();
    }

    if (httpStatus.size() > 0)
    {
      _handleReadEventFailure(httpStatus);
      PEG_METHOD_EXIT();
      return;
    }

    // -- See if the end of the message was reached (some peers signal end of 
    // -- the message by closing the connection; others use the content length
    // -- HTTP header and then there are those messages which have no bodies
    // -- at all).

    if ((bytesRead == 0 && !incompleteSecureReadOccurred) ||  
	_contentLength != -1 && 
	(Sint32(_incomingBuffer.size()) >= _contentLength + _contentOffset))
    {
	HTTPMessage* message = new HTTPMessage(_incomingBuffer, getQueueId());
        message->authInfo = _authInfo;

        //
        // increment request count 
        //
        if (bytesRead > 0)
        {
           _requestCount++;
           _connectionRequestCount++;
           _responsePending = true;
        }
        Tracer::trace(TRC_HTTP, Tracer::LEVEL4,
          "_requestCount = %d", _requestCount.value());
	message->dest = _outputMessageQueue->getQueueId();
//	SendForget(message);
	
#ifndef LOCK_CONNECTION_ENABLED
	_outputMessageQueue->enqueue(message);
#endif
	_clearIncoming();

#ifdef LOCK_CONNECTION_ENABLED
        unlock_connection();

	if (bytesRead > 0)
        {
	   _outputMessageQueue->enqueue(message);
        }
        else 
#else
	if (bytesRead == 0)
#endif
	{
	   Tracer::trace(TRC_HTTP, Tracer::LEVEL3,
			 "HTTPConnection::_handleReadEvent - bytesRead == 0 - Connection being closed.");
	   _closeConnection();
	   
	   //
	   // decrement request count
	   //
	   Tracer::trace(TRC_HTTP, Tracer::LEVEL4,
			 "_requestCount = %d", _requestCount.value());
	   
	   PEG_METHOD_EXIT();
	   return;
	}
    }
    PEG_METHOD_EXIT();
}

Uint32 HTTPConnection::getRequestCount()
{
    return(_requestCount.value());
}


Boolean HTTPConnection::run(Uint32 milliseconds)
{
   Boolean handled_events = false;
   int events = 0;
   
   fd_set fdread; // , fdwrite;
   struct timeval tv = { 0, 1 };
   do 
   {
      FD_ZERO(&fdread);
      FD_SET(getSocket(), &fdread);
      events = select(FD_SETSIZE, &fdread, NULL, NULL, &tv);
#ifdef PEGASUS_OS_TYPE_WINDOWS
      if(events == SOCKET_ERROR)
#else
      if(events == -1)
#endif
      {
	 return false;
      }

      if (events)
      {
	 events = 0;
	 if( FD_ISSET(getSocket(), &fdread))
	 {
	    events |= SocketMessage::READ;
	    Message *msg = new SocketMessage(getSocket(), events);
	    try 
	    {
	       handleEnqueue(msg);
	    }
	    catch(...)
	    {
              Tracer::trace(TRC_DISCARDED_DATA, Tracer::LEVEL2,
                  "HTTPConnection::run handleEnqueue(msg) failure");
	       return true;
	    }
	    handled_events = true;
	 }
      }
   } while(events != 0 && !_connectionClosePending);
   return handled_events;
}

AtomicInt HTTPConnection2::_requestCount(0);


HTTPConnection2::HTTPConnection2(pegasus_socket socket,
				 MessageQueue* outputMessageQueue)
    : 
   Base(PEGASUS_QUEUENAME_HTTPCONNECTION), 
   _socket(socket), 
   _outputMessageQueue(outputMessageQueue),
   _contentOffset(-1),
   _contentLength(-1),
   _closed(0)
{
   PEG_METHOD_ENTER(TRC_HTTP, "HTTPConnection2::HTTPConnection2");

   _authInfo = new AuthenticationInfo(true);

   // add SSL verification information to the authentication information
   if (_socket.is_secure() && _socket.isPeerVerificationEnabled() && _socket.isCertificateVerified()) 
   {
           _authInfo->setAuthStatus(AuthenticationInfoRep::AUTHENTICATED);
		   _authInfo->setAuthType(AuthenticationInfoRep::AUTH_TYPE_SSL);
   }

   PEG_METHOD_EXIT();
}

HTTPConnection2::~HTTPConnection2()
{
   PEG_METHOD_ENTER(TRC_HTTP, "HTTPConnection2::~HTTPConnection2");
   try 
   {
      _close_connection();
   }
   catch(...)
   {
   }
   
    delete _authInfo;

   PEG_METHOD_EXIT();
}


void HTTPConnection2::handleEnqueue(Message *message)
{

   PEG_METHOD_ENTER(TRC_HTTP, "HTTPConnection2::handleEnqueue");
 

   switch (message->getType())
   {
      case HTTP_MESSAGE:
      {
         Tracer::trace(TRC_HTTP, Tracer::LEVEL4,
            "HTTPConnection2::handleEnqueue - HTTP_MESSAGE");
	 
	 HTTPMessage* httpMessage = (HTTPMessage*)message;

#ifdef PEGASUS_KERBEROS_AUTHENTICATION
         // The following is processing to wrap (encrypt) the response from the
	 // server when using kerberos authentication.

	 // If the security association does not exist then kerberos authentication
	 // is not being used.
	 CIMKerberosSecurityAssociation *sa = _authInfo->getSecurityAssociation();
	 if (sa)
	 {
	     // The message needs to be parsed in order to distinguish between the
	     // headers and content. When parsing, the code breaks out
	     // of the loop as soon as it finds the double separator that terminates
	     // the headers so the headers and content can be easily separated.
	     // Parse the HTTP message:
	     String startLine;
	     Array<HTTPHeader> headers;
	     Uint32 contentLength = 0;
	     httpMessage->parse(startLine, headers, contentLength);

	     Boolean authrecExists = false;
	     String authorization = String::EMPTY;
	     if (HTTPMessage::lookupHeader(headers, "WWW-Authenticate", authorization, false))
	     {
		 authrecExists = true;
	     }

	     // The following is processing to wrap (encrypt) the response from the
	     // server when using kerberos authentications.
	     sa->wrapResponseMessage(httpMessage->message, contentLength, authrecExists);
	 }
#endif
	 //------------------------------------------------------------
	 // There is no need to convert the write calls to be asynchronous.
	 // The write is happening on a dedicated thread (not the monitor thread)
	 // so it is just fine if it blocks. << Tue Oct  7 09:48:06 2003 mdd >>
	 //------------------------------------------------------------

	 const Array<Sint8>& buffer = httpMessage->message;
 
	 const Uint32 CHUNK_SIZE = 16 * 1024;

	 SignalHandler::ignore(PEGASUS_SIGPIPE);

         Uint32 totalBytesWritten = 0;
	 for (Uint32 bytesRemaining = buffer.size(); bytesRemaining > 0; )
	 {
	    Uint32 bytesToWrite = _Min(bytesRemaining, CHUNK_SIZE);

	    Sint32 bytesWritten = _socket.write(
	       buffer.getData() + buffer.size() - bytesRemaining, 
	       bytesToWrite);

	    if (bytesWritten < 0)
	       break;

            totalBytesWritten += bytesWritten;
	    bytesRemaining -= bytesWritten;
	 }

     if (_socket.is_secure() && _socket.isPeerVerificationEnabled())
     {
         Tracer::trace(TRC_HTTP, Tracer::LEVEL3,
                       "Authenticated = %d; Username = %s",
                       _authInfo->isAuthenticated(), 
                       (const char*)_authInfo->getAuthenticatedUser().getCString());

         // If the client sent an untrusted certificate along with valid credentials
         // for the SSL truststore, add the certificate to the server's truststore.
         // This will fail in the addTrustedClient function if enableSSLTrustStoreAutoUpdate is 
         // not enabled.
         if (_authInfo->isAuthenticated() &&
             _socket.getPeerCertificate() && 
             _socket.getPeerCertificate()->getErrorCode() != (Uint32) SSLCertificateInfo::V_OK)
         {
             _socket.addTrustedClient(_authInfo->getAuthenticatedUser().getCString());
         }
         
     }

	 //
	 // decrement request count
	 //
	 _requestCount--;

         Tracer::trace(TRC_HTTP, Tracer::LEVEL4,
            "Total bytes written = %d; Buffer Size = %d; _requestCount = %d",
             totalBytesWritten,  buffer.size(), _requestCount.value());

	 break;
      }

      default:
	 // ATTN: need unexpected message error!
	 break;
   }

   delete message;

   PEG_METHOD_EXIT();
}

void HTTPConnection2::enqueue(Message* message) throw(IPCException)
{
   _reentry.lock(pegasus_thread_self());
   if(_closed.value())
   {
      delete message;
      
   }
   else
   {
      handleEnqueue(message);
   }
   _reentry.unlock();
}


void HTTPConnection2::handleEnqueue()
{
   Message* message = dequeue();

    if (!message)
        return;
    handleEnqueue(message);
}

Sint32 HTTPConnection2::getSocket(void)
{
   return (Sint32)_socket;
   
}


void HTTPConnection2::_getContentLengthAndContentOffset()
{
    char* data = (char*)_incomingBuffer.getData();
    Uint32 size = _incomingBuffer.size();
    char* line = (char*)data;
    char* sep;
    Uint32 lineNum = 0;
    Boolean bodylessMessage = false;

    while ((sep = _FindSeparator(line, size - (line - data))))
    {
	char save = *sep;
	*sep = '\0';

	// Did we find the double separator which terminates the headers?

	if (line == sep)
	{
	    *sep = save;
	    line = sep + ((save == '\r') ? 2 : 1);
	    _contentOffset = line - _incomingBuffer.getData();
	    break;
	}

	// If this is one of the bodyless methods, then we can assume the
	// message is complete when the "\r\n\r\n" is encountered.

	if (lineNum == 0 && _IsBodylessMessage(line))
	    bodylessMessage = true;

	// Look for the content-length if not already found:

	char* colon = strchr(line, ':');

	if (colon)
	{
	    *colon  = '\0';

	    if (System::strcasecmp(line, "content-length") == 0)
		_contentLength = atoi(colon + 1);

	    *colon = ':';
	}

	*sep = save;
	line = sep + ((save == '\r') ? 2 : 1);
	lineNum++;
    }

    if (_contentOffset != -1 && bodylessMessage)
	_contentLength = 0;
}

void HTTPConnection2::_clearIncoming()
{
    _contentOffset = -1;
    _contentLength = -1;
    _incomingBuffer.clear();
}

void HTTPConnection2::_handleReadEvent(monitor_2_entry* entry)
{
    PEG_METHOD_ENTER(TRC_HTTP, "HTTPConnection2::_handleReadEvent");

    // -- Append all data waiting on socket to incoming buffer:

    _socket.disableBlocking();
    Sint32 bytesRead = 0;
    Boolean incompleteSecureReadOccurred = false;
    Boolean would_block = false;

   Tracer::trace(TRC_HTTP, Tracer::LEVEL4, "Doing a read on %d.", (Sint32)entry->get_sock());

    for (;;)
    {
	char buffer[4096];
	
        Sint32 n = _socket.read(buffer, sizeof(buffer));

//------------------------------------------------------------
// Note on reading non-blocking sockets: 
// If there is no data to read from a non-blocking socket, the return
// code from _socket.read will be a -1 and errno will be EWOULDBLOCK.
// The correct thing to do in this case is nothing at all. The client on 
// the other end of the connection has not closed the socket, and the 
// connection will have data to read very soon. The connection needs to 
// be present in the monitor's select loop so it can be read as soon as the 
// data arrives. << Tue Oct  7 09:45:37 2003 mdd >>
//------------------------------------------------------------

#ifdef PEGASUS_OS_TYPE_WINDOWS
	if( n == SOCKET_ERROR && WSAGetLastError() == WSAEWOULDBLOCK )
#else
	if( n == -1 && errno == EWOULDBLOCK )
#endif
	{
	   would_block = true;
	}

	if (n <= 0)
	{
	    if (_socket.is_secure() && bytesRead == 0)
            {
	       // It is possible that SSL_read was not able to 
	       // read the entire SSL record.  This could happen
	       // if the record was send in multiple packets
	       // over the network and only some of the packets
	       // are available.  Since SSL requires the entire
	       // record to successfully decrypt, the SSL_read
	       // operation will return "0 bytes" read.
	       // Once all the bytes of the SSL record have been read,
	       // SSL_read will return the entire record.
	       // The following test was added to allow
	       // handleReadEvent to distinguish between a 
	       // disconnect and partial read of an SSL record.
	       //
               incompleteSecureReadOccurred = !_socket.incompleteReadOccurred(n);
            }

	    break;
	}

	
	_incomingBuffer.append(buffer, n);
	bytesRead += n;
    }
    _socket.enableBlocking();
    Tracer::trace(TRC_HTTP, Tracer::LEVEL4,
     "_socket.read bytesRead = %d", bytesRead);
   
    // -- If still waiting for beginning of content!

    if (_contentOffset == -1)
	_getContentLengthAndContentOffset();

    // -- See if the end of the message was reached (some peers signal end of 
    // -- the message by closing the connection; others use the content length
    // -- HTTP header and then there are those messages which have no bodies
    // -- at all).

    if ((bytesRead == 0 && !incompleteSecureReadOccurred) ||  
	_contentLength != -1 && 
	(Sint32(_incomingBuffer.size()) >= _contentLength + _contentOffset))
    {
	if (bytesRead > 0)
        {
           // We are setting the state of entry as IDLE so that monitor_2::run 
           // does a select on this FD. And only when next time _handleReadEvent
           // gets called, bytesRead would be 0, and the state of the entry
           // would be changed to CLOSED.
           entry->set_state(IDLE);
	   delete entry;
	   HTTPMessage* message = new HTTPMessage(_incomingBuffer, getQueueId());
	   message->authInfo = _authInfo;
	   
	   //
	   // increment request count 
	   //
	   _requestCount++;
	   Tracer::trace(TRC_HTTP, Tracer::LEVEL4,
			 "_requestCount = %d", _requestCount.value());
	   message->dest = _outputMessageQueue->getQueueId();
	   
	   _clearIncoming();
	   _outputMessageQueue->enqueue(message);
        }
        else if (bytesRead == 0)
	{
	   if(would_block == false)
	   {
	      
	   Tracer::trace(TRC_HTTP, Tracer::LEVEL3,
			 "HTTPConnection2::_handleReadEvent - bytesRead == 0 - Conection being closed.");
	   
           // Commenting out below line, since the decrement is happening
           // twice, and increment only once for every connection. One
           // decrement was happening here and another by handleEnqueue()
           // after response has been written on the socket.

	   // _requestCount--;

	   Tracer::trace(TRC_HTTP, Tracer::LEVEL4,
			 "_requestCount = %d", _requestCount.value());
	   
	   _close_connection();
       if(entry->get_type() != CLIENTSESSION)
	   entry->set_state(CLOSED);
	   delete entry;
	   
	   }
	}
    }
    PEG_METHOD_EXIT();
}


void HTTPConnection2::_close_connection(void)
{
   _reentry.lock(pegasus_thread_self());
   
   _closed = 1;
   remove_myself(_queueId);
   _reentry.unlock();
   

}


Uint32 HTTPConnection2::getRequestCount()
{
    return(_requestCount.value());
}


Boolean HTTPConnection2::operator ==(const HTTPConnection2& h2)
{
  if(this == &h2)
    return true;
  return false;
}

Boolean HTTPConnection2::operator ==(void* h2)
{
  if((void *)this == h2)
    return true;
  return false;
}


void HTTPConnection2::connection_dispatch(monitor_2_entry* entry)
{
  HTTPConnection2* myself = (HTTPConnection2*) entry->get_dispatch();
  myself->_socket = entry->get_sock();
  myself->_handleReadEvent(entry);
  
}


PEGASUS_NAMESPACE_END

No CVS admin address has been configured
Powered by
ViewCVS 0.9.2