Click here to Skip to main content
15,897,518 members
Articles / Desktop Programming / MFC

CHttpClient - A Helper Class Using WinInet

Rate me:
Please Sign up or sign in to vote.
4.96/5 (59 votes)
10 Aug 20073 min read 491.4K   24.9K   163  
A C++ class which helps you to interact with a HTTP web server.
/*!
 * \file	RyeolHttpClient.cpp
 * \brief	Implementations of Ryeol's HTTP client classes.
 * \author	Jo Hyeong-ryeol
 * \since	2004.04.12
 * \version	$LastChangedRevision: 103 $
 *			$LastChangedDate: 2006-02-05 00:38:41 +0900 (일, 05 2 2006) $
 * 
 * <dl compact>
 * <dt><b>Requirements:</b></dt>
 * <dd>Requires Internet Explorer 4.0 or later.</dd><br>
 * <dd>Unicode version class support on Windows Me/98/95 requires Microsoft Layer for Unicode.</dd><br>
 * <dd>UTF-8 encoding support on Windows 95 requires Microsoft Layer for Unicode.</dd>
 * </dl>
 * This file contains implementations of Ryeol's HTTP client classes.
 * \n\n\n
 * Copyright &copy; 2006 by <a href="mailto:hyeongryeol@gmail.com">Jo Hyeong-ryeol</a>\n
 * Permission to copy, use, modify, sell and distribute this software is
 * granted provided this copyright notice appears in all copies.
 * This software is provided "as is" without express or implied warranty,
 * and with no claim as to its suitability for any purpose.
 */
#include "stdafx.h"
#include "RyeolHttpClient.h"

#pragma warning (disable: 4290)	// avoids 'C++ Exception Specification ignored' message
#pragma warning (disable: 4660)
#pragma warning (disable: 4996)	// avoids 'This function or variable may be unsafe' message
#pragma comment (lib, "wininet.lib")
#pragma comment (lib, "urlmon.lib")

/*!
 * \brief	The namespace of the Ryeol's library
 *
 * This is the namespace for source codes written by Jo Hyeong-ryeol.
 */
namespace Ryeol {

/////////////////////////////// Global constant message table ////////////////////////////////////
#ifndef SAFEFREE
#	define SAFEFREE(x) if(x){ ::free((void *)x); x = NULL; }
#endif

#ifndef INVALID_SET_FILE_POINTER
#	define INVALID_SET_FILE_POINTER ((DWORD)-1)
#endif

#define	HTTPCLIENT_POSTCNTX_BUFF_SIZE			(1024 * 56)


// An assertion macro for the CHttpToolA and the CHttpToolW class
#define HTTPTOOL_ASSERT(expr, msg)										\
																		\
	if ( !(expr) ) {													\
		_ASSERTE ( expr ) ; ThrowException (msg) ;						\
	}

// An assertion macro for the CHttpClient related classes
#define HTTPCLIENT_ASSERT(expr, msg)									\
																		\
	if ( !(expr) ) {													\
		 _ASSERTE ( expr ) ;											\
		if ( HttpTool::IsAnsi () )										\
			CHttpToolA::ThrowException (msg) ;							\
		else															\
			CHttpToolW::ThrowException (L##msg) ;						\
   }

// An assertion macro for the CHttpClient related classes
#define HTTPCLIENT_ASSERTA(expr, msg)									\
																		\
	if ( !(expr) ) {													\
		 _ASSERTE ( expr ) ;											\
		CHttpToolA::ThrowException (msg) ;								\
   }

// An assertion macro for the CHttpClient related classes
#define HTTPCLIENT_ASSERTW(expr, msg)									\
																		\
	if ( !(expr) ) {													\
		 _ASSERTE ( expr ) ;											\
		CHttpToolW::ThrowException (L##msg) ;							\
   }


// not specified
static LPCSTR		g_NotSpecifiedA[] = {
	"Not specified"
} ;

static LPCWSTR		g_NotSpecifiedW[] = {
	L"Not specified"
} ;

// error messages
static LPCSTR		g_NormalMsgA[] = {
	"Unexpected error occurred."
	, "The index is out of range."
	, "Out of memory."
	, "The requested URL is not a valid URL."
	, "The post context is not started yet."
	, "Couldn't read expected bytes from a file."
	, "The post context has not been finished yet."
	, "The port number is not valid."
	, "std::exception occurred."
	, "The encoded URL is not valid."
	, "The UTF8 string contains an invalid character."
	, "An unexpected arithmetic error has been occurred."
	, "An arithmetic overflow error has been occurred."
	, "An interger divide by zero exception has been occurred."
	, "The file (%s) aleady exists."
} ;

static LPCWSTR		g_NormalMsgW[] = {
	L"Unexpected error occurred."
	, L"The index is out of range."
	, L"Out of memory."
	, L"The requested URL is not a valid URL."
	, L"The post context is not started yet."
	, L"Couldn't read expected bytes from a file."
	, L"The post context has not been finished yet."
	, L"The port number is not valid."
	, L"std::exception occurred."
	, L"The encoded URL is not valid."
	, L"The UTF8 string contains an invalid character."
	, L"An unexpected arithmetic error has been occurred."
	, L"An arithmetic overflow error has been occurred."
	, L"An interger divide by zero exception has been occurred."
	, L"The file (%s) aleady exists."
} ;

// error messages (which has a win32 error code) - Reserved
static LPCSTR		g_NormalMsgWin32A[] = {""} ;
static LPCWSTR		g_NormalMsgWin32W[] = {L""} ;

// WinInet error messages (which has a win32 error code)
static LPCSTR		g_WinInetMsgWin32A[] = {
	"::HttpQueryInfo failed."
	, "::InternetReadFile failed."
	, "::InternetOpen failed."
	, "::InternetConnect failed."
	, "::HttpOpenRequest failed."
	, "::HttpAddRequestHeaders failed."
	, "::HttpSendRequest failed."
	, "::HttpSendRequestEx failed."
	, "::InternetWriteFile failed."
	, "::HttpEndRequest failed."
	, "::InternetSetOption failed."
} ;

static LPCWSTR		g_WinInetMsgWin32W[] = {
	L"::HttpQueryInfo failed."
	, L"::InternetReadFile failed."
	, L"::InternetOpen failed."
	, L"::InternetConnect failed."
	, L"::HttpOpenRequest failed."
	, L"::HttpAddRequestHeaders failed."
	, L"::HttpSendRequest failed."
	, L"::HttpSendRequestEx failed."
	, L"::InternetWriteFile failed."
	, L"::HttpEndRequest failed."
	, L"::InternetSetOption failed."
} ;

// Win32 API error messages except the WinInet API (They have a win32 error code)
static LPCSTR		g_Win32MsgWin32A[] = {
	"::WideCharToMultiByte failed."
	, "::MultiByteToWideChar failed."
	, "::ReadFile failed."
	, "OpenFile (::CreateFile) failed (\"%s\")."
	, "::SetFilePointer failed."
	, "::GetFileSize failed (\"%s\")."
	, "::WriteFile failed (\"%s\")."
} ;

static LPCWSTR		g_Win32MsgWin32W[] = {
	L"::WideCharToMultiByte failed."
	, L"::MultiByteToWideChar failed."
	, L"::ReadFile failed."
	, L"OpenFile (::CreateFile) failed (\"%s\")."
	, L"::SetFilePointer failed."
	, L"::GetFileSize failed (\"%s\")."
	, L"::WriteFile failed (\"%s\")."
} ;


// user-defined error message
static LPCSTR		g_UsrErrMsgA[] = {
	"user-defined error message"
} ;

static LPCWSTR		g_UsrErrMsgW[] = {
	L"user-defined error message"
} ;


static LPCSTR		HTTPCLIENT_DEF_MIMETYPE = "application/octet-stream" ;

// Constants in the CHttpToolT
LPCSTR CHttpToolA::szDefUsrAgent = "Ryeol HTTP Client Class" ;
LPCSTR CHttpToolA::szGET = "GET" ;
LPCSTR CHttpToolA::szPost = "POST" ;
LPCSTR CHttpToolA::szHTTP = "HTTP://" ;
LPCSTR CHttpToolA::szHTTPS = "HTTPS://" ;
LPCSTR CHttpToolA::szSlash = "/" ;
LPCSTR CHttpToolA::szCacheControl = "Cache-Control" ;
LPCSTR CHttpToolA::szNoCache = "no-cache" ;
LPCSTR CHttpToolA::szContentType = "Content-Type" ;
LPCSTR CHttpToolA::szMultipartFormDataBoundary = "multipart/form-data; boundary=" ;
LPCSTR CHttpToolA::szFormUrlEncoded = "application/x-www-form-urlencoded" ;
LPCSTR CHttpToolA::szDefBoundary = "----RYEOL-FB3B405B7EAE495aB0C0295C54D4E096-" ;
LPCSTR CHttpToolA::szDefUploadContType = "multipart/form-data; boundary=" "----RYEOL-FB3B405B7EAE495aB0C0295C54D4E096-" ;
LPCSTR CHttpToolA::szNULL = "NULL" ;
LPCSTR CHttpToolA::szEmptyString = "" ;
LPCSTR CHttpToolA::szColonSlashSlash = "://" ;


LPCWSTR CHttpToolW::szDefUsrAgent = L"Ryeol HTTP Client Class" ;
LPCWSTR CHttpToolW::szGET = L"GET" ;
LPCWSTR CHttpToolW::szPost = L"POST" ;
LPCWSTR CHttpToolW::szHTTP = L"HTTP://" ;
LPCWSTR CHttpToolW::szHTTPS = L"HTTPS://" ;
LPCWSTR CHttpToolW::szSlash = L"/" ;
LPCWSTR CHttpToolW::szCacheControl = L"Cache-Control" ;
LPCWSTR CHttpToolW::szNoCache = L"no-cache" ;
LPCWSTR CHttpToolW::szContentType = L"Content-Type" ;
LPCWSTR CHttpToolW::szMultipartFormDataBoundary = L"multipart/form-data; boundary=" ;
LPCWSTR CHttpToolW::szFormUrlEncoded = L"application/x-www-form-urlencoded" ;
LPCWSTR CHttpToolW::szDefBoundary = L"----RYEOL-FB3B405B7EAE495aB0C0295C54D4E096-" ;
LPCWSTR CHttpToolW::szDefUploadContType = L"multipart/form-data; boundary=" L"----RYEOL-FB3B405B7EAE495aB0C0295C54D4E096-" ;
LPCWSTR CHttpToolW::szNULL = L"NULL" ;
LPCWSTR CHttpToolW::szEmptyString = L"" ;
LPCWSTR CHttpToolW::szColonSlashSlash = L"://" ;
/////////////////////////////// Global constant message table ////////////////////////////////////


///////////////////////////////////////// Explicit Instanciation /////////////////////////////////////////
template class CHttpClientMapT<CHttpToolA> ;
template class CHttpClientMapT<CHttpToolW> ;
template class CHttpResponseT<CHttpToolA> ;
template class CHttpResponseT<CHttpToolW> ;
template class CHttpPostStatT<CHttpToolA> ;
template class CHttpPostStatT<CHttpToolW> ;
template class CHttpUrlAnalyzerT<CHttpToolA> ;
template class CHttpUrlAnalyzerT<CHttpToolW> ;
template class CHttpClientT<CHttpToolA, CHttpEncoderA> ;
template class CHttpClientT<CHttpToolW, CHttpEncoderW> ;
///////////////////////////////////////// Explicit Instanciation /////////////////////////////////////////


///////////////////////////////////////// httpclientexception /////////////////////////////////////////
/*!
 * This is a default constructor with no argument.
 */
httpclientexceptionA::httpclientexceptionA (void)
	throw ()
{
	m_dwLastError = HTTPCLIENT_ERR_NOT_SPECIFIED ;
	m_dwWin32LastError = NO_ERROR ;
}

/*!
 * This is a constructor with an initial error message and initial error codes.
 * If memory allocation failed for the error message, the error message will not be copied
 * and Internal error message will point to NULL.
 *
 * \param szErrMsg			[in] An initial error message.
 * \param dwLastError		[in] An error code.
 * \param dwWin32LastError	[in] A win32 error code.
 */
httpclientexceptionA::httpclientexceptionA (LPCSTR szErrMsg, DWORD dwLastError, DWORD dwWin32LastError)
	throw ()
: errmsg_exceptionA (szErrMsg)
{
	m_dwLastError = dwLastError ;
	m_dwWin32LastError = dwWin32LastError ;
}

/*!
 * This is a default constructor with no argument.
 */
httpclientexceptionW::httpclientexceptionW (void)
	throw ()
{
	m_dwLastError = HTTPCLIENT_ERR_NOT_SPECIFIED ;
	m_dwWin32LastError = NO_ERROR ;
}

/*!
 * This is a constructor with an initial error message and initial error codes.
 * If memory allocation failed for the error message, the error message will not be copied
 * and Internal error message will point to NULL.
 *
 * \param szErrMsg			[in] An initial error message.
 * \param dwLastError		[in] An error code.
 * \param dwWin32LastError	[in] A win32 error code.
 */
httpclientexceptionW::httpclientexceptionW (LPCWSTR szErrMsg, DWORD dwLastError, DWORD dwWin32LastError)
	throw ()
: errmsg_exceptionW (szErrMsg)
{
	m_dwLastError = dwLastError ;
	m_dwWin32LastError = dwWin32LastError ;
}
///////////////////////////////////////// httpclientexception /////////////////////////////////////////


///////////////////////////////////////// CHttpToolA /////////////////////////////////////////
inline
LPCSTR CHttpToolA::GetConstMessage (DWORD nIdx)
	throw ()
{
	// user-defined error message
	if ( nIdx >= 1000 )
		return g_UsrErrMsgA[0] ;

	// Win32 API error (which has a win32 error code)
	if ( nIdx >= 600 )
		return g_Win32MsgWin32A[nIdx % 100] ;

	// WinInet error (which has a win32 error code)
	if ( nIdx >= 400 )
		return g_WinInetMsgWin32A[nIdx % 100] ;

	// Normal error (which has a win32 error code)
	if ( nIdx >= 200 )
		return g_NormalMsgWin32A[nIdx % 100] ;

	// Normal error
	if ( nIdx >= 100 )
		return g_NormalMsgA[nIdx % 100] ;

	return g_NotSpecifiedA[nIdx] ;
}

void CHttpToolA::ThrowException (DWORD nErrMsgIdx)
	throw (Exception &)
{
	throw Exception (GetConstMessage (nErrMsgIdx), nErrMsgIdx) ;
}

void CHttpToolA::ThrowException (LPCSTR szErrMsg, DWORD nErrMsgIdx)
	throw (Exception &)
{
	throw Exception (szErrMsg, nErrMsgIdx) ;
}

void CHttpToolA::ThrowException (DWORD nErrMsgIdx, DWORD dwErrCode, LPCSTR szStrArg)
	throw (Exception &)
{
	if ( szStrArg == NULL )
		throw Exception (GetConstMessage (nErrMsgIdx), nErrMsgIdx, dwErrCode) ;

	CHAR		szErrMsg[512] ;
	LPCSTR		szFormat = GetConstMessage (nErrMsgIdx) ;
	int			cchWritten = SNPrintf (szErrMsg, 511, szFormat, szStrArg ? szStrArg : "NULL") ;

	// if an error occurs
	if ( cchWritten < -1 )
		throw Exception (szFormat, nErrMsgIdx, dwErrCode) ;

	if ( cchWritten == -1 )
		cchWritten = 511 ;

	szErrMsg[cchWritten] = NULL ;
	throw Exception (szErrMsg, nErrMsgIdx, dwErrCode) ;
}

void CHttpToolA::ThrowException (LPCWSTR szErrMsg, DWORD nErrMsgIdx, DWORD dwErrCode)
	throw (Exception &)
{
	LPSTR		szErrMsgA = NULL ;

	try {
		szErrMsgA = CHttpToolA::Unicode2Ansi (szErrMsg) ;
	} catch (Exception &) {
		szErrMsgA = NULL ;
	}

	Exception		objException (szErrMsgA, nErrMsgIdx, dwErrCode) ;
	SAFEFREE (szErrMsgA) ;
	throw objException ;
}

void CHttpToolA::ThrowException (CHttpToolW::Exception & e)
	throw (Exception &)
{
	ThrowException (e.errmsg (), e.LastError (), e.Win32LastError ()) ;
}

void CHttpToolA::ThrowException (::SafeIntException & e)
	throw (Exception &)
{
	switch ( e.m_code ) {
		case ERROR_ARITHMETIC_OVERFLOW:
			ThrowException (HTTPCLIENT_ERR_ARITHMETIC_OVERFLOW) ;
			break ;

		case EXCEPTION_INT_DIVIDE_BY_ZERO:
			ThrowException (HTTPCLIENT_ERR_INT_DIVIDE_BY_ZERO) ;
			break ;

		default:
			ThrowException (HTTPCLIENT_ERR_UNEXPECTED_ARITHMETIC_ERROR) ;
			break ;
	}
}

// Conversion methods
// This method returns a converted ansi string from a unicode string.
// The returned string must be freed by using the ::free () function.
LPSTR CHttpToolA::Unicode2Ansi (LPCWSTR szStr, UINT CodePage)
	throw (Exception &)
{
	// The unicode encodings are not allowed
	HTTPTOOL_ASSERT ((CodePage != CP_UTF8) && (CodePage != CP_UTF7)
		, "CHttpToolA::Unicode2Ansi: CP_UTF8 and CP_UTF7 can not be used for the CodePage parameter.") ;

	if ( szStr == NULL )
		return NULL ;

	int		cchNeeded ;

	if ( 0 == (cchNeeded = ::WideCharToMultiByte (CodePage, 0, szStr, -1, NULL, 0, NULL, NULL)) )
		ThrowException (HTTPCLIENT_ERR_WIDECHARTOMULTIBYTE_FAILED, ::GetLastError ()) ;

	PSTR		szAnsi = (PSTR) ::malloc (sizeof (CHAR) * cchNeeded) ;
	if ( szAnsi == NULL )
		ThrowException (HTTPCLIENT_ERR_OUT_OF_MEMORY) ;

	if ( 0 == ::WideCharToMultiByte (CodePage, 0, szStr, -1, szAnsi, cchNeeded, NULL, NULL) ) {
		SAFEFREE (szAnsi) ;
		ThrowException (HTTPCLIENT_ERR_WIDECHARTOMULTIBYTE_FAILED, ::GetLastError ()) ;
	}

	return szAnsi ;
}

// This method returns a converted unicode string from a ansi string.
// The returned string must be freed by using the ::free () function.
LPWSTR CHttpToolA::Ansi2Unicode (LPCSTR szStr, UINT CodePage)
	throw (Exception &)
{
	// The unicode encodings are not allowed
	HTTPTOOL_ASSERT ((CodePage != CP_UTF8) && (CodePage != CP_UTF7)
		, "CHttpToolA::Ansi2Unicode: CP_UTF8 and CP_UTF7 can not be used for the CodePage parameter.") ;

	if ( szStr == NULL )
		return NULL ;

	int		cchNeeded ;
	if ( 0 == (cchNeeded = ::MultiByteToWideChar (CodePage, 0, szStr, -1, NULL, 0)) )
		ThrowException (HTTPCLIENT_ERR_MULTIBYTETOWIDECHAR_FAILED, ::GetLastError ()) ;

	PWSTR		szUni = (PWSTR) ::malloc (sizeof (WCHAR) * cchNeeded) ;
	if ( szUni == NULL )
		ThrowException (HTTPCLIENT_ERR_OUT_OF_MEMORY) ;

	if ( 0 == ::MultiByteToWideChar (CodePage, 0, szStr, -1, szUni, cchNeeded) ) {
		SAFEFREE (szUni) ;
		ThrowException (HTTPCLIENT_ERR_MULTIBYTETOWIDECHAR_FAILED, ::GetLastError ()) ;
	}

	return szUni ;
}

// comparison function (used by STL multimap)
bool CHttpToolA::operator () (LPCSTR szKey1, LPCSTR szKey2) const
	throw ()
{
	// return true if the two strings are null
	if ( szKey1 == NULL && szKey2 == NULL )
		return true ;

	if ( szKey1 == NULL )
		return true ;

	if ( szKey2 == NULL )
		return false ;

	// case insensitive
	return ::stricmp (szKey1, szKey2) < 0 ;
}

// Initializes a internet handle.
HINTERNET CHttpToolA::OpenInternet (LPCSTR szUserAgent, DWORD dwAccessType
									, LPCSTR szProxyName, LPCSTR szProxyBypass, DWORD dwFlags)
	throw (Exception &)
{
	HINTERNET		hInternet ;

	if ( NULL == (hInternet = ::InternetOpenA (
			szUserAgent				// user agent
			, dwAccessType			// use direct connection or proxy connection
			, szProxyName
			, szProxyBypass
			, dwFlags)
		) )
		ThrowException (HTTPCLIENT_ERR_INTERNETOPEN_FAILED, ::GetLastError ()) ;

	return hInternet ;
}

// Closes a internet handle
void CHttpToolA::CloseInternet (HINTERNET & hInternet)
	throw ()
{
	if ( hInternet == NULL )
		return ;

	// Ignore any errors while closing the handle
	::InternetCloseHandle (hInternet) ;
	hInternet = NULL ;
}

// Returns a connection handle
// The hInternet must be a valid internet handle.
HINTERNET CHttpToolA::OpenConnection (HINTERNET hInternet, LPCSTR szServerAddr, INTERNET_PORT nPort
											, LPCSTR szUsrName, LPCSTR szUsrPwd)
	throw (Exception &)
{
	HTTPTOOL_ASSERT (hInternet != NULL, "CHttpToolA::OpenConnection: hInternet can not be NULL.") ;
	HTTPTOOL_ASSERT (szServerAddr != NULL, "CHttpToolA::OpenConnection: szServerAddr can not be NULL.") ;
	HTTPTOOL_ASSERT (::strlen (szServerAddr) != 0, "CHttpToolA::OpenConnection: szServerAddr can not be an empty string.") ;

	HINTERNET			hConnection ;

	if ( NULL == (hConnection = ::InternetConnectA (
			hInternet
			, szServerAddr
			, nPort
			, szUsrName
			, szUsrPwd
			, INTERNET_SERVICE_HTTP
			, 0
			, NULL)
		) )
		ThrowException (HTTPCLIENT_ERR_INTERNETCONNECT_FAILED, ::GetLastError ()) ;

	return hConnection ;
}

// Closes a connection handle
void CHttpToolA::CloseConnection (HINTERNET & hConnection)
	throw ()
{
	if ( hConnection == NULL )
		return ;

	// Ignore any errors while closing the handle
	::InternetCloseHandle (hConnection) ;
	hConnection = NULL ;
}

// Returns a HTTP request handle
HINTERNET CHttpToolA::OpenRequest (HINTERNET hConnection, LPCSTR szMethod, LPCSTR szObjectName
										 , DWORD dwFlags, LPCSTR szReferer, UINT /* CodePage */)
	throw (Exception &)
{
	HTTPTOOL_ASSERT (hConnection != NULL, "CHttpToolA::OpenRequest: hConnection can not be NULL.") ;
	HTTPTOOL_ASSERT (szObjectName != NULL, "CHttpToolA::OpenRequest: szObjectName can not be NULL.") ;
	HTTPTOOL_ASSERT (::strlen (szObjectName) != 0, "CHttpToolA::OpenRequest: szObjectName can not be an empty string.") ;

	static LPCSTR			szAcceptedType[] = {
		"*/*"
		, NULL
	} ;

	HINTERNET		hRequest = NULL ;

	// Opens a HTTP request handle
	if ( NULL == (hRequest = ::HttpOpenRequestA (
			hConnection
			, szMethod				// HTTP Method
			, szObjectName			// Target object
			, "HTTP/1.1"			// HTTP/1.1
			, szReferer				// referer
			, szAcceptedType		// Accepts any type.
			, dwFlags				// Flags
			, NULL					// Doesn't use any context value.
			)
		) )
		ThrowException (HTTPCLIENT_ERR_HTTPOPENREQUEST_FAILED, ::GetLastError ()) ;

	return hRequest ;
}

// Closes a request handle
void CHttpToolA::CloseRequest (HINTERNET & hRequest)
	throw ()
{
	if ( hRequest == NULL )
		return ;

	// Ignore any errors while closing the handle
	::InternetCloseHandle (hRequest) ;
	hRequest = NULL ;
}

void CHttpToolA::AddHeader (HINTERNET hRequest, LPCSTR szName, LPCSTR szValue, UINT /* CodePage */)
	throw (Exception &)
{
	HTTPTOOL_ASSERT (hRequest != NULL, "CHttpToolA::AddHeader: hRequest can not be NULL.") ;
	HTTPTOOL_ASSERT (szName != NULL, "CHttpToolA::AddHeader: szName can not be NULL.") ;

	::SafeInt<size_t>		cbHeader ;
	::SafeInt<DWORD>		cchHeader ;

	try {
		cbHeader = ::strlen (szName) ;
		cbHeader += szValue ? ::strlen (szValue) : 0 ;
		cbHeader += (4 + 1) ;	// for ": ", "\r\n", '\0'
		cchHeader = cbHeader - 1 ;
	} catch (::SafeIntException & e) {
		ThrowException (e) ;
	}

	PSTR		szHeader = (PSTR) ::malloc (sizeof (CHAR) * (cbHeader.Value ())) ;
	if ( szHeader == NULL )
		ThrowException (HTTPCLIENT_ERR_OUT_OF_MEMORY) ;

	::strcpy (szHeader, szName) ;
	::strcat (szHeader, ": ") ;
	::strcat (szHeader, szValue ? szValue : "") ;
	::strcat (szHeader, "\r\n") ;

	// Adds a header
	if ( !::HttpAddRequestHeadersA (
			hRequest
			, szHeader								// headers to append to the request.
			, cchHeader.Value ()					// header length
			, HTTP_ADDREQ_FLAG_ADD					// flags
			)
		) {
		SAFEFREE (szHeader) ;
		ThrowException (HTTPCLIENT_ERR_HTTPADDREQUESTHEADERS_FAILED, ::GetLastError ()) ;
	}

	SAFEFREE (szHeader) ;
}

void CHttpToolA::SendRequest (HINTERNET hRequest, LPCSTR szPosted, UINT /* CodePage */)
	throw (Exception &)
{
	HTTPTOOL_ASSERT (hRequest != NULL, "CHttpToolA::SendRequest: hRequest can not be NULL.") ;

	::SafeInt<DWORD>	cchPosted ;

	try {
		cchPosted = szPosted ? ::strlen (szPosted) : 0 ;
	} catch (::SafeIntException & e) {
		ThrowException (e) ;
	}

	if ( !::HttpSendRequestA (
			hRequest
			, NULL						// Additional header
			, 0							// The length of the additional header
			, (void *) szPosted			// A posted data
			, cchPosted.Value ()		// The length of the posted data
		) )
		ThrowException (HTTPCLIENT_ERR_HTTPSENDREQUEST_FAILED, ::GetLastError ()) ;
}

void CHttpToolA::SendRequestEx (HINTERNET hRequest, DWORD dwPostedSize)
	throw (Exception &)
{
	HTTPTOOL_ASSERT (hRequest != NULL, "CHttpToolA::SendRequestEx: hRequest can not be NULL.") ;

	INTERNET_BUFFERSA BufferIn;

	BufferIn.dwStructSize = sizeof( INTERNET_BUFFERS ); // Must be set or error will occur
    BufferIn.Next = NULL; 
    BufferIn.lpcszHeader = NULL;
    BufferIn.dwHeadersLength = 0;
    BufferIn.dwHeadersTotal = 0;
    BufferIn.lpvBuffer = NULL;                
    BufferIn.dwBufferLength = 0;
    BufferIn.dwBufferTotal = dwPostedSize; // This is the only member used other than dwStructSize
    BufferIn.dwOffsetLow = 0;
    BufferIn.dwOffsetHigh = 0;

	if ( !::HttpSendRequestExA (
			hRequest
			, &BufferIn
			, NULL
			, 0
			, 0
		) )
		ThrowException (HTTPCLIENT_ERR_HTTPSENDREQUESTEX_FAILED, ::GetLastError ()) ;
}

void CHttpToolA::InternetWriteFile (HINTERNET hRequest, const BYTE * pbyBuff, DWORD cbyBuff)
	throw (Exception &)
{
	HTTPTOOL_ASSERT (hRequest != NULL, "CHttpToolA::InternetWriteFile: hRequest can not be NULL.") ;
	HTTPTOOL_ASSERT (pbyBuff != NULL, "CHttpToolA::InternetWriteFile: pbyBuff can not be NULL.") ;
	HTTPTOOL_ASSERT (cbyBuff != 0, "CHttpToolA::InternetWriteFile: cbyBuff can not be zero.") ;

	DWORD		dwTotalWritten = 0, dwWritten ;

	while ( dwTotalWritten < cbyBuff ) {
		if ( !::InternetWriteFile (
							hRequest
							, pbyBuff + dwTotalWritten
							, cbyBuff - dwTotalWritten
							, &dwWritten
				)
			)
			ThrowException (HTTPCLIENT_ERR_INTERNETWRITEFILE_FAILED, ::GetLastError ()) ;

		dwTotalWritten += dwWritten ;
	}
}

void CHttpToolA::EndRequest (HINTERNET hRequest)
	throw (Exception &)
{
	HTTPTOOL_ASSERT (hRequest != NULL, "CHttpToolA::EndRequest: hRequest can not be NULL.") ;

	if ( !::HttpEndRequest (
						hRequest
						, NULL
						, 0
						, 0
			)
		)
		ThrowException (HTTPCLIENT_ERR_HTTPENDREQUEST_FAILED, ::GetLastError ()) ;
}

// Checks whether the file exists.
// (Does not throw an exception)
BOOL CHttpToolA::FileExists (LPCSTR szFilePath)
	throw (Exception &)
{
	HTTPTOOL_ASSERT (szFilePath != NULL, "CHttpToolA::FileExists: szFilePath can not be NULL.") ;
	
	HANDLE		hFile = ::CreateFileA (
							szFilePath
							, 0
							, FILE_SHARE_READ
							, NULL
							, OPEN_EXISTING
							, FILE_ATTRIBUTE_NORMAL
							, NULL) ;

	if ( hFile != INVALID_HANDLE_VALUE )
	{
		::CloseHandle (hFile) ;
		return TRUE ;
	}

	return FALSE ;
}

// It returns a INVALID_HANDLE_VALUE if the specified file is not valid.
// (Does not throw an exception)
HANDLE CHttpToolA::OpenFile (LPCSTR szFilePath)
	throw (Exception &)
{
	HTTPTOOL_ASSERT (szFilePath != NULL, "CHttpToolA::OpenFile: szFilePath can not be NULL.") ;

	return ::CreateFileA (
			szFilePath
			, GENERIC_READ
			, FILE_SHARE_READ
			, NULL
			, OPEN_EXISTING
			, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN
			, NULL) ;
}

// If it fails to create an file, it will return INVALID_HANDLE_VALUE.
// (Does not throw an exception)
HANDLE CHttpToolA::CreateFileAlwaysToWrite (LPCSTR szFilePath)
	throw (Exception &)
{
	HTTPTOOL_ASSERT (szFilePath != NULL, "CHttpToolA::CreateFileAlwaysToWrite: szFilePath can not be NULL.") ;

	return ::CreateFileA (
			szFilePath
			, GENERIC_WRITE
			, 0
			, NULL
			, CREATE_ALWAYS
			, FILE_ATTRIBUTE_NORMAL
			, NULL) ;
}




DWORD CHttpToolA::GetFileSize (HANDLE hFile, LPCSTR szFilePath)
	throw (Exception &)
{
	HTTPTOOL_ASSERT (hFile != NULL, "CHttpToolA::GetFileSize: hFile can not be NULL.") ;
	HTTPTOOL_ASSERT (szFilePath != NULL, "CHttpToolA::GetFileSize: szFilePath can not be NULL.") ;

	DWORD		dwFileSize = ::GetFileSize (hFile, NULL) ;
	if ( dwFileSize == INVALID_FILE_SIZE )
		ThrowException (HTTPCLIENT_ERR_GETFILESIZE_FAILED, ::GetLastError (), szFilePath) ;

	return dwFileSize ;
}

// The file handle must point to BOF. If the file handle is INVALID_HANDLE_VALUE
// The default mime type is returned.
// The returned string must be freed by using the ::free () function.
LPSTR CHttpToolA::GetMimeType (HANDLE hFile, UINT CodePage)
	throw (Exception &)
{
	HTTPTOOL_ASSERT (hFile != NULL, "CHttpToolA::GetMimeType: hFile can not be NULL.") ;

	LPSTR		szMimeType = NULL ;
	try {
		szMimeType = CHttpToolW::GetMimeType (hFile, CodePage) ;
	} catch (CHttpToolW::Exception & e) {
		ThrowException (e) ;
	}

	return szMimeType ;
}

// Returns the HTTP status text from the HTTP request handle.
// The returned string must be freed by using the ::free () function.
LPSTR CHttpToolA::GetStatusText (HINTERNET hRequest)
	throw (Exception &)
{
	HTTPTOOL_ASSERT (hRequest != NULL, "CHttpToolA::GetStatusText: hRequest can not be NULL.") ;

	DWORD			cbBuff = 0 ;
	LPSTR			szStatusText = NULL ;

	// Get required buffer size
	if ( !::HttpQueryInfoA (
			hRequest
			, HTTP_QUERY_STATUS_TEXT 		// Get the status text
			, static_cast<void *> (szStatusText)
			, &cbBuff						// Required buffer size (byte)
			, NULL							// Don't use a header index
			)
		)
		if ( ::GetLastError () != ERROR_INSUFFICIENT_BUFFER )
			ThrowException (HTTPCLIENT_ERR_QUERYINFO_FAILED, ::GetLastError ()) ;

	_ASSERTE ( cbBuff != 0 ) ;

	szStatusText = (LPSTR) ::malloc (cbBuff) ;
	if ( szStatusText == NULL )
		ThrowException (HTTPCLIENT_ERR_OUT_OF_MEMORY) ;

	// Get the status text
	if ( !::HttpQueryInfoA (
			hRequest
			, HTTP_QUERY_STATUS_TEXT 		// Get the status text
			, static_cast<void *> (szStatusText)
			, &cbBuff						// Allocated buffer size (byte)
			, NULL							// Don't use a header index
			)
		) {
		::free (szStatusText) ;
		ThrowException (HTTPCLIENT_ERR_QUERYINFO_FAILED, ::GetLastError ()) ;
	}

	return szStatusText ;
}

// Get the HTTP header which has the name specified by szName from the HTTP request handle.
// If the header is not found, NULL is returned.
// The returned string must be freed by using the ::free () function.
// pnIdx exactly corresponds to the lpdwIndex parameter in the ::HttpQueryInfo function.
// For more information about this parameter, see microsoft's SDK documentation.
LPSTR CHttpToolA::GetHeader (HINTERNET hRequest, LPCSTR szName, DWORD * pnIdx)
	throw (Exception &)
{
	HTTPTOOL_ASSERT (hRequest != NULL, "CHttpToolA::GetHeader: hRequest can not be NULL.") ;
	HTTPTOOL_ASSERT (szName != NULL, "CHttpToolA::GetHeader: szName can not be NULL.") ;

	DWORD			nOrigIdx = 0 ;
	if ( pnIdx )	nOrigIdx = *pnIdx ;

	// Copy the header name
	::SafeInt<DWORD>	cbBuff ;
	try {
		cbBuff = ::strlen (szName) ;
		cbBuff++ ;
		cbBuff *= sizeof (CHAR) ;
	} catch (::SafeIntException & e) {
		ThrowException (e) ;
	}

	PSTR		szHeader = (PSTR) ::malloc (cbBuff.Value ()) ;
	if ( szHeader == NULL )
		ThrowException (HTTPCLIENT_ERR_OUT_OF_MEMORY) ;
	::strcpy (szHeader, szName) ;

	if ( ::HttpQueryInfoA (
			hRequest
			, HTTP_QUERY_CUSTOM				// Get a custom header
			, static_cast<void *> (szHeader)
			, cbBuff.Ptr ()
			, pnIdx	
			)
		)
		return szHeader ;

	SAFEFREE (szHeader) ;
	if ( pnIdx )	*pnIdx = nOrigIdx ;

	// If the function failed
	if ( ::GetLastError () != ERROR_INSUFFICIENT_BUFFER ) {
		// If the header does not exist
		if ( ::GetLastError () == ERROR_HTTP_HEADER_NOT_FOUND )
			return NULL ;

		ThrowException (HTTPCLIENT_ERR_QUERYINFO_FAILED, ::GetLastError ()) ;
	}

	// Allocates required memory
	szHeader = (LPSTR) ::malloc (cbBuff.Value ()) ;
	if ( szHeader == NULL )
		ThrowException (HTTPCLIENT_ERR_OUT_OF_MEMORY) ;
	::strcpy (szHeader, szName) ;

	if ( !::HttpQueryInfoA (
			hRequest
			, HTTP_QUERY_CUSTOM				// Get a custom header
			, static_cast<void *> (szHeader)
			, cbBuff.Ptr ()
			, pnIdx	
			)
		) {
		SAFEFREE (szHeader) ;
		ThrowException (HTTPCLIENT_ERR_QUERYINFO_FAILED, ::GetLastError ()) ;
	}

	return szHeader ;
}

void CHttpToolA::InternetSetOption (HINTERNET hInternet, DWORD dwOption, LPVOID lpBuffer, DWORD dwBufferLength)
	throw (Exception &)
{
	if ( ::InternetSetOptionA (hInternet, dwOption, lpBuffer, dwBufferLength) )
		return ;

	ThrowException (HTTPCLIENT_ERR_INTERNETSETOPTION_FAILED, ::GetLastError ()) ;
}

// Generates a new upload boundary. If an error occurs, NULL is returned
// The returned string must be freed by using the ::free () function.
LPSTR CHttpToolA::CreateUploadBoundary (void)
	throw ()
{
	GUID			guid ;

	if ( FAILED ( ::CoCreateGuid (&guid)) )
		return NULL ;

	PSTR		szBoundary = (PSTR) ::malloc (sizeof (CHAR) * 44) ;

	if ( szBoundary == NULL )
		return NULL ;

	::sprintf (szBoundary, "----LYOUL-%.08x%.04x%.04x%.02x%.02x%.02x%.02x%.02x%.02x%.02x%.02x-"
		, guid.Data1, guid.Data2, guid.Data3, guid.Data4[0], guid.Data4[1]
		, guid.Data4[2], guid.Data4[3], guid.Data4[4], guid.Data4[5], guid.Data4[6], guid.Data4[7]);

	return szBoundary ;
}
///////////////////////////////////////// CHttpToolA /////////////////////////////////////////


///////////////////////////////////////// CHttpToolW /////////////////////////////////////////
inline
LPCWSTR CHttpToolW::GetConstMessage (int nIdx)
	throw ()
{
	// user-defined error message
	if ( nIdx >= 1000 )
		return g_UsrErrMsgW[0] ;

	// Win32 API error (which has a win32 error code)
	if ( nIdx >= 600 )
		return g_Win32MsgWin32W[nIdx % 100] ;

	// WinInet error (which has a win32 error code)
	if ( nIdx >= 400 )
		return g_WinInetMsgWin32W[nIdx % 100] ;

	// Normal error (which has a win32 error code)
	if ( nIdx >= 200 )
		return g_NormalMsgWin32W[nIdx % 100] ;

	// Normal error
	if ( nIdx >= 100 )
		return g_NormalMsgW[nIdx % 100] ;

	return g_NotSpecifiedW[nIdx] ;
}

void CHttpToolW::ThrowException (DWORD nErrMsgIdx)
	throw (Exception &)
{
	throw Exception (GetConstMessage (nErrMsgIdx), nErrMsgIdx) ;
}

void CHttpToolW::ThrowException (LPCWSTR szErrMsg, DWORD nErrMsgIdx)
	throw (Exception &)
{
	throw Exception (szErrMsg, nErrMsgIdx) ;
}

void CHttpToolW::ThrowException (DWORD nErrMsgIdx, DWORD dwErrCode, LPCWSTR szStrArg)
	throw (Exception &)
{
	if ( szStrArg == NULL )
		throw Exception (GetConstMessage (nErrMsgIdx), nErrMsgIdx, dwErrCode) ;

	WCHAR		szErrMsg[512] ;
	LPCWSTR		szFormat = GetConstMessage (nErrMsgIdx) ;
	int			cchWritten = SNPrintf (szErrMsg, 511, szFormat, szStrArg ? szStrArg : L"NULL") ;

	// if an error occurs
	if ( cchWritten < -1 )
		throw Exception (szFormat, nErrMsgIdx, dwErrCode) ;

	if ( cchWritten == -1 )
		cchWritten = 511 ;

	szErrMsg[cchWritten] = NULL ;
	throw Exception (szErrMsg, nErrMsgIdx, dwErrCode) ;
}

void CHttpToolW::ThrowException (LPCSTR szErrMsg, DWORD nErrMsgIdx, DWORD dwErrCode)
	throw (Exception &)
{
	LPWSTR		szErrMsgW = NULL ;

	try {
		szErrMsgW = Ansi2Unicode (szErrMsg) ;
	} catch (Exception &) {
		;
	}

	Exception		objException (szErrMsgW, nErrMsgIdx, dwErrCode) ;
	SAFEFREE (szErrMsgW) ;
	throw objException ;
}

void CHttpToolW::ThrowException (httpclientexceptionA & e)
	throw (Exception &)
{
	ThrowException (e.errmsg (), e.LastError (), e.Win32LastError ()) ;
}

void CHttpToolW::ThrowException (::SafeIntException & e)
	throw (Exception &)
{
	switch ( e.m_code ) {
		case ERROR_ARITHMETIC_OVERFLOW:
			ThrowException (HTTPCLIENT_ERR_ARITHMETIC_OVERFLOW) ;
			break ;

		case EXCEPTION_INT_DIVIDE_BY_ZERO:
			ThrowException (HTTPCLIENT_ERR_INT_DIVIDE_BY_ZERO) ;
			break ;

		default:
			ThrowException (HTTPCLIENT_ERR_UNEXPECTED_ARITHMETIC_ERROR) ;
			break ;
	}
}


// Conversion methods
// This function returns a converted ansi string from a unicode string.
// The returned string must be freed by using the ::free () function.
LPSTR CHttpToolW::Unicode2Ansi (LPCWSTR szStr, UINT CodePage)
	throw (Exception &)
{
	// The unicode encodings are not allowed
	HTTPTOOL_ASSERT ((CodePage != CP_UTF8) && (CodePage != CP_UTF7)
		, "CHttpToolW::Unicode2Ansi: CP_UTF8 and CP_UTF7 can not be used for the CodePage parameter.") ;

	if ( szStr == NULL )
		return NULL ;

	int		cchNeeded ;

	if ( 0 == (cchNeeded = ::WideCharToMultiByte (CodePage, 0, szStr, -1, NULL, 0, NULL, NULL)) )
		ThrowException (HTTPCLIENT_ERR_WIDECHARTOMULTIBYTE_FAILED, ::GetLastError ()) ;

	PSTR		szAnsi = (PSTR) ::malloc (sizeof (CHAR) * cchNeeded) ;
	if ( szAnsi == NULL )
		ThrowException (HTTPCLIENT_ERR_OUT_OF_MEMORY) ;

	if ( 0 == ::WideCharToMultiByte (CodePage, 0, szStr, -1, szAnsi, cchNeeded, NULL, NULL) ) {
		SAFEFREE (szAnsi) ;
		ThrowException (HTTPCLIENT_ERR_WIDECHARTOMULTIBYTE_FAILED, ::GetLastError ()) ;
	}

	return szAnsi ;
}

// This method returns a converted unicode string from a ansi string.
// The returned string must be freed by using the ::free () function.
LPWSTR CHttpToolW::Ansi2Unicode (LPCSTR szStr, UINT CodePage)
	throw (Exception &)
{
	// The unicode encodings are not allowed
	HTTPTOOL_ASSERT ((CodePage != CP_UTF8) && (CodePage != CP_UTF7)
		, "CHttpToolW::Ansi2Unicode: CP_UTF8 and CP_UTF7 can not be used for the CodePage parameter.") ;

	if ( szStr == NULL )
		return NULL ;

	int		cchNeeded ;
	if ( 0 == (cchNeeded = ::MultiByteToWideChar (CodePage, 0, szStr, -1, NULL, 0)) )
		ThrowException (HTTPCLIENT_ERR_MULTIBYTETOWIDECHAR_FAILED, ::GetLastError ()) ;

	PWSTR		szUni = (PWSTR) ::malloc (sizeof (WCHAR) * cchNeeded) ;
	if ( szUni == NULL )
		ThrowException (HTTPCLIENT_ERR_OUT_OF_MEMORY) ;

	if ( 0 == ::MultiByteToWideChar (CodePage, 0, szStr, -1, szUni, cchNeeded) ) {
		SAFEFREE (szUni) ;
		ThrowException (HTTPCLIENT_ERR_MULTIBYTETOWIDECHAR_FAILED, ::GetLastError ()) ;
	}

	return szUni ;
}


// comparison function (used by STL multimap)
bool CHttpToolW::operator () (LPCWSTR szKey1, LPCWSTR szKey2) const
	throw ()
{
	// return true if the two strings are null
	if ( szKey1 == NULL && szKey2 == NULL )
		return true ;

	if ( szKey1 == NULL )
		return true ;

	if ( szKey2 == NULL )
		return false ;

	// case insensitive
	return ::wcsicmp (szKey1, szKey2) < 0 ;
}

// Initializes a internet handle.
HINTERNET CHttpToolW::OpenInternet (LPCWSTR szUserAgent, DWORD dwAccessType
									, LPCWSTR szProxyName, LPCWSTR szProxyBypass, DWORD dwFlags)
	throw (Exception &)
{
	HINTERNET		hInternet ;

	if ( NULL == (hInternet = InternetOpenW (
			szUserAgent				// user agent
			, dwAccessType			// use direct connection or proxy connection
			, szProxyName
			, szProxyBypass
			, dwFlags)
		) )
		ThrowException (HTTPCLIENT_ERR_INTERNETOPEN_FAILED, ::GetLastError ()) ;

	return hInternet ;
}

// Closes a internet handle
void CHttpToolW::CloseInternet (HINTERNET & hInternet)
	throw ()
{
	CHttpToolA::CloseInternet (hInternet) ;
}

// Returns a connection handle
// The hInternet must be a valid internet handle.
HINTERNET CHttpToolW::OpenConnection (HINTERNET hInternet, LPCWSTR szServerAddr, INTERNET_PORT nPort
											, LPCWSTR szUsrName, LPCWSTR szUsrPwd)
	throw (Exception &)
{
	HTTPTOOL_ASSERT (hInternet != NULL, "CHttpToolW::OpenConnection: hInternet can not be NULL.") ;
	HTTPTOOL_ASSERT (szServerAddr != NULL, "CHttpToolW::OpenConnection: szServerAddr can not be NULL.") ;
	HTTPTOOL_ASSERT (::wcslen (szServerAddr) != 0, "CHttpToolW::OpenConnection: szServerAddr can not be an empty string.") ;

	HINTERNET			hConnection ;

	if ( NULL == (hConnection = ::InternetConnectW (
			hInternet
			, szServerAddr
			, nPort
			, szUsrName
			, szUsrPwd
			, INTERNET_SERVICE_HTTP
			, 0
			, NULL)
		) )
		ThrowException (HTTPCLIENT_ERR_INTERNETCONNECT_FAILED, ::GetLastError ()) ;

	return hConnection ;
}

// Closes a connection handle
void CHttpToolW::CloseConnection (HINTERNET & hConnection)
	throw ()
{
	CHttpToolA::CloseConnection (hConnection) ;
}

// Returns a HTTP request handle
HINTERNET CHttpToolW::OpenRequest (HINTERNET hConnection, LPCWSTR szMethod, LPCWSTR szObjectName
										 , DWORD dwFlags, LPCWSTR szReferer, UINT CodePage)
	throw (Exception &)
{
	HTTPTOOL_ASSERT (hConnection != NULL, "CHttpToolW::OpenRequest: hConnection can not be NULL.") ;
	HTTPTOOL_ASSERT (szObjectName != NULL, "CHttpToolW::OpenRequest: szObjectName can not be NULL.") ;
	HTTPTOOL_ASSERT (::wcslen (szObjectName) != 0, "CHttpToolW::OpenRequest: szObjectName can not be an empty string.") ;

	// The unicode encodings are not allowed
	HTTPTOOL_ASSERT ((CodePage != CP_UTF8) && (CodePage != CP_UTF7)
		, "CHttpToolW::OpenRequest: CP_UTF8 and CP_UTF7 can not be used for the CodePage parameter.") ;

	LPSTR		szMethodA = NULL ;
	LPSTR		szObjectNameA = NULL ;
	LPSTR		szRefererA = NULL ;

	HINTERNET		hRequest = NULL ;

	try {
		szMethodA = Unicode2Ansi (szMethod, CodePage) ;
		szObjectNameA = Unicode2Ansi (szObjectName, CodePage) ;
		szRefererA = Unicode2Ansi (szReferer, CodePage) ;

		hRequest = CHttpToolA::OpenRequest (hConnection, szMethodA, szObjectNameA
													, dwFlags, szRefererA) ;
	}  catch (Exception &) {
		SAFEFREE (szMethodA) ;
		SAFEFREE (szObjectNameA) ;
		SAFEFREE (szRefererA) ;
		throw ;
	} catch (CHttpToolA::Exception & e) {
		SAFEFREE (szMethodA) ;
		SAFEFREE (szObjectNameA) ;
		SAFEFREE (szRefererA) ;
		ThrowException (e) ;
	}

	SAFEFREE (szMethodA) ;
	SAFEFREE (szObjectNameA) ;
	SAFEFREE (szRefererA) ;

	return hRequest ;
}

// Closes a request handle
void CHttpToolW::CloseRequest (HINTERNET & hRequest)
	throw ()
{
	CHttpToolA::CloseRequest (hRequest) ;
}

void CHttpToolW::AddHeader (HINTERNET hRequest, LPCWSTR szName, LPCWSTR szValue, UINT CodePage)
	throw (Exception &)
{
	HTTPTOOL_ASSERT (hRequest != NULL, "CHttpToolW::AddHeader: hRequest can not be NULL.") ;
	HTTPTOOL_ASSERT (szName != NULL, "CHttpToolW::AddHeader: szName can not be NULL.") ;

	// The unicode encodings are not allowed
	HTTPTOOL_ASSERT ((CodePage != CP_UTF8) && (CodePage != CP_UTF7)
		, "CHttpToolW::AddHeader: CP_UTF8 and CP_UTF7 can not be used for the CodePage parameter.") ;

	LPSTR		szNameA = NULL ;
	LPSTR		szValueA = NULL ;

	try {
		szNameA = Unicode2Ansi (szName, CodePage) ;
		szValueA = Unicode2Ansi (szValue, CodePage) ;

		CHttpToolA::AddHeader (hRequest, szNameA, szValueA) ;
	}  catch (Exception &) {
		SAFEFREE (szNameA) ;
		SAFEFREE (szValueA) ;
		throw ;
	} catch (CHttpToolA::Exception & e) {
		SAFEFREE (szNameA) ;
		SAFEFREE (szValueA) ;
		ThrowException (e) ;
	}
	SAFEFREE (szNameA) ;
	SAFEFREE (szValueA) ;
}

void CHttpToolW::SendRequest (HINTERNET hRequest, LPCWSTR szPosted, UINT CodePage)
	throw (Exception &)
{
	HTTPTOOL_ASSERT (hRequest != NULL, "CHttpToolW::SendRequest: hRequest can not be NULL.") ;

	// The unicode encodings are not allowed
	HTTPTOOL_ASSERT ((CodePage != CP_UTF8) && (CodePage != CP_UTF7)
		, "CHttpToolW::SendRequest: CP_UTF8 and CP_UTF7 can not be used for the CodePage parameter.") ;

	LPSTR		szPostedA = Unicode2Ansi (szPosted, CodePage) ;

	::SafeInt<DWORD>	cchPosted ;
	try {
		cchPosted = szPostedA ? ::strlen (szPostedA) : 0 ;
	} catch (::SafeIntException & e) {
		SAFEFREE (szPostedA) ;
		ThrowException (e) ;
	}

	if ( !::HttpSendRequestW (
			hRequest
			, NULL						// Additional header
			, 0							// The length of the additional header
			, (void *) szPostedA		// A posted data
			, cchPosted.Value ()		// The length of the posted data
		) ) {
		SAFEFREE (szPostedA) ;
		ThrowException (HTTPCLIENT_ERR_HTTPSENDREQUEST_FAILED, ::GetLastError ()) ;
	}
	SAFEFREE (szPostedA) ;
}

void CHttpToolW::SendRequestEx (HINTERNET hRequest, DWORD dwPostedSize)
	throw (Exception &)
{
	HTTPTOOL_ASSERT (hRequest != NULL, "CHttpToolW::SendRequestEx: hRequest can not be NULL.") ;

	INTERNET_BUFFERSW BufferIn;

	BufferIn.dwStructSize = sizeof( INTERNET_BUFFERS ); // Must be set or error will occur
    BufferIn.Next = NULL; 
    BufferIn.lpcszHeader = NULL;
    BufferIn.dwHeadersLength = 0;
    BufferIn.dwHeadersTotal = 0;
    BufferIn.lpvBuffer = NULL;                
    BufferIn.dwBufferLength = 0;
    BufferIn.dwBufferTotal = dwPostedSize; // This is the only member used other than dwStructSize
    BufferIn.dwOffsetLow = 0;
    BufferIn.dwOffsetHigh = 0;

	if ( !::HttpSendRequestExW (
			hRequest
			, &BufferIn
			, NULL
			, 0
			, 0
		) )
		ThrowException (HTTPCLIENT_ERR_HTTPSENDREQUESTEX_FAILED, ::GetLastError ()) ;
}

void CHttpToolW::InternetWriteFile (HINTERNET hRequest, const BYTE * pbyBuff, DWORD cbyBuff)
	throw (Exception &)
{
	HTTPTOOL_ASSERT (hRequest != NULL, "CHttpToolW::InternetWriteFile: hRequest can not be NULL.") ;
	HTTPTOOL_ASSERT (pbyBuff != NULL, "CHttpToolW::InternetWriteFile: pbyBuff can not be NULL.") ;
	HTTPTOOL_ASSERT (cbyBuff != 0, "CHttpToolW::InternetWriteFile: cbyBuff can not be zero.") ;

	DWORD		dwTotalWritten = 0, dwWritten ;

	while ( dwTotalWritten < cbyBuff ) {
		if ( !::InternetWriteFile (
							hRequest
							, pbyBuff + dwTotalWritten
							, cbyBuff - dwTotalWritten
							, &dwWritten
				)
			)
			ThrowException (HTTPCLIENT_ERR_INTERNETWRITEFILE_FAILED, ::GetLastError ()) ;

		dwTotalWritten += dwWritten ;
	}
}

void CHttpToolW::EndRequest (HINTERNET hRequest)
	throw (Exception &)
{
	HTTPTOOL_ASSERT (hRequest != NULL, "CHttpToolW::EndRequest: hRequest can not be NULL.") ;

	if ( !::HttpEndRequest (
						hRequest
						, NULL
						, 0
						, 0
			)
		)
		ThrowException (HTTPCLIENT_ERR_HTTPENDREQUEST_FAILED, ::GetLastError ()) ;
}

// Checks whether the file exists.
// (Does not throw an exception)
BOOL CHttpToolW::FileExists (LPCWSTR szFilePath)
	throw (Exception &)
{
	HTTPTOOL_ASSERT (szFilePath != NULL, "CHttpToolW::FileExists: szFilePath can not be NULL.") ;
	
	HANDLE		hFile = ::CreateFileW (
							szFilePath
							, 0
							, FILE_SHARE_READ
							, NULL
							, OPEN_EXISTING
							, FILE_ATTRIBUTE_NORMAL
							, NULL) ;

	if ( hFile != INVALID_HANDLE_VALUE )
	{
		::CloseHandle (hFile) ;
		return TRUE ;
	}

	return FALSE ;
}

// It returns a INVALID_HANDLE_VALUE if the specified file is not valid.
// (Does not throw an exception)
HANDLE CHttpToolW::OpenFile (LPCWSTR szFilePath)
	throw (Exception &)
{
	HTTPTOOL_ASSERT (szFilePath != NULL, "CHttpToolW::OpenFile: szFilePath can not be NULL.") ;

	return ::CreateFileW (
			szFilePath
			, GENERIC_READ
			, FILE_SHARE_READ
			, NULL
			, OPEN_EXISTING
			, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN
			, NULL) ;
}

// If it fails to create an file, it will return INVALID_HANDLE_VALUE.
// (Does not throw an exception)
HANDLE CHttpToolW::CreateFileAlwaysToWrite (LPCWSTR szFilePath)
	throw (Exception &)
{
	HTTPTOOL_ASSERT (szFilePath != NULL, "CHttpToolW::CreateFileAlwaysToWrite: szFilePath can not be NULL.") ;

	return ::CreateFileW (
			szFilePath
			, GENERIC_WRITE
			, 0
			, NULL
			, CREATE_ALWAYS
			, FILE_ATTRIBUTE_NORMAL
			, NULL) ;
}

DWORD CHttpToolW::GetFileSize (HANDLE hFile, LPCWSTR szFilePath)
	throw (Exception &)
{
	HTTPTOOL_ASSERT (hFile != NULL, "CHttpToolW::GetFileSize: hFile can not be NULL.") ;
	HTTPTOOL_ASSERT (szFilePath != NULL, "CHttpToolW::GetFileSize: szFilePath can not be NULL.") ;

	DWORD		dwFileSize = ::GetFileSize (hFile, NULL) ;
	if ( dwFileSize == INVALID_FILE_SIZE )
		ThrowException (HTTPCLIENT_ERR_GETFILESIZE_FAILED, ::GetLastError (), szFilePath) ;

	return dwFileSize ;
}

// The file handle must point to BOF. If the file handle is INVALID_HANDLE_VALUE
// The default mime type is returned.
// The returned string must be freed by using the ::free () function.
LPSTR CHttpToolW::GetMimeType (HANDLE hFile, UINT CodePage)
	throw (Exception &)
{
	HTTPTOOL_ASSERT (hFile != NULL, "CHttpToolW::GetMimeType: hFile can not be NULL.") ;

	// The unicode encodings are not allowed
	HTTPTOOL_ASSERT ((CodePage != CP_UTF8) && (CodePage != CP_UTF7)
		, "CHttpToolW::GetMimeType: CP_UTF8 and CP_UTF7 can not be used for the CodePage parameter.") ;

	// If the file handle is not valid, just returns the default MimeType.
	if ( hFile == INVALID_HANDLE_VALUE ) {
		PSTR		szMimeA = NULL ;
		szMimeA = (PSTR) ::malloc (sizeof (CHAR) * (::strlen (HTTPCLIENT_DEF_MIMETYPE) + 1)) ;
		if ( szMimeA == NULL )
			ThrowException (HTTPCLIENT_ERR_OUT_OF_MEMORY) ;
		::strcpy (szMimeA, HTTPCLIENT_DEF_MIMETYPE) ;
		return szMimeA ;
	}

	BYTE		byBuff[256] ;
	DWORD		dwRead ;

	if ( 0 == ::ReadFile (hFile
				, byBuff
				, 256
				, &dwRead
				, NULL) )
		ThrowException (HTTPCLIENT_ERR_READFILE_FAILED, ::GetLastError ()) ;

	// Moves the file pointer to the beginning of the file.
	if ( INVALID_SET_FILE_POINTER == ::SetFilePointer (hFile, 0, NULL, FILE_BEGIN) )
		ThrowException (HTTPCLIENT_ERR_SETFILEPOINTER_FAILED, ::GetLastError ()) ;

	PWSTR		szMimeW = NULL ;
	PSTR		szMimeA = NULL ;
	HRESULT		hResult ;

	// If the ::FindMimeFromData function failed to get a appropriate MimeType,
	// just returns the default MimeType.
	if ( NOERROR != (hResult = ::FindMimeFromData (
						NULL
						, NULL
						, byBuff
						, dwRead
						, NULL
						, 0
						, &szMimeW
						, 0)) ) {
		szMimeA = (PSTR) ::malloc (sizeof (CHAR) * (::strlen (HTTPCLIENT_DEF_MIMETYPE) + 1)) ;
		if ( szMimeA == NULL )
			ThrowException (HTTPCLIENT_ERR_OUT_OF_MEMORY) ;
		::strcpy (szMimeA, HTTPCLIENT_DEF_MIMETYPE) ;
		return szMimeA ;
	}

	try {
		szMimeA = Unicode2Ansi (szMimeW, CodePage) ;
	} catch (Exception &) {
		::CoTaskMemFree (szMimeW) ;
		throw ;
	}

	::CoTaskMemFree (szMimeW) ;
	return szMimeA ;
}

// Returns the HTTP status text from the HTTP request handle.
// The returned string must be freed by using the ::free () function.
LPWSTR CHttpToolW::GetStatusText (HINTERNET hRequest)
	throw (Exception &)
{
	HTTPTOOL_ASSERT (hRequest != NULL, "CHttpToolW::GetStatusText: hRequest can not be NULL.") ;

	DWORD			cbBuff = 0 ;
	PWSTR			szStatusText = NULL ;

	// Get required buffer size
	if ( !::HttpQueryInfoW (
			hRequest
			, HTTP_QUERY_STATUS_TEXT 		// Get the status text
			, static_cast<void *> (szStatusText)
			, &cbBuff						// Required buffer size (byte)
			, NULL							// Don't use a header index
			)
		)
		if ( ::GetLastError () != ERROR_INSUFFICIENT_BUFFER )
			ThrowException (HTTPCLIENT_ERR_QUERYINFO_FAILED, ::GetLastError ()) ;

	_ASSERTE ( cbBuff != 0 ) ;

	szStatusText = (PWSTR) ::malloc (cbBuff) ;
	if ( szStatusText == NULL )
		ThrowException (HTTPCLIENT_ERR_OUT_OF_MEMORY) ;

	// Get the status text
	if ( !::HttpQueryInfoW (
			hRequest
			, HTTP_QUERY_STATUS_TEXT 		// Get the status text
			, static_cast<void *> (szStatusText)
			, &cbBuff						// Required buffer size (byte)
			, NULL							// Don't use a header index
			)
		) {
		::free (szStatusText) ;
		ThrowException (HTTPCLIENT_ERR_QUERYINFO_FAILED, ::GetLastError ()) ;
	}

	return szStatusText ;
}

// Get the HTTP header which has the name specified by szName from the HTTP request handle.
// If the header is not found, NULL is returned.
// The returned string must be freed by using the ::free () function.
// pnIdx exactly corresponds to the lpdwIndex parameter in the ::HttpQueryInfo function.
// For more information about this parameter, see microsoft's SDK documentation.
LPWSTR CHttpToolW::GetHeader (HINTERNET hRequest, LPCWSTR szName, DWORD * pnIdx)
	throw (Exception &)
{
	HTTPTOOL_ASSERT (hRequest != NULL, "CHttpToolW::GetHeader: hRequest can not be NULL.") ;
	HTTPTOOL_ASSERT (szName != NULL, "CHttpToolW::GetHeader: szName can not be NULL.") ;

	DWORD			nOrigIdx = 0 ;
	if ( pnIdx )	nOrigIdx = *pnIdx ;


	// Copy the header name
	::SafeInt<DWORD>		cbBuff ;
	try {
		cbBuff = ::wcslen (szName) ;
		cbBuff++ ;		// for '\0' character
		cbBuff *= sizeof (WCHAR) ;
	} catch (::SafeIntException & e) {
		ThrowException (e) ;
	}

	PWSTR		szHeader = (PWSTR) ::malloc (cbBuff.Value ()) ;
	if ( szHeader == NULL )
		ThrowException (HTTPCLIENT_ERR_OUT_OF_MEMORY) ;
	::wcscpy (szHeader, szName) ;

	if ( ::HttpQueryInfoW (
			hRequest
			, HTTP_QUERY_CUSTOM				// Get a custom header
			, static_cast<void *> (szHeader)
			, cbBuff.Ptr ()
			, pnIdx	
			)
		)
		return szHeader ;

	SAFEFREE (szHeader) ;
	if ( pnIdx )	*pnIdx = nOrigIdx ;

	// If the function failed
	if ( ::GetLastError () != ERROR_INSUFFICIENT_BUFFER ) {
		// If the header does not exist
		if ( ::GetLastError () == ERROR_HTTP_HEADER_NOT_FOUND )
			return NULL ;

		ThrowException (HTTPCLIENT_ERR_QUERYINFO_FAILED, ::GetLastError ()) ;
	}

	// Allocates required memory
	szHeader = (PWSTR) ::malloc (cbBuff.Value ()) ;
	if ( szHeader == NULL )
		ThrowException (HTTPCLIENT_ERR_OUT_OF_MEMORY) ;
	::wcscpy (szHeader, szName) ;

	if ( !::HttpQueryInfoW (
			hRequest
			, HTTP_QUERY_CUSTOM				// Get a custom header
			, static_cast<void *> (szHeader)
			, cbBuff.Ptr ()
			, pnIdx
			)
		) {
		SAFEFREE (szHeader) ;
		ThrowException (HTTPCLIENT_ERR_QUERYINFO_FAILED, ::GetLastError ()) ;
	}

	return szHeader ;
}

void CHttpToolW::InternetSetOption (HINTERNET hInternet, DWORD dwOption, LPVOID lpBuffer, DWORD dwBufferLength)
	throw (Exception &)
{
	if ( ::InternetSetOptionW (hInternet, dwOption, lpBuffer, dwBufferLength) )
		return ;

	ThrowException (HTTPCLIENT_ERR_INTERNETSETOPTION_FAILED, ::GetLastError ()) ;
}

// Generates a new upload boundary. If an error occurs, NULL is returned
// The returned string must be freed by using the ::free () function.
LPWSTR CHttpToolW::CreateUploadBoundary (void)
	throw ()
{
	GUID			guid ;

	if ( FAILED ( ::CoCreateGuid (&guid)) )
		return NULL ;

	PWSTR			szBoundary = NULL ;
	szBoundary = (PWSTR) ::malloc (sizeof (WCHAR) * 44) ;

	if ( szBoundary == NULL )
		return NULL ;

	::swprintf (szBoundary, L"----LYOUL-%.08x%.04x%.04x%.02x%.02x%.02x%.02x%.02x%.02x%.02x%.02x-"
		, guid.Data1, guid.Data2, guid.Data3, guid.Data4[0], guid.Data4[1]
		, guid.Data4[2], guid.Data4[3], guid.Data4[4], guid.Data4[5], guid.Data4[6], guid.Data4[7]);

	return szBoundary ;
}
///////////////////////////////////////// CHttpToolW /////////////////////////////////////////


///////////////////////////////////////// CHttpClientMapT /////////////////////////////////////////
template <typename HttpTool>
CHttpClientMapT<HttpTool>::CHttpClientMapT (void)
	throw ()
{
	;	// do nothing yet
}

template <typename HttpTool>
CHttpClientMapT<HttpTool>::~CHttpClientMapT (void)
	throw ()
{
	Clear () ;
}

// If some data cleared, it will return TRUE, otherwise return FALSE.
// If memory is exhausted, it can leak memory.
template <typename HttpTool>
BOOL CHttpClientMapT<HttpTool>::Clear (void)
	throw ()
{
	if ( m_map.empty () )
		return FALSE ;

	// Allocates memory to save pointers for key name.
	// If an overflow exception or a memory allocation failure is occurs,
	// The memory pointed by the key name pointer are leaked.
	::SafeInt<MapSizeType>	cKeys = m_map.size () ;
	::SafeInt<size_t>		cbRequired = 0 ;
	PCSZ *					arrKeys = NULL ;

	try {
		cbRequired = cKeys ;
		cbRequired *= sizeof (PCSZ) ;
		arrKeys = (PCSZ *) ::malloc (cbRequired.Value ()) ;
	} catch (::SafeIntException &) {
		arrKeys = NULL ;
	}

	MapSizeType		nIdx = 0 ;
	for (MapIter iter = m_map.begin (); iter != m_map.end (); ++iter) {
		(iter->second).Delete () ;

		// Saves the key name pointer
		if ( arrKeys )
			arrKeys[nIdx++] = iter->first ;
	}

	m_map.clear () ;

	if ( arrKeys == NULL )
		return TRUE ;

	for (nIdx = 0; nIdx < cKeys; nIdx++)
		SAFEFREE ( arrKeys[nIdx] ) ;
	SAFEFREE ( arrKeys ) ;

	return TRUE ;
}

// This function deletes an element in position nIdx from the map.
// If the element deleted, it will return TRUE, otherwise return FALSE.
template <typename HttpTool>
BOOL CHttpClientMapT<HttpTool>::Remove (DWORD nIdx)
	throw ()
{
	if ( m_map.empty () )
		return FALSE ;

	// Find the item to delete
	MapIter	 iter = m_map.begin () ;
	for (DWORD i = 0; i < nIdx; i++) {
		++iter ;

		// If the nIdx is out of range.
		if ( iter == m_map.end () )
			return FALSE ;
	}

	(iter->second).Delete () ;
	PCSZ		szName = iter->first ;
	m_map.erase (iter) ;
	SAFEFREE (szName) ;
	return TRUE ;
}

// This function erases all elements of which key name equals to szName.
// If some data cleared, it will return TRUE, otherwise return FALSE.
// If memory is exhausted, memory will be leaked.
template <typename HttpTool>
BOOL CHttpClientMapT<HttpTool>::RemoveAll (PCSZ szName)
	throw (Exception &)
{
	HTTPCLIENT_ASSERT (szName != NULL, "CHttpClientMapT::RemoveAll: szName can not be NULL.") ;

	::SafeInt<MapSizeType>	cKeys = m_map.count (szName) ;

	// If the specified key name does not exist in the map
	if ( cKeys.Value () == 0 )
		return FALSE ;

	// Allocates memory to save pointers to key name.
	// If the allocation failed, the memory pointed by the key name pointer are leaked.
	::SafeInt<size_t>	cbRequired ;
	PCSZ *				arrKeys = NULL ;
	try {
		cbRequired = cKeys ;
		cbRequired *= sizeof (PCSZ) ;
		arrKeys = (PCSZ *) ::malloc (cbRequired.Value ()) ;
	} catch (::SafeIntException &) {
		arrKeys = NULL ;
	}

	std::pair<MapIter, MapIter>		pairIter = m_map.equal_range (szName) ;

	MapSizeType		nIdx = 0 ;
	for (MapIter iter = pairIter.first; iter != pairIter.second; ++iter) {
		(iter->second).Delete () ;

		// Saves the key name pointer
		if ( arrKeys )
			arrKeys[nIdx++] = iter->first ;
	}

	m_map.erase (pairIter.first, pairIter.second) ;

	if ( arrKeys == NULL )
		return TRUE ;

	for (nIdx = 0; nIdx < cKeys; nIdx++)
		SAFEFREE ( arrKeys[nIdx] ) ;
	SAFEFREE ( arrKeys ) ;

	return TRUE ;
}

// This function deletes an element of which the key name equals to szName and the index is nIdx.
// If some data cleared, it will return TRUE, otherwise return FALSE.
template <typename HttpTool>
BOOL CHttpClientMapT<HttpTool>::Remove (PCSZ szName, DWORD nIdx)
	throw (Exception &)
{
	HTTPCLIENT_ASSERT (szName != NULL, "CHttpClientMapT::Remove: szName can not be NULL.") ;

	std::pair<MapIter, MapIter>		pairIter = m_map.equal_range (szName) ;

	// If the specified key name does not exist in the map
	if ( pairIter.first == pairIter.second )
		return FALSE ;

	MapIter iter = pairIter.first ;
	for (DWORD i = 0; i < nIdx; i++) {
		++iter ;

		// If the nIdx is out of range.
		if ( iter == pairIter.second )
			return FALSE ;
	}

	// Deletes the element
	(iter->second).Delete () ;
	PCSZ	szKeyName = iter->first ;
	m_map.erase (iter) ;
	SAFEFREE (szKeyName) ;

	return TRUE ;
}

template <typename HttpTool>
BOOL CHttpClientMapT<HttpTool>::Exists (PCSZ szName, DWORD nIdx)
	throw (Exception &)
{
	HTTPCLIENT_ASSERT (szName != NULL, "CHttpClientMapT::Exists: szName can not be NULL.") ;

	::SafeInt<MapSizeType>		cKeys = m_map.count (szName) ;
	return (cKeys > nIdx) ;
}

// If the szName is NULL, it will return the count of elements.
template <typename HttpTool>
DWORD CHttpClientMapT<HttpTool>::Count (PCSZ szName)
	throw ()
{
	::SafeInt<DWORD>		cKeys ;
	try {
		cKeys = szName ? m_map.count (szName) : m_map.size () ;
	} catch (::SafeIntException &) {
		cKeys = cKeys.MaxInt () ;	// This statement is never executed.
	}
	return cKeys.Value () ;
}

template <typename HttpTool>
BOOL CHttpClientMapT<HttpTool>::Empty (void) const
	throw ()
{
	return m_map.empty () ? TRUE : FALSE ;
}

// If the nIdx is not valid, it will return NULL.
template <typename HttpTool>
typename CHttpClientMapT<HttpTool>::PCSZ
CHttpClientMapT<HttpTool>::GetKey (DWORD nIdx)
	throw ()
{
	if ( m_map.empty () )
		return NULL ;

	MapIter		iter = m_map.begin () ;
	for (DWORD i = 0; i < nIdx; i++) {
		++iter ;

		// If the nIdx is not valid.
		if ( iter == m_map.end () )
			return NULL ;
	}
	return iter->first ;
}

// If the specified element is not found in position nIdx,
// returns MapValue of which szValue is NULL.
template <typename HttpTool>
typename CHttpClientMapT<HttpTool>::MapValue
CHttpClientMapT<HttpTool>::Get (DWORD nIdx)
	throw ()
{
	MapValue	mapValue = { NULL, 0 } ;

	if ( m_map.empty () )
		return mapValue ;

	MapIter		iter = m_map.begin () ;
	for (DWORD i = 0; i < nIdx; i++) {
		++iter ;

		// If the nIdx is not valid.
		if ( iter == m_map.end () )
			return mapValue ;
	}

	mapValue = iter->second ;
	if ( mapValue.szValue == NULL )
		mapValue.szValue = HttpTool::szEmptyString ;

	return mapValue ;
}

// If the specified element is not found, it will return NULL.
template <typename HttpTool>
typename CHttpClientMapT<HttpTool>::PCSZ
CHttpClientMapT<HttpTool>::GetValue (DWORD nIdx)
	throw ()
{
	return Get (nIdx).szValue ;
}

// Returns 0 if the specified element is not found.
// (If the dwFlag in MapValue is 0, it also returns 0)
template <typename HttpTool>
DWORD CHttpClientMapT<HttpTool>::GetFlag (DWORD nIdx)
	throw ()
{
	return Get (nIdx).dwFlag ;
}

// If the specified element is not found, returns MapValue of which szValue is NULL.
template <typename HttpTool>
typename CHttpClientMapT<HttpTool>::MapValue
CHttpClientMapT<HttpTool>::Get (PCSZ szName, DWORD nIdx)
	throw (Exception &)
{
	HTTPCLIENT_ASSERT (szName != NULL, "CHttpClientMapT::Get: szName can not be NULL.") ;

	MapValue	mapValue = { NULL, 0 } ;
	std::pair<MapIter, MapIter>	pairIter ;
	pairIter = m_map.equal_range (szName) ;

	// If the key is not found.
	if ( pairIter.first == pairIter.second )
		return mapValue ;

	MapIter iter = pairIter.first ;
	for (DWORD i = 0; i < nIdx; i++) {
		++iter ;

		// If the nIdx is not valid
		if ( iter == pairIter.second )
			return mapValue ;
	}

	mapValue = iter->second ;
	if ( mapValue.szValue == NULL )
		mapValue.szValue = HttpTool::szEmptyString ;

	return mapValue ;
}

// Returns NULL if the specified element is not found.
template <typename HttpTool>
typename CHttpClientMapT<HttpTool>::PCSZ
CHttpClientMapT<HttpTool>::GetValue (PCSZ szName, DWORD nIdx)
	throw ()
{
	return Get (szName, nIdx).szValue ;
}

// Returns 0 if the specified element is not found.
// (If the dwFlag in MapValue is 0, it also returns 0)
template <typename HttpTool>
DWORD CHttpClientMapT<HttpTool>::GetFlag (PCSZ szName, DWORD nIdx)
	throw ()
{
	return Get (szName, nIdx).dwFlag ;
}

// Adds a new MapValue. It also receives the ownership of memory pointed by szName and szValue.
// The szName and szValue must be allocated by using ::malloc.
// The szValue could be NULL.
template <typename HttpTool>
void CHttpClientMapT<HttpTool>::AddPointerDirectly (PSZ szName, PSZ szValue, BOOL dwFlag)
	throw (Exception &)
{
	HTTPCLIENT_ASSERT (szName != NULL, "CHttpClientMapT::AddPointerDirectly: szName can not be NULL.") ;

	try {
		// Checks the arithmetic overflow exception
		::SafeInt<MapSizeType>	cKeys = Count () ;
		::SafeInt<DWORD>		cdwKeys = cKeys ;
		cKeys++ ;
		cdwKeys++ ;

		MapValue	newValue = { (PCSZ) szValue, dwFlag } ;
		m_map.insert (MapItem ((PCSZ) szName, newValue)) ;
	} catch (::SafeIntException & e) {
		HttpTool::ThrowException (e) ;
	} catch (std::exception & e) {
		HttpTool::ThrowException (e.what (), HTTPCLIENT_ERR_STD_EXCEPTION) ;
	} catch (...) {
		HttpTool::ThrowException (HTTPCLIENT_ERR_UNEXPECTED_ERROR) ;
	}
}

// The szValue could be NULL.
template <typename HttpTool>
void CHttpClientMapT<HttpTool>::Add (PCSZ szName, PCSZ szValue, BOOL dwFlag)
	throw (Exception &)
{
	HTTPCLIENT_ASSERT (szName != NULL, "CHttpClientMapT::Add: szName can not be NULL.") ;

	PSZ					szNewName = NULL ;
	PSZ					szNewValue = NULL ;
	::SafeInt<size_t>	cbName, cbValue ;

	try {
		cbName = HttpTool::StringLen (szName) ;
		cbName++ ;
		cbName *= sizeof (CharType) ;

		szNewName = (PSZ) ::malloc (cbName.Value ()) ;
		if ( szNewName == NULL )
			HttpTool::ThrowException (HTTPCLIENT_ERR_OUT_OF_MEMORY) ;
		HttpTool::StringCopy (szNewName, szName) ;

		if ( szValue != NULL ) {
			cbValue = HttpTool::StringLen (szValue) ;
			cbValue++ ;
			cbValue *= sizeof (CharType) ;

			szNewValue = (PSZ) ::malloc (cbValue.Value ()) ;
			if ( szNewValue == NULL )
				HttpTool::ThrowException (HTTPCLIENT_ERR_OUT_OF_MEMORY) ;
			HttpTool::StringCopy (szNewValue, szValue) ;
		}
		AddPointerDirectly (szNewName, szNewValue, dwFlag) ;

	} catch (::SafeIntException & e) {
		SAFEFREE (szNewName) ;
		SAFEFREE (szNewValue) ;
		HttpTool::ThrowException (e) ;
	} catch (Exception &) {
		SAFEFREE (szNewName) ;
		SAFEFREE (szNewValue) ;
		throw ;
	}
}

// If the specified element is not found, it will add a new value.
// otherwise it will modifiy the existing value.
// The szValue could be NULL.
template <typename HttpTool>
void CHttpClientMapT<HttpTool>::Set (PCSZ szName, PCSZ szValue, BOOL dwFlag, DWORD nIdx)
	throw (Exception &)
{
	HTTPCLIENT_ASSERT (szName != NULL, "CHttpClientMapT::Set: szName can not be NULL.") ;

	std::pair<MapIter, MapIter>	pairIter ;
	pairIter = m_map.equal_range (szName) ;

	// Adds a new value if the specified element is not found.
	if ( pairIter.first == pairIter.second ) {
		// The nIdx must be 0
		_ASSERTE ( nIdx == 0 ) ;
		Add (szName, szValue, dwFlag) ;
		return ;
	}

	MapIter iter = pairIter.first ;
	for (DWORD i = 0; i < nIdx; i++) {
		++iter ;

		// The nIdx must be valid
		_ASSERTE ( iter != pairIter.second ) ;
	}

	MapValue		newValue = { NULL, dwFlag } ;
	if ( szValue ) {
		::SafeInt<size_t>		cbValue ;

		try {
			cbValue = HttpTool::StringLen (szValue) ;
			cbValue++ ;
			cbValue *= sizeof (CharType) ;
		} catch (::SafeIntException & e) {
			HttpTool::ThrowException (e) ;
		}

		newValue.szValue = (PCSZ) ::malloc (cbValue.Value ()) ;
		if ( newValue.szValue == NULL )
			HttpTool::ThrowException (HTTPCLIENT_ERR_OUT_OF_MEMORY) ;

		HttpTool::StringCopy ((PSZ) newValue.szValue, szValue) ;
	}

	MapValue		oldValue = iter->second ;
	iter->second = newValue ;
	oldValue.Delete () ;
}

template <typename HttpTool>
typename CHttpClientMapT<HttpTool>::ConstMapIter
CHttpClientMapT<HttpTool>::Begin (void) const
	throw ()
{
	return m_map.begin () ;
}

template <typename HttpTool>
typename CHttpClientMapT<HttpTool>::ConstMapIter
CHttpClientMapT<HttpTool>::End (void) const
	throw ()
{
	return m_map.end () ;
}
///////////////////////////////////////// CHttpClientMapT /////////////////////////////////////////


///////////////////////////////////////// CHttpEncoder /////////////////////////////////////////
/*!
 * This method has no meaning because the input string is an Ansi string.
 * It just returns the length of the input string.
 *
 * \param szStr			[in] A string which is encoded.
 * \param CodePage		[in] Ignored.
 * \return				The number of bytes required. (Not including the terminating NULL character)
 * \throw				Throws a httpclientexception if an error occurs.
 */
DWORD CHttpEncoderA::AnsiEncodeLen (PCSTR szStr, UINT /* CodePage */)
	throw (Exception &)
{
	if ( szStr == NULL || szStr[0] == '\0' )
		return 0 ;

	::SafeInt<DWORD>	dwLen ;
	try {
		dwLen = ::strlen (szStr) ;
	} catch (::SafeIntException & e) {
		CHttpToolA::ThrowException (e) ;
	}
	return dwLen.Value () ;
}

/*!
 * This method has no meaning because the input string is an Ansi string.
 * It just returns the copy of the input string.
 *
 * \param szBuff		[out] A buffer to save the encoded string. The buffer can not be NULL.
 * \param szStr			[in] A string which is encoded.
 * \param CodePage		[in] Ignored.
 * \return				An encoded string.
 * \throw				Throws a httpclientexception if an error occurs.
 */
PSTR CHttpEncoderA::AnsiEncode (PSTR szBuff, PCSTR szStr, UINT /* CodePage */)
	throw (Exception &)
{
	HTTPCLIENT_ASSERTA (szBuff != NULL, "CHttpEncoderA::AnsiEncode: szBuff can not be NULL.") ;

	if ( (szStr == NULL) || (szStr[0] == '\0') ) {
		szBuff[0] = '\0' ;
		return szBuff ;
	}
	return ::strcpy (szBuff, szStr) ;
}

/*!
 * This method has no meaning because the decoded string is an Ansi string.
 * It just returns the length of the input string.
 *
 * \param szEncoded		[in] A string to decode.
 * \param CodePage		[in] Ignored.
 * \return				The number of bytes required. (Not including the terminating NULL character)
 * \throw				Throws a httpclientexception if an error occurs.
 */
DWORD CHttpEncoderA::AnsiDecodeLen (PCSTR szEncoded, UINT /* CodePage */)
	throw (Exception &)
{
	if ( szEncoded == NULL || szEncoded[0] == '\0' )
		return 0 ;

	::SafeInt<DWORD>	dwLen ;
	try {
		dwLen = ::strlen (szEncoded) ;
	} catch (::SafeIntException & e) {
		CHttpToolA::ThrowException (e) ;
	}
	return dwLen.Value () ;
}

/*!
 * This method has no meaning because the decoded string is an Ansi string.
 * It just returns the copy of the input string.
 *
 * \param szBuff		[out] A buffer to save the decoded string. The buffer can not be NULL.
 * \param szEncoded		[in] A string to decode.
 * \param CodePage		[in] Ignored.
 * \return				A decoded string.
 * \throw				Throws a httpclientexception if an error occurs.
 */
PSTR CHttpEncoderA::AnsiDecode (PSTR szBuff, PCSTR szEncoded, UINT /* CodePage */)
	throw (Exception &)
{
	HTTPCLIENT_ASSERTA (szBuff != NULL, "CHttpEncoderA::AnsiDecode: szBuff can not be NULL.") ;

	if ( (szEncoded == NULL) || (szEncoded[0] == '\0') ) {
		szBuff[0] = '\0' ;
		return szBuff ;
	}
	return ::strcpy (szBuff, szEncoded) ;
}

// Checks whether the character is a valid hexadecimal.
template <typename CharType>
static BOOL _HexIsValid(CharType chHex)
{
	if ( chHex >= '0' && chHex <= '9' )
		return TRUE ;

	if ( chHex >= 'A' && chHex <= 'F' )
		return TRUE ;

	if ( chHex >= 'a' && chHex <= 'f' )
		return TRUE ;

	return FALSE ;
}

template <typename CharType>
static CharType _HexToNum(CharType chHex)
{
	if ( chHex >= '0' && chHex <= '9' )
		return chHex - '0' ;

	if ( chHex >= 'A' && chHex <= 'F' )
		return chHex - 'A' + 10 ;

	if ( chHex >= 'a' && chHex <= 'f' )
		return chHex - 'a' + 10 ;

	return 0 ;
}

template <typename CharType>
static BOOL _IsAlNum (CharType chChr)
{
	if (chChr >= 'A' && chChr <= 'Z')
		return TRUE ;
	
	if (chChr >= 'a' && chChr <= 'z')
		return TRUE ;
		
	if (chChr >= '0' && chChr <= '9')
		return TRUE ;

	return FALSE ;
}

/*!
 * \internal
 * This method returns the number of characters required to make a URL-encoded string.
 *
 * \param szEncoded		[in] An Ansi string or a UTF-8 string. The string can not be NULL.
 * \return				The number of characters required. (Not including the terminating NULL character)
 * \throw				Throws a httpclientexception if an error occurs.
 */
template <typename HttpTool>
static DWORD _UrlEncodeLen (PCSTR szStr)
	throw (typename HttpTool::Exception &)
{
	HTTPCLIENT_ASSERT (szStr != NULL, "_UrlEncodeLen: szStr can not be NULL.") ;

	::SafeInt<DWORD>	cchEncoded = 0 ;
	PCSTR				pchStr = szStr ;
	try {
		while ( *pchStr ) {
			cchEncoded++ ;
			if ( !_IsAlNum (*pchStr) && *pchStr != ' ' )
				cchEncoded += 2 ;
			pchStr++ ;
		}
	} catch (::SafeIntException & e) {
		HttpTool::ThrowException (e) ;
	}
	return cchEncoded.Value () ;
}

template <typename CharType>
static CharType _ToHex (CharType chChr)
{
	_ASSERTE ( chChr < 16 ) ;
	return chChr > 9 ? 'A' + (chChr - 10) : '0' + chChr ;
}

/*!
 * \internal
 * This method makes a URL-encoded string.
 *
 * \param szBuff		[out] A buffer to save the encoded string. The buffer can not be NULL.
 * \param szEncoded		[in] An Ansi string or a UTF-8 string. The string can not be NULL.
 * \return				An URL-encoded string.
 */
template <typename StringType>
static StringType _UrlEncode (StringType szBuff, PCSTR szStr)
{
	_ASSERTE ( szBuff != NULL && szStr != NULL ) ;

	const CHAR *	pchStr = szStr ;
	StringType		pchOut = szBuff ;

	// do encoding
	for (; *pchStr != NULL; pchStr++) {
		if ( _IsAlNum (*pchStr) ) {
			*pchOut++ = *pchStr ;
			continue ;
		}

		if ( *pchStr == ' ' ) {
			*pchOut++ = '+' ;
			continue ;
		}

		*pchOut++ = '%';
		*pchOut++ = _ToHex (((*pchStr) & 0xF0) >> 4);
		*pchOut++ = _ToHex ((*pchStr) & 0x0F);
	}
	*pchOut = '\0' ;
	return szBuff ;
}

/*!
 * \internal
 * This method converts an Ansi character into an UTF-8 character.
 * The detailed infomation about the Code-Page Identifiers is described in the MSDN documentation.
 *
 * \param szUtf8Char	[out] A buffer to save the converted UTF-8 character. The buffer can not be NULL.
 *						      The buffer must be allocated at least 7 characters.
 * \param szAnsiChar	[in] A string which contains an Ansi character. The string can not be NULL.
 * \param CodePage		[in] A code page of the Ansi character.
 * \throw				Throws a httpclientexception if an error occurs.
 */
void CHttpEncoderA::_AnsiCharToUtf8Char (PSTR szUtf8Char, PCSTR szAnsiChar, UINT CodePage)
	throw (Exception &)
{
	HTTPCLIENT_ASSERTA (szUtf8Char != NULL && szAnsiChar != NULL
		, "CHttpEncoderA::_AnsiCharToUtf8Char: szUtf8Char and szAnsiChar can not be NULL.") ;

	WCHAR		wszUnicode[2] ;

	// Get a unicode character
	if ( 0 == ::MultiByteToWideChar (CodePage, 0, szAnsiChar, -1, wszUnicode, 2) )
		CHttpToolA::ThrowException (HTTPCLIENT_ERR_MULTIBYTETOWIDECHAR_FAILED, ::GetLastError ()) ;

	// Get a UTF-8 character sequence
	if ( 0 == ::WideCharToMultiByte (CP_UTF8, 0, wszUnicode, 2, szUtf8Char, 7, NULL, NULL) )
		CHttpToolA::ThrowException (HTTPCLIENT_ERR_WIDECHARTOMULTIBYTE_FAILED, ::GetLastError ()) ;
}

/*!
 * \internal
 * This method converts an UTF-8 character into an Ansi character.
 * The detailed infomation about the Code-Page Identifiers is described in the MSDN documentation.
 *
 * \param szAnsiChar	[out] A buffer to save the converted Ansi character. The buffer can not be NULL.
 *						      The buffer must be allocated at least 3 characters.
 * \param szUtf8Char	[in] A string which contains an UTF-8 character. The string can not be NULL.
 * \param CodePage		[in] A code page of the converted Ansi character.
 * \throw				Throws a httpclientexception if an error occurs.
 */
void CHttpEncoderA::_Utf8CharToAnsiChar (PSTR szAnsiChar, PCSTR szUtf8Char, UINT CodePage)
	throw (Exception &)
{
	HTTPCLIENT_ASSERTA (szAnsiChar != NULL && szUtf8Char != NULL
		, "CHttpEncoderA::_Utf8CharToAnsiChar: szAnsiChar and szUtf8Char can not be NULL.") ;

	WCHAR		wszUnicode[2] ;

	// Get a unicode character
	if ( 0 == ::MultiByteToWideChar (CP_UTF8, 0, szUtf8Char, -1, wszUnicode, 2) )
		CHttpToolA::ThrowException (HTTPCLIENT_ERR_MULTIBYTETOWIDECHAR_FAILED, ::GetLastError ()) ;

	// Get a Ansi character sequence
	if ( 0 == ::WideCharToMultiByte (CodePage, 0, wszUnicode, 2, szAnsiChar, 3, NULL, NULL) )
		CHttpToolA::ThrowException (HTTPCLIENT_ERR_WIDECHARTOMULTIBYTE_FAILED, ::GetLastError ()) ;
}

/*!
 * This method returns the number of bytes required to encode an Ansi string using UTF-8 encoding.
 * The returned value does not include the terminating NULL character.
 * The detailed infomation about the Code-Page Identifiers is described in the MSDN documentation.
 *
 * \param szStr			[in] A string which is encoded.
 * \param CodePage		[in] A code page of the szStr parameter.
 * \return				The number of bytes required. (Not including the terminating NULL character)
 * \throw				Throws a httpclientexception if an error occurs.
 */
DWORD CHttpEncoderA::Utf8EncodeLen (PCSTR szStr, UINT CodePage)
	throw (Exception &)
{
	if ( szStr == NULL || szStr[0] == '\0' )
		return 0 ;

	::SafeInt<DWORD>	cchEncoded = 0 ;
	PCSTR				pchStr = szStr ;
	CHAR				szAnsi[3], szUtf8[7] ;
	PSTR				pchUtf8 ;

	try {
		while ( *pchStr ) {
			szAnsi[0] = *pchStr ;
			if ( ::IsDBCSLeadByteEx (CodePage, *pchStr) ) {
				szAnsi[1] = *(++pchStr) ;
				szAnsi[2] = '\0' ;
			} else
				szAnsi[1] = '\0' ;

			_AnsiCharToUtf8Char (szUtf8, szAnsi, CodePage) ;
			for (pchUtf8 = szUtf8; *pchUtf8 != '\0'; pchUtf8++, cchEncoded++) ;
			pchStr++ ;
		}
	} catch (::SafeIntException & e) {
		CHttpToolA::ThrowException (e) ;
	}
	return cchEncoded.Value () ;
}

/*!
 * This method encodes an Ansi string using UTF-8 encoding.
 * The detailed infomation about the Code-Page Identifiers is described in the MSDN documentation.
 *
 * \param szBuff		[out] A buffer to save the encoded string. The buffer can not be NULL.
 * \param szStr			[in] A string which is encoded.
 * \param CodePage		[in] A code page of the szStr parameter.
 * \return				An encoded string.
 * \throw				Throws a httpclientexception if an error occurs.
 */
PSTR CHttpEncoderA::Utf8Encode (PSTR szBuff, PCSTR szStr, UINT CodePage)
	throw (Exception &)
{
	HTTPCLIENT_ASSERTA (szBuff != NULL, "CHttpEncoderA::Utf8Encode: szBuff can not be NULL.") ;

	if ( szStr == NULL || szStr[0] == '\0' ) {
		szBuff[0] = '\0' ;
		return szBuff ;
	}

	CHAR		szAnsi[3], szUtf8[7] ;
	PSTR		pchBuff = szBuff ;
	PCSTR		pchStr = szStr ;

	while ( *pchStr ) {
		szAnsi[0] = *pchStr ;
		if ( ::IsDBCSLeadByteEx (CodePage, *pchStr) ) {
			szAnsi[1] = *(++pchStr) ;
			szAnsi[2] = '\0' ;
		} else
			szAnsi[1] = '\0' ;

		_AnsiCharToUtf8Char (szUtf8, szAnsi, CodePage) ;
		::strcpy (pchBuff, szUtf8) ;
		for (; *pchBuff != NULL; pchBuff++) ;
		pchStr++ ;
	}

	return szBuff ;
}

/*!
 * \internal
 * This method saves an UTF-8 character into the buffer.
 * The pszSrc parameter is modified to point to the next UTF-8 character.
 *
 * \param szUtf8Char	[out] A buffer to save an UTF-8 character. The buffer can not be NULL.
 *						      The buffer must be allocated at least 7 characters.
 * \param pszSrc		[in] A pointer to a UTF-8 string. The pointer can not be NULL.
 * \return				Returns FALSE if the UTF-8 string contains an invalid byte.
 * \throw				Throws a httpclientexception if an error occurs.
 */
static BOOL _GetNextUtf8Char (PSTR szUtf8Char, PCSTR * pszSrc)
{
	_ASSERTE ( szUtf8Char != NULL ) ;
	_ASSERTE ( pszSrc != NULL && *pszSrc != NULL ) ;

	if ( '\0' == (szUtf8Char[0] = **pszSrc) )
		return TRUE ;

	(*pszSrc)++ ;

	// An ASCII character
	if ( szUtf8Char[0] >= 0 && szUtf8Char[0] <= 0x7F ) {
		szUtf8Char[1] = '\0' ;
		return TRUE ;
	}

	BYTE		cchUtf8, byChar ;

	byChar = static_cast<BYTE> (szUtf8Char[0]) ;

	// Following values are not allowed in UTF-8 encoding
	if ( byChar == 0xFE || byChar == 0xFF )
		return FALSE ;

	// It must be the first byte of the UTF-8 character sequence
	if ( !(byChar >= 0xC0 && byChar <= 0xFD) )
		return FALSE ;

	// Counts the number of bytes of the UTF-8 character sequence
	if ( (byChar & 0xFE) == 0xFC )					cchUtf8 = 6 ;
	else if ( (byChar & 0xFC) == 0xF8 )				cchUtf8 = 5 ;
	else if ( (byChar & 0xF8) == 0xF0 )				cchUtf8 = 4 ;
	else if ( (byChar & 0xF0) == 0xE0 )				cchUtf8 = 3 ;
	else if ( (byChar & 0xE0) == 0xC0 )				cchUtf8 = 2 ;
	else	return FALSE ;

	for (BYTE i = 1; i < cchUtf8; i++) {
		szUtf8Char[i] = *((*pszSrc)++) ;
		byChar = static_cast<BYTE> (szUtf8Char[i]) ;

		// It must be a valid byte of the UTF-8 multibyte sequence.
		if ( !(byChar >=0x80 && byChar <= 0xBF) )
			return FALSE ;
	}
	szUtf8Char[cchUtf8] = '\0' ;
	return TRUE ;
}

/*!
 * This method returns the number of bytes required to decode an UTF-8 string in ANSI.
 * The returned value does not include the terminating NULL character.
 * The detailed infomation about the Code-Page Identifiers is described in the MSDN documentation.
 *
 * \param szEncoded		[in] A string to decode.
 * \param CodePage		[in] A code page of the returned Ansi string.
 * \return				The number of bytes required. (Not including the terminating NULL character)
 * \throw				Throws a httpclientexception if an error occurs.
 */
DWORD CHttpEncoderA::Utf8DecodeLen (PCSTR szEncoded, UINT CodePage)
	throw (Exception &)
{
	if ( szEncoded == NULL || szEncoded[0] == '\0' )
		return 0 ;

	::SafeInt<DWORD>	cchDecoded = 0 ;
	PCSTR				pchEncoded = szEncoded ;
	CHAR				szAnsi[3], szUtf8[7] ;
	PSTR				pchAnsi ;

	try {
		while ( *pchEncoded ) {
			if ( !_GetNextUtf8Char (szUtf8, &pchEncoded) )
				CHttpToolA::ThrowException (HTTPCLIENT_ERR_INVALID_UTF8_CHARACTER) ;

			_Utf8CharToAnsiChar (szAnsi, szUtf8, CodePage) ;
			for (pchAnsi = szAnsi; *pchAnsi != '\0'; pchAnsi++, cchDecoded++) ;
		}
	} catch (::SafeIntException & e) {
		CHttpToolA::ThrowException (e) ;
	}
	return cchDecoded.Value () ;
}

/*!
 * This method decodes an UTF-8 string using Ansi encoding.
 * The detailed infomation about the Code-Page Identifiers is described in the MSDN documentation.
 *
 * \param szBuff		[out] A buffer to save the decoded string. The buffer can not be NULL.
 * \param szEncoded		[in] A string to decode.
 * \param CodePage		[in] A code page of the returned Ansi string.
 * \return				An encoded string.
 * \throw				Throws a httpclientexception if an error occurs.
 */
PSTR CHttpEncoderA::Utf8Decode (PSTR szBuff, PCSTR szEncoded, UINT CodePage)
	throw (Exception &)
{
	HTTPCLIENT_ASSERTA (szBuff != NULL, "CHttpEncoderA::Utf8Decode: szBuff can not be NULL.") ;

	if ( szEncoded == NULL || szEncoded[0] == '\0' ) {
		szBuff[0] = '\0' ;
		return szBuff ;
	}

	PCSTR		pchEncoded = szEncoded ;
	CHAR		szUtf8[7] ;
	PSTR		pchBuff = szBuff ;

	while ( *pchEncoded ) {
		if ( !_GetNextUtf8Char (szUtf8, &pchEncoded) )
			CHttpToolA::ThrowException (HTTPCLIENT_ERR_INVALID_UTF8_CHARACTER) ;

		_Utf8CharToAnsiChar (pchBuff, szUtf8, CodePage) ;
		for (; *pchBuff != '\0'; pchBuff++) ;
	}

	return szBuff ;
}

/*!
 * This method returns the number of bytes required to make a URL-encoded string
 * (in Ansi character set) from an Ansi string.
 * The URL-encoded string is a string that is safe to transmit from the Web server to a client.
 * The returned value does not include the terminating NULL character.
 * The detailed infomation about the Code-Page Identifiers is described in the MSDN documentation.
 *
 * \param szStr			[in] A string which is encoded.
 * \param bUtf8Encoding	[in] If this is TRUE, the string is encoded using UTF-8 encoding
 *						     and that string is used to make a URL-encoded string.
 * \param CodePage		[in] A code page of the szStr parameter.
 * \return				The number of bytes required. (Not including the terminating NULL character)
 * \throw				Throws a httpclientexception if an error occurs.
 */
DWORD CHttpEncoderA::UrlEncodeLenA (PCSTR szStr, BOOL bUtf8Encoding, UINT CodePage)
	throw (Exception &)
{
	if ( szStr == NULL || szStr[0] == '\0' )
		return 0 ;

	if ( !bUtf8Encoding )
		return _UrlEncodeLen<CHttpToolA> (szStr) ;

	::SafeInt<DWORD>	cchEncoded = 0 ;
	PCSTR				pchStr = szStr ;
	CHAR				szAnsi[3], szUtf8[7] ;

	try {
		while ( *pchStr ) {
			szAnsi[0] = *pchStr ;
			if ( ::IsDBCSLeadByteEx (CodePage, *pchStr) ) {
				szAnsi[1] = *(++pchStr) ;
				szAnsi[2] = '\0' ;
			} else
				szAnsi[1] = '\0' ;

			_AnsiCharToUtf8Char (szUtf8, szAnsi, CodePage) ;
			cchEncoded += _UrlEncodeLen<CHttpToolA> (szUtf8) ;
			pchStr++ ;
		}
	} catch (::SafeIntException & e) {
		CHttpToolA::ThrowException (e) ;
	}
	return cchEncoded.Value () ;
}

/*!
 * This method encodes a Ansi string to make a URL-encoded string (in Ansi character set).
 * The URL-encoded string is a string that is safe to transmit from the Web server to a client.
 * The detailed infomation about the Code-Page Identifiers is described in the MSDN documentation.
 *
 * \param szBuff		[out] A buffer to save the encoded string. The buffer can not be NULL.
 * \param szStr			[in] A string which is encoded.
 * \param bUtf8Encoding	[in] If this is TRUE, the string is encoded using UTF-8 encoding
 *						     and that string is used to make a URL-encoded string.
 * \param CodePage		[in] A code page of the szStr parameter.
 * \return				An encoded string.
 * \throw				Throws a httpclientexception if an error occurs.
 */
PSTR CHttpEncoderA::UrlEncodeA (PSTR szBuff, PCSTR szStr, BOOL bUtf8Encoding, UINT CodePage)
	throw (Exception &)
{
	HTTPCLIENT_ASSERTA (szBuff != NULL, "CHttpEncoderA::UrlEncodeA: szBuff can not be NULL.") ;

	if ( szStr == NULL || szStr[0] == '\0' ) {
		szBuff[0] = '\0' ;
		return szBuff ;
	}

	if ( !bUtf8Encoding )
		return _UrlEncode (szBuff, szStr) ;

	CHAR		szAnsi[3], szUtf8[7] ;
	PSTR		pchBuff = szBuff ;
	PCSTR		pchStr = szStr ;

	while ( *pchStr ) {
		szAnsi[0] = *pchStr ;
		if ( ::IsDBCSLeadByteEx (CodePage, *pchStr) ) {
			szAnsi[1] = *(++pchStr) ;
			szAnsi[2] = '\0' ;
		} else
			szAnsi[1] = '\0' ;

		_AnsiCharToUtf8Char (szUtf8, szAnsi, CodePage) ;
		_UrlEncode (pchBuff, szUtf8) ;
		for (; *pchBuff != NULL; pchBuff++) ;
		pchStr++ ;
	}

	return szBuff ;
}

/*!
 * This method encodes a Ansi string to make a URL-encoded string (in Unicode character set).
 * The URL-encoded string is a string that is safe to transmit from the Web server to a client.
 * The detailed infomation about the Code-Page Identifiers is described in the MSDN documentation.
 *
 * \param szBuff		[out] A buffer to save the encoded string. The buffer can not be NULL.
 * \param szStr			[in] A string which is encoded.
 * \param bUtf8Encoding	[in] If this is TRUE, the string is encoded using UTF-8 encoding
 *						     and that string is used to make a URL-encoded string.
 * \param CodePage		[in] A code page of the szStr parameter.
 * \return				An encoded string.
 * \throw				Throws a httpclientexception if an error occurs.
 */
PWSTR CHttpEncoderA::UrlEncodeW (PWSTR szBuff, PCSTR szStr, BOOL bUtf8Encoding, UINT CodePage)
	throw (Exception &)
{
	HTTPCLIENT_ASSERTA (szBuff != NULL, "CHttpEncoderA::UrlEncodeW: szBuff can not be NULL.") ;

	if ( szStr == NULL || szStr[0] == '\0' ) {
		szBuff[0] = '\0' ;
		return szBuff ;
	}

	if ( !bUtf8Encoding )
		return _UrlEncode (szBuff, szStr) ;

	CHAR		szAnsi[3], szUtf8[7] ;
	PWSTR		pchBuff = szBuff ;
	PCSTR		pchStr = szStr ;

	while ( *pchStr ) {
		szAnsi[0] = *pchStr ;
		if ( ::IsDBCSLeadByteEx (CodePage, *pchStr) ) {
			szAnsi[1] = *(++pchStr) ;
			szAnsi[2] = '\0' ;
		} else
			szAnsi[1] = '\0' ;

		_AnsiCharToUtf8Char (szUtf8, szAnsi, CodePage) ;
		_UrlEncode (pchBuff, szUtf8) ;
		for (; *pchBuff != NULL; pchBuff++) ;
		pchStr++ ;
	}

	return szBuff ;
}

// [out] pchDecoded		A pointer to a character to save the next URL decoded character.
// [in] pszSrc			A pointer to a URL Encoded string.
// Returns TRUE if the operation success
template <typename StringType>
static BOOL _GetNextUrlDecodedChar (CHAR * pchDecoded, StringType * pszSrc)
{
	_ASSERTE ( pchDecoded != NULL && pszSrc != NULL && *pszSrc != NULL ) ;

	if ( **pszSrc == NULL ) {
		*pchDecoded = '\0' ;
		return TRUE ;
	}

	if ( **pszSrc == '+' ) {
		*pchDecoded = ' ' ;
		(*pszSrc)++ ;
		return TRUE ;
	}

	if ( **pszSrc != '%' ) {
		*pchDecoded = static_cast<CHAR> (*((*pszSrc)++)) ;
		return FALSE ;
	}

	(*pszSrc)++ ;

	if ( !_HexIsValid (**pszSrc) )
		return FALSE ;

	*pchDecoded = _HexToNum (**pszSrc) * 16 ;
	(*pszSrc)++ ;

	if ( !_HexIsValid (**pszSrc) )
		return FALSE ;

	*pchDecoded += _HexToNum (**pszSrc) ;
	(*pszSrc)++ ;

	return TRUE ;
}

// [out] szAnsiChar		a buffer to save the next URL decoded Ansi character sequence.
//						The buffer must be allocated at least 3 characters.
// [in] pszSrc			A pointer to a URL Encoded string.
// [in] CodePage		codepage
// Returns TRUE if the operation success
template <typename StringType>
static BOOL _GetNextUrlDecodedAnsiChar (PSTR szAnsiChar, StringType * pszSrc, UINT CodePage = CP_ACP)
{
	_ASSERTE ( szAnsiChar != NULL ) ;
	_ASSERTE ( pszSrc != NULL && *pszSrc != NULL ) ;

	if ( !_GetNextUrlDecodedChar (szAnsiChar, pszSrc) )
		return FALSE ;

	if ( szAnsiChar[0] == '\0' )
		return TRUE ;

	if ( !::IsDBCSLeadByteEx (CodePage, szAnsiChar[0]) ) {
		szAnsiChar[1] = '\0' ;
		return TRUE ;
	}

	if ( !_GetNextUrlDecodedChar (szAnsiChar + 1, pszSrc) )
		return FALSE ;

	// It can not be '\0'
	if ( szAnsiChar[1] == '\0' )
		return FALSE ;

	szAnsiChar[2] = '\0' ;
	return TRUE ;
}

// [out] szUtf8Char		a buffer to save a returned UTF-8 character sequence.
//						The buffer must be allocated at least 7 characters.
// [in] pszSrc			A pointer to a URL Encoded string.
// Returns TRUE if the operation success
template <typename StringType>
static BOOL _GetNextUrlDecodedUtf8Char (PSTR szUtf8Char, StringType * pszSrc)
{
	_ASSERTE ( szUtf8Char != NULL ) ;
	_ASSERTE ( pszSrc != NULL && *pszSrc != NULL ) ;

	if ( !_GetNextUrlDecodedChar (szUtf8Char, pszSrc) )
		return FALSE ;

	if ( szUtf8Char[0] == '\0' )
		return TRUE ;

	// An ASCII character
	if ( szUtf8Char[0] >= 0 && szUtf8Char[0] <= 0x7F ) {
		szUtf8Char[1] = '\0' ;
		return TRUE ;
	}

	BYTE		cchUtf8, byChar ;

	byChar = static_cast<BYTE> (szUtf8Char[0]) ;

	// following values are not allowed in UTF-8 encoding
	if ( byChar == 0xFE || byChar == 0xFF )
		return FALSE ;				// Invalid UTF-8 byte

	// It must be the first character of a UTF-8 character sequence
	if ( !(byChar >= 0xC0 && byChar <= 0xFD) )
		return FALSE ;

	// Counts the number of bytes of the UTF-8 character sequence
	if ( (byChar & 0xFE) == 0xFC )					cchUtf8 = 6 ;
	else if ( (byChar & 0xFC) == 0xF8 )				cchUtf8 = 5 ;
	else if ( (byChar & 0xF8) == 0xF0 )				cchUtf8 = 4 ;
	else if ( (byChar & 0xF0) == 0xE0 )				cchUtf8 = 3 ;
	else if ( (byChar & 0xE0) == 0xC0 )				cchUtf8 = 2 ;
	else	return FALSE ;

	for (BYTE i = 1; i < cchUtf8; i++) {
		if ( !_GetNextUrlDecodedChar (szUtf8Char + i, pszSrc) )
			return FALSE ;

		byChar = static_cast<BYTE> (szUtf8Char[i]) ;

		// It must be a byte of the UTF-8 multibyte sequence.
		if ( !(byChar >=0x80 && byChar <= 0xBF) )
			return FALSE ;
	}
	szUtf8Char[cchUtf8] = '\0' ;
	return TRUE ;
}

/*!
 * This method returns the number of bytes required to decode an URL-encoded string. (Ansi version)
 * The returned value does not include the terminating NULL character.
 * This method does not support the URL-encoded string which contains a unicode character by using the %u or %x prefix.
 * The detailed infomation about the Code-Page Identifiers is described in the MSDN documentation.
 *
 * \param szEncoded		[in] A string to decode.
 * \param bUtf8Encoding	[in] If this is TRUE, the decoded string is assumed an UTF-8 string.
 *						     So the decoded string is converted into an Ansi string.
 * \param CodePage		[in] A code page of the decoded string.
 * \return				The number of bytes required. (Not including the terminating NULL character)
 * \throw				Throws a httpclientexception if an error occurs.
 */
DWORD CHttpEncoderA::UrlDecodeLenA (PCSTR szEncoded, BOOL bUtf8Encoding, UINT CodePage)
	throw (Exception &)
{
	if ( szEncoded == NULL || szEncoded[0] == '\0' )
		return 0 ;

	::SafeInt<DWORD>	cchDecoded = 0 ;
	PCSTR				pchEncoded = szEncoded ;
	CHAR				szAnsi[3], szUtf8[7] ;
	CHAR *				pchAnsi ;

	try {
		do {
			if ( bUtf8Encoding ) {
				if ( !_GetNextUrlDecodedUtf8Char (szUtf8, &pchEncoded) )
					CHttpToolA::ThrowException (HTTPCLIENT_ERR_ENCODED_URL_NOT_VALID) ;

				// Converts into Ansi
				_Utf8CharToAnsiChar (szAnsi, szUtf8, CodePage) ;
			} else {
				if ( !_GetNextUrlDecodedAnsiChar (szAnsi, &pchEncoded, CodePage) )
					CHttpToolA::ThrowException (HTTPCLIENT_ERR_ENCODED_URL_NOT_VALID) ;
			}

			for (pchAnsi = szAnsi; *pchAnsi != NULL; pchAnsi++, cchDecoded++) ;

		} while ( szAnsi[0] != '\0' ) ;
	} catch (::SafeIntException & e) {
		CHttpToolA::ThrowException (e) ;
	}
	return cchDecoded.Value () ;
}

/*!
 * This method returns the number of unicode characters required to decode an URL-encoded string.
 * The returned value does not include the terminating NULL character.
 * This method does not support the URL-encoded string which contains a unicode character by using the %u or %x prefix.
 * The detailed infomation about the Code-Page Identifiers is described in the MSDN documentation.
 *
 * \param szEncoded		[in] A string to decode.
 * \param bUtf8Encoding	[in] If this is TRUE, the decoded string is assumed an UTF-8 string.
 *						     So the decoded string is converted into an Unicode string.
 * \param CodePage		[in] A code page of the decoded string.
 * \return				The number of unicode characters required. (Not including the terminating NULL character)
 * \throw				Throws a httpclientexception if an error occurs.
 */
DWORD CHttpEncoderA::UrlDecodeLenW (PCSTR szEncoded, BOOL bUtf8Encoding, UINT CodePage)
	throw (Exception &)
{
	if ( szEncoded == NULL || szEncoded[0] == '\0' )
		return 0 ;

	::SafeInt<DWORD>	cchDecoded = 0 ;
	PCSTR				pchEncoded = szEncoded ;

	try {
		if ( bUtf8Encoding ) {
			CHAR		szUtf8[7] ;

			while ( true ) {
				if ( !_GetNextUrlDecodedUtf8Char (szUtf8, &pchEncoded) )
					CHttpToolA::ThrowException (HTTPCLIENT_ERR_ENCODED_URL_NOT_VALID) ;

				if ( szUtf8[0] == '\0' )
					break ;

				cchDecoded++ ;
			}
			return cchDecoded.Value () ;
		}

		CHAR		szAnsi[3] ;
		while ( true ) {
			if ( !_GetNextUrlDecodedAnsiChar (szAnsi, &pchEncoded, CodePage) )
				CHttpToolA::ThrowException (HTTPCLIENT_ERR_ENCODED_URL_NOT_VALID) ;

			if ( szAnsi[0] == '\0' )
				break ;

			cchDecoded++ ;
		}
	} catch (::SafeIntException & e) {
		CHttpToolA::ThrowException (e) ;
	}
	return cchDecoded.Value () ;
}

/*!
 * This method decodes an URL-encoded string.
 * This method does not support the URL-encoded string which contains a unicode character by using the %u or %x prefix.
 * The detailed infomation about the Code-Page Identifiers is described in the MSDN documentation.
 *
 * \param szBuff		[out] A buffer to save the decoded string. The buffer can not be NULL.
 * \param szEncoded		[in] A string to decode.
 * \param bUtf8Encoding	[in] If this is TRUE, the decoded string is assumed an UTF-8 string.
 *						     So the decoded string is converted into an Ansi string.
 * \param CodePage		[in] A code page of the decoded string.
 * \return				A decoded string.
 * \throw				Throws a httpclientexception if an error occurs.
 */
PSTR CHttpEncoderA::UrlDecodeA (PSTR szBuff, PCSTR szEncoded, BOOL bUtf8Encoding, UINT CodePage)
	throw (Exception &)
{
	HTTPCLIENT_ASSERTA (szBuff != NULL, "CHttpEncoderA::UrlDecodeA: szBuff can not be NULL.") ;

	if ( szEncoded == NULL || szEncoded[0] == '\0' ) {
		szBuff[0] = '\0' ;
		return szBuff ;
	}

	CHAR		szAnsi[3], szUtf8[7] ;
	PCSTR		pchEncoded = szEncoded ;
	PSTR		pchBuff = szBuff ;
	PSTR		pchAnsi ;

	do {
		if ( bUtf8Encoding ) {
			if ( !_GetNextUrlDecodedUtf8Char (szUtf8, &pchEncoded) )
				CHttpToolA::ThrowException (HTTPCLIENT_ERR_ENCODED_URL_NOT_VALID) ;

			// Converts into a Ansi character
			_Utf8CharToAnsiChar (szAnsi, szUtf8, CodePage) ;
		} else {
			if ( !_GetNextUrlDecodedAnsiChar (szAnsi, &pchEncoded, CodePage) )
				CHttpToolA::ThrowException (HTTPCLIENT_ERR_ENCODED_URL_NOT_VALID) ;
		}

		for (pchAnsi = szAnsi; *pchAnsi != '\0'; *(pchBuff++) = *pchAnsi, pchAnsi++) ;

	} while ( szAnsi[0] != '\0' ) ;

	*pchBuff = '\0' ;
	return szBuff ;
}

/*!
 * This method decodes an URL-encoded string.
 * This method does not support the URL-encoded string which contains a unicode character by using the %u or %x prefix.
 * The detailed infomation about the Code-Page Identifiers is described in the MSDN documentation.
 *
 * \param szBuff		[out] A buffer to save the decoded string. The buffer can not be NULL.
 * \param szEncoded		[in] A string to decode.
 * \param bUtf8Encoding	[in] If this is TRUE, the decoded string is assumed an UTF-8 string.
 *						     So the decoded string is converted into an Unicode string.
 * \param CodePage		[in] A code page of the decoded string.
 * \return				A decoded string.
 * \throw				Throws a httpclientexception if an error occurs.
 */
PWSTR CHttpEncoderA::UrlDecodeW (PWSTR szBuff, PCSTR szEncoded, BOOL bUtf8Encoding, UINT CodePage)
	throw (Exception &)
{
	HTTPCLIENT_ASSERTA (szBuff != NULL, "CHttpEncoderA::UrlDecodeW: szBuff can not be NULL.") ;

	if ( szEncoded == NULL || szEncoded[0] == '\0' ) {
		szBuff[0] = '\0' ;
		return szBuff ;
	}

	PCSTR		pchEncoded = szEncoded ;
	PWSTR		pchBuff = szBuff ;

	if ( bUtf8Encoding ) {
		CHAR		szUtf8[7] ;

		while ( true ) {
			if ( !_GetNextUrlDecodedUtf8Char (szUtf8, &pchEncoded) )
				CHttpToolA::ThrowException (HTTPCLIENT_ERR_ENCODED_URL_NOT_VALID) ;

			if ( szUtf8[0] == '\0' )
				break ;

			// Converts into a unicode character
			if ( 0 == ::MultiByteToWideChar (CP_UTF8, 0, szUtf8, -1, pchBuff, 2) )
				CHttpToolA::ThrowException (HTTPCLIENT_ERR_MULTIBYTETOWIDECHAR_FAILED, ::GetLastError ()) ;

			pchBuff++ ;
		}
	} else {
		CHAR		szAnsi[3] ;

		while ( true ) {
			if ( !_GetNextUrlDecodedAnsiChar (szAnsi, &pchEncoded, CodePage) )
				CHttpToolA::ThrowException (HTTPCLIENT_ERR_ENCODED_URL_NOT_VALID) ;

			if ( szAnsi[0] == '\0' )
				break ;

			// Converts into a unicode character
			if ( 0 == ::MultiByteToWideChar (CodePage, 0, szAnsi, -1, pchBuff, 2) )
				CHttpToolA::ThrowException (HTTPCLIENT_ERR_MULTIBYTETOWIDECHAR_FAILED, ::GetLastError ()) ;

			pchBuff++ ;
		}
	}

	*pchBuff = '\0' ;
	return szBuff ;
}


/*!
 * This method returns the number of bytes required to convert an Unicode string into an Ansi string.
 * The returned value does not include the terminating NULL character.
 * The detailed infomation about the Code-Page Identifiers is described in the MSDN documentation.
 *
 * \param szStr			[in] A string which is encoded.
 * \param CodePage		[in] A code page of the encoded string.
 * \return				The number of bytes required. (Not including the terminating NULL character)
 * \throw				Throws a httpclientexception if an error occurs.
 */
DWORD CHttpEncoderW::AnsiEncodeLen (PCWSTR szStr, UINT CodePage)
	throw (Exception &)
{
	// The unicode encodings are not allowed
	HTTPCLIENT_ASSERTW ((CodePage != CP_UTF8) && (CodePage != CP_UTF7)
		, "CHttpEncoderW::AnsiEncodeLen: CP_UTF8 and CP_UTF7 can not be used for the CodePage parameter.") ;

	if ( szStr == NULL || szStr[0] == '\0' )
		return 0 ;

	::SafeInt<DWORD>	cchAnsiLen ;
	try {
		if ( 0 == (cchAnsiLen = ::WideCharToMultiByte (CodePage, 0, szStr, -1, NULL, 0, NULL, NULL)) )
			CHttpToolW::ThrowException (HTTPCLIENT_ERR_WIDECHARTOMULTIBYTE_FAILED, ::GetLastError ()) ;
		cchAnsiLen-- ;
	} catch (::SafeIntException & e) {
		CHttpToolW::ThrowException (e) ;
	}
	return cchAnsiLen.Value () ;
}

/*!
 * This method converts an Unicode string into an Ansi string.
 * The detailed infomation about the Code-Page Identifiers is described in the MSDN documentation.
 *
 * \param szBuff		[out] A buffer to save the encoded string. The buffer can not be NULL.
 * \param szStr			[in] A string which is encoded.
 * \param CodePage		[in] A code page of the encoded string.
 * \return				An encoded string.
 * \throw				Throws a httpclientexception if an error occurs.
 */
PSTR CHttpEncoderW::AnsiEncode (PSTR szBuff, PCWSTR szStr, UINT CodePage)
	throw (Exception &)
{
	// The unicode encodings are not allowed
	HTTPCLIENT_ASSERTW ((CodePage != CP_UTF8) && (CodePage != CP_UTF7)
		, "CHttpEncoderW::AnsiEncode: CP_UTF8 and CP_UTF7 can not be used for the CodePage parameter.") ;
	HTTPCLIENT_ASSERTW (szBuff != NULL, "CHttpEncoderW::AnsiEncode: szBuff can not be NULL.") ;

	if ( (szStr == NULL) || (szStr[0] == '\0') ) {
		szBuff[0] = '\0' ;
		return szBuff ;
	}

	::SafeInt<int>		cbAnsiLen ;
	try {
		cbAnsiLen = AnsiEncodeLen (szStr, CodePage) ;
		cbAnsiLen++ ;
	} catch (::SafeIntException & e) {
		CHttpToolW::ThrowException (e) ;
	}

	if ( 0 == ::WideCharToMultiByte (CodePage, 0, szStr, -1, szBuff, cbAnsiLen.Value (), NULL, NULL) )
		CHttpToolW::ThrowException (HTTPCLIENT_ERR_WIDECHARTOMULTIBYTE_FAILED, ::GetLastError ()) ;

	return szBuff ;
}

/*!
 * This method returns the number of unicode characters required to convert an Ansi string into an Unicode string.
 * The returned value does not include the terminating NULL character.
 * The detailed infomation about the Code-Page Identifiers is described in the MSDN documentation.
 *
 * \param szEncoded		[in] A string to decode.
 * \param CodePage		[in] A code page of the szEncoded parameter.
 * \return				The number of unicode characters required. (Not including the terminating NULL character)
 * \throw				Throws a httpclientexception if an error occurs.
 */
DWORD CHttpEncoderW::AnsiDecodeLen (PCSTR szEncoded, UINT CodePage)
	throw (Exception &)
{
	// The unicode encodings are not allowed
	HTTPCLIENT_ASSERTW ((CodePage != CP_UTF8) && (CodePage != CP_UTF7)
		, "CHttpEncoderW::AnsiDecodeLen: CP_UTF8 and CP_UTF7 can not be used for the CodePage parameter.") ;

	if ( szEncoded == NULL || szEncoded[0] == '\0' )
		return 0 ;

	::SafeInt<DWORD>	cchUnicodeLen ;
	try {
		if ( 0 == (cchUnicodeLen = ::MultiByteToWideChar (CodePage, 0, szEncoded, -1, NULL, 0)) )
			CHttpToolW::ThrowException (HTTPCLIENT_ERR_MULTIBYTETOWIDECHAR_FAILED, ::GetLastError ()) ;
		cchUnicodeLen-- ;
	} catch (::SafeIntException & e) {
		CHttpToolW::ThrowException (e) ;
	}
	return cchUnicodeLen.Value () ;
}

/*!
 * This method converts an Ansi string into an Unicode string.
 * The detailed infomation about the Code-Page Identifiers is described in the MSDN documentation.
 *
 * \param szBuff		[out] A buffer to save the decoded string. The buffer can not be NULL.
 * \param szEncoded		[in] A string to decode.
 * \param CodePage		[in] A code page of the szEncoded parameter.
 * \return				A decoded string.
 * \throw				Throws a httpclientexception if an error occurs.
 */
PWSTR CHttpEncoderW::AnsiDecode (PWSTR szBuff, PCSTR szEncoded, UINT CodePage)
	throw (Exception &)
{
	// The unicode encodings are not allowed
	HTTPCLIENT_ASSERTW ((CodePage != CP_UTF8) && (CodePage != CP_UTF7)
		, "CHttpEncoderW::AnsiDecode: CP_UTF8 and CP_UTF7 can not be used for the CodePage parameter.") ;
	HTTPCLIENT_ASSERTW (szBuff != NULL, "CHttpEncoderW::AnsiDecode: szBuff can not be NULL.") ;

	if ( (szEncoded == NULL) || (szEncoded[0] == '\0') ) {
		szBuff[0] = '\0' ;
		return szBuff ;
	}

	::SafeInt<int>		cchUnicode ;
	try {
		cchUnicode = AnsiDecodeLen (szEncoded, CodePage) ;
		cchUnicode++ ;
	} catch (::SafeIntException & e) {
		CHttpToolW::ThrowException (e) ;
	}
	if ( 0 == ::MultiByteToWideChar (CodePage, 0, szEncoded, -1, szBuff, cchUnicode.Value ()) )
		CHttpToolW::ThrowException (HTTPCLIENT_ERR_MULTIBYTETOWIDECHAR_FAILED, ::GetLastError ()) ;

	return szBuff ;
}

/*!
 * This method returns the number of characters required to encode an Unicode string using UTF-8 encoding.
 * The returned value does not include the terminating NULL character.
 *
 * \param szStr			[in] A string which is encoded.
 * \param CodePage		[in] Ignored.
 * \return				The number of characters required. (Not including the terminating NULL character)
 * \throw				Throws a httpclientexception if an error occurs.
 */
DWORD CHttpEncoderW::Utf8EncodeLen (PCWSTR szStr, UINT /* CodePage */)
	throw (Exception &)
{
	if ( szStr == NULL || szStr[0] == '\0' )
		return 0 ;

	::SafeInt<DWORD>	cchRequired ;
	try  {
		if ( 0 == (cchRequired = ::WideCharToMultiByte (CP_UTF8, 0, szStr, -1, NULL, 0, NULL, NULL)) )
			CHttpToolW::ThrowException (HTTPCLIENT_ERR_WIDECHARTOMULTIBYTE_FAILED, ::GetLastError ()) ;
		cchRequired-- ;
	} catch (::SafeIntException & e) {
		CHttpToolW::ThrowException (e) ;
	}
	return cchRequired.Value () ;
}

/*!
 * This method encodes an Unicode string using UTF-8 encoding.
 *
 * \param szBuff		[out] A buffer to save the encoded string. The buffer can not be NULL.
 * \param szStr			[in] A string which is encoded.
 * \param CodePage		[in] Ignored.
 * \return				An encoded string.
 * \throw				Throws a httpclientexception if an error occurs.
 */
PSTR CHttpEncoderW::Utf8Encode (PSTR szBuff, PCWSTR szStr, UINT /* CodePage */)
	throw (Exception &)
{
	HTTPCLIENT_ASSERTW (szBuff != NULL, "CHttpEncoderW::Utf8Encode: szBuff can not be NULL.") ;

	if ( szStr == NULL || szStr[0] == '\0' ) {
		szBuff[0] = '\0' ;
		return szBuff ;
	}

	::SafeInt<int>		cchRequired ;
	try  {
		cchRequired = Utf8EncodeLen (szStr) ;
		cchRequired++ ;
	} catch (::SafeIntException & e) {
		CHttpToolW::ThrowException (e) ;
	}
	if ( 0 == ::WideCharToMultiByte (CP_UTF8, 0, szStr, -1, szBuff, cchRequired.Value (), NULL, NULL) )
		CHttpToolW::ThrowException (HTTPCLIENT_ERR_WIDECHARTOMULTIBYTE_FAILED, ::GetLastError ()) ;

	return szBuff ;
}

/*!
 * This method returns the number of unicode characters required to decode an UTF-8 string in unicode.
 * The returned value does not include the terminating NULL character.
 *
 * \param szEncoded		[in] A string to decode.
 * \param CodePage		[in] Ignored.
 * \return				The number of bytes required. (Not including the terminating NULL character)
 * \throw				Throws a httpclientexception if an error occurs.
 */
DWORD CHttpEncoderW::Utf8DecodeLen (PCSTR szEncoded, UINT /* CodePage */)
	throw (Exception &)
{
	if ( szEncoded == NULL || szEncoded[0] == '\0' )
		return 0 ;

	::SafeInt<DWORD>	cchRequired ;
	try {
		if ( 0 == (cchRequired = ::MultiByteToWideChar (CP_UTF8, 0, szEncoded, -1, NULL, 0)) )
			CHttpToolW::ThrowException (HTTPCLIENT_ERR_MULTIBYTETOWIDECHAR_FAILED, ::GetLastError ()) ;	
		cchRequired-- ;
	} catch (::SafeIntException & e) {
		CHttpToolW::ThrowException (e) ;
	}
	return cchRequired.Value () ;
}

/*!
 * This method decodes an UTF-8 string in unicode.
 *
 * \param szBuff		[out] A buffer to save the decoded string. The buffer can not be NULL.
 * \param szEncoded		[in] A string to decode.
 * \param CodePage		[in] Ignored.
 * \return				An encoded string.
 * \throw				Throws a httpclientexception if an error occurs.
 */
PWSTR CHttpEncoderW::Utf8Decode (PWSTR szBuff, PCSTR szEncoded, UINT /* CodePage */)
	throw (Exception &)
{
	HTTPCLIENT_ASSERTW (szBuff != NULL, "CHttpEncoderW::Utf8Decode: szBuff can not be NULL.") ;

	if ( szEncoded == NULL || szEncoded[0] == '\0' ) {
		szBuff[0] = '\0' ;
		return szBuff ;
	}

	::SafeInt<int>		cchRequired ;
	try {
		cchRequired = Utf8DecodeLen (szEncoded) ;
		cchRequired++ ;
	} catch (::SafeIntException & e) {
		CHttpToolW::ThrowException (e) ;
	}

	if ( 0 == ::MultiByteToWideChar (CP_UTF8, 0, szEncoded, -1, szBuff, cchRequired.Value ()) )
		CHttpToolW::ThrowException (HTTPCLIENT_ERR_MULTIBYTETOWIDECHAR_FAILED, ::GetLastError ()) ;	
	return szBuff ;
}

/*!
 * This method returns the number of bytes required to make a URL-encoded string
 * (in Ansi character set) from an Unicode string.
 * The URL-encoded string is a string that is safe to transmit from the Web server to a client.
 * The returned value does not include the terminating NULL character.
 * The detailed infomation about the Code-Page Identifiers is described in the MSDN documentation.
 *
 * \param szStr			[in] A string which is encoded.
 * \param bUtf8Encoding	[in] If this is TRUE, the string is encoded using UTF-8 encoding
 *						     and that string is used to make a URL-encoded string.
 * \param CodePage		[in] A code page of the encoded string.
 * \return				The number of bytes required. (Not including the terminating NULL character)
 * \throw				Throws a httpclientexception if an error occurs.
 */
DWORD CHttpEncoderW::UrlEncodeLenA (PCWSTR szStr, BOOL bUtf8Encoding, UINT CodePage)
	throw (Exception &)
{
	if ( szStr == NULL || szStr[0] == '\0' )
		return 0 ;

	::SafeInt<DWORD>	cchEncoded = 0 ;
	PCWSTR				pchStr = szStr ;
	WCHAR				szUnicode[2] = {'\0', '\0'} ;
	CHAR				szAnsiOrUtf8[7] ;
	UINT				nActualCodePage = bUtf8Encoding ? CP_UTF8 : CodePage ;

	try {
		while ( *pchStr ) {
			szUnicode[0] = *(pchStr++) ;

			if ( 0 == ::WideCharToMultiByte (nActualCodePage, 0, szUnicode, 2, szAnsiOrUtf8, 7, NULL, NULL) )
				CHttpToolW::ThrowException (HTTPCLIENT_ERR_WIDECHARTOMULTIBYTE_FAILED, ::GetLastError ()) ;
			cchEncoded += _UrlEncodeLen<CHttpToolW> (szAnsiOrUtf8) ;
		}
	} catch (::SafeIntException & e) {
		CHttpToolW::ThrowException (e) ;
	}
	return cchEncoded.Value () ;
}

/*!
 * This method encodes a Unicode string to make a URL-encoded string (in Ansi character set).
 * The URL-encoded string is a string that is safe to transmit from the Web server to a client.
 * The detailed infomation about the Code-Page Identifiers is described in the MSDN documentation.
 *
 * \param szBuff		[out] A buffer to save the encoded string. The buffer can not be NULL.
 * \param szStr			[in] A string which is encoded.
 * \param bUtf8Encoding	[in] If this is TRUE, the string is encoded using UTF-8 encoding
 *						     and that string is used to make a URL-encoded string.
 * \param CodePage		[in] A code page of the encoded string.
 * \return				An encoded string.
 * \throw				Throws a httpclientexception if an error occurs.
 */
PSTR CHttpEncoderW::UrlEncodeA (PSTR szBuff, PCWSTR szStr, BOOL bUtf8Encoding, UINT CodePage)
	throw (Exception &)
{
	HTTPCLIENT_ASSERTW (szBuff != NULL, "CHttpEncoderW::UrlEncodeA: szBuff can not be NULL.") ;

	if ( szStr == NULL || szStr[0] == '\0' ) {
		szBuff[0] = '\0' ;
		return szBuff ;
	}

	PCWSTR		pchStr = szStr ;
	PSTR		pchBuff = szBuff ;
	WCHAR		szUnicode[2] = {'\0', '\0'} ;
	CHAR		szAnsiOrUtf8[7] ;
	UINT		nActualCodePage = bUtf8Encoding ? CP_UTF8 : CodePage ;

	while ( *pchStr ) {
		szUnicode[0] = *(pchStr++) ;

		if ( 0 == ::WideCharToMultiByte (nActualCodePage, 0, szUnicode, 2, szAnsiOrUtf8, 7, NULL, NULL) )
			CHttpToolW::ThrowException (HTTPCLIENT_ERR_WIDECHARTOMULTIBYTE_FAILED, ::GetLastError ()) ;
		_UrlEncode (pchBuff, szAnsiOrUtf8) ;
		for (; *pchBuff != NULL; pchBuff++) ;
	}

	return szBuff ;
}

/*!
 * This method encodes a Unicode string to make a URL-encoded string (in Unicode character set).
 * The URL-encoded string is a string that is safe to transmit from the Web server to a client.
 * The detailed infomation about the Code-Page Identifiers is described in the MSDN documentation.
 *
 * \param szBuff		[out] A buffer to save the encoded string. The buffer can not be NULL.
 * \param szStr			[in] A string which is encoded.
 * \param bUtf8Encoding	[in] If this is TRUE, the string is encoded using UTF-8 encoding
 *						     and that string is used to make a URL-encoded string.
 * \param CodePage		[in] A code page of the encoded string.
 * \return				An encoded string.
 * \throw				Throws a httpclientexception if an error occurs.
 */
PWSTR CHttpEncoderW::UrlEncodeW (PWSTR szBuff, PCWSTR szStr, BOOL bUtf8Encoding, UINT CodePage)
	throw (Exception &)
{
	HTTPCLIENT_ASSERTW (szBuff != NULL, "CHttpEncoderW::UrlEncodeW: szBuff can not be NULL.") ;

	if ( szStr == NULL || szStr[0] == '\0' ) {
		szBuff[0] = '\0' ;
		return szBuff ;
	}

	PCWSTR		pchStr = szStr ;
	PWSTR		pchBuff = szBuff ;
	WCHAR		szUnicode[2] = {'\0', '\0'} ;
	CHAR		szAnsiOrUtf8[7] ;
	UINT		nActualCodePage = bUtf8Encoding ? CP_UTF8 : CodePage ;

	while ( *pchStr ) {
		szUnicode[0] = *(pchStr++) ;

		if ( 0 == ::WideCharToMultiByte (nActualCodePage, 0, szUnicode, 2, szAnsiOrUtf8, 7, NULL, NULL) )
			CHttpToolW::ThrowException (HTTPCLIENT_ERR_WIDECHARTOMULTIBYTE_FAILED, ::GetLastError ()) ;
		_UrlEncode (pchBuff, szAnsiOrUtf8) ;
		for (; *pchBuff != NULL; pchBuff++) ;
	}

	return szBuff ;
}

// [out] szAnsiChar		a buffer to save a returned Ansi character sequence.
//						The buffer must be allocated at least 3 characters.
// [in] szUtf8Char		An Utf8 character
void CHttpEncoderW::_Utf8CharToAnsiChar (PSTR szAnsiChar, PCSTR szUtf8Char, UINT CodePage)
	throw (Exception &)
{
	HTTPCLIENT_ASSERTW (szAnsiChar != NULL && szUtf8Char != NULL
		, "CHttpEncoderW::_Utf8CharToAnsiChar: szAnsiChar and szUtf8Char can not be NULL.") ;

	WCHAR		wszUnicode[2] ;

	// Get a unicode character
	if ( 0 == ::MultiByteToWideChar (CP_UTF8, 0, szUtf8Char, -1, wszUnicode, 2) )
		CHttpToolW::ThrowException (HTTPCLIENT_ERR_MULTIBYTETOWIDECHAR_FAILED, ::GetLastError ()) ;

	// Get a Ansi character sequence
	if ( 0 == ::WideCharToMultiByte (CodePage, 0, wszUnicode, 2, szAnsiChar, 3, NULL, NULL) )
		CHttpToolW::ThrowException (HTTPCLIENT_ERR_WIDECHARTOMULTIBYTE_FAILED, ::GetLastError ()) ;
}

/*!
 * This method returns the number of bytes required to decode an URL-encoded string. (Ansi version)
 * The returned value does not include the terminating NULL character.
 * This method does not support the URL-encoded string which contains a unicode character by using the %u or %x prefix.
 * The detailed infomation about the Code-Page Identifiers is described in the MSDN documentation.
 *
 * \param szEncoded		[in] A string to decode.
 * \param bUtf8Encoding	[in] If this is TRUE, the decoded string is assumed an UTF-8 string.
 *						     So the decoded string is converted into an Ansi string.
 * \param CodePage		[in] A code page of the decoded string.
 * \return				The number of bytes required. (Not including the terminating NULL character)
 * \throw				Throws a httpclientexception if an error occurs.
 */
DWORD CHttpEncoderW::UrlDecodeLenA (PCWSTR szEncoded, BOOL bUtf8Encoding, UINT CodePage)
	throw (Exception &)
{
	if ( szEncoded == NULL || szEncoded[0] == '\0' )
		return 0 ;

	::SafeInt<DWORD>	cchDecoded = 0 ;
	PCWSTR				pchEncoded = szEncoded ;
	CHAR				szAnsi[3], szUtf8[7] ;
	CHAR *				pchAnsi ;

	try {
		do {
			if ( bUtf8Encoding ) {
				if ( !_GetNextUrlDecodedUtf8Char (szUtf8, &pchEncoded) )
					CHttpToolW::ThrowException (HTTPCLIENT_ERR_ENCODED_URL_NOT_VALID) ;

				// Converts into Ansi
				_Utf8CharToAnsiChar (szAnsi, szUtf8, CodePage) ;
			} else {
				if ( !_GetNextUrlDecodedAnsiChar (szAnsi, &pchEncoded, CodePage) )
					CHttpToolW::ThrowException (HTTPCLIENT_ERR_ENCODED_URL_NOT_VALID) ;
			}

			for (pchAnsi = szAnsi; *pchAnsi != NULL; pchAnsi++, cchDecoded++) ;

		} while ( szAnsi[0] != '\0' ) ;
	} catch (::SafeIntException & e) {
		CHttpToolW::ThrowException (e) ;
	}

	return cchDecoded.Value () ;
}

/*!
 * This method returns the number of unicode characters required to decode an URL-encoded string.
 * The returned value does not include the terminating NULL character.
 * This method does not support the URL-encoded string which contains a unicode character by using the %u or %x prefix.
 * The detailed infomation about the Code-Page Identifiers is described in the MSDN documentation.
 *
 * \param szEncoded		[in] A string to decode.
 * \param bUtf8Encoding	[in] If this is TRUE, the decoded string is assumed an UTF-8 string.
 *						     So the decoded string is converted into an Unicode string.
 * \param CodePage		[in] A code page of the decoded string.
 * \return				The number of unicode characters required. (Not including the terminating NULL character)
 * \throw				Throws a httpclientexception if an error occurs.
 */
DWORD CHttpEncoderW::UrlDecodeLenW (PCWSTR szEncoded, BOOL bUtf8Encoding, UINT CodePage)
	throw (Exception &)
{
	if ( szEncoded == NULL || szEncoded[0] == '\0' )
		return 0 ;

	::SafeInt<DWORD>	cchDecoded = 0 ;
	PCWSTR				pchEncoded = szEncoded ;

	try {
		if ( bUtf8Encoding ) {
			CHAR		szUtf8[7] ;

			while ( true ) {
				if ( !_GetNextUrlDecodedUtf8Char (szUtf8, &pchEncoded) )
					CHttpToolW::ThrowException (HTTPCLIENT_ERR_ENCODED_URL_NOT_VALID) ;

				if ( szUtf8[0] == '\0' )
					break ;

				cchDecoded++ ;
			}
			return cchDecoded.Value () ;
		}

		CHAR		szAnsi[3] ;
		while ( true ) {
			if ( !_GetNextUrlDecodedAnsiChar (szAnsi, &pchEncoded, CodePage) )
				CHttpToolW::ThrowException (HTTPCLIENT_ERR_ENCODED_URL_NOT_VALID) ;

			if ( szAnsi[0] == '\0' )
				break ;

			cchDecoded++ ;
		}
	} catch (::SafeIntException & e) {
		CHttpToolW::ThrowException (e) ;
	}

	return cchDecoded.Value () ;
}

/*!
 * This method decodes an URL-encoded string.
 * This method does not support the URL-encoded string which contains a unicode character by using the %u or %x prefix.
 * The detailed infomation about the Code-Page Identifiers is described in the MSDN documentation.
 *
 * \param szBuff		[out] A buffer to save the decoded string. The buffer can not be NULL.
 * \param szEncoded		[in] A string to decode.
 * \param bUtf8Encoding	[in] If this is TRUE, the decoded string is assumed an UTF-8 string.
 *						     So the decoded string is converted into an Ansi string.
 * \param CodePage		[in] A code page of the decoded string.
 * \return				A decoded string.
 * \throw				Throws a httpclientexception if an error occurs.
 */
PSTR CHttpEncoderW::UrlDecodeA (PSTR szBuff, PCWSTR szEncoded, BOOL bUtf8Encoding, UINT CodePage)
	throw (Exception &)
{
	HTTPCLIENT_ASSERTW (szBuff != NULL, "CHttpEncoderW::UrlDecodeA: szBuff can not be NULL.") ;

	if ( szEncoded == NULL || szEncoded[0] == '\0' ) {
		szBuff[0] = '\0' ;
		return szBuff ;
	}

	CHAR		szAnsi[3], szUtf8[7] ;
	PCWSTR		pchEncoded = szEncoded ;
	PSTR		pchBuff = szBuff ;
	PSTR		pchAnsi ;

	do {
		if ( bUtf8Encoding ) {
			if ( !_GetNextUrlDecodedUtf8Char (szUtf8, &pchEncoded) )
				CHttpToolW::ThrowException (HTTPCLIENT_ERR_ENCODED_URL_NOT_VALID) ;

			// Converts into a Ansi character
			_Utf8CharToAnsiChar (szAnsi, szUtf8, CodePage) ;
		} else {
			if ( !_GetNextUrlDecodedAnsiChar (szAnsi, &pchEncoded, CodePage) )
				CHttpToolW::ThrowException (HTTPCLIENT_ERR_ENCODED_URL_NOT_VALID) ;
		}

		for (pchAnsi = szAnsi; *pchAnsi != '\0'; *(pchBuff++) = *pchAnsi, pchAnsi++) ;

	} while ( szAnsi[0] != '\0' ) ;

	*pchBuff = '\0' ;
	return szBuff ;
}

/*!
 * This method decodes an URL-encoded string.
 * This method does not support the URL-encoded string which contains a unicode character by using the %u or %x prefix.
 * The detailed infomation about the Code-Page Identifiers is described in the MSDN documentation.
 *
 * \param szBuff		[out] A buffer to save the decoded string. The buffer can not be NULL.
 * \param szEncoded		[in] A string to decode.
 * \param bUtf8Encoding	[in] If this is TRUE, the decoded string is assumed an UTF-8 string.
 *						     So the decoded string is converted into an Unicode string.
 * \param CodePage		[in] A code page of the decoded string.
 * \return				A decoded string.
 * \throw				Throws a httpclientexception if an error occurs.
 */
PWSTR CHttpEncoderW::UrlDecodeW (PWSTR szBuff, PCWSTR szEncoded, BOOL bUtf8Encoding, UINT CodePage)
	throw (Exception &)
{
	HTTPCLIENT_ASSERTW (szBuff != NULL, "CHttpEncoderW::UrlDecodeW: szBuff can not be NULL.") ;

	if ( szEncoded == NULL || szEncoded[0] == '\0' ) {
		szBuff[0] = '\0' ;
		return szBuff ;
	}

	PCWSTR		pchEncoded = szEncoded ;
	PWSTR		pchBuff = szBuff ;

	if ( bUtf8Encoding ) {
		CHAR		szUtf8[7] ;

		while ( true ) {
			if ( !_GetNextUrlDecodedUtf8Char (szUtf8, &pchEncoded) )
				CHttpToolW::ThrowException (HTTPCLIENT_ERR_ENCODED_URL_NOT_VALID) ;

			if ( szUtf8[0] == '\0' )
				break ;

			// Converts into a unicode character
			if ( 0 == ::MultiByteToWideChar (CP_UTF8, 0, szUtf8, -1, pchBuff, 2) )
				CHttpToolW::ThrowException (HTTPCLIENT_ERR_MULTIBYTETOWIDECHAR_FAILED, ::GetLastError ()) ;

			pchBuff++ ;
		}
	} else {
		CHAR		szAnsi[3] ;

		while ( true ) {
			if ( !_GetNextUrlDecodedAnsiChar (szAnsi, &pchEncoded, CodePage) )
				CHttpToolW::ThrowException (HTTPCLIENT_ERR_ENCODED_URL_NOT_VALID) ;

			if ( szAnsi[0] == '\0' )
				break ;

			// Converts into a unicode character
			if ( 0 == ::MultiByteToWideChar (CodePage, 0, szAnsi, -1, pchBuff, 2) )
				CHttpToolW::ThrowException (HTTPCLIENT_ERR_MULTIBYTETOWIDECHAR_FAILED, ::GetLastError ()) ;

			pchBuff++ ;
		}
	}

	*pchBuff = '\0' ;
	return szBuff ;
}
///////////////////////////////////////// CHttpEncoder /////////////////////////////////////////


///////////////////////////////////////// CHttpResponseT /////////////////////////////////////////
/*!
 * This is the constructor which has three parameters which are the WinInet internet handles.
 * The handles are owned by the CHttpResponseT and the lifetime of the handles are managed by the CHttpResponseT.
 * If you pass NULL for hInternet or hConnection parameter, user has to manage the lifetime of the handles
 * which are not passed to the CHttpResponseT until the CHttpResponseT is destoried.
 *
 * \param hInternet		[in] An internet handle returned by ::InternetOpen.
 * \param hConnection	[in] A connection handle returned by ::InternetConnect.
 * \param hRequest		[in] A request handle returned by ::HttpSendRequest or ::HttpEndRequest function.
 *						     The hRequest parameter can not be NULL.
 */
template <typename HttpTool>
CHttpResponseT<HttpTool>::CHttpResponseT (HINTERNET hInternet, HINTERNET hConnection, HINTERNET hRequest)
	throw ()
{
	_Initialize (hInternet, hConnection, hRequest) ;
}

/*!
 * This is the constructor which has two parameters which are the WinInet internet handles.
 * The handles are owned by the CHttpResponseT and the lifetime of the handles are managed by the CHttpResponseT.
 * If you pass NULL for hConnection parameter, user has to manage the lifetime of the hConnection handle
 * which is not passed to the CHttpResponseT until the CHttpResponseT is destoried.
 * The user also has to manage the lifetime of the internet handle returned by ::InternetOpen
 * until the CHttpResponseT is destoried.
 *
 * \param hConnection	[in] A connection handle returned by ::InternetConnect.
 * \param hRequest		[in] A request handle returned by ::HttpSendRequest or ::HttpEndRequest function.
 *						     The hRequest parameter can not be NULL.
 */
template <typename HttpTool>
CHttpResponseT<HttpTool>::CHttpResponseT (HINTERNET hConnection, HINTERNET hRequest)
	throw ()
{
	_Initialize (NULL, hConnection, hRequest) ;
}

/*!
 * This is the constructor which has a parameter which is the WinInet internet handle.
 * The handle is owned by the CHttpResponseT and the lifetime of the handle is managed by the CHttpResponseT.
 * user has to manage the lifetime of the internet handles returned by ::InternetOpen and ::InternetConnect
 * until the CHttpResponseT is destoried.
 *
 * \param hRequest		[in] A request handle returned by ::HttpSendRequest or ::HttpEndRequest function.
 *						     The hRequest parameter can not be NULL.
 */
template <typename HttpTool>
CHttpResponseT<HttpTool>::CHttpResponseT (HINTERNET hRequest)
	throw ()
{
	_Initialize (NULL, NULL, hRequest) ;
}

/*!
 * This is a default destructor. It closes all internet handles saved in this object.
 */
template <typename HttpTool>
CHttpResponseT<HttpTool>::~CHttpResponseT (void)
	throw ()
{
	HttpTool::CloseRequest (m_hRequest) ;
	HttpTool::CloseConnection (m_hConnection) ;
	HttpTool::CloseInternet (m_hInternet) ;
	SAFEFREE (m_szStatusText) ;
}

/*!
 * This method initializes the internal member variables.
 *
 * \param hInternet		[in] An internet handle returned by ::InternetOpen.
 * \param hConnection	[in] A connection handle returned by ::InternetConnect.
 * \param hRequest		[in] A request handle returned by ::HttpSendRequest or ::HttpEndRequest function.
 *						     The hRequest parameter can not be NULL.
 */
template <typename HttpTool>
void CHttpResponseT<HttpTool>::_Initialize (HINTERNET hInternet, HINTERNET hConnection, HINTERNET hRequest)
	throw ()
{
	_ASSERTE ( hRequest != NULL ) ;

	m_hInternet = hInternet ;
	m_hConnection = hConnection ;
	m_hRequest = hRequest ;

	m_dwStatus = 0 ;
	m_szStatusText = NULL ;
	m_cbContLen = 0 ;
}

/*!
 * \internal
 * This method caches all HTTP headers of which name is the szName.
 *
 * \param szName		[in] The case-insensitive name of the header. NULL is not allowed.
 * \return				The number of cached headers.
 * \throw				Throws a httpclientexception if an error occurs.
 */
template <typename HttpTool>
DWORD CHttpResponseT<HttpTool>::_LoadHeader (PCSZ szName)
	throw (Exception &)
{
	HTTPCLIENT_ASSERT (m_hRequest != NULL, "CHttpResponseT::_LoadHeader: m_hRequest can not be NULL.") ;
	HTTPCLIENT_ASSERT (szName != NULL, "CHttpResponseT::_LoadHeader: szName can not be NULL.") ;

	PSZ					szHeader = NULL ;
	PSZ					szCopiedName = NULL ;
	::SafeInt<DWORD>	cHeader = 0 ;

	try {
		try {
			DWORD				nIdx = 0 ;
			::SafeInt<size_t>	cbName = 1 ;
			cbName += HttpTool::StringLen (szName) ;
			cbName *= sizeof (CharType) ;

			while ( true ) {
				if ( NULL == (szHeader = HttpTool::GetHeader (m_hRequest, szName, &nIdx)) )
					break ;

				cHeader++ ;

				// Copy the header name
				if ( NULL == (szCopiedName = (PSZ) ::malloc (cbName.Value ())) )
					HttpTool::ThrowException (HTTPCLIENT_ERR_OUT_OF_MEMORY) ;

				HttpTool::StringCopy (szCopiedName, szName) ;
				m_mapHeader.AddPointerDirectly (szCopiedName, szHeader) ;
				szCopiedName = szHeader = NULL ;
			}
		} catch (::SafeIntException & e) {
			HttpTool::ThrowException (e) ;
		}
	} catch (Exception &) {
		m_mapHeader.RemoveAll (szName) ;	// All or nothing
		SAFEFREE (szHeader) ;
		SAFEFREE (szCopiedName) ;
		throw ;
	}

	return cHeader.Value () ;
}

/*!
 * This method returns the number of headers of which name is szName.
 *
 * \param szName		[in] A case-insensitive header name. NULL is not allowed.
 * \return				The number of headers of which name is bstrName.
 * \throw				Throws a httpclientexception if an error occurs.
 */
template <typename HttpTool>
DWORD CHttpResponseT<HttpTool>::GetHeaderCount (PCSZ szName)
	throw (Exception &)
{
	HTTPCLIENT_ASSERT (szName != NULL, "CHttpResponseT::GetHeaderCount: szName can not be NULL.") ;

	DWORD		cHeader = m_mapHeader.Count (szName) ;

	// Tries to load headers if the header does not exist
	if ( cHeader == 0 )
		cHeader = _LoadHeader (szName) ;

	return cHeader ;
}

/*!
 * This method returns the header of which name is the szName.
 * If a header name has multiple values, you can specify a zero-based index for a specific value.
 * If a header specified by szName is not found or the index is out of range, it will return NULL.
 * Otherwise it always returns a null-terminated string. The returned string is owned by
 * the CHttpResponseT. So you can not free it.
 *
 * \param szName		[in] A case-insensitive header name. NULL is not allowed.
 * \param nIdx			[in] A zero-based index for a header which has multiple values.
 * \return				The requested header.
 * \throw				Throws a httpclientexception if an error occurs.
 */
template <typename HttpTool>
typename CHttpResponseT<HttpTool>::PCSZ
CHttpResponseT<HttpTool>::GetHeader (PCSZ szName, DWORD nIdx = 0)
	throw (Exception &)
{
	HTTPCLIENT_ASSERT (szName != NULL, "CHttpResponseT::GetHeader: szName can not be NULL.") ;

	// Tries to load headers if the header does not exist
	if ( !m_mapHeader.Count (szName) && !_LoadHeader (szName) )
		return NULL ;

	return m_mapHeader.GetValue (szName, nIdx) ;
}

/*!
 * This method returns the HTTP status code of a HTTP response.
 *
 * \return				The HTTP status code.
 * \throw				Throws a httpclientexception if an error occurs.
 */
template <typename HttpTool>
DWORD CHttpResponseT<HttpTool>::GetStatus (void)
	throw (Exception &)
{
	HTTPCLIENT_ASSERT (m_hRequest != NULL, "CHttpResponseT::GetStatus: m_hRequest can not be NULL.") ;

	// If the cached one exists
	if ( m_dwStatus != 0 )
		return m_dwStatus ;

	DWORD		dwBuffSize = sizeof (DWORD) ;

	if ( !::HttpQueryInfo (
			m_hRequest
			, HTTP_QUERY_STATUS_CODE		// Get the HTTP status code
				| HTTP_QUERY_FLAG_NUMBER
			, static_cast<void *> (&m_dwStatus)
			, &dwBuffSize					// Buffer size (byte)
			, NULL							// Does not use a header index
			)
		)
		HttpTool::ThrowException (HTTPCLIENT_ERR_QUERYINFO_FAILED, ::GetLastError ()) ;

	return m_dwStatus ;
}

/*!
 * This method returns the HTTP status text of a HTTP response.
 * It always returns a null-terminated string. The returned string is owned by
 * the CHttpResponse. So you can not free it.
 *
 * \return				The HTTP status text returned by a HTTP server.
 * \throw				Throws a httpclientexception if an error occurs.
 */
template <typename HttpTool>
typename CHttpResponseT<HttpTool>::PCSZ
CHttpResponseT<HttpTool>::GetStatusText (void)
	throw (Exception &)
{
	HTTPCLIENT_ASSERT (m_hRequest != NULL, "CHttpResponseT::GetStatusText: m_hRequest can not be NULL.") ;

	// If the cached one exists
	if ( m_szStatusText != NULL )
		return m_szStatusText ;

	return (m_szStatusText = HttpTool::GetStatusText (m_hRequest)) ;
}

/*!
 * This method retrieves the content length of a returned HTTP response.
 * The content is the data stream except the HTTP headers. If the length is retrieved,
 * it will return TRUE, otherwise return FALSE.
 *
 * \param cbContLen		[out] A reference variable to save the content length.
 * \return				TRUE if the content length is available, otherwise FALSE.
 * \throw				Throws a httpclientexception if an error occurs.
 */
template <typename HttpTool>
BOOL CHttpResponseT<HttpTool>::GetContentLength (DWORD & cbContLen)
	throw (Exception &)
{
	HTTPCLIENT_ASSERT (m_hRequest != NULL, "CHttpResponseT::GetContentLength: m_hRequest can not be NULL.") ;

	DWORD		dwBuffSize = sizeof (DWORD) ;

	if ( !::HttpQueryInfo (
			m_hRequest
			, HTTP_QUERY_CONTENT_LENGTH		// Reads the 'content-length' header
				| HTTP_QUERY_FLAG_NUMBER
			, static_cast<void *> (&cbContLen)
			, &dwBuffSize					// Buffer size (byte)
			, NULL							// Does not use a header index
			)
		) {

		if ( ::GetLastError () == ERROR_HTTP_HEADER_NOT_FOUND )
			return FALSE ;

		HttpTool::ThrowException (HTTPCLIENT_ERR_QUERYINFO_FAILED, ::GetLastError ()) ;
	}

	return TRUE ;
}

/*!
 * This method reads the content (the data stream except headers) of a returned HTTP response.
 *
 * \param pbyBuff		[out] A buffer to save the read content.
 * \param cbBuff		[in] A buffer size in byte.
 * \return				Returns the number of bytes read. Returns zero if all content is read.
 * \throw				Throws a httpclientexception if an error occurs.
 */
template <typename HttpTool>
DWORD CHttpResponseT<HttpTool>::ReadContent (BYTE * pbyBuff, DWORD cbBuff)
	throw (Exception &)
{
	HTTPCLIENT_ASSERT (m_hRequest != NULL, "CHttpResponseT::ReadContent: m_hRequest can not be NULL.") ;
	HTTPCLIENT_ASSERT (pbyBuff != NULL, "CHttpResponseT::ReadContent: pbyBuff can not be NULL.") ;
	HTTPCLIENT_ASSERT (cbBuff != 0, "CHttpResponseT::ReadContent: cbBuff can not be zero.") ;

	DWORD			cbRead = 0 ;
	if ( !::InternetReadFile (
			m_hRequest
			, pbyBuff
			, cbBuff
			, &cbRead
			)
		)
		HttpTool::ThrowException (HTTPCLIENT_ERR_INTERNETREADFILE_FAILED, ::GetLastError ()) ;

	return cbRead ;
}

/*!
 * This method saves the content (the data stream except headers) of a returned HTTP response
 * to a file which is specified by szFilePath.
 *
 * \param szFilePath	[in] A file path to save the return content.
 * \param bOverwrite	[in] If this is TRUE, a file is overwritten if aleady exists. otherwise an exception is thrown.
 * \throw				Throws a httpclientexception if an error occurs.
 */
template <typename HttpTool>
void CHttpResponseT<HttpTool>::SaveContent (PCSZ szFilePath, BOOL bOverwrite)
	throw (Exception &)
{
	HTTPCLIENT_ASSERT (szFilePath != NULL, "CHttpResponseT::SaveContent: szFilePath can not be NULL.") ;

	// Checks whether the target file aleady exists.
	if ( !bOverwrite )
	{
		if ( HttpTool::FileExists (szFilePath) )
			HttpTool::ThrowException (HTTPCLIENT_ERR_FILE_ALEADY_EXISTS, NO_ERROR, szFilePath) ;
	}

	// Create a file to save content stream
	HANDLE		hFile = HttpTool::CreateFileAlwaysToWrite (szFilePath) ;

	if ( hFile == INVALID_HANDLE_VALUE )
		HttpTool::ThrowException (HTTPCLIENT_ERR_OPENFILE_FAILED, ::GetLastError (), szFilePath) ;

	const DWORD		cbBuff = 1024 * 10 ;				// use 10k buffer
	BYTE			byBuff[cbBuff] ;
	DWORD			cbRead, cbWritten, cbRemain ;

	// Reads the data stream returned by the HTTP server.
	while ( cbRead = ReadContent (byBuff, cbBuff) ) {
		cbRemain = cbRead ;

		do {
			if ( !::WriteFile (hFile, byBuff + (cbRead - cbRemain) , cbRemain, &cbWritten, NULL) ) {
				DWORD		dwLastError = ::GetLastError () ;
				::CloseHandle (hFile) ;
				HttpTool::ThrowException (HTTPCLIENT_ERR_WRITEFILE_FAILED, dwLastError, szFilePath) ;
			}
			cbRemain -= cbWritten ;
		} while ( cbRemain > 0 ) ;
	}
	::CloseHandle (hFile) ;
}


///////////////////////////////////////// CHttpResponseT /////////////////////////////////////////


///////////////////////////////////////// CHttpPostStatT /////////////////////////////////////////
/*!
 * This is a default constructor with no argument.
 */
template <typename HttpTool>
CHttpPostStatT<HttpTool>::CHttpPostStatT (void)
	throw ()
{
	// Initialize member variables
	_InitMemberVariables () ;
}

/*!
 * This is a copy constructor.
 * If memory allocation failed, the messages contained in the objPostStat object will
 * not be copied and the internal string pointers will point to NULL.
 *
 * \param objPostStat			[in] An CHttpPostStat object.
 */
template <typename HttpTool>
CHttpPostStatT<HttpTool>::CHttpPostStatT (const CHttpPostStatT & objPostStat)
	throw ()
{
	operator= (objPostStat) ;
}

/*!
 * This is a default destructor.
 */
template <typename HttpTool>
CHttpPostStatT<HttpTool>::~CHttpPostStatT (void)
	throw ()
{
	_MakeUnActive () ;
}

// Initialize member variables
template <typename HttpTool>
void CHttpPostStatT<HttpTool>::_InitMemberVariables (void)
	throw ()
{
	m_bIsActive = FALSE ;

	m_cbActualTotal = 0 ;
	m_cbActualPosted = 0 ;

	m_cbTotal = 0 ;
	m_cbPosted = 0 ;

	m_cParam = 0 ;
	m_cParamPosted = 0 ;
	m_cFile = 0 ;
	m_cFilePosted = 0 ;

	m_szCurrParam = NULL ;
	m_szCurrFile = NULL ;
	m_cbCurrParam = 0 ;
	m_cbCurrParamPosted = 0 ;
}

// Copy a string.
// The destination pointer will point to NULL if memory allocation failed.
template <typename HttpTool>
void CHttpPostStatT<HttpTool>::_SetString (PSZ * pszDest, PCSZ szSrc)
	throw ()
{
	_ASSERTE ( pszDest != NULL ) ;

	if ( szSrc == NULL ) {
		SAFEFREE ( (*pszDest) ) ;
		return ;
	}

	// If the source and destination string are the same, just return.
	if ( *pszDest != NULL ) {
		if ( 0 == HttpTool::StringCmp (*pszDest, szSrc) )
			return ;

		SAFEFREE ( (*pszDest) ) ;
	}

	*pszDest = (PSZ) ::malloc (sizeof (CharType) * (HttpTool::StringLen (szSrc) + 1)) ;

	// If memory allocation failed, just return.
	if ( *pszDest == NULL )
		return ;

	HttpTool::StringCopy (*pszDest, szSrc) ;
}

/*!
 * This method returns a boolean which indicates whether the POST is in progress or not.
 *
 * \return			TRUE if the POST is in progress, otherwise FALSE
 */
template <typename HttpTool>
BOOL CHttpPostStatT<HttpTool>::IsActive (void) const
	throw ()
{
	return m_bIsActive ;
}

/*!
 * This method returns the actual number of bytes to send to a HTTP web server.
 * The returned size includes the size of boundaries, parameter names, and other elements.
 * If the IsActive is FALSE, you shouldn't call this method.
 *
 * \return			The actual number of bytes which is going to be sent to a HTTP web server
 */
template <typename HttpTool>
size_t CHttpPostStatT<HttpTool>::ActualTotalByte (void) const
	throw (Exception &)
{
	HTTPCLIENT_ASSERT (m_bIsActive, "CHttpPostStatT::ActualTotalByte: The post context is not active.") ;
	return m_cbActualTotal ;
}

/*!
 * This method returns the actual number of bytes posted to a HTTP web server.
 * The returned size includes the size of boundaries, parameter names, and other elements.
 * If the IsActive is FALSE, you shouldn't call this method.
 *
 * \return			The actual number of bytes posted to a HTTP web server
 */
template <typename HttpTool>
size_t CHttpPostStatT<HttpTool>::ActualPostedByte (void) const
	throw (Exception &)
{
	HTTPCLIENT_ASSERT (m_bIsActive, "CHttpPostStatT::ActualPostedByte: The post context is not active.") ;
	return m_cbActualPosted ;
}

/*!
 * This method returns the number of bytes to send to a HTTP web server.
 * If the IsActive is FALSE, you shouldn't call this method.
 *
 * \return			The number of bytes to send to a HTTP web server
 */
template <typename HttpTool>
size_t CHttpPostStatT<HttpTool>::TotalByte (void) const
	throw (Exception &)
{
	HTTPCLIENT_ASSERT (m_bIsActive, "CHttpPostStatT::TotalByte: The post context is not active.") ;
	return m_cbTotal ;
}

/*!
 * This method returns the number of bytes posted to a HTTP web server.
 * If the IsActive is FALSE, you shouldn't call this method.
 *
 * \return			The number of bytes posted to a HTTP web server
 */
template <typename HttpTool>
size_t CHttpPostStatT<HttpTool>::PostedByte (void) const
	throw (Exception &)
{
	HTTPCLIENT_ASSERT (m_bIsActive, "CHttpPostStatT::PostedByte: The post context is not active.") ;
	return m_cbPosted ;
}

/*!
 * This method returns the total number of parameters to send to a HTTP web server.
 * If the IsActive is FALSE, you shouldn't call this method.
 *
 * \return			The total number of parameters
 */
template <typename HttpTool>
DWORD CHttpPostStatT<HttpTool>::TotalCount (void) const
	throw (Exception &)
{
	HTTPCLIENT_ASSERT (m_bIsActive, "CHttpPostStatT::TotalCount: The post context is not active.") ;
	return m_cParam ;
}

/*!
 * This method returns the number of posted parameters.
 * The count includes the current parameter which is being sent to the server.
 * If the IsActive is FALSE, you shouldn't call this method.
 *
 * \return			The number of posted parameters
 */
template <typename HttpTool>
DWORD CHttpPostStatT<HttpTool>::PostedCount (void) const
	throw (Exception &)
{
	HTTPCLIENT_ASSERT (m_bIsActive, "CHttpPostStatT::PostedCount: The post context is not active.") ;
	return m_cParamPosted ;
}

/*!
 * This method returns the number of file parameters.
 * The file parameter is a parameter which contains a file path to upload.
 * If the IsActive is FALSE, you shouldn't call this method.
 *
 * \return			The number of file parameters
 */
template <typename HttpTool>
DWORD CHttpPostStatT<HttpTool>::FileCount (void) const
	throw (Exception &)
{
	HTTPCLIENT_ASSERT (m_bIsActive, "CHttpPostStatT::FileCount: The post context is not active.") ;
	return m_cFile ;
}

/*!
 * This method returns the number of posted file parameters.
 * The file parameter is a parameter which contains a file path to upload.
 * If the current parameter is a file parameter, then the count includes the current parameter.
 * If the IsActive is FALSE, you shouldn't call this method.
 *
 * \return			The number of posted file parameters
 */
template <typename HttpTool>
DWORD CHttpPostStatT<HttpTool>::PostedFileCount (void) const
	throw (Exception &)
{
	HTTPCLIENT_ASSERT (m_bIsActive, "CHttpPostStatT::PostedFileCount: The post context is not active.") ;
	return m_cFilePosted ;
}

/*!
 * This method returns the current parameter name.
 * The current parameter is a parameter which is being sent to the server.
 * It always returns a null-terminated string. The returned string is owned by
 * the CHttpPostStat. So you can not free it.
 * If the system fails to allocate memory for the current parameter name,
 * it will return "NULL" instead of NULL.
 * If the IsActive is FALSE, you shouldn't call this method.
 *
 * \return			The current parameter name
 */
template <typename HttpTool>
typename CHttpPostStatT<HttpTool>::PCSZ
CHttpPostStatT<HttpTool>::CurrParam (void) const
	throw (Exception &)
{
	HTTPCLIENT_ASSERT (m_bIsActive, "CHttpPostStatT::CurrParam: The post context is not active.") ;

	// If the string of the current parameter is NULL,
	// returns "NULL" instead of NULL
	if ( m_szCurrParam == NULL )
		return HttpTool::szNULL ;

	return m_szCurrParam ;
}

/*!
 * This method returns the file path which is currently being uploaded.
 * It always returns a null-terminated string. The returned string is owned by
 * the CHttpPostStat. So you can not free it.
 * If the current parameter is not a file parameter or the system fails to allocate
 * memory for the current file path, it will return "NULL".
 * If the IsActive is FALSE, you shouldn't call this method.
 *
 * \return			The current file path
 */
template <typename HttpTool>
typename CHttpPostStatT<HttpTool>::PCSZ
CHttpPostStatT<HttpTool>::CurrFile (void) const
	throw (Exception &)
{
	HTTPCLIENT_ASSERT (m_bIsActive, "CHttpPostStatT::CurrFile: The post context is not active.") ;

	// If the string of the current file is NULL,
	// returns "NULL" instead of NULL
	if ( m_szCurrFile == NULL )
		return HttpTool::szNULL ;

	return m_szCurrFile ;
}

/*!
 * This method returns the number of bytes of the current parameter.
 * The current parameter is a parameter which is being sent to the server.
 * If the IsActive is FALSE, you shouldn't call this method.
 *
 * \return			The number of bytes of the current parameter
 */
template <typename HttpTool>
size_t CHttpPostStatT<HttpTool>::CurrParamTotalByte (void) const
	throw (Exception &)
{
	HTTPCLIENT_ASSERT (m_bIsActive, "CHttpPostStatT::CurrParamTotalByte: The post context is not active.") ;
	return m_cbCurrParam ;
}

/*!
 * This method returns the number of posted bytes of the current parameter.
 * The current parameter is a parameter which is being sent to the server.
 * If the IsActive is FALSE, you shouldn't call this method.
 *
 * \return			The number of posted bytes of the current parameter
 */
template <typename HttpTool>
size_t CHttpPostStatT<HttpTool>::CurrParamPostedByte (void) const
	throw (Exception &)
{
	HTTPCLIENT_ASSERT (m_bIsActive, "CHttpPostStatT::CurrParamPostedByte: The post context is not active.") ;
	return m_cbCurrParamPosted ;
}

/*!
 * This method returns the number of remained bytes of the current parameter.
 * The current parameter is a parameter which is being sent to the server.
 * If the IsActive is FALSE, you shouldn't call this method.
 *
 * \return			The number of remained bytes of the current parameter
 */
template <typename HttpTool>
size_t CHttpPostStatT<HttpTool>::CurrParamRemainByte (void) const
	throw (Exception &)
{
	HTTPCLIENT_ASSERT (m_bIsActive, "CHttpPostStatT::CurrParamRemainByte: The post context is not active.") ;
	return m_cbCurrParam - m_cbCurrParamPosted ;
}

/*!
 * This method returns TRUE if the current parameter is a file parameter,
 * otherwise it will return FALSE.
 * The current parameter is a parameter which is being sent to the server.
 * If the IsActive is FALSE, you shouldn't call this method.
 *
 * \return			TRUE if the current parameter is a file parameter, otherwise FALSE
 */
template <typename HttpTool>
BOOL CHttpPostStatT<HttpTool>::CurrParamIsFile (void) const
	throw (Exception &)
{
	HTTPCLIENT_ASSERT (m_bIsActive, "CHttpPostStatT::CurrParamIsFile: The post context is not active.") ;
	return (m_szCurrFile != NULL) ;
}

/*!
 * This method returns TRUE if the current parameter is completely posted,
 * otherwise it will return FALSE.
 * The current parameter is a parameter which is being sent to the server.
 * If the IsActive is FALSE, you shouldn't call this method.
 *
 * \return			TRUE if the current parameter is completely posted, otherwise FALSE
 */
template <typename HttpTool>
BOOL CHttpPostStatT<HttpTool>::CurrParamIsComplete (void) const
	throw (Exception &)
{
	HTTPCLIENT_ASSERT (m_bIsActive, "CHttpPostStatT::CurrParamIsComplete: The post context is not active.") ;
	return (CurrParamTotalByte () == CurrParamPostedByte ()) ;
}

/*!
 * \internal
 * This method returns TRUE if all bytes are sent.
 *
 * \return			Returns TRUE if all bytes are sent
 */
template <typename HttpTool>
BOOL CHttpPostStatT<HttpTool>::_IsComplete (void) const
	throw ()
{
	_ASSERTE ( m_bIsActive ) ;
	return (ActualTotalByte () == ActualPostedByte ()) ;
}

/*!
 * \internal
 * This method tests whether the m_cbActualPosted is safe to increase.
 *
 * \param nBytes		[in] The number of bytes to add to the m_cbActualPosted.
 * \throw				Throws a httpclientexception if an error occurs.
 */
template <typename HttpTool>
void CHttpPostStatT<HttpTool>::_TestAddActualPostedBytes (size_t nBytes)
	throw (Exception &)
{
	HTTPCLIENT_ASSERT (m_bIsActive, "CHttpPostStatT::_TestAddActualPostedBytes: The post context is not active.") ;

	try {
		::SafeInt<size_t>	cbActualPosted = m_cbActualPosted ;

		_ASSERTE ( (cbActualPosted + nBytes) <= m_cbActualTotal ) ;
		cbActualPosted += nBytes ;
	} catch (::SafeIntException & e) {
		HttpTool::ThrowException (e) ;
	}
}

/*!
 * \internal
 * This method increases the m_cbActualPosted member variable.
 *
 * \param nBytes		[in] The number of bytes to add to the m_cbActualPosted.
 * \throw				Throws a httpclientexception if an error occurs.
 */
template <typename HttpTool>
void CHttpPostStatT<HttpTool>::_AddActualPostedBytes (size_t nBytes)
	throw (Exception &)
{
	_TestAddActualPostedBytes (nBytes) ;
	m_cbActualPosted += nBytes ;
}

/*!
 * \internal
 * This method cleans all internal states and closes the POST state.
 */
template <typename HttpTool>
void CHttpPostStatT<HttpTool>::_MakeUnActive (void)
	throw ()
{
	SAFEFREE (m_szCurrParam) ;
	SAFEFREE (m_szCurrFile) ;

	_InitMemberVariables () ;
}

/*!
 * \internal
 * This method starts a new state of the HTTP POST.
 *
 * \param cbActualTotal		[in] The actual total number of bytes of the request.
 * \param cbTotal			[in] The total number of bytes of the request.
 * \param cParam			[in] The number of parameters.
 * \param cFile				[in] The number of file parameters.
 */
template <typename HttpTool>
void CHttpPostStatT<HttpTool>::_MakeActive (size_t cbActualTotal, size_t cbTotal, DWORD cParam
												, DWORD cFile)
	throw ()
{
	_MakeUnActive () ;

	m_bIsActive = TRUE ;
	m_cbActualTotal = cbActualTotal ;
	m_cbTotal = cbTotal ;
	m_cParam = cParam ;
	m_cFile = cFile ;
}

/*!
 * \internal
 * This method tests whether a new current parameter is safely set.
 *
 * \param szCurrParam		[in] The name of the current parameter.
 * \param cbCurrParam		[in] The number of bytes of the current parameter.
 * \param bIsFile			[in] TRUE if the current parameter is a file parameter.
 * \param szCurrFile		[in] The file path of the current parameter.
 * \throw					Throws a httpclientexception if an error occurs.
 */
template <typename HttpTool>
void CHttpPostStatT<HttpTool>::_TestStartNewEntry (PCSZ /* szCurrParam */, size_t /* cbCurrParam */
												, BOOL bIsFile, PCSZ /* szCurrFile */)
	throw (Exception &)
{
	HTTPCLIENT_ASSERT (m_bIsActive, "CHttpPostStatT::_TestStartNewEntry: The post context is not active.") ;

	try {
		::SafeInt<DWORD>		cParamPosted = m_cParamPosted ;
		_ASSERTE ( (cParamPosted + 1) <= m_cParam ) ;
		cParamPosted++ ;

		if ( bIsFile ) {
			::SafeInt<DWORD>	cFilePosted = m_cFilePosted ;
			_ASSERTE ( (cFilePosted + 1) <= m_cFile ) ;
			cFilePosted++ ;
		}
	} catch (::SafeIntException & e) {
		HttpTool::ThrowException (e) ;
	}
}

/*!
 * \internal
 * This method sets a new current parameter.
 * If memory allocation is failed for strings (szCurrParam, szCurrFile),
 * NULL is saved. (and "NULL" is returned when the user requests these values)
 *
 * \param szCurrParam		[in] The name of the current parameter.
 * \param cbCurrParam		[in] The number of bytes of the current parameter.
 * \param bIsFile			[in] TRUE if the current parameter is a file parameter.
 * \param szCurrFile		[in] The file path of the current parameter.
 * \throw					Throws a httpclientexception if an error occurs.
 */
template <typename HttpTool>
void CHttpPostStatT<HttpTool>::_StartNewEntry (PCSZ szCurrParam, size_t cbCurrParam
												, BOOL bIsFile, PCSZ szCurrFile)
	throw (Exception &)
{
	_TestStartNewEntry (szCurrParam, cbCurrParam, bIsFile, szCurrFile) ;

	if ( bIsFile ) {
		m_cFilePosted++ ;
		_SetString (&m_szCurrFile, szCurrFile) ;
	} else {
		_SetString (&m_szCurrFile, NULL) ;
	}

	m_cParamPosted++ ;
	_SetString (&m_szCurrParam, szCurrParam) ;

	m_cbCurrParam = cbCurrParam ;
	m_cbCurrParamPosted = 0 ;
}

/*!
 * \internal
 * This method tests whether the counters related to the number of posted bytes is safe to increase.
 * (m_cbPosted, m_cbCurrParamPosted, m_cbActualPosted)
 *
 * \param nBytes			[in] The number of bytes to add.
 * \throw					Throws a httpclientexception if an error occurs.
 */
template <typename HttpTool>
void CHttpPostStatT<HttpTool>::_TestAddPostedBytes (size_t nBytes)
	throw (Exception &)
{
	HTTPCLIENT_ASSERT (m_bIsActive, "CHttpPostStatT::_TestAddPostedBytes: The post context is not active.") ;

	try {
		_TestAddActualPostedBytes (nBytes) ;

		::SafeInt<size_t>		cbPosted = m_cbPosted ;
		::SafeInt<size_t>		cbCurrParamPosted = m_cbCurrParamPosted ;

		_ASSERTE ( (cbPosted + nBytes) <= m_cbTotal ) ;
		_ASSERTE ( (cbCurrParamPosted + nBytes) <= m_cbCurrParam ) ;

		cbPosted += nBytes ;
		cbCurrParamPosted += nBytes ;
	} catch (::SafeIntException & e) {
		HttpTool::ThrowException (e) ;
	}
}

/*!
 * \internal
 * This method increases all counters related to the number of posted bytes.
 * (m_cbPosted, m_cbCurrParamPosted, m_cbActualPosted)
 *
 * \param nBytes			[in] The number of bytes to add.
 * \throw					Throws a httpclientexception if an error occurs.
 */
template <typename HttpTool>
void CHttpPostStatT<HttpTool>::_AddPostedBytes (size_t nBytes)
	throw (Exception &)
{
	_TestAddPostedBytes (nBytes) ;

	m_cbPosted += nBytes ;
	m_cbCurrParamPosted += nBytes ;
	_AddActualPostedBytes (nBytes) ;
}

/*!
 * This operator copies the state information from the source object.
 * All strings contained in the targeted object can be NULL if memory allocation failed.
 * (and "NULL" is returned when the user requests these values)
 *
 * \param objPostStat		[in] A CHttpPostStat object.
 * \return					The targeted object.
 */
template <typename HttpTool>
CHttpPostStatT<HttpTool> & 
CHttpPostStatT<HttpTool>::operator= (const CHttpPostStatT<HttpTool> & objPostStat)
	throw ()
{
	if ( this == &objPostStat )
		return *this ;

	_MakeUnActive () ;

	m_bIsActive = objPostStat.m_bIsActive ;

	m_cbActualTotal = objPostStat.m_cbActualTotal ;
	m_cbActualPosted = objPostStat.m_cbActualPosted ;

	m_cbTotal = objPostStat.m_cbTotal ;
	m_cbPosted = objPostStat.m_cbPosted ;

	m_cParam = objPostStat.m_cParam ;
	m_cParamPosted = objPostStat.m_cParamPosted ;
	m_cFile = objPostStat.m_cFile ;
	m_cFilePosted = objPostStat.m_cFilePosted ;

	_SetString (&m_szCurrParam, objPostStat.m_szCurrParam) ;
	_SetString (&m_szCurrFile, objPostStat.m_szCurrFile) ;

	m_cbCurrParam = objPostStat.m_cbCurrParam ;
	m_cbCurrParamPosted = objPostStat.m_cbCurrParamPosted ;

	return *this ;
}
///////////////////////////////////////// CHttpPostStatT /////////////////////////////////////////

///////////////////////////////////////// CHttpUrlAnalyzer /////////////////////////////////////////
/*!
 * This is the default constructor. If you pass a URL to the constructor, 
 * It analyzes a URL using Analyze method.
 *
 * \param szUrl			[in] An URL to analyze. 
 * \param CodePage		[in] A code page of the URL. If the URL is a unicode string,
 *						     this parameter is ignored.
 *						     CP_UTF8, CP_UTF7 are not allowed.
 * \throw				Throws a httpclientexception if an error occurs.
 *
 * \sa	Analyze
 */
template <typename HttpTool>
CHttpUrlAnalyzerT<HttpTool>::CHttpUrlAnalyzerT (PCSZ szUrl, UINT CodePage)
	throw (Exception &)
{
	Analyze (szUrl, CodePage) ;
}

/*!
 * This method clears all saved information and sets to 0.
 */
template <typename HttpTool>
void CHttpUrlAnalyzerT<HttpTool>::Reset (void)
	throw ()
{
	m_nProtocolIdx = m_cchProtocol = m_nServerAddrIdx = m_cchServerAddr
		= m_nServerPortIdx = m_cchServerPort = m_nUrlPathIdx = m_cchUrlPath
		= m_nSearchIdx = m_cchSearch = m_nBookmarkIdx = m_cchBookmark = 0 ;
}

/*!
 * This method analyzes a URL and saves information about its component parts.
 * It does not check whether the URL is valid. The URL itself is not saved.
 *
 * \param szUrl			[in] An URL to analyze. 
 * \param CodePage		[in] A code page of the URL. If the URL is a unicode string,
 *						     this parameter is ignored.
 *						     CP_UTF8, CP_UTF7 are not allowed.
 * \throw				Throws a httpclientexception if an error occurs.
 */
template <typename HttpTool>
void CHttpUrlAnalyzerT<HttpTool>::Analyze (PCSZ szUrl, UINT CodePage)
	throw (Exception &)
{
	// The unicode encodings are not allowed
	HTTPCLIENT_ASSERT ((CodePage != CP_UTF8) && (CodePage != CP_UTF7)
		, "CHttpUrlAnalyzerT::Analyze: CP_UTF8 and CP_UTF7 can not be used for the CodePage parameter.") ;

	Reset () ;

	if ( szUrl == NULL || szUrl[0] == '\0' )
		return ;

	// Checks overflow
	try {
		::SafeInt<DWORD>	dw = HttpTool::StringLen (szUrl) ;
	} catch (::SafeIntException & e) {
		HttpTool::ThrowException (e) ;
	}

	PCSZ		pchNextUrl = szUrl ;
	DWORD		nNextUrlCharIdx = 0 ;

	{
		// Get the protocol
		PCSZ		pchColonSlashSlash = NULL ;

		{
			// Finds "://" in the URL
			for (pchColonSlashSlash = pchNextUrl; *pchColonSlashSlash != '\0'; pchColonSlashSlash++) {

				// If the character is a leadbyte of the double byte character set
				if ( HttpTool::IsDBCSLeadByteEx (CodePage, *pchColonSlashSlash) ) {
					// Ignores a trailing byte
					if ( *(pchColonSlashSlash + 1) != '\0' )
						pchColonSlashSlash++ ;
					continue ;
				}

				if ( (*pchColonSlashSlash == ':')
						&& (*(pchColonSlashSlash + 1) != '\0') && (*(pchColonSlashSlash + 1) == '/')
						&& (*(pchColonSlashSlash + 2) != '\0') && (*(pchColonSlashSlash + 2) == '/') )
					break ;
			}
		}

		if ( *pchColonSlashSlash != '\0' ) {
			for (; pchNextUrl != pchColonSlashSlash; pchNextUrl++, m_cchProtocol++, nNextUrlCharIdx++) ;

			pchNextUrl += 3/* == strlen (szColonSlashSlash)*/ ;
			nNextUrlCharIdx += 3 ;
		}
	}

	{
		// Get server address and server port
		BOOL		bFoundColon = FALSE ;

		m_nServerAddrIdx = nNextUrlCharIdx ;
		for (; *pchNextUrl != '/' && *pchNextUrl != '?' && *pchNextUrl != '#' && *pchNextUrl != '\0';
				pchNextUrl++, nNextUrlCharIdx++) {

			// If the character is a leadbyte of the double byte character set
			if ( HttpTool::IsDBCSLeadByteEx (CodePage, *pchNextUrl) ) {
				m_cchServerPort += (bFoundColon ? 1 : 0) ;
				m_cchServerAddr += (bFoundColon ? 0 : 1) ;

				// Ignores a trailing byte
				if ( *(pchNextUrl + 1) != '\0' ) {
					pchNextUrl++ ;
					nNextUrlCharIdx++ ;
					m_cchServerPort += (bFoundColon ? 1 : 0) ;
					m_cchServerAddr += (bFoundColon ? 0 : 1) ;
				}
				continue ;
			}

			if ( bFoundColon ) {
				m_cchServerPort++ ;
				continue ;
			}

			if ( *pchNextUrl == ':' ) {
				m_nServerPortIdx = nNextUrlCharIdx + 1 ;
				bFoundColon = TRUE ;
				continue ;
			}

			m_cchServerAddr++ ;
		}

		if ( !bFoundColon )
			m_nServerPortIdx = nNextUrlCharIdx ;
	}

	{
		// Get path and search string
		BOOL		bFoundQuestionMark = FALSE ;

		m_nUrlPathIdx = nNextUrlCharIdx ;
		for (; *pchNextUrl != '#' && *pchNextUrl != '\0'; pchNextUrl++, nNextUrlCharIdx++) {

			// If the character is a leadbyte of the double byte character set
			if ( HttpTool::IsDBCSLeadByteEx (CodePage, *pchNextUrl) ) {
				m_cchSearch += (bFoundQuestionMark ? 1 : 0) ;
				m_cchUrlPath += (bFoundQuestionMark ? 0 : 1) ;

				// Ignores a trailing byte
				if ( *(pchNextUrl + 1) != '\0' ) {
					pchNextUrl++ ;
					nNextUrlCharIdx++ ;
					m_cchSearch += (bFoundQuestionMark ? 1 : 0) ;
					m_cchUrlPath += (bFoundQuestionMark ? 0 : 1) ;
				}
				continue ;
			}

			if ( bFoundQuestionMark ) {
				m_cchSearch++ ;
				continue ;
			}

			if ( *pchNextUrl == '?' ) {
				m_nSearchIdx = nNextUrlCharIdx ;
				m_cchSearch++ ;
				bFoundQuestionMark = TRUE ;
				continue ;
			}

			m_cchUrlPath++ ;
		}

		if ( !bFoundQuestionMark )
			m_nSearchIdx = nNextUrlCharIdx ;
	}

	{
		// Get bookmark
		m_nBookmarkIdx = nNextUrlCharIdx ;
		for (; *pchNextUrl != '\0'; pchNextUrl++, nNextUrlCharIdx++, m_cchBookmark++) ;
	}
}
///////////////////////////////////////// CHttpUrlAnalyzer /////////////////////////////////////////


///////////////////////////////////////// CHttpClientT /////////////////////////////////////////
/*!
 * This is a default constructor with no argument.
 */
template <typename HttpTool, typename HttpEncoder>
CHttpClientT<HttpTool, HttpEncoder>::CHttpClientT (void)
	throw ()
{
	// Initializes member variables
	m_bStrictFileCheck = FALSE ;
	m_bUseUtf8 = FALSE ;
	m_nAnsiCodePage = CP_ACP ;

	// variables related to the WinInet API
	m_szInternetUserAgent = NULL ;
	m_dwInternetAccessType = INTERNET_OPEN_TYPE_PRECONFIG ;
	m_szInternetProxyName = NULL ;
	m_szInternetProxyBypass = NULL ;
	m_dwInternetFlags = 0 ;
	m_szProxyUserName = NULL ;
	m_szProxyPassword = NULL ;


	// Initializes a boundary
	m_szBoundary = NULL ;
	m_szUploadContType = NULL ;
	m_szBoundaryA = NULL ;
	m_bNeedToFreeBoundaryA = FALSE ;
	try {
		m_szBoundary = HttpTool::CreateUploadBoundary () ;
		if ( m_szBoundary ) {
			m_szUploadContType = 
				(PSZ) ::malloc (sizeof (CharType) 
						* (30 /* ::strlen (szMultipartFormDataBoundary) */
						+ HttpTool::StringLen (m_szBoundary)
						+ 1)
				) ;
			if ( m_szUploadContType == NULL )
				throw "Out of memory" ;

			HttpTool::StringCopy (m_szUploadContType, HttpTool::szMultipartFormDataBoundary) ;
			HttpTool::StringCat (m_szUploadContType, m_szBoundary) ;
			m_szBoundaryA = _String2Ansi (m_szBoundary, m_bNeedToFreeBoundaryA) ;
		}
	} catch (...) {
		SAFEFREE (m_szBoundary) ;
		SAFEFREE (m_szUploadContType) ;
	}

	// Initializes the context related to the POST context
	_InitPostContext () ;
}

/*!
 * This is a default destructor.
 */
template <typename HttpTool, typename HttpEncoder>
CHttpClientT<HttpTool, HttpEncoder>::~CHttpClientT (void)
	throw ()
{
	// Releases the POST context
	_EndPostContext () ;

	// Releases the Boundary
	SAFEFREE (m_szBoundary) ;
	SAFEFREE (m_szUploadContType) ;
	if ( m_bNeedToFreeBoundaryA )
		SAFEFREE (m_szBoundaryA) ;

	// Releases the variables related to the WinInet API
	SAFEFREE (m_szInternetUserAgent) ;
	SAFEFREE (m_szInternetProxyName) ;
	SAFEFREE (m_szInternetProxyBypass) ;
	SAFEFREE (m_szProxyUserName) ;
	SAFEFREE (m_szProxyPassword) ;
}



template <typename HttpTool, typename HttpEncoder>
DWORD CHttpClientT<HttpTool, HttpEncoder>::_String2AnsiLen (PCSZ szStr)
	throw (Exception &)
{
	return HttpEncoder::AnsiEncodeLen (szStr, m_nAnsiCodePage) ;
}

template <typename HttpTool, typename HttpEncoder>
PCSTR CHttpClientT<HttpTool, HttpEncoder>::_String2Ansi (PCSZ szStr, BOOL & bNeedToFree)
	throw (Exception &)
{
	if ( HttpTool::IsAnsi () ) {
		bNeedToFree = FALSE ;
		return reinterpret_cast<PCSTR> (szStr) ;
	}

	DWORD				cchAnsi = HttpEncoder::AnsiEncodeLen (szStr, m_nAnsiCodePage) ;
	::SafeInt<size_t>	cbRequired = 0 ;

	try {
		cbRequired += cchAnsi ;
		cbRequired += 1 ;
		cbRequired *= sizeof (CHAR) ;
	} catch (::SafeIntException & e) {
		HttpTool::ThrowException (e) ;
	}

	PSTR		szBuff = (PSTR) ::malloc (cbRequired.Value ()) ;
	if ( szBuff == NULL )
		HttpTool::ThrowException (HTTPCLIENT_ERR_OUT_OF_MEMORY) ;

	try {
		HttpEncoder::AnsiEncode (szBuff, szStr, m_nAnsiCodePage) ;
	} catch (Exception &) {
		SAFEFREE (szBuff) ;
		throw ;
	}

	bNeedToFree = TRUE ;
	return szBuff ;
}

template <typename HttpTool, typename HttpEncoder>
DWORD CHttpClientT<HttpTool, HttpEncoder>::_UrlEncodeLenA (PCSZ szStr)
	throw (Exception &)
{
	return HttpEncoder::UrlEncodeLenA (szStr, m_bUseUtf8, m_nAnsiCodePage) ;
}

template <typename HttpTool, typename HttpEncoder>
PCSTR CHttpClientT<HttpTool, HttpEncoder>::_UrlEncodeA (PSTR szBuff, PCSZ szStr)
	throw (Exception &)
{
	return HttpEncoder::UrlEncodeA (szBuff, szStr, m_bUseUtf8, m_nAnsiCodePage) ;
}

template <typename HttpTool, typename HttpEncoder>
PCSTR CHttpClientT<HttpTool, HttpEncoder>::_UrlEncodeA (PCSZ szStr)
	throw (Exception &)
{
	DWORD				cchEncoded = _UrlEncodeLenA (szStr) ;
	::SafeInt<size_t>	cbRequired = 0 ;
	try {
		cbRequired += cchEncoded ;
		cbRequired += 1 ;
		cbRequired *= sizeof (CHAR) ;
	} catch (::SafeIntException & e) {
		HttpTool::ThrowException (e) ;
	}

	PSTR		szBuff = (PSTR) ::malloc (cbRequired.Value ()) ;
	if ( szBuff == NULL )
		HttpTool::ThrowException (HTTPCLIENT_ERR_OUT_OF_MEMORY) ;

	try {
		_UrlEncodeA (szBuff, szStr) ;
	} catch (Exception &) {
		SAFEFREE (szBuff) ;
		throw ;
	}
	return szBuff ;
}

template <typename HttpTool, typename HttpEncoder>
DWORD CHttpClientT<HttpTool, HttpEncoder>::_UrlEncodeLen (PCSZ szStr)
	throw (Exception &)
{
	return HttpEncoder::UrlEncodeLen (szStr, m_bUseUtf8, m_nAnsiCodePage) ;
}

template <typename HttpTool, typename HttpEncoder>
typename CHttpClientT<HttpTool, HttpEncoder>::PCSZ
CHttpClientT<HttpTool, HttpEncoder>::_UrlEncode (PSZ szBuff, PCSZ szStr)
	throw (Exception &)
{
	return HttpEncoder::UrlEncode (szBuff, szStr, m_bUseUtf8, m_nAnsiCodePage) ;
}

template <typename HttpTool, typename HttpEncoder>
typename CHttpClientT<HttpTool, HttpEncoder>::PCSZ
CHttpClientT<HttpTool, HttpEncoder>::_UrlEncode (PCSZ szStr)
	throw (Exception &)
{
	DWORD				cchEncoded = _UrlEncodeLen (szStr) ;
	::SafeInt<size_t>	cbRequired = 0 ;
	try {
		cbRequired += cchEncoded ;
		cbRequired += 1 ;
		cbRequired *= sizeof (CharType) ;
	} catch (::SafeIntException & e) {
		HttpTool::ThrowException (e) ;
	}

	PSZ			szBuff = (PSZ) ::malloc (cbRequired.Value ()) ;
	if ( szBuff == NULL )
		HttpTool::ThrowException (HTTPCLIENT_ERR_OUT_OF_MEMORY) ;

	try {
		_UrlEncode (szBuff, szStr) ;
	} catch (Exception &) {
		SAFEFREE (szBuff) ;
		throw ;
	}
	return szBuff ;
}

template <typename HttpTool, typename HttpEncoder>
DWORD CHttpClientT<HttpTool, HttpEncoder>::_Utf8EncodeLen (PCSZ szStr)
	throw (Exception &)
{
	return HttpEncoder::Utf8EncodeLen (szStr, m_nAnsiCodePage) ;
}

template <typename HttpTool, typename HttpEncoder>
PCSTR CHttpClientT<HttpTool, HttpEncoder>::_Utf8Encode (PSTR szBuff, PCSZ szStr)
	throw (Exception &)
{
	return HttpEncoder::Utf8Encode (szBuff, szStr, m_nAnsiCodePage) ;
}

template <typename HttpTool, typename HttpEncoder>
PCSTR CHttpClientT<HttpTool, HttpEncoder>::_Utf8Encode (PCSZ szStr)
	throw (Exception &)
{
	DWORD				cchEncoded = _Utf8EncodeLen (szStr) ;
	::SafeInt<size_t>	cbRequired = 0 ;
	try {
		cbRequired += cchEncoded ;
		cbRequired += 1 ;
		cbRequired *= sizeof (CHAR) ;
	} catch (::SafeIntException & e) {
		HttpTool::ThrowException (e) ;
	}

	PSTR		szBuff = (PSTR) ::malloc (cbRequired.Value ()) ;
	if ( szBuff == NULL )
		HttpTool::ThrowException (HTTPCLIENT_ERR_OUT_OF_MEMORY) ;

	try {
		_Utf8Encode (szBuff, szStr) ;
	} catch (Exception &) {
		SAFEFREE (szBuff) ;
		throw ;
	}
	return szBuff ;
}

/*!
 * This method allocates memory and copies the target string if the szTargetString and the szOldString
 * are not the same. If the szTargetString and the szOldString are the same, it returns szOldString.
 *
 * \param szTargetString	[in] A target string to compare.
 * \param szOldString		[in] An original string to compare.
 * \return					A copied target string or szOldString.
 * \throw					Throws a httpclientexception if an error occurs.
 */
template <typename HttpTool, typename HttpEncoder>
typename CHttpClientT<HttpTool, HttpEncoder>::PSZ
CHttpClientT<HttpTool, HttpEncoder>::_AllocNCopyIfNewString (PCSZ szTargetString, PSZ szOldString)
	throw (Exception &)
{
	if ( szTargetString == szOldString )
		return szOldString ;

	if ( szTargetString == NULL )
		return NULL ;

	if ( szOldString && !HttpTool::StringCmp (szTargetString, szOldString) )
		return szOldString ;

	::SafeInt<size_t>	cbRequired = 0 ;
	try {
		cbRequired = HttpTool::StringLen (szTargetString) ;
		cbRequired++ ;
		cbRequired *= sizeof (CharType) ;
	} catch (::SafeIntException & e) {
		HttpTool::ThrowException (e) ;
	}

	PSZ		szNewString = (PSZ) ::malloc (cbRequired.Value ()) ;
	if ( szNewString == NULL )
		HttpTool::ThrowException (HTTPCLIENT_ERR_OUT_OF_MEMORY) ;

	HttpTool::StringCopy (szNewString, szTargetString) ;
	return szNewString ;
}

/*!
 * This method sets the current StrictFileCheck property.
 * If the StrictFileCheck is TRUE, an exception will be thrown
 * when it fails to open a file which is going to be uploaded.
 * If FALSE, it does nothing. The default is FALSE.
 *
 * \param bStrict		[in] A boolean value which specifies StrictFileCheck.
 * \return				Previously set value.
 *
 * The following code snippet demonstrates the usage of this property.
 * \code
...
CHttpClient					objHttpReq ;

// CHttpClient will throw a httpclientexception if it fails to open a file.
objHttpReq.SetStrictFileCheck (TRUE) ;

try {
	// Adds HTTP parameters
	objHttpReq.AddParam (_T ("title"), _T ("The memory of K-NET")) ;
	objHttpReq.AddParam (_T ("photo"), _T ("C:\\myphoto\\k-net.jpg"), ParamFile) ;

	// Starts a new HTTP UPLOAD
	objHttpReq.RequestUpload (...) or objHttpReq.BeginUpload (...) ;
	...

} catch (httpclientexception & e) {
	// If it failed to open the file (C:\myphoto\k-net.jpg)
	if ( e.LastError () == HTTPCLIENT_ERR_OPENFILE_FAILED ) {
		...		// Place error handling codes here.
	}
	...
}
 * \endcode
 */
template <typename HttpTool, typename HttpEncoder>
BOOL CHttpClientT<HttpTool, HttpEncoder>::SetStrictFileCheck (BOOL bStrict)
	throw ()
{
	BOOL	bOldStrictFileCheck = m_bStrictFileCheck ;
	m_bStrictFileCheck = bStrict ;
	return bOldStrictFileCheck ;
}

/*!
 * This method returns the current StrictFileCheck property.
 * If the StrictFileCheck is TRUE, an exception will be thrown
 * when it fails to open a file which is going to be uploaded.
 * If FALSE, it does nothing. The default is FALSE.
 *
 * \return				StrictFileCheck property.
 */
template <typename HttpTool, typename HttpEncoder>
BOOL CHttpClientT<HttpTool, HttpEncoder>::GetStrictFileCheck (void) const
	throw ()
{
	return m_bStrictFileCheck ;
}

/*!
 * This method returns the current StrictFileCheck property.
 * If the StrictFileCheck is TRUE, an exception will be thrown
 * when it fails to open a file which is going to be uploaded.
 * If FALSE, it does nothing. The default is FALSE.
 *
 * \return				StrictFileCheck property.
 */
template <typename HttpTool, typename HttpEncoder>
BOOL CHttpClientT<HttpTool, HttpEncoder>::StrictFileCheck (void) const
	throw ()
{
	return GetStrictFileCheck () ;
}


/*!
 * This method sets the current UseUtf8 property.
 * If the UseUtf8 is TRUE, all request will be sent in UTF-8 encoding,
 * otherwise, all request will be sent in ANSI encoding.
 * If a web server uses UTF-8 encoding, you should set TRUE.
 * It is not allowed to call this method if the BeginPost or BeginUpload method is not finished.
 * The default is FALSE.
 * 
 * \param bUseUtf8		[in] A boolean value which specifies UseUtf8.
 * \return				previously set value.
 */
template <typename HttpTool, typename HttpEncoder>
BOOL CHttpClientT<HttpTool, HttpEncoder>::SetUseUtf8 (BOOL bUseUtf8)
	throw (Exception &)
{
	// It is not allowed to call this method if the POST context is active.
	HTTPCLIENT_ASSERT (!_PostContextIsActive ()
		, "CHttpClientT::SetUseUtf8: It is not allowed to call this method if the POST context is active.") ;

	BOOL	bOldUseUtf8 = m_bUseUtf8 ;
	m_bUseUtf8 = bUseUtf8 ;
	return bOldUseUtf8 ;
}

/*!
 * This method returns the current UseUtf8 property.
 * If the UseUtf8 is TRUE, all request will be sent in UTF-8 encoding,
 * otherwise, all request will be sent in ANSI encoding.
 * If a web server uses UTF-8 encoding, you should set TRUE.
 * The default is FALSE.
 *
 * \return				UseUtf8 property.
 */
template <typename HttpTool, typename HttpEncoder>
BOOL CHttpClientT<HttpTool, HttpEncoder>::GetUseUtf8 (void) const
	throw ()
{
	return m_bUseUtf8 ;
}

/*!
 * This method returns the current UseUtf8 property.
 * If the UseUtf8 is TRUE, all request will be sent in UTF-8 encoding,
 * otherwise, all request will be sent in ANSI encoding.
 * If a web server uses UTF-8 encoding, you should set TRUE.
 * The default is FALSE.
 *
 * \return				UseUtf8 property.
 */
template <typename HttpTool, typename HttpEncoder>
BOOL CHttpClientT<HttpTool, HttpEncoder>::UseUtf8 (void) const
	throw ()
{
	return GetUseUtf8 () ;
}

/*!
 * This method sets the current ANSI code page which is used for all non-unicode strings.
 * If a web server uses an ANSI character set, you should set an appropriate ANSI code page.
 * It is not allowed to call this method if the BeginPost or BeginUpload method is not finished.
 * For more infomation about the Code-Page Identifiers, see the MSDN documentation.
 * The default is CP_ACP (which is the system's ANSI code page).
 * 
 * \param nAnsiCodePage	[in] An ANSI code page to set.
 * \return				previously set value.
 *
 * \sa	::WideCharToMultiByte, ::MultiByteToWideChar
 */
template <typename HttpTool, typename HttpEncoder>
UINT CHttpClientT<HttpTool, HttpEncoder>::SetAnsiCodePage (UINT nAnsiCodePage)
	throw (Exception &)
{
	// It is not allowed to call this method if the POST context is active.
	HTTPCLIENT_ASSERT (!_PostContextIsActive ()
		, "CHttpClientT::SetAnsiCodePage: It is not allowed to call this method if the POST context is active.") ;

	// It is not allowed to set the unicode encodings
	HTTPCLIENT_ASSERT ((nAnsiCodePage != CP_UTF8) && (nAnsiCodePage != CP_UTF7)
		, "CHttpClientT::SetAnsiCodePage: CP_UTF8 and CP_UTF7 can not be used for the nAnsiCodePage parameter.") ;

	UINT	nOldAnsiCodePage = m_nAnsiCodePage ;
	m_nAnsiCodePage = nAnsiCodePage ;
	return nOldAnsiCodePage ;
}

/*!
 * This method returns the current ANSI code page which is used for all non-unicode strings.
 * If a web server uses an ANSI character set, you should set an appropriate ANSI code page.
 * For more infomation about the Code-Page Identifiers, see the MSDN documentation.
 * The default is CP_ACP (which is the system's ANSI code page).
 *
 * \return				The current ANSI code page.
 */
template <typename HttpTool, typename HttpEncoder>
UINT CHttpClientT<HttpTool, HttpEncoder>::GetAnsiCodePage (void) const
	throw ()
{
	return m_nAnsiCodePage ;
}

/*!
 * This method returns the current ANSI code page which is used for all non-unicode strings.
 * If a web server uses an ANSI character set, you should set an appropriate ANSI code page.
 * For more infomation about the Code-Page Identifiers, see the MSDN documentation.
 * The default is CP_ACP (which is the system's ANSI code page).
 *
 * \return				The current ANSI code page.
 */
template <typename HttpTool, typename HttpEncoder>
UINT CHttpClientT<HttpTool, HttpEncoder>::AnsiCodePage (void) const
	throw ()
{
	return GetAnsiCodePage () ;
}

/*!
 * This method erases all HTTP headers which is sent to a web server.
 *
 * \return				FALSE if all header is aleady erased, otherwise TRUE.
 */
template <typename HttpTool, typename HttpEncoder>
BOOL CHttpClientT<HttpTool, HttpEncoder>::ClearHeader (void)
	throw ()
{
	return m_mapHeader.Clear () ;
}

/*!
 * This method erases a HTTP header at the given index specified by nIdx.
 *
 * \param nIdx			[in] A header index to remove.
 * \return				TRUE if the header is found and removed, otherwise FALSE.
 */
template <typename HttpTool, typename HttpEncoder>
BOOL CHttpClientT<HttpTool, HttpEncoder>::RemoveHeader (DWORD nIdx)
	throw ()
{
	return m_mapHeader.Remove (nIdx) ;
}

/*!
 * This method erases a HTTP header of which name is szName.
 * If the header has multiple values, you can specify a zero-based index for a specific value.
 *
 * \param szName		[in] A case-insensitive header name to remove. NULL is not allowed.
 * \param nIdx			[in] A zero-based value index if the header has multiple values. The default is zero.
 * \return				TRUE if the header is found and removed, otherwise FALSE.
 */
template <typename HttpTool, typename HttpEncoder>
BOOL CHttpClientT<HttpTool, HttpEncoder>::RemoveHeader (PCSZ szName, DWORD nIdx)
	throw ()
{
	return m_mapHeader.Remove (szName, nIdx) ;
}

/*!
 * This method erases all HTTP headers of which name is szName.
 *
 * \param szName		[in] A case-insensitive header name to remove. NULL is not allowed.
 * \return				TRUE if the header is found and removed, otherwise FALSE.
 */
template <typename HttpTool, typename HttpEncoder>
BOOL CHttpClientT<HttpTool, HttpEncoder>::RemoveAllHeader (PCSZ szName)
	throw ()
{
	return m_mapHeader.RemoveAll (szName) ;
}

/*!
 * This method adds a new HTTP header.
 * The header's name and the header's value can not be NULL or an empty string.
 *
 * \param szName		[in] A case-insensitive header name. NULL or an empty string is not allowed.
 * \param szValue		[in] A header value. NULL or an empty string is not allowed.
 * \throw				Throws a httpclientexception if an error occurs.
 */
template <typename HttpTool, typename HttpEncoder>
void CHttpClientT<HttpTool, HttpEncoder>::AddHeader (PCSZ szName, PCSZ szValue)
	throw (Exception &)
{
	HTTPCLIENT_ASSERT (szName != NULL, "CHttpClientT::AddHeader: szName can not be NULL.") ;
	HTTPCLIENT_ASSERT (szName[0] != '\0', "CHttpClientT::AddHeader: szName can not be an empty string.") ;
	HTTPCLIENT_ASSERT (szValue != NULL, "CHttpClientT::AddHeader: szValue can not be NULL.") ;
	HTTPCLIENT_ASSERT (szValue[0] != '\0', "CHttpClientT::AddHeader: szValue can not be an empty string.") ;
	m_mapHeader.Add (szName, szValue) ;
}

/*!
 * This method modifies the HTTP header value of which name is szName.
 * If the header has multiple values, you can specify a zero-based index for a specific value.
 * If the header is not found and the nIdx is zero, it will add a new header.
 * The header's name and the header's value can not be NULL or an empty string.
 *
 * \param szName		[in] A case-insensitive header name. NULL or an empty string is not allowed.
 * \param szValue		[in] A header value. NULL or an empty string is not allowed.
 * \param nIdx			[in] A zero-based value index if the header has multiple values. The default is zero.
 * \throw				Throws a httpclientexception if an error occurs.
 */
template <typename HttpTool, typename HttpEncoder>
void CHttpClientT<HttpTool, HttpEncoder>::SetHeader (PCSZ szName, PCSZ szValue, DWORD nIdx)
	throw (Exception &)
{
	HTTPCLIENT_ASSERT (szName != NULL, "CHttpClientT::SetHeader: szName can not be NULL.") ;
	HTTPCLIENT_ASSERT (szName[0] != '\0', "CHttpClientT::SetHeader: szName can not be an empty string.") ;
	HTTPCLIENT_ASSERT (szValue != NULL, "CHttpClientT::SetHeader: szValue can not be NULL.") ;
	HTTPCLIENT_ASSERT (szValue[0] != '\0', "CHttpClientT::SetHeader: szValue can not be an empty string.") ;
	m_mapHeader.Set (szName, szValue, 0, nIdx) ;
}

/*!
 * This method returns a HTTP header name at the given index specified by nIdx.
 * If the index is out of range, it will return NULL. Otherwise it always returns a null-terminated string.
 * The returned string is owned by the CHttpClientT. So you can not free it.
 *
 * \param nIdx			[in] A zero-based header index.
 * \return				A header name.
 */
template <typename HttpTool, typename HttpEncoder>
typename CHttpClientT<HttpTool, HttpEncoder>::PCSZ
CHttpClientT<HttpTool, HttpEncoder>::GetHeaderName (DWORD nIdx)
	throw ()
{
	return m_mapHeader.GetKey (nIdx) ;
}

/*!
 * This method returns a HTTP header at the given index specified by nIdx.
 * If the index is out of range, it will return NULL. Otherwise it always returns a null-terminated string.
 * The returned string is owned by the CHttpClient. So you can not free it.
 *
 * \param nIdx			[in] A zero-based header index.
 * \return				A header.
 */
template <typename HttpTool, typename HttpEncoder>
typename CHttpClientT<HttpTool, HttpEncoder>::PCSZ
CHttpClientT<HttpTool, HttpEncoder>::GetHeader (DWORD nIdx)
	throw ()
{
	return m_mapHeader.GetValue (nIdx) ;
}

/*!
 * This method returns a HTTP header of which name is szName.
 * If the header has multiple values, you can specify a zero-based index for a specific value.
 * If the header is not found, it will return NULL. Otherwise it always returns a null-terminated string.
 * The returned string is owned by the CHttpClient. So you can not free it.
 *
 * \param szName		[in] A case-insensitive header name to find. NULL is not allowed.
 * \param nIdx			[in] A zero-based value index if the header has multiple values. The default is zero.
 * \return				A header.
 */
template <typename HttpTool, typename HttpEncoder>
typename CHttpClientT<HttpTool, HttpEncoder>::PCSZ
CHttpClientT<HttpTool, HttpEncoder>::GetHeader (PCSZ szName, DWORD nIdx)
	throw ()
{
	return m_mapHeader.GetValue (szName, nIdx) ;
}

/*!
 * This method returns whether the HTTP header of which name is szName exists.
 * If the header has multiple values, you can specify a zero-based index for a specific value.
 *
 * \param szName		[in] A case-insensitive header name to find. NULL is not allowed.
 * \param nIdx			[in] A zero-based value index if the header has multiple values. The default is zero.
 * \return				TRUE if the header is found, otherwise FALSE.
 */
template <typename HttpTool, typename HttpEncoder>
BOOL CHttpClientT<HttpTool, HttpEncoder>::HeaderExists (PCSZ szName, DWORD nIdx)
	throw ()
{
	return m_mapHeader.Exists (szName, nIdx) ;
}

/*!
 * This method returns the number of HTTP headers of which name is szName.
 * If the szName is NULL, it will return the number of all headers.
 *
 * \param szName		[in] A case-insensitive header name to find.
 * \return				The number of headers.
 */
template <typename HttpTool, typename HttpEncoder>
DWORD CHttpClientT<HttpTool, HttpEncoder>::GetHeaderCount (PCSZ szName)
	throw ()
{
	return m_mapHeader.Count (szName) ;
}

/*!
 * This method erases all HTTP parameters.
 * It is not allowed to call this method if the BeginPost or BeginUpload method is not finished.
 *
 * \return				FALSE if all parameter is aleady erased, otherwise TRUE.
 */
template <typename HttpTool, typename HttpEncoder>
BOOL CHttpClientT<HttpTool, HttpEncoder>::ClearParam (void)
	throw (Exception &)
{
	// It is not allowed to call this method if the POST context is active.
	HTTPCLIENT_ASSERT (!_PostContextIsActive ()
		, "CHttpClientT::ClearParam: It is not allowed to call this method if the POST context is active.") ;
	return m_mapParam.Clear () ;
}

/*!
 * This method erases a HTTP parameter at the given index specified by nIdx.
 * It is not allowed to call this method if the BeginPost or BeginUpload method is not finished.
 *
 * \param nIdx			[in] A zero-based parameter index to remove.
 * \return				TRUE if the parameter is found and removed, otherwise FALSE.
 */
template <typename HttpTool, typename HttpEncoder>
BOOL CHttpClientT<HttpTool, HttpEncoder>::RemoveParam (DWORD nIdx)
	throw (Exception &)
{
	// It is not allowed to call this method if the POST context is active.
	HTTPCLIENT_ASSERT (!_PostContextIsActive ()
		, "CHttpClientT::RemoveParam: It is not allowed to call this method if the POST context is active.") ;
	return m_mapParam.Remove (nIdx) ;
}

/*!
 * This method erases a HTTP parameter of which name is szName.
 * If the parameter has multiple values, you can specify a zero-based index for a specific value.
 * It is not allowed to call this method if the BeginPost or BeginUpload method is not finished.
 *
 * \param szName		[in] A case-insensitive parameter name to remove. NULL is not allowed.
 * \param nIdx			[in] A zero-based value index if the parameter has multiple values. The default is zero.
 * \return				TRUE if the parameter is found and removed, otherwise FALSE.
 */
template <typename HttpTool, typename HttpEncoder>
BOOL CHttpClientT<HttpTool, HttpEncoder>::RemoveParam (PCSZ szName, DWORD nIdx)
	throw (Exception &)
{
	// It is not allowed to call this method if the POST context is active.
	HTTPCLIENT_ASSERT (!_PostContextIsActive ()
		, "CHttpClientT::RemoveParam: It is not allowed to call this method if the POST context is active.") ;
	return m_mapParam.Remove (szName, nIdx) ;
}

/*!
 * This method erases all HTTP parameters of which name is szName.
 * It is not allowed to call this method if the BeginPost or BeginUpload method is not finished.
 *
 * \param szName		[in] A case-insensitive parameter name to remove. NULL is not allowed.
 * \return				TRUE if the parameter is found and removed, otherwise FALSE.
 */
template <typename HttpTool, typename HttpEncoder>
BOOL CHttpClientT<HttpTool, HttpEncoder>::RemoveAllParam (PCSZ szName)
	throw (Exception &)
{
	// It is not allowed to call this method if the POST context is active.
	HTTPCLIENT_ASSERT (!_PostContextIsActive ()
		, "CHttpClientT::RemoveAllParam: It is not allowed to call this method if the POST context is active.") ;
	return m_mapParam.RemoveAll (szName) ;
}

/*!
 * This method adds a new HTTP parameter. The dwParamAttr specifies parameter attributes.
 * For more information about parameter attributes, see the ParamAttr enumerator.
 * It is not allowed to call this method if the BeginPost or BeginUpload method is not finished.
 *
 * \param szName		[in] A case-insensitive parameter name. NULL is not allowed.
 * \param szValue		[in] A parameter value. The default is NULL.
 * \param dwParamAttr	[in] A parameter attribute.
 *							 This is a bitwise Ored value of the ParamAttr enumerator.
 *							 The default is ParamNormal.
 * \throw				Throws a httpclientexception if an error occurs.
 *
 * \sa	ParamAttr
 */
template <typename HttpTool, typename HttpEncoder>
void CHttpClientT<HttpTool, HttpEncoder>::AddParam (PCSZ szName, PCSZ szValue, DWORD dwParamAttr)
	throw (Exception &)
{
	// It is not allowed to call this method if the POST context is active.
	HTTPCLIENT_ASSERT (!_PostContextIsActive ()
		, "CHttpClientT::AddParam: It is not allowed to call this method if the POST context is active.") ;
	m_mapParam.Add (szName, szValue, dwParamAttr) ;
}

/*!
 * This method modifies the HTTP parameter value of which name is szName.
 * If the parameter has multiple values, you can specify a zero-based index for a specific value.
 * If the parameter is not found and the nIdx is zero, it will add a new parameter.
 * The dwParamAttr specifies parameter attributes.
 * For more information about parameter attributes, see the ParamAttr enumerator.
 * It is not allowed to call this method if the BeginPost or BeginUpload method is not finished.
 *
 * \param szName		[in] A case-insensitive parameter name. NULL is not allowed.
 * \param szValue		[in] A parameter value. The default is NULL.
 * \param dwParamAttr	[in] A parameter attribute.
 *							 This is a bitwise Ored value of the ParamAttr enumerator.
 *							 The default is ParamNormal.
 * \param nIdx			[in] A zero-based value index if the parameter has multiple values.
 *							 The default is zero.
 * \throw				Throws a httpclientexception if an error occurs.
 *
 * \sa	ParamAttr
 */
template <typename HttpTool, typename HttpEncoder>
void CHttpClientT<HttpTool, HttpEncoder>::SetParam (PCSZ szName, PCSZ szValue, DWORD dwParamAttr, DWORD nIdx)
	throw (Exception &)
{
	// It is not allowed to call this method if the POST context is active.
	HTTPCLIENT_ASSERT (!_PostContextIsActive ()
		, "CHttpClientT::SetParam: It is not allowed to call this method if the POST context is active.") ;
	m_mapParam.Set (szName, szValue, dwParamAttr, nIdx) ;
}

/*!
 * This method returns a HTTP parameter name at the given index specified by nIdx.
 * If the index is out of range, it will return NULL. Otherwise it always returns a null-terminated string.
 * The returned string is owned by the CHttpClient. So you can not free it.
 *
 * \param nIdx			[in] A zero-based parameter index.
 * \return				A parameter name.
 */
template <typename HttpTool, typename HttpEncoder>
typename CHttpClientT<HttpTool, HttpEncoder>::PCSZ
CHttpClientT<HttpTool, HttpEncoder>::GetParamName (DWORD nIdx)
	throw ()
{
	return m_mapParam.GetKey (nIdx) ;
}

/*!
 * This method returns a HTTP parameter at the given index specified by nIdx.
 * If the index is out of range, it will return NULL. Otherwise it always returns a null-terminated string.
 * The returned string is owned by the CHttpClient. So you can not free it.
 *
 * \param nIdx			[in] A zero-based parameter index.
 * \return				A parameter.
 */
template <typename HttpTool, typename HttpEncoder>
typename CHttpClientT<HttpTool, HttpEncoder>::PCSZ
CHttpClientT<HttpTool, HttpEncoder>::GetParam (DWORD nIdx)
	throw ()
{
	return m_mapParam.GetValue (nIdx) ;
}

/*!
 * This method returns a HTTP parameter of which name is szName.
 * If the parameter has multiple values, you can specify a zero-based index for a specific value.
 * If the parameter is not found, it will return NULL. Otherwise it always returns a null-terminated string.
 * The returned string is owned by the CHttpClient. So you can not free it.
 *
 * \param szName		[in] A case-insensitive parameter name to find. NULL is not allowed.
 * \param nIdx			[in] A zero-based value index if the parameter has multiple values.
 *							 The default is zero.
 * \return				A parameter.
 */
template <typename HttpTool, typename HttpEncoder>
typename CHttpClientT<HttpTool, HttpEncoder>::PCSZ
CHttpClientT<HttpTool, HttpEncoder>::GetParam (PCSZ szName, DWORD nIdx)
	throw ()
{
	return m_mapParam.GetValue (szName, nIdx) ;
}

/*!
 * This method returns a HTTP parameter attribute at the given index specified by nIdx.
 * If the index is out of range or the attribute is 0, it will return 0.
 *
 * \param nIdx			[in] A zero-based parameter index.
 * \return				A parameter attribute.
 *
 * \sa	ParamAttr
 */
template <typename HttpTool, typename HttpEncoder>
DWORD CHttpClientT<HttpTool, HttpEncoder>::GetParamAttr (DWORD nIdx)
	throw ()
{
	return m_mapParam.GetFlag (nIdx) ;
}

/*!
 * This method returns an attribute of the HTTP parameter of which name is szName.
 * If the parameter has multiple values, you can specify a zero-based index for a specific value.
 * If the parameter is not found or the attribute is 0, it will return 0.
 *
 * \param szName		[in] A case-insensitive parameter name to find. NULL is not allowed.
 * \param nIdx			[in] A zero-based value index if the parameter has multiple values.
 *							 The default is zero.
 * \return				A parameter attribute.
 *
 * \sa	ParamAttr
 */
template <typename HttpTool, typename HttpEncoder>
DWORD CHttpClientT<HttpTool, HttpEncoder>::GetParamAttr (PCSZ szName, DWORD nIdx)
	throw ()
{
	return m_mapParam.GetFlag (szName, nIdx) ;
}

/*!
 * This method returns whether the HTTP parameter of which name is szName exists.
 * If the parameter has multiple values, you can specify a zero-based index for a specific value.
 *
 * \param szName		[in] A case-insensitive parameter name to find. NULL is not allowed.
 * \param nIdx			[in] A zero-based value index if the parameter has multiple values.
 *							 The default is zero.
 * \return				TRUE if the parameter is found, otherwise FALSE.
 */
template <typename HttpTool, typename HttpEncoder>
BOOL CHttpClientT<HttpTool, HttpEncoder>::ParamExists (PCSZ szName, DWORD nIdx)
	throw ()
{
	return m_mapParam.Exists (szName, nIdx) ;
}

/*!
 * This method returns the number of HTTP parameters of which name is szName.
 * If the szName is NULL, it will return the number of all parameters.
 *
 * \param szName		[in] A case-insensitive parameter name to find.
 *							 The default is NULL.
 * \return				The number of parameters.
 */
template <typename HttpTool, typename HttpEncoder>
DWORD CHttpClientT<HttpTool, HttpEncoder>::GetParamCount (PCSZ szName)
	throw ()
{
	return m_mapParam.Count (szName) ;
}



/*!
 * This method sets parameters for the ::InternetOpen function which is called internally before sending a request.
 * All parameters passed to this method is forwarded to the ::InternetOpen function.
 * For more infomation about the ::InternetOpen function, see the MSDN documentation.
 *
 * The following code snippet demonstrates the usage of this method.
 * \code
...
CHttpClient					objHttpReq ;

try {
	// Changes the HTTP user agent.
	objHttpReq.SetInternet (_T ("My Custom User Agent")) ;
	...

} catch (httpclientexception & e) {
	...		// Place error handling codes here.
}
 * \endcode
 *
 * \param szUserAgent		[in] A User Agent which corresponds to the lpszAgent parameter of the ::InternetOpen function.
 * \param dwAccessType		[in] A type of access which corresponds to the dwAccessType parameter of the ::InternetOpen function.
 * \param szProxyName		[in] A name of a proxy server which corresponds to the lpszProxyName parameter of the ::InternetOpen function.
 * \param szProxyBypass		[in] A proxy bypass list which corresponds to the lpszProxyBypass parameter of the ::InternetOpen function.
 * \param dwFlags			[in] Options which corresponds to the dwFlags parameter of the ::InternetOpen function.
 * \throw					Throws a httpclientexception if an error occurs.
 *
 * \sa		::InternetOpen
 */
template <typename HttpTool, typename HttpEncoder>
void CHttpClientT<HttpTool, HttpEncoder>::SetInternet (PCSZ szUserAgent, DWORD dwAccessType
													, PCSZ szProxyName, PCSZ szProxyBypass
													, DWORD dwFlags)
	throw (Exception &)
{
	PSZ			szNewUserAgent = NULL ;
	PSZ			szNewProxyName = NULL ;
	PSZ			szNewProxyBypass = NULL ;

	try {
		szNewUserAgent = _AllocNCopyIfNewString (szUserAgent, m_szInternetUserAgent) ;
		szNewProxyName = _AllocNCopyIfNewString (szProxyName, m_szInternetProxyName) ;
		szNewProxyBypass = _AllocNCopyIfNewString (szProxyBypass, m_szInternetProxyBypass) ;
	} catch (Exception &) {
		if ( m_szInternetUserAgent != szNewUserAgent )		SAFEFREE (szNewUserAgent) ;
		if ( m_szInternetProxyName != szNewProxyName )		SAFEFREE (szNewProxyName) ;
		if ( m_szInternetProxyBypass != szNewProxyBypass )	SAFEFREE (szNewProxyBypass) ;
		throw ;
	}

	if ( m_szInternetUserAgent != szNewUserAgent ) {
		SAFEFREE (m_szInternetUserAgent) ;
		m_szInternetUserAgent = szNewUserAgent ;
	}

	if ( m_szInternetProxyName != szNewProxyName ) {
		SAFEFREE (m_szInternetProxyName) ;
		m_szInternetProxyName = szNewProxyName ;
	}

	if ( m_szInternetProxyBypass != szNewProxyBypass ) {
		SAFEFREE (m_szInternetProxyBypass) ;
		m_szInternetProxyBypass = szNewProxyBypass ;
	}

	m_dwInternetAccessType = dwAccessType ;
	m_dwInternetFlags = dwFlags ;
}

/*!
 * This method sets the lpszAgent parameter for the ::InternetOpen function which is called internally before sending a request.
 * The parameter passed to this method is forwarded to the ::InternetOpen function.
 * For more infomation about the ::InternetOpen function, see the MSDN documentation.
 *
 * \param szUserAgent		[in] A User Agent which corresponds to the lpszAgent parameter of the ::InternetOpen function.
 *								 If this is NULL, The default is used.
 * \throw					Throws a httpclientexception if an error occurs.
 *
 * \sa		::InternetOpen, SetInternet
 */
template <typename HttpTool, typename HttpEncoder>
void CHttpClientT<HttpTool, HttpEncoder>::SetInternetUserAgent (PCSZ szUserAgent)
	throw (Exception &)
{
	PSZ			szNewUserAgent = NULL ;

	try {
		szNewUserAgent = _AllocNCopyIfNewString (szUserAgent, m_szInternetUserAgent) ;
	} catch (Exception &) {
		if ( m_szInternetUserAgent != szNewUserAgent )		SAFEFREE (szNewUserAgent) ;
		throw ;
	}

	if ( m_szInternetUserAgent != szNewUserAgent ) {
		SAFEFREE (m_szInternetUserAgent) ;
		m_szInternetUserAgent = szNewUserAgent ;
	}
}

/*!
 * This method returns the lpszAgent parameter for the ::InternetOpen function which is called internally before sending a request.
 * For more infomation about the ::InternetOpen function, see the MSDN documentation.
 *
 * \return					A User Agent which corresponds to the lpszAgent parameter of the ::InternetOpen function.
 *
 * \sa		::InternetOpen, SetInternet
 */
template <typename HttpTool, typename HttpEncoder>
typename CHttpClientT<HttpTool, HttpEncoder>::PCSZ
CHttpClientT<HttpTool, HttpEncoder>::GetInternetUserAgent (void)
	throw ()
{
	return m_szInternetUserAgent ? m_szInternetUserAgent : HttpTool::szDefUsrAgent ;
}


/*!
 * This method sets the dwAccessType parameter for the ::InternetOpen function which is called internally before sending a request.
 * The parameter passed to this method is forwarded to the ::InternetOpen function.
 * For more infomation about the ::InternetOpen function, see the MSDN documentation.
 *
 * \param dwAccessType		[in] A type of access which corresponds to the dwAccessType parameter of the ::InternetOpen function.
 *
 * \sa		::InternetOpen, SetInternet
 */
template <typename HttpTool, typename HttpEncoder>
void CHttpClientT<HttpTool, HttpEncoder>::SetInternetAccessType (DWORD dwAccessType)
	throw ()
{
	m_dwInternetAccessType = dwAccessType ;
}

/*!
 * This method returns the dwAccessType parameter for the ::InternetOpen function which is called internally before sending a request.
 * For more infomation about the ::InternetOpen function, see the MSDN documentation.
 *
 * \return					A type of access which corresponds to the dwAccessType parameter of the ::InternetOpen function.
 *
 * \sa		::InternetOpen, SetInternet
 */
template <typename HttpTool, typename HttpEncoder>
DWORD CHttpClientT<HttpTool, HttpEncoder>::GetInternetAccessType (void)
	throw ()
{
	return m_dwInternetAccessType ;
}

/*!
 * This method sets the lpszProxyName parameter for the ::InternetOpen function which is called internally before sending a request.
 * The parameter passed to this method is forwarded to the ::InternetOpen function.
 * For more infomation about the ::InternetOpen function, see the MSDN documentation.
 *
 * \param szProxyName		[in] A proxy server name which corresponds to the lpszProxyName parameter of the ::InternetOpen function.
 * \throw					Throws a httpclientexception if an error occurs.
 *
 * \sa		::InternetOpen, SetInternet
 */
template <typename HttpTool, typename HttpEncoder>
void CHttpClientT<HttpTool, HttpEncoder>::SetInternetProxyName (PCSZ szProxyName)
	throw (Exception &)
{
	PSZ			szNewProxyName = NULL ;

	try {
		szNewProxyName = _AllocNCopyIfNewString (szProxyName, m_szInternetProxyName) ;
	} catch (Exception &) {
		if ( m_szInternetProxyName != szNewProxyName )		SAFEFREE (szNewProxyName) ;
		throw ;
	}

	if ( m_szInternetProxyName != szNewProxyName ) {
		SAFEFREE (m_szInternetProxyName) ;
		m_szInternetProxyName = szNewProxyName ;
	}
}

/*!
 * This method returns the lpszProxyName parameter for the ::InternetOpen function which is called internally before sending a request.
 * For more infomation about the ::InternetOpen function, see the MSDN documentation.
 *
 * \return					A proxy server name which corresponds to the lpszProxyName parameter of the ::InternetOpen function.
 *
 * \sa		::InternetOpen, SetInternet
 */
template <typename HttpTool, typename HttpEncoder>
typename CHttpClientT<HttpTool, HttpEncoder>::PCSZ
CHttpClientT<HttpTool, HttpEncoder>::GetInternetProxyName (void)
	throw ()
{
	return m_szInternetProxyName ;
}


/*!
 * This method sets the lpszProxyBypass parameter for the ::InternetOpen function which is called internally before sending a request.
 * The parameter passed to this method is forwarded to the ::InternetOpen function.
 * For more infomation about the ::InternetOpen function, see the MSDN documentation.
 *
 * \param szProxyBypass		[in] A proxy bypass list which corresponds to the lpszProxyBypass parameter of the ::InternetOpen function.
 * \throw					Throws a httpclientexception if an error occurs.
 *
 * \sa		::InternetOpen, SetInternet
 */
template <typename HttpTool, typename HttpEncoder>
void CHttpClientT<HttpTool, HttpEncoder>::SetInternetProxyBypass (PCSZ szProxyBypass)
	throw (Exception &)
{
	PSZ			szNewProxyBypass = NULL ;

	try {
		szNewProxyBypass = _AllocNCopyIfNewString (szProxyBypass, m_szInternetProxyBypass) ;
	} catch (Exception &) {
		if ( m_szInternetProxyBypass != szNewProxyBypass )	SAFEFREE (szNewProxyBypass) ;
		throw ;
	}

	if ( m_szInternetProxyBypass != szNewProxyBypass ) {
		SAFEFREE (m_szInternetProxyBypass) ;
		m_szInternetProxyBypass = szNewProxyBypass ;
	}
}

/*!
 * This method returns the lpszProxyBypass parameter for the ::InternetOpen function which is called internally before sending a request.
 * For more infomation about the ::InternetOpen function, see the MSDN documentation.
 *
 * \return					A proxy bypass list which corresponds to the lpszProxyBypass parameter of the ::InternetOpen function.
 *
 * \sa		::InternetOpen, SetInternet
 */
template <typename HttpTool, typename HttpEncoder>
typename CHttpClientT<HttpTool, HttpEncoder>::PCSZ
CHttpClientT<HttpTool, HttpEncoder>::GetInternetProxyBypass (void)
	throw ()
{
	return m_szInternetProxyBypass ;
}

/*!
 * This method sets the dwFlags parameter for the ::InternetOpen function which is called internally before sending a request.
 * The parameter passed to this method is forwarded to the ::InternetOpen function.
 * For more infomation about the ::InternetOpen function, see the MSDN documentation.
 *
 * \param dwFlags			[in] Options which corresponds to the dwFlags parameter of the ::InternetOpen function.
 *
 * \sa		::InternetOpen, SetInternet
 */
template <typename HttpTool, typename HttpEncoder>
void CHttpClientT<HttpTool, HttpEncoder>::SetInternetFlags (DWORD dwFlags)
	throw ()
{
	m_dwInternetFlags = dwFlags ;
}

/*!
 * This method returns the dwFlags parameter for the ::InternetOpen function which is called internally before sending a request.
 * For more infomation about the ::InternetOpen function, see the MSDN documentation.
 *
 * \return					Options which corresponds to the dwFlags parameter of the ::InternetOpen function.
 *
 * \sa		::InternetOpen, SetInternet
 */
template <typename HttpTool, typename HttpEncoder>
DWORD CHttpClientT<HttpTool, HttpEncoder>::GetInternetFlags (void)
	throw ()
{
	return m_dwInternetFlags ;
}


/*!
 * This method returns the number of characters required to make a URL for a HTTP 
 * GET request. If you want to pass some parameters to a web server using HTTP GET,
 * you have to append parameters to the requested URL. This method calculates 
 * the length of the URL for that situation. If the CHttpClient does not contain any
 * HTTP parameters, it returns the length of the szUrl. Otherwise it returns
 * the calculated length. It does not check whether the szUrl is valid.
 * If the szUrl is NULL, it returns the length of the appended parameters.
 * (Not including the beginning '?' character)
 * The returned value does not include the terminating NULL character.
 *
 * \param szUrl		[in] An URL which is used to calculate the length of the GET URL.
 * \return			The number of characters required. (Not including the terminating NULL character)
 * \throw			Throws a httpclientexception if an error occurs.
 */
template <typename HttpTool, typename HttpEncoder>
DWORD CHttpClientT<HttpTool, HttpEncoder>::MakeGetUrlLen (PCSZ szUrl)
	throw (Exception &)
{
	// Calculates the number of required characters
	::SafeInt<DWORD>		cchUrlForGet = 0 ;

	try {
		cchUrlForGet = szUrl ? HttpTool::StringLen (szUrl) : 0 ;

		if ( m_mapParam.Empty () )
			return cchUrlForGet.Value () ;

		for (ConstMapIter iter = m_mapParam.Begin (); iter != m_mapParam.End (); ++iter) {
			// If the parameter's name is a URL-encoded string
			if ( (iter->second).dwFlag & ParamEncodedName )
				cchUrlForGet += (iter->first ? HttpTool::StringLen (iter->first) : 0) ;
			else
				cchUrlForGet += _UrlEncodeLen (iter->first) ;

			// If the parameter's value is a URL-encoded string
			if ( (iter->second).dwFlag & ParamEncodedValue )
				cchUrlForGet += ((iter->second).szValue ? HttpTool::StringLen ((iter->second).szValue) : 0) ;
			else
				cchUrlForGet += _UrlEncodeLen ((iter->second).szValue) ;

			cchUrlForGet += 2 ;		// for ('?' or '&') and '='
		}

		// If the szUrl is NULL, it does not insert '?' or '&' character
		// which divides the GET URL into the szUrl and HTTP parameters.
		if ( szUrl == NULL && m_mapParam.Count () >= 0 )
			cchUrlForGet-- ;

		// If the URL does not contain the path (ex. 'http://www.kw.ac.kr')
		// we have to append '/' to the URL.
		// It does not care whether the server address exists.
		// (It does not check the URL)
		if ( szUrl ) {
			CHttpUrlAnalyzer	objAnalyzer (szUrl, m_nAnsiCodePage) ;

			if ( objAnalyzer.PathLen () == 0 )
				cchUrlForGet++ ;
		}
	} catch (::SafeIntException & e) {
		HttpTool::ThrowException (e) ;
	}

	return cchUrlForGet.Value () ;
}

/*!
 * This method returns a URL for a HTTP GET request.
 * If you want to pass some parameters to a web server using HTTP GET,
 * you have to append parameters to the requested URL. This method creates a URL for that situation. 
 * If the CHttpClient does not contain any HTTP parameters, it returns the szUrl.
 * It does not check whether the szUrl is valid.
 * If the szUrl is NULL, it copies only the appended parameters.
 * (Not including the beginning '?' character)
 *
 * \param szBuff	[out] A buffer to save the GET URL. The buffer can not be NULL.
 * \param szUrl		[in] An URL which is used to make a URL for a HTTP GET request.
 * \return			A generated URL.
 * \throw			Throws a httpclientexception if an error occurs.
 */
template <typename HttpTool, typename HttpEncoder>
typename CHttpClientT<HttpTool, HttpEncoder>::PSZ 
CHttpClientT<HttpTool, HttpEncoder>::MakeGetUrl (PSZ szBuff, PCSZ szUrl)
	throw (Exception &)
{
	HTTPCLIENT_ASSERT (szBuff != NULL, "CHttpClientT::MakeGetUrl: szBuff can not be NULL.") ;

	if ( m_mapParam.Empty () ) {
		if ( szUrl == NULL ) {
			szBuff[0] = '\0' ;
			return szBuff ;
		}

		HttpTool::StringCopy (szBuff, szUrl) ;
		return szBuff ;
	}

	PSZ					pchBuff = szBuff ;
	DWORD				i ;
	CHttpUrlAnalyzer	objAnalyzer (szUrl, m_nAnsiCodePage) ;

	if ( szUrl ) {
		for (i = 0; i < objAnalyzer.PathIdx (); *(pchBuff++) = szUrl[i], i++) ;

		// If the path does not exist, it is needed to append '/' to the server address.
		if ( objAnalyzer.PathLen () == 0 )
			*(pchBuff++) = '/' ;

		// Appends the path and the search string.
		for (i = objAnalyzer.PathIdx (); i < objAnalyzer.BookmarkIdx (); *(pchBuff++) = szUrl[i], i++) ;

		// Copies '?' or '&'
		*(pchBuff++) = objAnalyzer.SearchLen () == 0 ? '?' : '&' ;
	}

	// Copies the HTTP parameters
	for (ConstMapIter iter = m_mapParam.Begin (); iter != m_mapParam.End (); ++iter) {
		if ( iter != m_mapParam.Begin () )
			*(pchBuff++) = '&' ;

		// If the parameter's name is a URL-encoded string
		if ( (iter->second).dwFlag & ParamEncodedName )
			HttpTool::StringCopy (pchBuff
				, iter->first ? iter->first : HttpTool::szEmptyString) ;
		else
			_UrlEncode (pchBuff, iter->first) ;

		for (; *pchBuff != '\0'; pchBuff++) ;

		*(pchBuff++) = '=' ;

		// If the parameter's value is a URL-encoded string
		if ( (iter->second).dwFlag & ParamEncodedValue )
			HttpTool::StringCopy (pchBuff
				, (iter->second).szValue ? (iter->second).szValue : HttpTool::szEmptyString) ;
		else
			_UrlEncode (pchBuff, (iter->second).szValue) ;

		for (; *pchBuff != '\0'; pchBuff++) ;
	}

	// Appends the bookmark
	for (i = objAnalyzer.BookmarkIdx (); i < objAnalyzer.BookmarkIdx () + objAnalyzer.BookmarkLen (); *(pchBuff++) = szUrl[i], i++) ;
	*pchBuff = '\0' ;

	return szBuff ;
}

/*!
 * This method returns a new HINTERNET handle which is returned by ::InternetOpen
 * using the settings saved in the CHttpClient.
 * The caller is responsible for closing the returned handle when it has finished with it.
 *
 * \return			A new HINTERNET handle.
 * \throw			Throws a httpclientexception if an error occurs.
 *
 * \sa	::InternetOpen
 */
template <typename HttpTool, typename HttpEncoder>
HINTERNET CHttpClientT<HttpTool, HttpEncoder>::OpenInternet (void)
	throw (Exception &)
{
	return HttpTool::OpenInternet (
		GetInternetUserAgent (), GetInternetAccessType (), GetInternetProxyName (),
		GetInternetProxyBypass (), GetInternetFlags ()) ;
}

/*!
 * This method returns a new HINTERNET handle which is returned by ::InternetConnect.
 * It only checks the protocol, server address and server port of the URL.
 * So you can use the following URLs.
 * <UL>
 *	<LI>http://www.k-net.or.kr:80/member.html?type=5 -> A full HTTP URL (or a full HTTPS URL)</LI>
 *	<LI>http://club.hooriza.com:80 -> A HTTP URL without path (or a HTTPS URL without path)</LI>
 *	<LI>wedding.makeself.net:80 -> Only a server location (http is assumed)</LI>
 *	<LI>www.kw.ac.kr/index.html -> A URL without protocol (http is assumed)</LI>
 *	<LI>ftp://knews.k-net.or.kr -> An URL with another protocol (http is assumed)</LI>
 * </UL>
 *
 * The caller is responsible for closing the returned handle when it has finished with it.
 *
 * \param hInternet		[in] A valid handle returned by ::InternetOpen.
 * \param szUrl			[in] An URL which is used to return a new handle.
 * \param szUsrName		[in] An user name which corresponds to the lpszUsername parameter of the ::InternetConnect function.
 * \param szUsrPwd		[in] An user password which corresponds to the lpszPassword parameter of the ::InternetConnect function.
 * \return				A new HINTERNET handle.
 * \throw				Throws a httpclientexception if an error occurs.
 *
 * \sa	::InternetConnect
 */
template <typename HttpTool, typename HttpEncoder>
HINTERNET CHttpClientT<HttpTool, HttpEncoder>::OpenConnection (HINTERNET hInternet, PCSZ szUrl, PCSZ szUsrName, PCSZ szUsrPwd)
	throw (Exception &)
{
	HTTPCLIENT_ASSERT (hInternet != NULL, "CHttpClientT::OpenConnection: hInternet can not be NULL.") ;

	INTERNET_PORT		nPort = INTERNET_DEFAULT_HTTP_PORT ;
	CHttpUrlAnalyzer	objAnalyzer (szUrl, m_nAnsiCodePage) ;

	// If the protocol is HTTPS, the default port is INTERNET_DEFAULT_HTTPS_PORT.
	if ( (objAnalyzer.ProtocolLen () == 5 /* ::strlen ("HTTPS") */)
			&& (0 == HttpTool::StringNICmp (
				szUrl + objAnalyzer.ProtocolIdx (), HttpTool::szHTTPS, objAnalyzer.ProtocolLen ())) )
			nPort = INTERNET_DEFAULT_HTTPS_PORT ;

	// Get the server port
	if ( objAnalyzer.PortLen () > 0 ) {
		// The port number can not be greater than 65535
		if ( objAnalyzer.PortLen () > 5 )
			HttpTool::ThrowException (HTTPCLIENT_ERR_INTERNET_PORT_NOT_VALID) ;

		CharType		szPort[6] ;
		HttpTool::StringNCopy (szPort, szUrl + objAnalyzer.PortIdx (), objAnalyzer.PortLen ()) ;
		szPort[objAnalyzer.PortLen ()] = '\0' ;

		unsigned long	ulPort ;
		PSZ				pEndPtr ;
		ulPort = HttpTool::StringToUL (szPort, &pEndPtr, 10) ;
		if ( *pEndPtr != NULL || ulPort > 65535 )
			HttpTool::ThrowException (HTTPCLIENT_ERR_INTERNET_PORT_NOT_VALID) ;
		nPort = static_cast<INTERNET_PORT> (ulPort) ;
	}

	// Get the server address
	if ( objAnalyzer.AddressLen () == 0 )
		HttpTool::ThrowException (HTTPCLIENT_ERR_INVALID_URL) ;

	::SafeInt<size_t>	cbAddress = 0 ;
	try {
		cbAddress = objAnalyzer.AddressLen () ;
		cbAddress += 1 ;
		cbAddress *= sizeof (CharType) ;
	} catch (::SafeIntException & e) {
		HttpTool::ThrowException (e) ;
	}

	PSZ				szServerAddr = (PSZ) ::malloc (cbAddress.Value ()) ;
	if ( szServerAddr == NULL )
		HttpTool::ThrowException (HTTPCLIENT_ERR_OUT_OF_MEMORY) ;

	HttpTool::StringNCopy (szServerAddr, szUrl + objAnalyzer.AddressIdx (), objAnalyzer.AddressLen ()) ;
	szServerAddr[objAnalyzer.AddressLen ()] = '\0' ;

	HINTERNET		hConnection = NULL ;
	try {
		hConnection = HttpTool::OpenConnection (hInternet, szServerAddr, nPort, szUsrName, szUsrPwd) ;
		// Applys proxy server account
		ApplyProxyAccount (hConnection) ;
	} catch (Exception &) {
		SAFEFREE (szServerAddr) ;
		HttpTool::CloseConnection (hConnection) ;
		throw ;
	}

	SAFEFREE (szServerAddr) ;
	return hConnection ;
}

/*!
 * This method sets a user name and a password which are used to access HTTP proxy server.
 * If the szUserName parameter is NULL, the proxy account saved in CHttpClient will be removed.
 * You can handle proxy authorization using this method, but there are some restrictions.
 * First, The HTTP POST is not safe if you are not an authorized user.
 * (It is possible that the proxy server closes the connection before the request is finished).
 * Second, Only the HTTP GET can be used to request proxy authorization.
 * (This restriction is caused by the WinInet API).
 * 
 * The following code snippet demonstrates the usage of this method.
 * \code
...
CHttpClient			objHttpReq ;
CHttpResponse *		pobjHttpRes = NULL ;

try {
	...		// Initialize the CHttpClient object

	// It is safe to use HTTP GET method.
    pobjHttpRes = objHttpReq.RequestGet (...) ;

	// If the server requests proxy authorization..
	if ( pobjHttpRes->GetStatus () == HTTP_STATUS_PROXY_AUTH_REQ ) {
		delete pobjHttpRes ;
		pobjHttpRes = NULL ;
	
		LPTSTR			szUserName = NULL ;
		LPTSTR			szPassword = NULL ;

		...		// Get a proxy user name and password

		// Set the proxy user name and password
		objHttpReq.SetProxyAccount (szUserName, szPassword) ;

		// Only a HTTP GET method can be used to request proxy authorization.
		pobjHttpRes = objHttpReq.RequestGet (...) ;

		if ( pobjHttpRes->GetStatus () == HTTP_STATUS_PROXY_AUTH_REQ ) {
			...		// If the authorization fails, try again or stop
		}
	}

	...		// Continue to execute

} catch (httpclientexception & e) {
	...		// Place error handling codes here.
}
 * \endcode
 *
 * \param szUserName	[in] A proxy user name. Empty string is not allowed.
 *							 If it is NULL, account information saved in CHttpClient will be removed.
 * \param szPassword	[in] A proxy password. NULL and empty string are not allowed.
 * \throw				Throws a httpclientexception if an error occurs.
 *
 * \sa	ApplyProxyAccount, ::InternetSetOption, <a href="http://support.microsoft.com/?kbid=195650" target="_blank">How To Handle Proxy Authorization with WinInet</a>
 */
template <typename HttpTool, typename HttpEncoder>
void CHttpClientT<HttpTool, HttpEncoder>::SetProxyAccount (PCSZ szUserName, PCSZ szPassword)
	throw (Exception &)
{
	if ( szUserName == NULL ) {
		SAFEFREE (m_szProxyUserName) ;
		SAFEFREE (m_szProxyPassword) ;
		return ;
	}

	HTTPCLIENT_ASSERT (szUserName != NULL, "CHttpClientT::SetProxyAccount: szUserName can not be NULL.") ;
	HTTPCLIENT_ASSERT (HttpTool::StringLen (szUserName) > 0, "CHttpClientT::SetProxyAccount: szUserName can not be an empty string.") ;
	HTTPCLIENT_ASSERT (szPassword != NULL, "CHttpClientT::SetProxyAccount: szPassword can not be NULL.") ;
	HTTPCLIENT_ASSERT (HttpTool::StringLen (szPassword) > 0, "CHttpClientT::SetProxyAccount: szPassword can not be an empty string.") ;

	PSZ		szNewUserName = NULL ;
	PSZ		szNewPassword = NULL ;

	try {
		szNewUserName = _AllocNCopyIfNewString (szUserName, m_szProxyUserName) ;
		szNewPassword = _AllocNCopyIfNewString (szPassword, m_szProxyPassword) ;
	} catch (Exception &) {
		if ( m_szProxyUserName != szNewUserName )	SAFEFREE (szNewUserName) ;
		if ( m_szProxyPassword != szNewPassword )	SAFEFREE (szNewPassword) ;
		throw ;
	}

	if ( m_szProxyUserName != szNewUserName ) {
		SAFEFREE (m_szProxyUserName) ;
		m_szProxyUserName = szNewUserName ;
	}

	if ( m_szProxyPassword != szNewPassword ) {
		SAFEFREE (m_szProxyPassword) ;
		m_szProxyPassword = szNewPassword ;
	}
}

/*!
 * This method returns a proxy user name which is used to access HTTP proxy server.
 * It always returns a null-terminated string. The returned string is owned by
 * the CHttpClientT. So you can not free it.
 *
 * \return				A proxy user name.
 */
template <typename HttpTool, typename HttpEncoder>
typename CHttpClientT<HttpTool, HttpEncoder>::PCSZ
CHttpClientT<HttpTool, HttpEncoder>::GetProxyUserName (void) const
	throw ()
{
	return m_szProxyUserName ? m_szProxyUserName : HttpTool::szEmptyString ;
}

/*!
 * This method returns the current proxy password.
 * It always returns a null-terminated string. The returned string is owned by
 * the CHttpClientT. So you can not free it.
 *
 * \return				The current proxy password.
 */
template <typename HttpTool, typename HttpEncoder>
typename CHttpClientT<HttpTool, HttpEncoder>::PCSZ
CHttpClientT<HttpTool, HttpEncoder>::GetProxyPassword (void) const
	throw ()
{
	return m_szProxyPassword ? m_szProxyPassword : HttpTool::szEmptyString ;
}

/*!
 * This method sets a proxy user name and password on the hConnection handle
 * using the settings saved in CHttpClient.
 * You can handle proxy authorization using this handle, but there are some restrictions.
 * First, The HTTP POST method is not safe if you are not authorized
 * (It is possible that the proxy server closes the connection before the request is finished).
 * Second, Only a HTTP GET method can be used to request proxy authorization
 * (This restriction is caused by the WinInet API).
 * If an exception occurs, it is possible that the proxy user name is only applied.
 * 
 * The following code snippet demonstrates the usage of this method.
 * \code
...
CHttpClient			objHttpReq ;
CHttpResponse *		pobjHttpRes = NULL ;
HINTERNET			hInternet = NULL ;
HINTERNET			hConnection = NULL ;

try {
	...		// Initialize the CHttpClient object

	// Get a new internet handle
	hInternet = objHttpReq.OpenInternet () ;

	// Get a new connection handle
	hConnection = objHttpReq.OpenConnection (hInternet, _T ("http://www.k-net.or.kr")) ;

	// It is safe to use HTTP GET method.
    pobjHttpRes = objHttpReq.RequestGet (hInternet, hConnection, ...) ;

	// If the server requests proxy authorization..
	if ( pobjHttpRes->GetStatus () == HTTP_STATUS_PROXY_AUTH_REQ ) {
		delete pobjHttpRes ;
		pobjHttpRes = NULL ;
	
		LPTSTR			szUserName = NULL ;
		LPTSTR			szPassword = NULL ;

		...		// Get a proxy user name and password

		// Set the proxy user name and password
		objHttpReq.SetProxyAccount (szUserName, szPassword) ;
		objHttpReq.ApplyProxyAccount (hConnection) ;

		// Only a HTTP GET method can be used to request proxy authorization.
		pobjHttpRes = objHttpReq.RequestGet (hInternet, hConnection, ...) ;

		if ( pobjHttpRes->GetStatus () == HTTP_STATUS_PROXY_AUTH_REQ ) {
			...		// If the authorization fails, try again or stop
		}
	}

	...		// Continue to execute

} catch (httpclientexception & e) {
	...		// Place error handling codes here.
}
 * \endcode
 *
 * \param hConnection	[in] A HINTERNET handle returned by ::InternetConnect function.
 *							 NULL is not allowed.
 * \throw				Throws a httpclientexception if an error occurs.
 *
 * \sa	SetProxyAccount, ::InternetSetOption, <a href="http://support.microsoft.com/?kbid=195650" target="_blank">How To Handle Proxy Authorization with WinInet</a>
 */
template <typename HttpTool, typename HttpEncoder>
void CHttpClientT<HttpTool, HttpEncoder>::ApplyProxyAccount (HINTERNET hConnection)
	throw (Exception &)
{
	HTTPCLIENT_ASSERT (hConnection != NULL, "CHttpClientT::ApplyProxyAccount: hConnection can not be NULL.") ;

	if ( m_szProxyUserName == NULL || m_szProxyPassword == NULL )
		return ;

	::SafeInt<DWORD>	cchProxyUserName ;
	::SafeInt<DWORD>	cchProxyPassword ;

	try {
		cchProxyUserName = HttpTool::StringLen (m_szProxyUserName) ;
		cchProxyPassword = HttpTool::StringLen (m_szProxyPassword) ;
	} catch (::SafeIntException & e) {
		throw (e) ;
	}

	if ( cchProxyUserName == 0 || cchProxyPassword == 0 )
		return ;

	// Set a user name for Proxy
	HttpTool::InternetSetOption (
			hConnection
			, INTERNET_OPTION_PROXY_USERNAME
			, (LPVOID) m_szProxyUserName
			, cchProxyUserName.Value ()
		) ;
	
	// Set a password for Proxy
	HttpTool::InternetSetOption (
			hConnection
			, INTERNET_OPTION_PROXY_PASSWORD
			, (LPVOID) m_szProxyPassword
			, cchProxyPassword.Value ()
		) ;
}

/*!
 * This method returns a new HINTERNET handle which is returned by ::HttpOpenRequest
 * using the settings saved in the CHttpClient.
 * It only checks the protocol and the path of the URL.
 * So you can use the following URLs.
 * <UL>
 *	<LI>http://drama.kbs.co.kr/winter/people/cast_02.shtml -> A full HTTP URL (or a full HTTPS URL)</LI>
 *	<LI>http:///winter/people/cast_02.shtml -> A HTTP URL without server location (or a HTTPS URL without server location)</LI>
 *	<LI>www.eggfilm.com/classic/intro.htm -> A HTTP URL without protocol (http is assumed)</LI>
 *	<LI>/classic/intro.htm -> Only A path (http is assumed)</LI>
 *	<LI>http://www.k-net.or.kr -> An HTTP URL without path (uses "/" as the path)</LI>
 *	<LI>www.k-net.or.kr -> Only A server location (http is assumed, uses "/" as the path)</LI>
 *	<LI> -> A NULL or an empty string (http is assumed, uses "/" as the path)</LI>
 *	<LI>ftp://www.k-net.or.kr/member.html -> An URL with another protocol (http is assumed)</LI>
 * </UL>
 * 
 * The caller is responsible for closing the returned handle when it has finished with it.
 *
 * \param hConnection	[in] A valid handle returned by ::InternetConnect.
 * \param szVerb		[in] A HTTP verb which corresponds to the lpszVerb parameter of the ::HttpOpenRequest function.
 * \param szUrl			[in] An URL which is used to return a new handle.
 * \param dwFlags		[in] A flags which corresponds to the dwFlags parameter of the ::HttpOpenRequest function.
 * \param szReferer		[in] A referer which corresponds to the lpszReferer parameter of the ::HttpOpenRequest function.
 * \return				A new HINTERNET handle.
 * \throw				Throws a httpclientexception if an error occurs.
 *
 * \sa	::HttpOpenRequest
 */
template <typename HttpTool, typename HttpEncoder>
HINTERNET CHttpClientT<HttpTool, HttpEncoder>::OpenRequest (HINTERNET hConnection
																  , PCSZ szVerb, PCSZ szUrl
																  , DWORD dwFlags, PCSZ szReferer)
	throw (Exception &)
{
	HTTPCLIENT_ASSERT (hConnection != NULL, "CHttpClientT::OpenRequest: hConnection can not be NULL.") ;

	CHttpUrlAnalyzer	objAnalyzer (szUrl, m_nAnsiCodePage) ;

	// If the protocol is HTTPS, adds the SSL flag.
	if ( (objAnalyzer.ProtocolLen () == 5 /* ::strlen ("HTTPS") */)
			&& (0 == HttpTool::StringNICmp (
				szUrl + objAnalyzer.ProtocolIdx (), HttpTool::szHTTPS, objAnalyzer.ProtocolLen ())) )
			dwFlags |= INTERNET_FLAG_SECURE ;

	PCSZ		szUrlPath ;
	BOOL		bNeedToFreeUrlPath = FALSE ;

	// If the path does not exist, use "/" as the path
	if ( objAnalyzer.PathLen () == 0 ) {
		size_t		cchAfterPath = szUrl ? HttpTool::StringLen (szUrl + objAnalyzer.PathIdx ()) : 0 ;

		if ( cchAfterPath == 0 )
			szUrlPath = HttpTool::szSlash ;
		else {

			::SafeInt<size_t>	cbAfterPath = 0 ;
			try {
				cbAfterPath = cchAfterPath ;
				cbAfterPath += 2 ;			// for '/' & '\0'
				cbAfterPath *= sizeof (CharType) ;
			} catch (::SafeIntException & e) {
				HttpTool::ThrowException (e) ;
			}

			szUrlPath = (PCSZ) ::malloc (cbAfterPath.Value ()) ;
			if ( szUrlPath == NULL )
				HttpTool::ThrowException (HTTPCLIENT_ERR_OUT_OF_MEMORY) ;
			bNeedToFreeUrlPath = TRUE ;

			HttpTool::StringCopy (const_cast<PSZ> (szUrlPath), HttpTool::szSlash) ;
			HttpTool::StringCat (const_cast<PSZ> (szUrlPath), szUrl + objAnalyzer.PathIdx ()) ;
		}
	} else
		szUrlPath = szUrl + objAnalyzer.PathIdx () ;

	HINTERNET		hRequest = NULL ;

	try {
		hRequest = HttpTool::OpenRequest (hConnection, szVerb, szUrlPath, dwFlags, szReferer, m_nAnsiCodePage) ;

		// Adds the cache-control header if it is needed
		if ( dwFlags & INTERNET_FLAG_PRAGMA_NOCACHE )
			HttpTool::AddHeader (hRequest, HttpTool::szCacheControl, HttpTool::szNoCache, m_nAnsiCodePage) ;

	} catch (Exception &) {
		HttpTool::CloseRequest (hRequest) ;
		if ( bNeedToFreeUrlPath )
			SAFEFREE (szUrlPath) ;
		throw ;
	}

	if ( bNeedToFreeUrlPath )
		SAFEFREE (szUrlPath) ;

	return hRequest ;
}

/*!
 * This method adds the HTTP headers to the handle which is returned by ::HttpOpenRequest
 * using the settings saved in the CHttpClient.
 * It uses the headers which is saved in the CHttpClient.
 * If an exception occurs, it is possible that the headers are added partially.
 *
 * \param hRequest	[in] A valid handle which is returned by ::HttpOpenRequest.
 * \throw			Throws a httpclientexception if an error occurs.
 *
 * \sa		::HttpAddRequestHeaders
 */
template <typename HttpTool, typename HttpEncoder>
void CHttpClientT<HttpTool, HttpEncoder>::AddRequestHeader (HINTERNET hRequest)
	throw (Exception &)
{
	HTTPCLIENT_ASSERT (hRequest != NULL, "CHttpClientT::AddRequestHeader: hRequest can not be NULL.") ;

	// Adds user's headers.
	for (ConstMapIter iter = m_mapHeader.Begin (); iter != m_mapHeader.End (); ++iter)
		HttpTool::AddHeader (hRequest, iter->first, (iter->second).szValue, m_nAnsiCodePage) ;
}

/*!
 * \internal
 * This method retrieves the resource specified by the szUrl using HTTP GET request.
 * If the szUrl does not start with "https://", "http" is used as the protocol.
 * If the caller passes the hInternet parameter which is returned by ::InternetOpen,
 * the CHttpClient uses the passed handle instead of creating a new one.
 * The caller is responsible for closing the hInternet handle when it has finished with it.
 * If the caller passes the hConnection parameter which is returned by ::InternetConnect,
 * the CHttpClient also uses the passed handle instead of creating a new one.
 * In this case, the caller has to pass the hInternet parameter which is used to get the hConnection parameter.
 * The caller is responsible for closing the hConnection handle when it has finished with it.
 * If the hConnection is not NULL, the CHttpClient does not use the server location part in the szUrl.
 * You can omit the server location part of the szUrl.
 * The returned CHttpResponseT object is allocated by the new operator.
 * So you have to delete the returned CHttpResponseT object.
 *
 * \param hInternet		[in] A valid handle returned by ::InternetOpen. NULL is allowed.
 * \param hConnection	[in] A valid handle returned by ::InternetConnect. NULL is allowed.
 *						     If this parameter is not NULL, the caller has to pass the hInternet parameter
 *						     which is used to get the hConnection parameter.
 * \param szUrl			[in] A HTTP URL.
 * \param dwFlags		[in] A flags which corresponds to the dwFlags parameter of the ::HttpOpenRequest function.
 * \param szReferer		[in] A referer which corresponds to the lpszReferer parameter of the ::HttpOpenRequest function.
 * \param szUsrName		[in] An user name which corresponds to the lpszUsername parameter of the ::InternetConnect function.
 * \param szUsrPwd		[in] An user password which corresponds to the lpszPassword parameter of the ::InternetConnect function.
 * \return				A pointer to a CHttpResponseT object.
 * \throw				Throws a httpclientexception if an error occurs.
 *
 * \sa		CHttpResponseT, OpenInternet, OpenConnection, ::InternetOpen, ::InternetConnect
 */
template <typename HttpTool, typename HttpEncoder>
typename CHttpClientT<HttpTool, HttpEncoder>::CHttpResponse * 
CHttpClientT<HttpTool, HttpEncoder>::_RequestGetEx (HINTERNET hInternet, HINTERNET hConnection
														 , PCSZ szUrl, DWORD dwFlags, PCSZ szReferer
														 , PCSZ szUsrName, PCSZ szUsrPwd)
	throw (Exception &)
{
	// Releases any existing Post Context
	_EndPostContext () ;

	PCSZ		szGetUrl = NULL ;
	BOOL		bNeedToFreeGetUrl = FALSE ;

	BOOL		bBorrowedInternet = TRUE ;
	BOOL		bBorrowedConnection = TRUE ;
	HINTERNET	hRequest = NULL ;

	CHttpResponse *		pobjRes = NULL ;

	try {

		// Get WinInet API handles
		if ( hInternet == NULL ) {
			hInternet = OpenInternet () ;
			bBorrowedInternet = FALSE ;
		}

		if ( hConnection == NULL ) {
			hConnection = OpenConnection (hInternet, szUrl, szUsrName, szUsrPwd) ;
			bBorrowedConnection = FALSE ;
		}

		szGetUrl = szUrl ;

		// Appends the GET parameters to the URL
		if ( !m_mapParam.Empty () ) {
			::SafeInt<DWORD>	cbGetUrl ;

			try {
				cbGetUrl = MakeGetUrlLen (szUrl) ;
				cbGetUrl += 1 ;
				cbGetUrl *= sizeof (CharType) ;
			} catch (::SafeIntException & e) {
				HttpTool::ThrowException (e) ;
			}

			if ( NULL == (szGetUrl = (PCSZ) ::malloc (cbGetUrl.Value ())) )
				HttpTool::ThrowException (HTTPCLIENT_ERR_OUT_OF_MEMORY) ;

			bNeedToFreeGetUrl = TRUE ;
			MakeGetUrl (const_cast<PSZ> (szGetUrl), szUrl) ;
		}

		hRequest = OpenRequest (hConnection, HttpTool::szGET, szGetUrl, dwFlags, szReferer) ;
		AddRequestHeader (hRequest) ;			// Adds user's custom header

		if ( bNeedToFreeGetUrl ) {
			SAFEFREE (szGetUrl) ;
			bNeedToFreeGetUrl = FALSE ;
		}

		// Connect to the HTTP server
		HttpTool::SendRequest (hRequest, NULL, m_nAnsiCodePage) ;

		if ( NULL == (pobjRes = new CHttpResponse (
											bBorrowedInternet ? NULL : hInternet
											, bBorrowedConnection ? NULL : hConnection
											, hRequest)) )
			HttpTool::ThrowException (HTTPCLIENT_ERR_OUT_OF_MEMORY) ;

	} catch (Exception &) {
		if ( bNeedToFreeGetUrl )	SAFEFREE (szGetUrl) ;
		HttpTool::CloseRequest (hRequest) ;
		if ( !bBorrowedConnection )	HttpTool::CloseRequest (hConnection) ;
		if ( !bBorrowedInternet )	HttpTool::CloseRequest (hInternet) ;
		throw ;
	} catch (...) {			// for the exception thrown by the new operator
		if ( bNeedToFreeGetUrl )	SAFEFREE (szGetUrl) ;
		HttpTool::CloseRequest (hRequest) ;
		if ( !bBorrowedConnection )	HttpTool::CloseRequest (hConnection) ;
		if ( !bBorrowedInternet )	HttpTool::CloseRequest (hInternet) ;
		HttpTool::ThrowException (HTTPCLIENT_ERR_UNEXPECTED_ERROR) ;
	}

	return pobjRes ;
}

/*!
 * This method retrieves the resource specified by the szUrl using HTTP GET request.
 * If the szUrl does not start with "https://", "http" is used as the protocol.
 * If the caller passes the hInternet parameter which is returned by ::InternetOpen,
 * the CHttpClient uses the passed handle instead of creating a new one.
 * The caller is responsible for closing the hInternet handle when it has finished with it.
 * If the caller passes the hConnection parameter which is returned by ::InternetConnect,
 * the CHttpClient also uses the passed handle instead of creating a new one.
 * In this case, the caller has to pass the hInternet parameter which is used to get the hConnection parameter.
 * The caller is responsible for closing the hConnection handle when it has finished with it.
 * If the hConnection is not NULL, the CHttpClient does not use the server location part in the szUrl.
 * You can omit the server location part of the szUrl.
 * The returned CHttpResponseT object is allocated by the new operator.
 * So you have to delete the returned CHttpResponseT object.
 *
 * \param hInternet		[in] A valid handle returned by ::InternetOpen. NULL is allowed.
 * \param hConnection	[in] A valid handle returned by ::InternetConnect. NULL is allowed.
 *						     If this parameter is not NULL, the caller has to pass the hInternet parameter
 *						     which is used to get the hConnection parameter.
 * \param szUrl			[in] A HTTP URL.
 * \param bUseCache		[in] Specifies whether to use cache.
 * \return				A pointer to a CHttpResponseT object.
 * \throw				Throws a httpclientexception if an error occurs.
 *
 * \sa		CHttpResponseT, OpenInternet, OpenConnection, ::InternetOpen, ::InternetConnect
 */
template <typename HttpTool, typename HttpEncoder>
typename CHttpClientT<HttpTool, HttpEncoder>::CHttpResponse * 
CHttpClientT<HttpTool, HttpEncoder>::RequestGet (HINTERNET hInternet, HINTERNET hConnection, PCSZ szUrl, BOOL bUseCache)
	throw (Exception &)
{
	return RequestGetEx (hInternet, hConnection, szUrl
		, bUseCache ? HTTPCLIENT_DEF_REQUEST_FLAGS : HTTPCLIENT_DEF_REQUEST_FLAGS_NOCACHE) ;
}

/*!
 * This method retrieves the resource specified by the szUrl using HTTP GET request.
 * If the szUrl does not start with "https://", "http" is used as the protocol.
 * If the caller passes the hInternet parameter which is returned by ::InternetOpen,
 * the CHttpClient uses the passed handle instead of creating a new one.
 * The caller is responsible for closing the hInternet handle when it has finished with it.
 * The returned CHttpResponseT object is allocated by the new operator.
 * So you have to delete the returned CHttpResponseT object.
 *
 * \param hInternet		[in] A valid handle returned by ::InternetOpen. NULL is allowed.
 * \param szUrl			[in] A HTTP URL.
 * \param bUseCache		[in] Specifies whether to use cache.
 * \return				A pointer to a CHttpResponseT object.
 * \throw				Throws a httpclientexception if an error occurs.
 *
 * \sa		CHttpResponseT, OpenInternet, ::InternetOpen
 */
template <typename HttpTool, typename HttpEncoder>
typename CHttpClientT<HttpTool, HttpEncoder>::CHttpResponse *
CHttpClientT<HttpTool, HttpEncoder>::RequestGet (HINTERNET hInternet, PCSZ szUrl, BOOL bUseCache)
	throw (Exception &)
{
	return RequestGetEx (hInternet, szUrl
		, bUseCache ? HTTPCLIENT_DEF_REQUEST_FLAGS : HTTPCLIENT_DEF_REQUEST_FLAGS_NOCACHE) ;
}

/*!
 * This method retrieves the resource specified by the szUrl using HTTP GET request.
 * If the szUrl does not start with "https://", "http" is used as the protocol.
 * The returned CHttpResponseT object is allocated by the new operator.
 * So you have to delete the returned CHttpResponseT object.
 *
 * \param szUrl			[in] A HTTP URL.
 * \param bUseCache		[in] Specifies whether to use cache.
 * \return				A pointer to a CHttpResponseT object.
 * \throw				Throws a httpclientexception if an error occurs.
 *
 * \sa		CHttpResponseT
 */
template <typename HttpTool, typename HttpEncoder>
typename CHttpClientT<HttpTool, HttpEncoder>::CHttpResponse * 
CHttpClientT<HttpTool, HttpEncoder>::RequestGet (PCSZ szUrl, BOOL bUseCache)
	throw (Exception &)
{
	return RequestGetEx (szUrl
		, bUseCache ? HTTPCLIENT_DEF_REQUEST_FLAGS : HTTPCLIENT_DEF_REQUEST_FLAGS_NOCACHE) ;
}

/*!
 * This method retrieves the resource specified by the szUrl using HTTP GET request.
 * If the szUrl does not start with "https://", "http" is used as the protocol.
 * If the caller passes the hInternet parameter which is returned by ::InternetOpen,
 * the CHttpClient uses the passed handle instead of creating a new one.
 * The caller is responsible for closing the hInternet handle when it has finished with it.
 * If the caller passes the hConnection parameter which is returned by ::InternetConnect,
 * the CHttpClient also uses the passed handle instead of creating a new one.
 * In this case, the caller has to pass the hInternet parameter which is used to get the hConnection parameter.
 * The caller is responsible for closing the hConnection handle when it has finished with it.
 * If the hConnection is not NULL, the CHttpClient does not use the server location part in the szUrl.
 * You can omit the server location part of the szUrl.
 * The returned CHttpResponseT object is allocated by the new operator.
 * So you have to delete the returned CHttpResponseT object.
 *
 * \param hInternet		[in] A valid handle returned by ::InternetOpen. NULL is allowed.
 * \param hConnection	[in] A valid handle returned by ::InternetConnect. NULL is allowed.
 *						     If this parameter is not NULL, the caller has to pass the hInternet parameter
 *						     which is used to get the hConnection parameter.
 * \param szUrl			[in] A HTTP URL.
 * \param dwFlags		[in] A flags which corresponds to the dwFlags parameter of the ::HttpOpenRequest function.
 * \param szReferer		[in] A referer which corresponds to the lpszReferer parameter of the ::HttpOpenRequest function.
 * \param szUsrName		[in] An user name which corresponds to the lpszUsername parameter of the ::InternetConnect function.
 * \param szUsrPwd		[in] An user password which corresponds to the lpszPassword parameter of the ::InternetConnect function.
 * \return				A pointer to a CHttpResponseT object.
 * \throw				Throws a httpclientexception if an error occurs.
 *
 * \sa		CHttpResponseT, OpenInternet, OpenConnection, ::InternetOpen, ::InternetConnect, ::HttpOpenRequest
 */
template <typename HttpTool, typename HttpEncoder>
typename CHttpClientT<HttpTool, HttpEncoder>::CHttpResponse * 
CHttpClientT<HttpTool, HttpEncoder>::RequestGetEx (HINTERNET hInternet, HINTERNET hConnection, PCSZ szUrl, DWORD dwFlags
														, PCSZ szReferer, PCSZ szUsrName, PCSZ szUsrPwd)
	throw (Exception &)
{
	return _RequestGetEx (hInternet, hConnection, szUrl, dwFlags, szReferer, szUsrName, szUsrPwd) ;
}

/*!
 * This method retrieves the resource specified by the szUrl using HTTP GET request.
 * If the szUrl does not start with "https://", "http" is used as the protocol.
 * If the caller passes the hInternet parameter which is returned by ::InternetOpen,
 * the CHttpClient uses the passed handle instead of creating a new one.
 * The caller is responsible for closing the hInternet handle when it has finished with it.
 * The returned CHttpResponseT object is allocated by the new operator.
 * So you have to delete the returned CHttpResponseT object.
 *
 * \param hInternet		[in] A valid handle returned by ::InternetOpen. NULL is allowed.
 * \param szUrl			[in] A HTTP URL.
 * \param dwFlags		[in] A flags which corresponds to the dwFlags parameter of the ::HttpOpenRequest function.
 * \param szReferer		[in] A referer which corresponds to the lpszReferer parameter of the ::HttpOpenRequest function.
 * \param szUsrName		[in] An user name which corresponds to the lpszUsername parameter of the ::InternetConnect function.
 * \param szUsrPwd		[in] An user password which corresponds to the lpszPassword parameter of the ::InternetConnect function.
 * \return				A pointer to a CHttpResponseT object.
 * \throw				Throws a httpclientexception if an error occurs.
 *
 * \sa		CHttpResponseT, OpenInternet, ::InternetOpen, ::InternetConnect, ::HttpOpenRequest
 */
template <typename HttpTool, typename HttpEncoder>
typename CHttpClientT<HttpTool, HttpEncoder>::CHttpResponse * 
CHttpClientT<HttpTool, HttpEncoder>::RequestGetEx (HINTERNET hInternet, PCSZ szUrl, DWORD dwFlags
														 , PCSZ szReferer, PCSZ szUsrName, PCSZ szUsrPwd)
	throw (Exception &)
{
	return _RequestGetEx (hInternet, NULL, szUrl, dwFlags, szReferer, szUsrName, szUsrPwd) ;
}

/*!
 * This method retrieves the resource specified by the szUrl using HTTP GET request.
 * If the szUrl does not start with "https://", "http" is used as the protocol.
 * The returned CHttpResponseT object is allocated by the new operator.
 * So you have to delete the returned CHttpResponseT object.
 *
 * \param szUrl			[in] A HTTP URL.
 * \param dwFlags		[in] A flags which corresponds to the dwFlags parameter of the ::HttpOpenRequest function.
 * \param szReferer		[in] A referer which corresponds to the lpszReferer parameter of the ::HttpOpenRequest function.
 * \param szUsrName		[in] An user name which corresponds to the lpszUsername parameter of the ::InternetConnect function.
 * \param szUsrPwd		[in] An user password which corresponds to the lpszPassword parameter of the ::InternetConnect function.
 * \return				A pointer to a CHttpResponseT object.
 * \throw				Throws a httpclientexception if an error occurs.
 *
 * \sa		CHttpResponseT, ::InternetConnect, ::HttpOpenRequest
 */
template <typename HttpTool, typename HttpEncoder>
typename CHttpClientT<HttpTool, HttpEncoder>::CHttpResponse * 
CHttpClientT<HttpTool, HttpEncoder>::RequestGetEx (PCSZ szUrl, DWORD dwFlags, PCSZ szReferer
																	 , PCSZ szUsrName, PCSZ szUsrPwd)
	throw (Exception &)
{
	return _RequestGetEx (NULL, NULL, szUrl, dwFlags, szReferer, szUsrName, szUsrPwd) ;
}

/*!
 * This method starts a new HTTP POST request. If you call this method, you can retrieve progress
 * information of the POST request by using the Query method.
 * To finish the request, you have to call the Proceed method.
 * If the szUrl does not start with "https://", "http" is used as the protocol.
 * If the caller passes the hInternet parameter which is returned by ::InternetOpen,
 * the CHttpClient uses the passed handle instead of creating a new one.
 * The caller is responsible for closing the hInternet handle when it has finished with it.
 * If the caller passes the hConnection parameter which is returned by ::InternetConnect,
 * the CHttpClient also uses the passed handle instead of creating a new one.
 * In this case, the caller has to pass the hInternet parameter which is used to get the hConnection parameter.
 * The caller is responsible for closing the hConnection handle when it has finished with it.
 * If the hConnection is not NULL, the CHttpClient does not use the server location part in the szUrl.
 * You can omit the server location part of the szUrl.
 *
 * \param hInternet		[in] A valid handle returned by ::InternetOpen. NULL is allowed.
 * \param hConnection	[in] A valid handle returned by ::InternetConnect. NULL is allowed.
 *						     If this parameter is not NULL, the caller has to pass the hInternet parameter
 *						     which is used to get the hConnection parameter.
 * \param szUrl			[in] A HTTP URL.
 * \param bUseCache		[in] Specifies whether to use cache.
 * \throw				Throws a httpclientexception if an error occurs.
 *
 * \sa		OpenInternet, OpenConnection, ::InternetOpen, ::InternetConnect
 */
template <typename HttpTool, typename HttpEncoder>
void CHttpClientT<HttpTool, HttpEncoder>::BeginPost (HINTERNET hInternet, HINTERNET hConnection
																			, PCSZ szUrl, BOOL bUseCache)
	throw (Exception &)
{
	BeginPostEx (hInternet, hConnection, szUrl
		, bUseCache ? HTTPCLIENT_DEF_REQUEST_FLAGS : HTTPCLIENT_DEF_REQUEST_FLAGS_NOCACHE) ;
}

/*!
 * This method starts a new HTTP POST request. If you call this method, you can retrieve progress
 * information of the POST request by using the Query method.
 * To finish the request, you have to call the Proceed method.
 * If the szUrl does not start with "https://", "http" is used as the protocol.
 * If the caller passes the hInternet parameter which is returned by ::InternetOpen,
 * the CHttpClient uses the passed handle instead of creating a new one.
 * The caller is responsible for closing the hInternet handle when it has finished with it.
 *
 * \param hInternet		[in] A valid handle returned by ::InternetOpen. NULL is allowed.
 * \param szUrl			[in] A HTTP URL.
 * \param bUseCache		[in] Specifies whether to use cache.
 * \throw				Throws a httpclientexception if an error occurs.
 *
 * \sa		OpenInternet, ::InternetOpen
 */
template <typename HttpTool, typename HttpEncoder>
void CHttpClientT<HttpTool, HttpEncoder>::BeginPost (HINTERNET hInternet, PCSZ szUrl, BOOL bUseCache)
	throw (Exception &)
{
	BeginPostEx (hInternet, szUrl
		, bUseCache ? HTTPCLIENT_DEF_REQUEST_FLAGS : HTTPCLIENT_DEF_REQUEST_FLAGS_NOCACHE) ;
}

/*!
 * This method starts a new HTTP POST request. If you call this method, you can retrieve progress
 * information of the POST request by using the Query method.
 * To finish the request, you have to call the Proceed method.
 * If the szUrl does not start with "https://", "http" is used as the protocol.
 *
 * \param szUrl			[in] A HTTP URL.
 * \param bUseCache		[in] Specifies whether to use cache.
 * \throw				Throws a httpclientexception if an error occurs.
 *
 * \sa		Query, Proceed
 */
template <typename HttpTool, typename HttpEncoder>
void CHttpClientT<HttpTool, HttpEncoder>::BeginPost (PCSZ szUrl, BOOL bUseCache)
	throw (Exception &)
{
	BeginPostEx (szUrl
		, bUseCache ? HTTPCLIENT_DEF_REQUEST_FLAGS : HTTPCLIENT_DEF_REQUEST_FLAGS_NOCACHE) ;
}

/*!
 * This method starts a new HTTP POST request. If you call this method, you can retrieve progress
 * information of the POST request by using the Query method.
 * To finish the request, you have to call the Proceed method.
 * If the szUrl does not start with "https://", "http" is used as the protocol.
 * If the caller passes the hInternet parameter which is returned by ::InternetOpen,
 * the CHttpClient uses the passed handle instead of creating a new one.
 * The caller is responsible for closing the hInternet handle when it has finished with it.
 * If the caller passes the hConnection parameter which is returned by ::InternetConnect,
 * the CHttpClient also uses the passed handle instead of creating a new one.
 * In this case, the caller has to pass the hInternet parameter which is used to get the hConnection parameter.
 * The caller is responsible for closing the hConnection handle when it has finished with it.
 * If the hConnection is not NULL, the CHttpClient does not use the server location part in the szUrl.
 * You can omit the server location part of the szUrl.
 *
 * \param hInternet		[in] A valid handle returned by ::InternetOpen. NULL is allowed.
 * \param hConnection	[in] A valid handle returned by ::InternetConnect. NULL is allowed.
 *						     If this parameter is not NULL, the caller has to pass the hInternet parameter
 *						     which is used to get the hConnection parameter.
 * \param szUrl			[in] A HTTP URL.
 * \param dwFlags		[in] A flags which corresponds to the dwFlags parameter of the ::HttpOpenRequest function.
 * \param szReferer		[in] A referer which corresponds to the lpszReferer parameter of the ::HttpOpenRequest function.
 * \param szUsrName		[in] An user name which corresponds to the lpszUsername parameter of the ::InternetConnect function.
 * \param szUsrPwd		[in] An user password which corresponds to the lpszPassword parameter of the ::InternetConnect function.
 * \throw				Throws a httpclientexception if an error occurs.
 *
 * \sa		Query, Proceed, OpenInternet, OpenConnection, ::InternetOpen, ::InternetConnect, ::HttpOpenRequest
 */
template <typename HttpTool, typename HttpEncoder>
void CHttpClientT<HttpTool, HttpEncoder>::BeginPostEx (HINTERNET hInternet, HINTERNET hConnection, PCSZ szUrl, DWORD dwFlags
																	, PCSZ szReferer, PCSZ szUsrName, PCSZ szUsrPwd)
	throw (Exception &)
{
	_StartPostContext (hInternet, hConnection, szUrl, dwFlags, szReferer, szUsrName, szUsrPwd) ;	// Starts a new POST context
}

/*!
 * This method starts a new HTTP POST request. If you call this method, you can retrieve progress
 * information of the POST request by using the Query method.
 * To finish the request, you have to call the Proceed method.
 * If the szUrl does not start with "https://", "http" is used as the protocol.
 * If the caller passes the hInternet parameter which is returned by ::InternetOpen,
 * the CHttpClient uses the passed handle instead of creating a new one.
 * The caller is responsible for closing the hInternet handle when it has finished with it.
 *
 * \param hInternet		[in] A valid handle returned by ::InternetOpen. NULL is allowed.
 * \param szUrl			[in] A HTTP URL.
 * \param dwFlags		[in] A flags which corresponds to the dwFlags parameter of the ::HttpOpenRequest function.
 * \param szReferer		[in] A referer which corresponds to the lpszReferer parameter of the ::HttpOpenRequest function.
 * \param szUsrName		[in] An user name which corresponds to the lpszUsername parameter of the ::InternetConnect function.
 * \param szUsrPwd		[in] An user password which corresponds to the lpszPassword parameter of the ::InternetConnect function.
 * \throw				Throws a httpclientexception if an error occurs.
 *
 * \sa		Query, Proceed, OpenInternet, ::InternetOpen, ::InternetConnect, ::HttpOpenRequest
 */
template <typename HttpTool, typename HttpEncoder>
void CHttpClientT<HttpTool, HttpEncoder>::BeginPostEx (HINTERNET hInternet, PCSZ szUrl, DWORD dwFlags, PCSZ szReferer
																	, PCSZ szUsrName, PCSZ szUsrPwd)
	throw (Exception &)
{
	_StartPostContext (hInternet, NULL, szUrl, dwFlags, szReferer, szUsrName, szUsrPwd) ;	// Starts a new POST context
}

/*!
 * This method starts a new HTTP POST request. If you call this method, you can retrieve progress
 * information of the POST request by using the Query method.
 * To finish the request, you have to call the Proceed method.
 * If the szUrl does not start with "https://", "http" is used as the protocol.
 *
 * \param szUrl			[in] A HTTP URL.
 * \param dwFlags		[in] A flags which corresponds to the dwFlags parameter of the ::HttpOpenRequest function.
 * \param szReferer		[in] A referer which corresponds to the lpszReferer parameter of the ::HttpOpenRequest function.
 * \param szUsrName		[in] An user name which corresponds to the lpszUsername parameter of the ::InternetConnect function.
 * \param szUsrPwd		[in] An user password which corresponds to the lpszPassword parameter of the ::InternetConnect function.
 * \throw				Throws a httpclientexception if an error occurs.
 *
 * \sa		Query, Proceed, ::InternetConnect, ::HttpOpenRequest
 */
template <typename HttpTool, typename HttpEncoder>
void CHttpClientT<HttpTool, HttpEncoder>::BeginPostEx (PCSZ szUrl, DWORD dwFlags, PCSZ szReferer
																	, PCSZ szUsrName, PCSZ szUsrPwd)
	throw (Exception &)
{
	_StartPostContext (NULL, NULL, szUrl, dwFlags, szReferer, szUsrName, szUsrPwd) ;	// Starts a new POST context
}

/*!
 * This method starts a new HTTP UPLOAD request which is a HTTP POST request with another content-type (multipart/form-data).
 * If you call this method, you can retrieve progress information of the POST request by using the Query method.
 * To finish the request, you have to call the Proceed method.
 * If the szUrl does not start with "https://", "http" is used as the protocol.
 * If the caller passes the hInternet parameter which is returned by ::InternetOpen,
 * the CHttpClient uses the passed handle instead of creating a new one.
 * The caller is responsible for closing the hInternet handle when it has finished with it.
 * If the caller passes the hConnection parameter which is returned by ::InternetConnect,
 * the CHttpClient also uses the passed handle instead of creating a new one.
 * In this case, the caller has to pass the hInternet parameter which is used to get the hConnection parameter.
 * The caller is responsible for closing the hConnection handle when it has finished with it.
 * If the hConnection is not NULL, the CHttpClient does not use the server location part in the szUrl.
 * You can omit the server location part of the szUrl.
 *
 * \param hInternet		[in] A valid handle returned by ::InternetOpen. NULL is allowed.
 * \param hConnection	[in] A valid handle returned by ::InternetConnect. NULL is allowed.
 *						     If this parameter is not NULL, the caller has to pass the hInternet parameter
 *						     which is used to get the hConnection parameter.
 * \param szUrl			[in] A HTTP URL.
 * \param bUseCache		[in] Specifies whether to use cache.
 * \throw				Throws a httpclientexception if an error occurs.
 *
 * \sa		Query, Proceed, OpenInternet, OpenConnection, ::InternetOpen, ::InternetConnect
 */
template <typename HttpTool, typename HttpEncoder>
void CHttpClientT<HttpTool, HttpEncoder>::BeginUpload (HINTERNET hInternet, HINTERNET hConnection, PCSZ szUrl, BOOL bUseCache)
	throw (Exception &)
{
	BeginUploadEx (hInternet, hConnection, szUrl
		, bUseCache ? HTTPCLIENT_DEF_REQUEST_FLAGS : HTTPCLIENT_DEF_REQUEST_FLAGS_NOCACHE) ;
}

/*!
 * This method starts a new HTTP UPLOAD request which is a HTTP POST request with another content-type (multipart/form-data).
 * If you call this method, you can retrieve progress information of the POST request by using the Query method.
 * To finish the request, you have to call the Proceed method.
 * If the szUrl does not start with "https://", "http" is used as the protocol.
 * If the caller passes the hInternet parameter which is returned by ::InternetOpen,
 * the CHttpClient uses the passed handle instead of creating a new one.
 * The caller is responsible for closing the hInternet handle when it has finished with it.
 *
 * \param hInternet		[in] A valid handle returned by ::InternetOpen. NULL is allowed.
 * \param szUrl			[in] A HTTP URL.
 * \param bUseCache		[in] Specifies whether to use cache.
 * \throw				Throws a httpclientexception if an error occurs.
 *
 * \sa		Query, Proceed, OpenInternet, ::InternetOpen
 */
template <typename HttpTool, typename HttpEncoder>
void CHttpClientT<HttpTool, HttpEncoder>::BeginUpload (HINTERNET hInternet, PCSZ szUrl, BOOL bUseCache)
	throw (Exception &)
{
	BeginUploadEx (hInternet, szUrl
		, bUseCache ? HTTPCLIENT_DEF_REQUEST_FLAGS : HTTPCLIENT_DEF_REQUEST_FLAGS_NOCACHE) ;
}

/*!
 * This method starts a new HTTP UPLOAD request which is a HTTP POST request with another content-type (multipart/form-data).
 * If you call this method, you can retrieve progress information of the POST request by using the Query method.
 * To finish the request, you have to call the Proceed method.
 * If the szUrl does not start with "https://", "http" is used as the protocol.
 *
 * \param szUrl			[in] A HTTP URL.
 * \param bUseCache		[in] Specifies whether to use cache.
 * \throw				Throws a httpclientexception if an error occurs.
 *
 * \sa		Query, Proceed
 */
template <typename HttpTool, typename HttpEncoder>
void CHttpClientT<HttpTool, HttpEncoder>::BeginUpload (PCSZ szUrl, BOOL bUseCache)
	throw (Exception &)
{
	BeginUploadEx (szUrl
		, bUseCache ? HTTPCLIENT_DEF_REQUEST_FLAGS : HTTPCLIENT_DEF_REQUEST_FLAGS_NOCACHE) ;
}

/*!
 * This method starts a new HTTP UPLOAD request which is a HTTP POST request with another content-type (multipart/form-data).
 * If you call this method, you can retrieve progress information of the POST request by using the Query method.
 * To finish the request, you have to call the Proceed method.
 * If the szUrl does not start with "https://", "http" is used as the protocol.
 * If the caller passes the hInternet parameter which is returned by ::InternetOpen,
 * the CHttpClient uses the passed handle instead of creating a new one.
 * The caller is responsible for closing the hInternet handle when it has finished with it.
 * If the caller passes the hConnection parameter which is returned by ::InternetConnect,
 * the CHttpClient also uses the passed handle instead of creating a new one.
 * In this case, the caller has to pass the hInternet parameter which is used to get the hConnection parameter.
 * The caller is responsible for closing the hConnection handle when it has finished with it.
 * If the hConnection is not NULL, the CHttpClient does not use the server location part in the szUrl.
 * You can omit the server location part of the szUrl.
 *
 * \param hInternet		[in] A valid handle returned by ::InternetOpen. NULL is allowed.
 * \param hConnection	[in] A valid handle returned by ::InternetConnect. NULL is allowed.
 *						     If this parameter is not NULL, the caller has to pass the hInternet parameter
 *						     which is used to get the hConnection parameter.
 * \param szUrl			[in] A HTTP URL.
 * \param dwFlags		[in] A flags which corresponds to the dwFlags parameter of the ::HttpOpenRequest function.
 * \param szReferer		[in] A referer which corresponds to the lpszReferer parameter of the ::HttpOpenRequest function.
 * \param szUsrName		[in] An user name which corresponds to the lpszUsername parameter of the ::InternetConnect function.
 * \param szUsrPwd		[in] An user password which corresponds to the lpszPassword parameter of the ::InternetConnect function.
 * \throw				Throws a httpclientexception if an error occurs.
 *
 * \sa		Query, Proceed, OpenInternet, OpenConnection, ::InternetOpen, ::InternetConnect, ::HttpOpenRequest
 */
template <typename HttpTool, typename HttpEncoder>
void CHttpClientT<HttpTool, HttpEncoder>::BeginUploadEx (HINTERNET hInternet, HINTERNET hConnection, PCSZ szUrl
															, DWORD dwFlags, PCSZ szReferer, PCSZ szUsrName, PCSZ szUsrPwd)
	throw (Exception &)
{
	_StartUploadContext (hInternet, hConnection, szUrl, dwFlags, szReferer, szUsrName, szUsrPwd) ;	// Starts a new UPLOAD context
}

/*!
 * This method starts a new HTTP UPLOAD request which is a HTTP POST request with another content-type (multipart/form-data).
 * If you call this method, you can retrieve progress information of the POST request by using the Query method.
 * To finish the request, you have to call the Proceed method.
 * If the szUrl does not start with "https://", "http" is used as the protocol.
 * If the caller passes the hInternet parameter which is returned by ::InternetOpen,
 * the CHttpClient uses the passed handle instead of creating a new one.
 * The caller is responsible for closing the hInternet handle when it has finished with it.
 *
 * \param hInternet		[in] A valid handle returned by ::InternetOpen. NULL is allowed.
 * \param szUrl			[in] A HTTP URL.
 * \param dwFlags		[in] A flags which corresponds to the dwFlags parameter of the ::HttpOpenRequest function.
 * \param szReferer		[in] A referer which corresponds to the lpszReferer parameter of the ::HttpOpenRequest function.
 * \param szUsrName		[in] An user name which corresponds to the lpszUsername parameter of the ::InternetConnect function.
 * \param szUsrPwd		[in] An user password which corresponds to the lpszPassword parameter of the ::InternetConnect function.
 * \throw				Throws a httpclientexception if an error occurs.
 *
 * \sa		Query, Proceed, OpenInternet, ::InternetOpen, ::InternetConnect, ::HttpOpenRequest
 */
template <typename HttpTool, typename HttpEncoder>
void CHttpClientT<HttpTool, HttpEncoder>::BeginUploadEx (HINTERNET hInternet, PCSZ szUrl, DWORD dwFlags, PCSZ szReferer
														   , PCSZ szUsrName, PCSZ szUsrPwd)
	throw (Exception &)
{
	_StartUploadContext (hInternet, NULL, szUrl, dwFlags, szReferer, szUsrName, szUsrPwd) ;	// Starts a new UPLOAD context
}

/*!
 * This method starts a new HTTP UPLOAD request which is a HTTP POST request with another content-type (multipart/form-data).
 * If you call this method, you can retrieve progress information of the POST request by using the Query method.
 * To finish the request, you have to call the Proceed method.
 * If the szUrl does not start with "https://", "http" is used as the protocol.
 *
 * \param szUrl			[in] A HTTP URL.
 * \param dwFlags		[in] A flags which corresponds to the dwFlags parameter of the ::HttpOpenRequest function.
 * \param szReferer		[in] A referer which corresponds to the lpszReferer parameter of the ::HttpOpenRequest function.
 * \param szUsrName		[in] An user name which corresponds to the lpszUsername parameter of the ::InternetConnect function.
 * \param szUsrPwd		[in] An user password which corresponds to the lpszPassword parameter of the ::InternetConnect function.
 * \throw				Throws a httpclientexception if an error occurs.
 *
 * \sa		Query, Proceed, ::InternetConnect, ::HttpOpenRequest
 */
template <typename HttpTool, typename HttpEncoder>
void CHttpClientT<HttpTool, HttpEncoder>::BeginUploadEx (PCSZ szUrl, DWORD dwFlags, PCSZ szReferer
																	, PCSZ szUsrName, PCSZ szUsrPwd)
	throw (Exception &)
{
	// Starts a new UPLOAD context
	_StartUploadContext (NULL, NULL, szUrl, dwFlags, szReferer, szUsrName, szUsrPwd) ;
}

/*!
 * This method queries progress information of the current POST context.
 * If you call the BeginPost or the BeginUpload method which starts a new POST context,
 * you can retrieve progress information by using this method.
 * If an exception occurs, the current POST context is automatically canceled.
 *
 * \param objPostStat	[in] A CHttpPostStatT object.
 *
 * \sa		CHttpPostStatT, BeginPost, BeginPostEx, BeginUpload, BeginUploadEx, Cancel, Proceed
 */
template <typename HttpTool, typename HttpEncoder>
void CHttpClientT<HttpTool, HttpEncoder>::Query (CHttpPostStat & objPostStat)
	throw ()
{
	if ( _PostContextIsPost () )
		_QueryPostStat (objPostStat) ;
	else
		_QueryUploadStat (objPostStat) ;
}

/*!
 * This method cancels the current POST context which is started by
 * the BeginPost or the BeginUpload method.
 *
 * \return				Whether the operation canceled.
 *
 * \sa		BeginPost, BeginPostEx, BeginUpload, BeginUploadEx, Query, Proceed
 */
template <typename HttpTool, typename HttpEncoder>
BOOL CHttpClientT<HttpTool, HttpEncoder>::Cancel (void)
	throw ()
{
	if ( _PostContextIsPost () )
		return _CancelPostContext () ;

	return _CancelUploadContext () ;
}

/*!
 * This method proceeds with the current POST context which is started by
 * the BeginPost or the BeginUpload method.
 * It transmits data of the current POST context to the web server.
 * If all bytes are transmitted, a pointer to a CHttpResponseT object is returned.
 * The returned pointer is allocated by the new operator.
 * So you have to delete the returned pointer.
 * If an exception occurs, the current POST context is automatically canceled.
 *
 * \param cbDesired		[in] The number of bytes to be transmitted.
 * \return				A pointer to a CHttpResponseT object if all bytes are transmitted,
 *						otherwise NULL.
 * \throw				Throws a httpclientexception if an error occurs.
 *
 * \sa		CHttpResponseT, BeginPost, BeginPostEx, BeginUpload, BeginUploadEx, Query, Cancel
 */
template <typename HttpTool, typename HttpEncoder>
typename CHttpClientT<HttpTool, HttpEncoder>::CHttpResponse * 
CHttpClientT<HttpTool, HttpEncoder>::Proceed (DWORD cbDesired)
	throw (Exception &)
{
	if ( _PostContextIsPost () )
		return _ProceedPostContext (cbDesired) ;

	return _ProceedUploadContext (cbDesired) ;
}

/*!
 * This method retrieves the resource specified by the szUrl using HTTP POST request.
 * If the szUrl does not start with "https://", "http" is used as the protocol.
 * If the caller passes the hInternet parameter which is returned by ::InternetOpen,
 * the CHttpClient uses the passed handle instead of creating a new one.
 * The caller is responsible for closing the hInternet handle when it has finished with it.
 * If the caller passes the hConnection parameter which is returned by ::InternetConnect,
 * the CHttpClient also uses the passed handle instead of creating a new one.
 * In this case, the caller has to pass the hInternet parameter which is used to get the hConnection parameter.
 * The caller is responsible for closing the hConnection handle when it has finished with it.
 * If the hConnection is not NULL, the CHttpClient does not use the server location part in the szUrl.
 * You can omit the server location part of the szUrl.
 * The returned CHttpResponseT object is allocated by the new operator.
 * So you have to delete the returned CHttpResponseT object.
 *
 * \param hInternet		[in] A valid handle returned by ::InternetOpen. NULL is allowed.
 * \param hConnection	[in] A valid handle returned by ::InternetConnect. NULL is allowed.
 *						     If this parameter is not NULL, the caller has to pass the hInternet parameter
 *						     which is used to get the hConnection parameter.
 * \param szUrl			[in] A HTTP URL.
 * \param bUseCache		[in] Specifies whether to use cache.
 * \return				A pointer to a CHttpResponseT object.
 * \throw				Throws a httpclientexception if an error occurs.
 *
 * \sa		CHttpResponseT, OpenInternet, OpenConnection, ::InternetOpen, ::InternetConnect
 */
template <typename HttpTool, typename HttpEncoder>
typename CHttpClientT<HttpTool, HttpEncoder>::CHttpResponse * 
CHttpClientT<HttpTool, HttpEncoder>::RequestPost (HINTERNET hInternet, HINTERNET hConnection, PCSZ szUrl, BOOL bUseCache)
	throw (Exception &)
{
	return RequestPostEx (hInternet, hConnection, szUrl
		, bUseCache ? HTTPCLIENT_DEF_REQUEST_FLAGS : HTTPCLIENT_DEF_REQUEST_FLAGS_NOCACHE) ;
}

/*!
 * This method retrieves the resource specified by the szUrl using HTTP POST request.
 * If the szUrl does not start with "https://", "http" is used as the protocol.
 * If the caller passes the hInternet parameter which is returned by ::InternetOpen,
 * the CHttpClient uses the passed handle instead of creating a new one.
 * The caller is responsible for closing the hInternet handle when it has finished with it.
 * The returned CHttpResponseT object is allocated by the new operator.
 * So you have to delete the returned CHttpResponseT object.
 *
 * \param hInternet		[in] A valid handle returned by ::InternetOpen. NULL is allowed.
 * \param szUrl			[in] A HTTP URL.
 * \param bUseCache		[in] Specifies whether to use cache.
 * \return				A pointer to a CHttpResponseT object.
 * \throw				Throws a httpclientexception if an error occurs.
 *
 * \sa		CHttpResponseT, OpenInternet, ::InternetOpen
 */
template <typename HttpTool, typename HttpEncoder>
typename CHttpClientT<HttpTool, HttpEncoder>::CHttpResponse * 
CHttpClientT<HttpTool, HttpEncoder>::RequestPost (HINTERNET hInternet, PCSZ szUrl, BOOL bUseCache)
	throw (Exception &)
{
	return RequestPostEx (hInternet, szUrl
		, bUseCache ? HTTPCLIENT_DEF_REQUEST_FLAGS : HTTPCLIENT_DEF_REQUEST_FLAGS_NOCACHE) ;
}

/*!
 * This method retrieves the resource specified by the szUrl using HTTP POST request.
 * If the szUrl does not start with "https://", "http" is used as the protocol.
 * The returned CHttpResponseT object is allocated by the new operator.
 * So you have to delete the returned CHttpResponseT object.
 *
 * \param szUrl			[in] A HTTP URL.
 * \param bUseCache		[in] Specifies whether to use cache.
 * \return				A pointer to a CHttpResponseT object.
 * \throw				Throws a httpclientexception if an error occurs.
 *
 * \sa		CHttpResponseT
 */
template <typename HttpTool, typename HttpEncoder>
typename CHttpClientT<HttpTool, HttpEncoder>::CHttpResponse * 
CHttpClientT<HttpTool, HttpEncoder>::RequestPost (PCSZ szUrl, BOOL bUseCache)
	throw (Exception &)
{
	return RequestPostEx (szUrl
		, bUseCache ? HTTPCLIENT_DEF_REQUEST_FLAGS : HTTPCLIENT_DEF_REQUEST_FLAGS_NOCACHE) ;
}

/*!
 * This method retrieves the resource specified by the szUrl using HTTP POST request.
 * If the szUrl does not start with "https://", "http" is used as the protocol.
 * If the caller passes the hInternet parameter which is returned by ::InternetOpen,
 * the CHttpClient uses the passed handle instead of creating a new one.
 * The caller is responsible for closing the hInternet handle when it has finished with it.
 * If the caller passes the hConnection parameter which is returned by ::InternetConnect,
 * the CHttpClient also uses the passed handle instead of creating a new one.
 * In this case, the caller has to pass the hInternet parameter which is used to get the hConnection parameter.
 * The caller is responsible for closing the hConnection handle when it has finished with it.
 * If the hConnection is not NULL, the CHttpClient does not use the server location part in the szUrl.
 * You can omit the server location part of the szUrl.
 * The returned CHttpResponseT object is allocated by the new operator.
 * So you have to delete the returned CHttpResponseT object.
 *
 * \param hInternet		[in] A valid handle returned by ::InternetOpen. NULL is allowed.
 * \param hConnection	[in] A valid handle returned by ::InternetConnect. NULL is allowed.
 *						     If this parameter is not NULL, the caller has to pass the hInternet parameter
 *						     which is used to get the hConnection parameter.
 * \param szUrl			[in] A HTTP URL.
 * \param dwFlags		[in] A flags which corresponds to the dwFlags parameter of the ::HttpOpenRequest function.
 * \param szReferer		[in] A referer which corresponds to the lpszReferer parameter of the ::HttpOpenRequest function.
 * \param szUsrName		[in] An user name which corresponds to the lpszUsername parameter of the ::InternetConnect function.
 * \param szUsrPwd		[in] An user password which corresponds to the lpszPassword parameter of the ::InternetConnect function.
 * \return				A pointer to a CHttpResponseT object.
 * \throw				Throws a httpclientexception if an error occurs.
 *
 * \sa		CHttpResponseT, OpenInternet, OpenConnection, ::InternetOpen, ::InternetConnect, ::HttpOpenRequest
 */
template <typename HttpTool, typename HttpEncoder>
typename CHttpClientT<HttpTool, HttpEncoder>::CHttpResponse * 
CHttpClientT<HttpTool, HttpEncoder>::RequestPostEx (HINTERNET hInternet, HINTERNET hConnection, PCSZ szUrl
													, DWORD dwFlags, PCSZ szReferer, PCSZ szUsrName, PCSZ szUsrPwd)
	throw (Exception &)
{
	BeginPostEx (hInternet, hConnection, szUrl, dwFlags, szReferer, szUsrName, szUsrPwd) ;

	CHttpResponse *			pResponse = NULL ;
	while ( !(pResponse = Proceed (::SafeInt<DWORD>::MaxInt ())) ) ;
	return pResponse ;
}

/*!
 * This method retrieves the resource specified by the szUrl using HTTP POST request.
 * If the szUrl does not start with "https://", "http" is used as the protocol.
 * If the caller passes the hInternet parameter which is returned by ::InternetOpen,
 * the CHttpClient uses the passed handle instead of creating a new one.
 * The caller is responsible for closing the hInternet handle when it has finished with it.
 * The returned CHttpResponseT object is allocated by the new operator.
 * So you have to delete the returned CHttpResponseT object.
 *
 * \param hInternet		[in] A valid handle returned by ::InternetOpen. NULL is allowed.
 * \param szUrl			[in] A HTTP URL.
 * \param dwFlags		[in] A flags which corresponds to the dwFlags parameter of the ::HttpOpenRequest function.
 * \param szReferer		[in] A referer which corresponds to the lpszReferer parameter of the ::HttpOpenRequest function.
 * \param szUsrName		[in] An user name which corresponds to the lpszUsername parameter of the ::InternetConnect function.
 * \param szUsrPwd		[in] An user password which corresponds to the lpszPassword parameter of the ::InternetConnect function.
 * \return				A pointer to a CHttpResponseT object.
 * \throw				Throws a httpclientexception if an error occurs.
 *
 * \sa		CHttpResponseT, OpenInternet, ::InternetOpen, ::InternetConnect, ::HttpOpenRequest
 */
template <typename HttpTool, typename HttpEncoder>
typename CHttpClientT<HttpTool, HttpEncoder>::CHttpResponse * 
CHttpClientT<HttpTool, HttpEncoder>::RequestPostEx (HINTERNET hInternet, PCSZ szUrl, DWORD dwFlags
														  , PCSZ szReferer, PCSZ szUsrName, PCSZ szUsrPwd)
	throw (Exception &)
{
	BeginPostEx (hInternet, szUrl, dwFlags, szReferer, szUsrName, szUsrPwd) ;

	CHttpResponse *			pResponse = NULL ;
	while ( !(pResponse = Proceed (::SafeInt<DWORD>::MaxInt ())) ) ;
	return pResponse ;
}

/*!
 * This method retrieves the resource specified by the szUrl using HTTP POST request.
 * If the szUrl does not start with "https://", "http" is used as the protocol.
 * The returned CHttpResponseT object is allocated by the new operator.
 * So you have to delete the returned CHttpResponseT object.
 *
 * \param szUrl			[in] A HTTP URL.
 * \param dwFlags		[in] A flags which corresponds to the dwFlags parameter of the ::HttpOpenRequest function.
 * \param szReferer		[in] A referer which corresponds to the lpszReferer parameter of the ::HttpOpenRequest function.
 * \param szUsrName		[in] An user name which corresponds to the lpszUsername parameter of the ::InternetConnect function.
 * \param szUsrPwd		[in] An user password which corresponds to the lpszPassword parameter of the ::InternetConnect function.
 * \return				A pointer to a CHttpResponseT object.
 * \throw				Throws a httpclientexception if an error occurs.
 *
 * \sa		CHttpResponseT, ::InternetConnect, ::HttpOpenRequest
 */
template <typename HttpTool, typename HttpEncoder>
typename CHttpClientT<HttpTool, HttpEncoder>::CHttpResponse * 
CHttpClientT<HttpTool, HttpEncoder>::RequestPostEx (PCSZ szUrl, DWORD dwFlags, PCSZ szReferer
																, PCSZ szUsrName, PCSZ szUsrPwd)
	throw (Exception &)
{
	BeginPostEx (szUrl, dwFlags, szReferer, szUsrName, szUsrPwd) ;

	CHttpResponse *			pResponse = NULL ;
	while ( !(pResponse = Proceed (::SafeInt<DWORD>::MaxInt ())) ) ;
	return pResponse ;
}

/*!
 * This method retrieves the resource specified by the szUrl using HTTP UPLOAD request
 * which is a HTTP POST request with another content-type (multipart/form-data).
 * If the szUrl does not start with "https://", "http" is used as the protocol.
 * If the caller passes the hInternet parameter which is returned by ::InternetOpen,
 * the CHttpClient uses the passed handle instead of creating a new one.
 * The caller is responsible for closing the hInternet handle when it has finished with it.
 * If the caller passes the hConnection parameter which is returned by ::InternetConnect,
 * the CHttpClient also uses the passed handle instead of creating a new one.
 * In this case, the caller has to pass the hInternet parameter which is used to get the hConnection parameter.
 * The caller is responsible for closing the hConnection handle when it has finished with it.
 * If the hConnection is not NULL, the CHttpClient does not use the server location part in the szUrl.
 * You can omit the server location part of the szUrl.
 * The returned CHttpResponseT object is allocated by the new operator.
 * So you have to delete the returned CHttpResponseT object.
 *
 * \param hInternet		[in] A valid handle returned by ::InternetOpen. NULL is allowed.
 * \param hConnection	[in] A valid handle returned by ::InternetConnect. NULL is allowed.
 *						     If this parameter is not NULL, the caller has to pass the hInternet parameter
 *						     which is used to get the hConnection parameter.
 * \param szUrl			[in] A HTTP URL.
 * \param bUseCache		[in] Specifies whether to use cache.
 * \return				A pointer to a CHttpResponseT object.
 * \throw				Throws a httpclientexception if an error occurs.
 *
 * \sa		CHttpResponseT, OpenInternet, OpenConnection, ::InternetOpen, ::InternetConnect
 */
template <typename HttpTool, typename HttpEncoder>
typename CHttpClientT<HttpTool, HttpEncoder>::CHttpResponse * 
CHttpClientT<HttpTool, HttpEncoder>::RequestUpload (HINTERNET hInternet, HINTERNET hConnection, PCSZ szUrl, BOOL bUseCache)
	throw (Exception &)
{
	return RequestUploadEx (hInternet, hConnection, szUrl
		, bUseCache ? HTTPCLIENT_DEF_REQUEST_FLAGS : HTTPCLIENT_DEF_REQUEST_FLAGS_NOCACHE) ;
}

/*!
 * This method retrieves the resource specified by the szUrl using HTTP UPLOAD request
 * which is a HTTP POST request with another content-type (multipart/form-data).
 * If the szUrl does not start with "https://", "http" is used as the protocol.
 * If the caller passes the hInternet parameter which is returned by ::InternetOpen,
 * the CHttpClient uses the passed handle instead of creating a new one.
 * The caller is responsible for closing the hInternet handle when it has finished with it.
 * The returned CHttpResponseT object is allocated by the new operator.
 * So you have to delete the returned CHttpResponseT object.
 *
 * \param hInternet		[in] A valid handle returned by ::InternetOpen. NULL is allowed.
 * \param szUrl			[in] A HTTP URL.
 * \param bUseCache		[in] Specifies whether to use cache.
 * \return				A pointer to a CHttpResponseT object.
 * \throw				Throws a httpclientexception if an error occurs.
 *
 * \sa		CHttpResponseT, OpenInternet, ::InternetOpen
 */
template <typename HttpTool, typename HttpEncoder>
typename CHttpClientT<HttpTool, HttpEncoder>::CHttpResponse * 
CHttpClientT<HttpTool, HttpEncoder>::RequestUpload (HINTERNET hInternet, PCSZ szUrl, BOOL bUseCache)
	throw (Exception &)
{
	return RequestUploadEx (hInternet, szUrl
		, bUseCache ? HTTPCLIENT_DEF_REQUEST_FLAGS : HTTPCLIENT_DEF_REQUEST_FLAGS_NOCACHE) ;
}

/*!
 * This method retrieves the resource specified by the szUrl using HTTP UPLOAD request
 * which is a HTTP POST request with another content-type (multipart/form-data).
 * If the szUrl does not start with "https://", "http" is used as the protocol.
 * The returned CHttpResponseT object is allocated by the new operator.
 * So you have to delete the returned CHttpResponseT object.
 *
 * \param szUrl			[in] A HTTP URL.
 * \param bUseCache		[in] Specifies whether to use cache.
 * \return				A pointer to a CHttpResponseT object.
 * \throw				Throws a httpclientexception if an error occurs.
 *
 * \sa		CHttpResponseT
 */
template <typename HttpTool, typename HttpEncoder>
typename CHttpClientT<HttpTool, HttpEncoder>::CHttpResponse * 
CHttpClientT<HttpTool, HttpEncoder>::RequestUpload (PCSZ szUrl, BOOL bUseCache)
	throw (Exception &)
{
	return RequestUploadEx (szUrl
		, bUseCache ? HTTPCLIENT_DEF_REQUEST_FLAGS : HTTPCLIENT_DEF_REQUEST_FLAGS_NOCACHE) ;
}

/*!
 * This method retrieves the resource specified by the szUrl using HTTP UPLOAD request
 * which is a HTTP POST request with another content-type (multipart/form-data).
 * If the szUrl does not start with "https://", "http" is used as the protocol.
 * If the caller passes the hInternet parameter which is returned by ::InternetOpen,
 * the CHttpClient uses the passed handle instead of creating a new one.
 * The caller is responsible for closing the hInternet handle when it has finished with it.
 * If the caller passes the hConnection parameter which is returned by ::InternetConnect,
 * the CHttpClient also uses the passed handle instead of creating a new one.
 * In this case, the caller has to pass the hInternet parameter which is used to get the hConnection parameter.
 * The caller is responsible for closing the hConnection handle when it has finished with it.
 * If the hConnection is not NULL, the CHttpClient does not use the server location part in the szUrl.
 * You can omit the server location part of the szUrl.
 * The returned CHttpResponseT object is allocated by the new operator.
 * So you have to delete the returned CHttpResponseT object.
 *
 * \param hInternet		[in] A valid handle returned by ::InternetOpen. NULL is allowed.
 * \param hConnection	[in] A valid handle returned by ::InternetConnect. NULL is allowed.
 *						     If this parameter is not NULL, the caller has to pass the hInternet parameter
 *						     which is used to get the hConnection parameter.
 * \param szUrl			[in] A HTTP URL.
 * \param dwFlags		[in] A flags which corresponds to the dwFlags parameter of the ::HttpOpenRequest function.
 * \param szReferer		[in] A referer which corresponds to the lpszReferer parameter of the ::HttpOpenRequest function.
 * \param szUsrName		[in] An user name which corresponds to the lpszUsername parameter of the ::InternetConnect function.
 * \param szUsrPwd		[in] An user password which corresponds to the lpszPassword parameter of the ::InternetConnect function.
 * \return				A pointer to a CHttpResponseT object.
 * \throw				Throws a httpclientexception if an error occurs.
 *
 * \sa		CHttpResponseT, OpenInternet, OpenConnection, ::InternetOpen, ::InternetConnect, ::HttpOpenRequest
 */
template <typename HttpTool, typename HttpEncoder>
typename CHttpClientT<HttpTool, HttpEncoder>::CHttpResponse * 
CHttpClientT<HttpTool, HttpEncoder>::RequestUploadEx (HINTERNET hInternet, HINTERNET hConnection, PCSZ szUrl, DWORD dwFlags, PCSZ szReferer, PCSZ szUsrName, PCSZ szUsrPwd)
	throw (Exception &)
{
	BeginUploadEx (hInternet, hConnection, szUrl, dwFlags, szReferer, szUsrName, szUsrPwd) ;

	CHttpResponse *			pResponse = NULL ;
	while ( !(pResponse = Proceed (::SafeInt<DWORD>::MaxInt ())) ) ;
	return pResponse ;
}

/*!
 * This method retrieves the resource specified by the szUrl using HTTP UPLOAD request
 * which is a HTTP POST request with another content-type (multipart/form-data).
 * If the szUrl does not start with "https://", "http" is used as the protocol.
 * If the caller passes the hInternet parameter which is returned by ::InternetOpen,
 * the CHttpClient uses the passed handle instead of creating a new one.
 * The caller is responsible for closing the hInternet handle when it has finished with it.
 * The returned CHttpResponseT object is allocated by the new operator.
 * So you have to delete the returned CHttpResponseT object.
 *
 * \param hInternet		[in] A valid handle returned by ::InternetOpen. NULL is allowed.
 * \param szUrl			[in] A HTTP URL.
 * \param dwFlags		[in] A flags which corresponds to the dwFlags parameter of the ::HttpOpenRequest function.
 * \param szReferer		[in] A referer which corresponds to the lpszReferer parameter of the ::HttpOpenRequest function.
 * \param szUsrName		[in] An user name which corresponds to the lpszUsername parameter of the ::InternetConnect function.
 * \param szUsrPwd		[in] An user password which corresponds to the lpszPassword parameter of the ::InternetConnect function.
 * \return				A pointer to a CHttpResponseT object.
 * \throw				Throws a httpclientexception if an error occurs.
 *
 * \sa		CHttpResponseT, OpenInternet, ::InternetOpen, ::InternetConnect, ::HttpOpenRequest
 */
template <typename HttpTool, typename HttpEncoder>
typename CHttpClientT<HttpTool, HttpEncoder>::CHttpResponse * 
CHttpClientT<HttpTool, HttpEncoder>::RequestUploadEx (HINTERNET hInternet, PCSZ szUrl, DWORD dwFlags, PCSZ szReferer, PCSZ szUsrName, PCSZ szUsrPwd)
	throw (Exception &)
{
	BeginUploadEx (hInternet, szUrl, dwFlags, szReferer, szUsrName, szUsrPwd) ;

	CHttpResponse *			pResponse = NULL ;
	while ( !(pResponse = Proceed (::SafeInt<DWORD>::MaxInt ())) ) ;
	return pResponse ;
}

/*!
 * This method retrieves the resource specified by the szUrl using HTTP UPLOAD request
 * which is a HTTP POST request with another content-type (multipart/form-data).
 * If the szUrl does not start with "https://", "http" is used as the protocol.
 * The returned CHttpResponseT object is allocated by the new operator.
 * So you have to delete the returned CHttpResponseT object.
 *
 * \param szUrl			[in] A HTTP URL.
 * \param dwFlags		[in] A flags which corresponds to the dwFlags parameter of the ::HttpOpenRequest function.
 * \param szReferer		[in] A referer which corresponds to the lpszReferer parameter of the ::HttpOpenRequest function.
 * \param szUsrName		[in] An user name which corresponds to the lpszUsername parameter of the ::InternetConnect function.
 * \param szUsrPwd		[in] An user password which corresponds to the lpszPassword parameter of the ::InternetConnect function.
 * \return				A pointer to a CHttpResponseT object.
 * \throw				Throws a httpclientexception if an error occurs.
 *
 * \sa		CHttpResponseT, ::InternetConnect, ::HttpOpenRequest
 */
template <typename HttpTool, typename HttpEncoder>
typename CHttpClientT<HttpTool, HttpEncoder>::CHttpResponse * 
CHttpClientT<HttpTool, HttpEncoder>::RequestUploadEx (PCSZ szUrl, DWORD dwFlags, PCSZ szReferer, PCSZ szUsrName, PCSZ szUsrPwd)
	throw (Exception &)
{
	BeginUploadEx (szUrl, dwFlags, szReferer, szUsrName, szUsrPwd) ;

	CHttpResponse *			pResponse = NULL ;
	while ( !(pResponse = Proceed (::SafeInt<DWORD>::MaxInt ())) ) ;
	return pResponse ;
}


template <typename HttpTool, typename HttpEncoder>
void CHttpClientT<HttpTool, HttpEncoder>::_InitPostContext (void)
	throw ()
{
	m_ahPostedFiles = NULL ;
	m_aszMimeTypes = NULL ;
	m_szPostedValue = NULL ;
	m_bNeedToFreePostedValue = FALSE ;
	m_hInternet = NULL ;
	m_bBorrowedInternet = FALSE ;
	m_hConnection = NULL ;
	m_bBorrowedConnection = FALSE ;
	m_hRequest = NULL ;
	m_bIsPost = TRUE ;
	m_pbyCntxBuff = NULL ;
}

template <typename HttpTool, typename HttpEncoder>
BOOL CHttpClientT<HttpTool, HttpEncoder>::_PostContextIsActive (void) const
	throw ()
{
	return m_objPostStat.IsActive () ;
}

template <typename HttpTool, typename HttpEncoder>
BOOL CHttpClientT<HttpTool, HttpEncoder>::_PostContextIsPost (void) const
	throw ()
{
	return m_bIsPost ;
}

template <typename HttpTool, typename HttpEncoder>
void CHttpClientT<HttpTool, HttpEncoder>::_SetPostedValue (LPCSTR szPostedValue, BOOL bNeedToFree)
	throw ()
{
	if ( m_bNeedToFreePostedValue )
		SAFEFREE (m_szPostedValue) ;

	m_szPostedValue = szPostedValue ;
	m_bNeedToFreePostedValue = bNeedToFree ;
}

template <typename HttpTool, typename HttpEncoder>
void CHttpClientT<HttpTool, HttpEncoder>::_InitPostCntxBuff (void)
	throw (Exception &)
{
	// If the buffer is aleady allocated
	if ( m_pbyCntxBuff )
		return ;

	if ( NULL == (m_pbyCntxBuff = (BYTE *) ::malloc (HTTPCLIENT_POSTCNTX_BUFF_SIZE)) )
		HttpTool::ThrowException (HTTPCLIENT_ERR_OUT_OF_MEMORY) ;
}

template <typename HttpTool, typename HttpEncoder>
void CHttpClientT<HttpTool, HttpEncoder>::_WritePost (BOOL bUseUtf8, PCSZ szValue, BOOL bIsValue)
	throw (Exception &)
{
	if ( szValue == NULL || szValue[0] == '\0' )
		return ;

	PCSTR		szValueA = NULL ;
	BOOL		bNeedToFreeValueA = FALSE ;

	try {
		if ( bUseUtf8 ) {
			szValueA = _Utf8Encode (szValue) ;
			bNeedToFreeValueA = TRUE ;
		} else
			szValueA = _String2Ansi (szValue, bNeedToFreeValueA) ;

		_WritePost (szValueA, bIsValue) ;
	} catch (Exception &) {
		if ( bNeedToFreeValueA )
			SAFEFREE (szValueA) ;
		throw ;
	}

	if ( bNeedToFreeValueA )
		SAFEFREE (szValueA) ;
}

template <typename HttpTool, typename HttpEncoder>
void CHttpClientT<HttpTool, HttpEncoder>::_WritePost (LPCSTR szValue, BOOL bIsValue)
	throw (Exception &)
{
	HTTPCLIENT_ASSERT (m_hRequest != NULL, "CHttpClientT::_WritePost: m_hRequest can not be NULL.") ;

	if ( szValue == NULL )
		return ;

	::SafeInt<DWORD>		cbValue ;
	try {
		cbValue = ::strlen (szValue) ;
	} catch (::SafeIntException & e) {
		HttpTool::ThrowException (e) ;
	}

	_WritePost (reinterpret_cast<const BYTE *> (szValue), cbValue.Value (), bIsValue) ;
}

template <typename HttpTool, typename HttpEncoder>
void CHttpClientT<HttpTool, HttpEncoder>::_WritePost (const BYTE * pbyBuff, DWORD cbyBuff, BOOL bIsValue)
	throw (Exception &)
{
	HTTPCLIENT_ASSERT (m_hRequest != NULL, "CHttpClientT::_WritePost: m_hRequest can not be NULL.") ;

	if ( pbyBuff == NULL || cbyBuff == 0 )
		return ;

	// Before increasing values. Checks overflow exception.
	if ( bIsValue )		m_objPostStat._TestAddPostedBytes (cbyBuff) ;
	else				m_objPostStat._TestAddActualPostedBytes (cbyBuff) ;

	HttpTool::InternetWriteFile (m_hRequest, pbyBuff, cbyBuff) ;

	if ( bIsValue )		m_objPostStat._AddPostedBytes (cbyBuff) ;
	else				m_objPostStat._AddActualPostedBytes (cbyBuff) ;
}

template <typename HttpTool, typename HttpEncoder>
void CHttpClientT<HttpTool, HttpEncoder>::_StartPostContext (HINTERNET hInternet, HINTERNET hConnection, PCSZ szUrl, DWORD dwFlags, PCSZ szReferer
																		, PCSZ szUsrName, PCSZ szUsrPwd)
	throw (Exception &)
{
	// Closes the POST context if it exists.
	_EndPostContext () ;

	BOOL		bBorrowedInternet = TRUE ;
	BOOL		bBorrowedConnection = TRUE ;
	HINTERNET	hRequest = NULL ;

	try {
		// Calculates the total number of bytes to upload
		::SafeInt<size_t>	nTotalByte = 0 ;
		::SafeInt<size_t>	nValuesTotalByte = 0 ;
		PCSZ				szFirstParamName = NULL ;
		size_t				cbFirstParamValue = 0 ;

		if ( !m_mapParam.Empty () ) {
			ConstMapIter		iter ;

			size_t		cbValue ;
			for (iter = m_mapParam.Begin (); iter != m_mapParam.End (); ++iter) {
				nTotalByte += 1 ;		// '&'

				if ( (iter->second).dwFlag & ParamEncodedName )
					nTotalByte += _String2AnsiLen (iter->first) ;
				else
					nTotalByte += _UrlEncodeLenA (iter->first) ;

				nTotalByte += 1 ;		// '='

				if ( (iter->second).dwFlag & ParamEncodedValue )
					cbValue = _String2AnsiLen ((iter->second).szValue) ;
				else
					cbValue = _UrlEncodeLenA ((iter->second).szValue) ;

				nTotalByte += cbValue ;
				nValuesTotalByte += cbValue ;

				// If the item is the first item
				if ( iter == m_mapParam.Begin () ) {
					szFirstParamName = iter->first ;
					cbFirstParamValue = cbValue ;
				}
			}

			if ( nTotalByte > 0 )
				nTotalByte-- ;		// for the first '&'
		}

		::SafeInt<DWORD>		dwTotalByte = nTotalByte ;

		// Get WinInet handles
		if ( hInternet == NULL ) {
			hInternet = OpenInternet () ;
			bBorrowedInternet = FALSE ;
		}

		if ( hConnection == NULL ) {
			hConnection = OpenConnection (hInternet, szUrl, szUsrName, szUsrPwd) ;
			bBorrowedConnection = FALSE ;
		}

		hRequest = OpenRequest (hConnection, HttpTool::szPost, szUrl, dwFlags, szReferer) ;
		AddRequestHeader (hRequest) ;			// Adds user's custom header

		// Adds the Content-Type header
		HttpTool::AddHeader (hRequest, HttpTool::szContentType, HttpTool::szFormUrlEncoded, m_nAnsiCodePage) ;

		// Connects to the HTTP server
		HttpTool::SendRequestEx (hRequest, dwTotalByte.Value ()) ;

		// Starts a new POST state
		m_objPostStat._MakeActive (nTotalByte.Value (), nValuesTotalByte.Value (), m_mapParam.Count (), 0) ;

		m_bBorrowedInternet = bBorrowedInternet ;
		m_hInternet = hInternet ;
		m_bBorrowedConnection = bBorrowedConnection ;
		m_hConnection = hConnection ;
		m_hRequest = hRequest ;
		m_bIsPost = TRUE ;

		// Initializes the initial POST state
		if ( !m_mapParam.Empty () ) {
			m_objInitialStat = m_objPostStat ;
			// It always does not throw an overflow exception.
			// So it's safe. (doesn't need to restore the internal states)
			m_objInitialStat._StartNewEntry (szFirstParamName, cbFirstParamValue) ;	
		}

	} catch (::SafeIntException & e) {
		HttpTool::CloseRequest (hRequest) ;
		if ( !bBorrowedConnection )	HttpTool::CloseRequest (hConnection) ;
		if ( !bBorrowedInternet )	HttpTool::CloseRequest (hInternet) ;
		HttpTool::ThrowException (e) ;
	} catch (Exception &) {
		HttpTool::CloseRequest (hRequest) ;
		if ( !bBorrowedConnection )	HttpTool::CloseRequest (hConnection) ;
		if ( !bBorrowedInternet )	HttpTool::CloseRequest (hInternet) ;
		throw ;
	}
}

template <typename HttpTool, typename HttpEncoder>
void CHttpClientT<HttpTool, HttpEncoder>::_QueryPostStat (CHttpPostStat & objPostStat)
	throw ()
{
	// If the _ProceedPOSTContext method is not called yet,
	if ( (m_objPostStat.IsActive ()) && (m_objPostStat.TotalCount () > 0) 
									&& (m_objPostStat.PostedCount () == 0) )
		objPostStat = m_objInitialStat ;
	else
		objPostStat = m_objPostStat ;
}

template <typename HttpTool, typename HttpEncoder>
BOOL CHttpClientT<HttpTool, HttpEncoder>::_CancelPostContext (void)
	throw ()
{
	if ( !m_objPostStat.IsActive () )
		return FALSE ;

	// Cancels the POST context
	_EndPostContext () ;
	return TRUE ;
}

// If all parameters are sent, returns a pointer to the CHttpResponseT.
template <typename HttpTool, typename HttpEncoder>
typename CHttpClientT<HttpTool, HttpEncoder>::CHttpResponse * 
CHttpClientT<HttpTool, HttpEncoder>::_ProceedPostContext (DWORD nDesired)
	throw (Exception &)
{
	// If the Post context is not started
	if ( !m_objPostStat.IsActive () )
		HttpTool::ThrowException (HTTPCLIENT_ERR_POST_NOT_STARTED) ;

	HTTPCLIENT_ASSERT (m_hInternet != NULL, "CHttpClientT::_ProceedPostContext: m_hInternet can not be NULL.") ;
	HTTPCLIENT_ASSERT (m_hConnection != NULL, "CHttpClientT::_ProceedPostContext: m_hConnection can not be NULL.") ;
	HTTPCLIENT_ASSERT (m_hRequest != NULL, "CHttpClientT::_ProceedPostContext: m_hRequest can not be NULL.") ;
	HTTPCLIENT_ASSERT (nDesired != 0, "CHttpClientT::_ProceedPostContext: nDesired can not be zero.") ;

	try {
		// If the current parameter is completed,
		if ( m_objPostStat.CurrParamIsComplete () ) {

			// If all parameters are sent
			if ( m_objPostStat.TotalCount () == m_objPostStat.PostedCount () ) {
				HttpTool::EndRequest (m_hRequest) ;

				// Releases the POST context
				return _ReleasePostResponse () ;
			}

			DWORD			nNextIdx = m_objPostStat.PostedCount () ;

			ConstMapIter	iter = m_mapParam.Begin () ;
			for (DWORD i = 0; i < nNextIdx; i++, ++iter) ;

			if ( nNextIdx != 0 )
				_WritePost ("&") ;

			// Writes the parameter name
			{
				PSTR			szNameA = NULL ;
				BOOL			bNeedToFreeNameA = FALSE ;

				try {
					if ( (iter->second).dwFlag & ParamEncodedName ) {
						szNameA = const_cast<PSTR> (_String2Ansi (iter->first, bNeedToFreeNameA)) ;
						_WritePost (szNameA) ;
					} else {
						::SafeInt<size_t>		cbRequired = _UrlEncodeLenA (iter->first) ;

						if ( cbRequired > 0 ) {
							cbRequired += 1 ;
							cbRequired *= sizeof (CHAR) ;

							if ( NULL == (szNameA = (PSTR) ::malloc (cbRequired.Value ())) )
								HttpTool::ThrowException (HTTPCLIENT_ERR_OUT_OF_MEMORY) ;
							bNeedToFreeNameA = TRUE ;
							_UrlEncodeA (szNameA, iter->first) ;
							_WritePost (szNameA) ;
						}
					}
				} catch (::SafeIntException & e) {
					if ( bNeedToFreeNameA )
						SAFEFREE (szNameA) ;
					HttpTool::ThrowException (e) ;
				} catch (Exception &) {
					if ( bNeedToFreeNameA )
						SAFEFREE (szNameA) ;
					throw ;
				}

				if ( bNeedToFreeNameA ) {
					SAFEFREE (szNameA) ;
					bNeedToFreeNameA = FALSE ;
				}
			}

			_WritePost ("=") ;

			size_t			cbValue = 0 ;

			// Sets the value
			if ( (iter->second).szValue ) {
				PSTR			szValueA = NULL ;
				BOOL			bNeedToFreeValueA = FALSE ;

				if ( (iter->second).dwFlag & ParamEncodedValue ) {
					szValueA = const_cast<PSTR> (_String2Ansi ((iter->second).szValue, bNeedToFreeValueA)) ;
					cbValue = szValueA ? CHttpToolA::StringLen (szValueA) : 0 ;
				} else {
					cbValue = _UrlEncodeLenA ((iter->second).szValue) ;

					if ( cbValue > 0 ) {
						::SafeInt<size_t>	cbRequired = cbValue ;

						try {
							cbRequired += 1 ;
							cbRequired *= sizeof (CHAR) ;
						} catch (::SafeIntException & e) {
							HttpTool::ThrowException (e) ;
						}

						if ( NULL == (szValueA = static_cast<PSTR> (::malloc (cbRequired.Value ()))) )
							HttpTool::ThrowException (HTTPCLIENT_ERR_OUT_OF_MEMORY) ;
						bNeedToFreeValueA = TRUE ;

						try {
							_UrlEncodeA (szValueA, (iter->second).szValue) ;
						} catch (Exception &) {
							if ( bNeedToFreeValueA )
								SAFEFREE (szValueA) ;
							throw ;
						}
					}
				}

				_SetPostedValue (szValueA, bNeedToFreeValueA) ;
			}

			// Starts a new parameter
			m_objPostStat._StartNewEntry (iter->first, cbValue) ;

			return NULL ;
		}

		DWORD			cbToWrite = nDesired ;

		if ( cbToWrite > m_objPostStat.CurrParamRemainByte () )
			cbToWrite = static_cast<DWORD> (m_objPostStat.CurrParamRemainByte ()) ;

		_WritePost (
			reinterpret_cast<const BYTE *> (m_szPostedValue + m_objPostStat.CurrParamPostedByte ())
			, cbToWrite, TRUE) ;

	} catch (Exception &) {
		_EndPostContext () ;				// Aborts the Post Context
		throw ;
	}

	return NULL ;
}

template <typename HttpTool, typename HttpEncoder>
typename CHttpClientT<HttpTool, HttpEncoder>::PCSZ 
CHttpClientT<HttpTool, HttpEncoder>::_GetUploadBoundary (void)
	throw ()
{
	return m_szBoundary ? m_szBoundary : HttpTool::szDefBoundary ;
}

template <typename HttpTool, typename HttpEncoder>
LPCSTR CHttpClientT<HttpTool, HttpEncoder>::_GetUploadBoundaryA (void)
	throw ()
{
	return m_szBoundaryA ? m_szBoundaryA : CHttpToolA::szDefBoundary ;
}

template <typename HttpTool, typename HttpEncoder>
typename CHttpClientT<HttpTool, HttpEncoder>::PCSZ 
CHttpClientT<HttpTool, HttpEncoder>::_GetUploadContType (void)
	throw ()
{
	return m_szUploadContType ? m_szUploadContType : HttpTool::szDefUploadContType ;
}

template <typename HttpTool, typename HttpEncoder>
void CHttpClientT<HttpTool, HttpEncoder>::_StartUploadContext (HINTERNET hInternet, HINTERNET hConnection, PCSZ szUrl
																	 , DWORD dwFlags, PCSZ szReferer, PCSZ szUsrName, PCSZ szUsrPwd)
	throw (Exception &)
{
	// Closes any existing Post Context
	_EndPostContext () ;

	BOOL				bBorrowedInternet = TRUE ;
	BOOL				bBorrowedConnection = TRUE ;
	HINTERNET			hRequest = NULL ;

	HANDLE *			ahFileHandles = NULL ;
	LPSTR *				aszMimeTypes = NULL ;
	::SafeInt<DWORD>	nPostedFileCount = 0 ;

	try {
		// Calculates the nubmer of bytes to upload
		::SafeInt<size_t>	nTotalByte = 0 ;
		::SafeInt<size_t>	nValuesTotalByte = 0 ;
		size_t				cchBoundary = HttpTool::StringLen (_GetUploadBoundary ()) ;

		PCSZ				szFirstParamName = NULL ;
		PCSZ				szFirstParamFileName = NULL ;
		size_t				cbFirstParamValue = 0 ;
		BOOL				bFirstParamIsFile = FALSE ;

		if ( !m_mapParam.Empty () ) {
			try {
				ConstMapIter		iter ;

				// Get the number of files
				for (iter = m_mapParam.Begin (); iter != m_mapParam.End (); ++iter) {
					if ( (iter->second).dwFlag & ParamFile )
						nPostedFileCount++ ;
				}

				if ( nPostedFileCount.Value () ) {
					// Allocates memory for handles and MimeTypes of the uploaded files
					ahFileHandles = (HANDLE *) ::calloc (nPostedFileCount.Value (), sizeof (HANDLE)) ;
					if ( ahFileHandles == NULL )
						HttpTool::ThrowException (HTTPCLIENT_ERR_OUT_OF_MEMORY) ;

					// Initializes the file handles
					for (DWORD i = 0; i < nPostedFileCount; i++)
						ahFileHandles[i] = INVALID_HANDLE_VALUE ;

					aszMimeTypes = (LPSTR *) ::calloc (nPostedFileCount.Value (), sizeof (LPSTR)) ;
					if ( aszMimeTypes == NULL )
						HttpTool::ThrowException (HTTPCLIENT_ERR_OUT_OF_MEMORY) ;
				}

				/*
					Calculates the total upload size

					<1> The number of bytes to upload If the parameter is not a file
						= strlen ("--") + strlen (Boundary) + strlen ("\r\n") 
							+ strlen ("Content-Disposition: form-data; name=\"\"\r\n\r\n")
							+ strlen (The name of the parameter) + strlen (The value of the parameter) + strlen ("\r\n") ;

					<2> The number of bytes to upload If the parameter is a file
						= strlen ("--") + strlen (Boundary) + strlen ("\r\n") 
							+ strlen ("Content-Disposition: form-data; name=\"\"; filename=\"\"\r\n")
							+ strlen (The name of the parameter) + strlen (The value of the parameter)
							+ strlen ("Content-Type: \r\n\r\n") + strlen (The Mime Type of the file)
							+ The length of the file + strlen ("\r\n")

					The last boundary
						= strlen ("--") + strlen (Boundary) + strlen ("--\r\n") 

					The total upload size
						= ( <1> * The number of normal parameters)
							+ ( <2> * The number of file parameters)
							+ The last boundary
				*/

				::SafeInt<size_t>	cbValue ;
				DWORD				nFileIdx = 0 ;

				nTotalByte = 0 ;
				nValuesTotalByte = 0 ;
				for (iter = m_mapParam.Begin (); iter != m_mapParam.End (); ++iter) {
					if ( !((iter->second).dwFlag & ParamFile) ) {
						// If the parameter is not a file
						//	= strlen ("--") + strlen (Boundary) + strlen ("\r\n") 
						//		+ strlen ("Content-Disposition: form-data; name=\"\"\r\n\r\n")
						//		+ strlen (The name of the parameter) + strlen (The value of the parameter) + strlen ("\r\n") ;
						nTotalByte += (cchBoundary + 4) ;
						nTotalByte += 43 ;

						if ( m_bUseUtf8 ) {
							nTotalByte += _Utf8EncodeLen (iter->first) ;
							cbValue = _Utf8EncodeLen ((iter->second).szValue) ;
						} else {
							nTotalByte += _String2AnsiLen (iter->first) ;
							cbValue = _String2AnsiLen ((iter->second).szValue) ;
						}

						nTotalByte += cbValue ;
						nValuesTotalByte += cbValue ;

						nTotalByte += 2 ;

						// Saves the state of the first parameter
						if ( iter == m_mapParam.Begin () ) {
							szFirstParamName = iter->first ;
							cbFirstParamValue = cbValue.Value () ;
							bFirstParamIsFile = FALSE ;
							szFirstParamFileName = NULL ;
						}
					} else {
						// If the parameter is a file
						//	= strlen ("--") + strlen (Boundary) + strlen ("\r\n") 
						//		+ strlen ("Content-Disposition: form-data; name=\"\"; filename=\"\"\r\n")
						//		+ strlen (The name of the parameter) + strlen (The value of the parameter)
						//		+ strlen ("Content-Type: \r\n\r\n") + strlen (The Mime Type of the file)
						//		+ The length of the file + strlen ("\r\n")
						nTotalByte += (cchBoundary + 4) ;
						nTotalByte += 54 ;

						if ( m_bUseUtf8 ) {
							nTotalByte += _Utf8EncodeLen (iter->first) ;
							nTotalByte += _Utf8EncodeLen ((iter->second).szValue) ;
						} else {
							nTotalByte += _String2AnsiLen (iter->first) ;
							nTotalByte += _String2AnsiLen ((iter->second).szValue) ;
						}

						nTotalByte += 18 ;

						// Get the file size and MimeType
						cbValue = 0 ;
						if ( (iter->second).szValue ) {
							// Open the file
							ahFileHandles[nFileIdx] = HttpTool::OpenFile ((iter->second).szValue) ;

							// Get the file size
							if ( ahFileHandles[nFileIdx] != INVALID_HANDLE_VALUE )
								cbValue = HttpTool::GetFileSize (ahFileHandles[nFileIdx], (iter->second).szValue) ;
						}

						// Throws an exception
						if ( m_bStrictFileCheck && (ahFileHandles[nFileIdx] == INVALID_HANDLE_VALUE) )
							HttpTool::ThrowException (HTTPCLIENT_ERR_OPENFILE_FAILED, ::GetLastError (), (iter->second).szValue) ;

						// Get the MimeType of the file
						aszMimeTypes[nFileIdx] = HttpTool::GetMimeType (ahFileHandles[nFileIdx], m_nAnsiCodePage) ;
						nTotalByte += CHttpToolA::StringLen (aszMimeTypes[nFileIdx]) ;
						nTotalByte += cbValue ;
						nValuesTotalByte += cbValue ;
						nTotalByte += 2 ;
						nFileIdx++ ;

						// Saves the state of the first parameter
						if ( iter == m_mapParam.Begin () ) {
							szFirstParamName = iter->first ;
							cbFirstParamValue = cbValue.Value () ;
							bFirstParamIsFile = TRUE ;
							szFirstParamFileName = (iter->second).szValue ;
						}
					}
				}

				// The last boundary
				//	= strlen ("--") + strlen (Boundary) + strlen ("--\r\n") 
				nTotalByte += (cchBoundary + 6) ;
			} catch (::SafeIntException & e) {
				HttpTool::ThrowException (e) ;
			}

		} else {
			// The total upload size
			//  = (strlen ("\r\n") + strlen ("--") + strlen (Boundary) + strlen ("--\r\n")
			nTotalByte = cchBoundary + 8 ;
			nValuesTotalByte = 0 ;
		}

		::SafeInt<DWORD>	dwTotalByte ;
		try {
			dwTotalByte = nTotalByte ;
		} catch (::SafeIntException & e) {
			HttpTool::ThrowException (e) ;
		}

		// Get WinInet handles
		if ( hInternet == NULL ) {
			hInternet = OpenInternet () ;
			bBorrowedInternet = FALSE ;
		}

		if ( hConnection == NULL ) {
			hConnection = OpenConnection (hInternet, szUrl, szUsrName, szUsrPwd) ;
			bBorrowedConnection = FALSE ;
		}

		hRequest = OpenRequest (hConnection, HttpTool::szPost, szUrl, dwFlags, szReferer) ;
		AddRequestHeader (hRequest) ;			// Adds user's custom header

		// Adds the Content-Type header
		HttpTool::AddHeader (hRequest, HttpTool::szContentType, _GetUploadContType (), m_nAnsiCodePage) ;

		// Make a connection to the server
		HttpTool::SendRequestEx (hRequest, dwTotalByte.Value ()) ;

		// Activates the Post Context
		m_objPostStat._MakeActive (nTotalByte.Value (), nValuesTotalByte.Value (), m_mapParam.Count (), nPostedFileCount.Value ()) ;
		m_bBorrowedInternet = bBorrowedInternet ;
		m_hInternet = hInternet ;
		m_bBorrowedConnection = bBorrowedConnection ;
		m_hConnection = hConnection ;
		m_hRequest = hRequest ;
		m_ahPostedFiles = ahFileHandles ;
		ahFileHandles = NULL ;
		m_aszMimeTypes = aszMimeTypes ;
		aszMimeTypes = NULL ;
		m_bIsPost = FALSE ;

		// Saves the initial Post context
		if ( !m_mapParam.Empty () ) {
			m_objInitialStat = m_objPostStat ;
			// It always does not throw an overflow exception.
			// So it's safe. (doesn't need to restore the internal states)
			m_objInitialStat._StartNewEntry (szFirstParamName, cbFirstParamValue
										, bFirstParamIsFile, szFirstParamFileName) ;
		}

	} catch (Exception &) {
		HttpTool::CloseRequest (hRequest) ;
		if ( !bBorrowedConnection )	HttpTool::CloseRequest (hConnection) ;
		if ( !bBorrowedInternet )	HttpTool::CloseRequest (hInternet) ;

		for (DWORD i = 0; i < nPostedFileCount; i++) {
			if ( ahFileHandles ) {
				if ( ahFileHandles[i] != INVALID_HANDLE_VALUE ) {
					::CloseHandle (ahFileHandles[i]) ;
					ahFileHandles[i] = INVALID_HANDLE_VALUE ;
				}
			}

			if ( aszMimeTypes )
				SAFEFREE ( (aszMimeTypes[i]) ) ;
		}
		SAFEFREE (ahFileHandles) ;
		SAFEFREE (aszMimeTypes) ;
		throw ;
	}
}

template <typename HttpTool, typename HttpEncoder>
void CHttpClientT<HttpTool, HttpEncoder>::_QueryUploadStat (CHttpPostStat & objPostStat)
	throw ()
{
	// If the _ProceedUploadContext method is not called yet,
	if ( (m_objPostStat.IsActive ()) && (m_objPostStat.TotalCount () > 0)
										&& (m_objPostStat.PostedCount () == 0) )
		objPostStat = m_objInitialStat ;
	else
		objPostStat = m_objPostStat ;
}

template <typename HttpTool, typename HttpEncoder>
BOOL CHttpClientT<HttpTool, HttpEncoder>::_CancelUploadContext (void)
	throw ()
{
	if ( !m_objPostStat.IsActive () )
		return FALSE ;

	// Cancels the Post Context
	_EndPostContext () ;
	return TRUE ;
}

// If all parameters are sent, returns a pointer to the CHttpResponseT.
template <typename HttpTool, typename HttpEncoder>
typename CHttpClientT<HttpTool, HttpEncoder>::CHttpResponse * 
CHttpClientT<HttpTool, HttpEncoder>::_ProceedUploadContext (DWORD nDesired)
	throw (Exception &)
{
	// If the Post context is not started
	if ( !m_objPostStat.IsActive () )
		HttpTool::ThrowException (HTTPCLIENT_ERR_POST_NOT_STARTED) ;

	HTTPCLIENT_ASSERT (m_hInternet != NULL, "CHttpClientT::_ProceedUploadContext: m_hInternet can not be NULL.") ;
	HTTPCLIENT_ASSERT (m_hConnection != NULL, "CHttpClientT::_ProceedUploadContext: m_hConnection can not be NULL.") ;
	HTTPCLIENT_ASSERT (m_hRequest != NULL, "CHttpClientT::_ProceedUploadContext: m_hRequest can not be NULL.") ;
	HTTPCLIENT_ASSERT (nDesired != 0, "CHttpClientT::_ProceedUploadContext: nDesired can not be zero.") ;

	try {
		// If all parameters are posted
		// releases the Post context
		if ( m_objPostStat._IsComplete () ) {
			HttpTool::EndRequest (m_hRequest) ;
			return _ReleasePostResponse () ;
		}

		LPCSTR		szBoundary = _GetUploadBoundaryA () ;

		// If there is no parameter to upload
		if ( m_objPostStat.TotalCount () == 0 ) {
			// Writes the last boundary
			_WritePost ("\r\n--") ;
			_WritePost (szBoundary) ;
			_WritePost ("--\r\n") ;
			return NULL ;
		}

		// If the current parameter is completed
		if ( m_objPostStat.CurrParamIsComplete () ) {

			// If all parameters are sent
			if ( m_objPostStat.TotalCount () == m_objPostStat.PostedCount () ) {
				// Writes the last boundary
				_WritePost ("--") ;
				_WritePost (szBoundary) ;
				_WritePost ("--\r\n") ;
				return NULL ;
			}

			DWORD				nNextIdx = m_objPostStat.PostedCount () ;
			DWORD				nNextFileIdx = m_objPostStat.PostedFileCount () ;
			PCSZ				szEntryFile = NULL ;
			::SafeInt<size_t>	nEntryValueTotalByte = 0 ;

			ConstMapIter	iter = m_mapParam.Begin () ;
			for (size_t i = 0; i < nNextIdx; i++, ++iter) ;

			if ( (iter->second).dwFlag & ParamFile ) {
				// If the parameter is a file parameter

				szEntryFile = (iter->second).szValue ;
				if ( m_ahPostedFiles[nNextFileIdx] != INVALID_HANDLE_VALUE )
					nEntryValueTotalByte = HttpTool::GetFileSize (m_ahPostedFiles[nNextFileIdx], szEntryFile) ;

				// Writes the boundary and headers
				_WritePost ("--") ;
				_WritePost (szBoundary) ;
				_WritePost ("\r\n") ;
				_WritePost ("Content-Disposition: form-data; name=\"") ;
				_WritePost (m_bUseUtf8, iter->first) ;		// Writes form name
				_WritePost ("\"; filename=\"") ;
				_WritePost (m_bUseUtf8, szEntryFile) ;		// Writes file path
				_WritePost ("\"\r\nContent-Type: ") ;
				_WritePost (m_aszMimeTypes[nNextFileIdx]) ;
				_WritePost ("\r\n\r\n") ;

			} else {
				// If the parameter is not a file parameter

				if ( (iter->second).szValue && (iter->second).szValue[0] != '\0' ) {
					LPCSTR		szPostedValue ;
					BOOL		bNeedToFree ;

					if ( m_bUseUtf8 ) {
						szPostedValue = _Utf8Encode ((iter->second).szValue) ;
						bNeedToFree = TRUE ;
					} else
						// Converts into a Ansi string
						szPostedValue = _String2Ansi ((iter->second).szValue, bNeedToFree) ;

					nEntryValueTotalByte = CHttpToolA::StringLen (szPostedValue) ;
					_SetPostedValue (szPostedValue, bNeedToFree) ;
				} else {
					_SetPostedValue (NULL, FALSE) ;
					nEntryValueTotalByte = 0 ;
				}

				// Writes the boundary and headers
				_WritePost ("--") ;
				_WritePost (szBoundary) ;
				_WritePost ("\r\n") ;
				_WritePost ("Content-Disposition: form-data; name=\"") ;
				_WritePost (m_bUseUtf8, iter->first) ;		// Write form name
				_WritePost ("\"\r\n\r\n") ;
			}

			// Starts a new parameter
			m_objPostStat._StartNewEntry (iter->first, nEntryValueTotalByte.Value ()
										, static_cast<BOOL> ((iter->second).dwFlag & ParamFile), szEntryFile) ;

			if ( nEntryValueTotalByte == 0 )
				_WritePost ("\r\n") ;
			return NULL ;
		}

		// Writes the requested number of bytes
		DWORD		cbToWrite = nDesired ;
		if ( cbToWrite > m_objPostStat.CurrParamRemainByte () )
			cbToWrite = static_cast<DWORD> (m_objPostStat.CurrParamRemainByte ()) ;

		DWORD			cbWritten = 0 ;

		if ( m_objPostStat.CurrParamIsFile () ) {
			_InitPostCntxBuff () ;

			DWORD			cbBuff ;				// The number of bytes to read
			DWORD			cbRead ;				// The number of bytes read from the file
			DWORD			nFileIdx = m_objPostStat.PostedFileCount () - 1 ;

			while ( cbWritten < cbToWrite ) {
				cbBuff = cbToWrite - cbWritten > 
					HTTPCLIENT_POSTCNTX_BUFF_SIZE ? HTTPCLIENT_POSTCNTX_BUFF_SIZE : cbToWrite - cbWritten ;

				// Read from file
				if ( !::ReadFile (m_ahPostedFiles[nFileIdx]
						, m_pbyCntxBuff
						, cbBuff
						, &cbRead
						, NULL)
					)
					HttpTool::ThrowException (HTTPCLIENT_ERR_READFILE_FAILED, ::GetLastError ()) ;

				// cbBuff and cbRead must be the same
				if ( cbBuff != cbRead )
					HttpTool::ThrowException (HTTPCLIENT_ERR_READ_UNEXPECTED_SIZE) ;

				_WritePost (m_pbyCntxBuff, cbBuff, TRUE) ;
				cbWritten += cbBuff ;
			}
		} else {
			_WritePost (
				reinterpret_cast<const BYTE *> (m_szPostedValue + m_objPostStat.CurrParamPostedByte ())
				, cbToWrite, TRUE) ;
			cbWritten = cbToWrite ;
		}

		// If all bytes are written, writes the last new line character
		if ( m_objPostStat.CurrParamRemainByte () == 0 )
			_WritePost ("\r\n") ;

	} catch (::SafeIntException & e) {
		_EndPostContext () ;			// Aborts the POST context if an error occurs
		HttpTool::ThrowException (e) ;
	} catch (Exception &) {
		_EndPostContext () ;			// Aborts the POST context if an error occurs
		throw ;
	}
	return NULL ;
}

template <typename HttpTool, typename HttpEncoder>
typename CHttpClientT<HttpTool, HttpEncoder>::CHttpResponse * 
CHttpClientT<HttpTool, HttpEncoder>::_ReleasePostResponse ()
	throw (Exception &)
{
	HTTPCLIENT_ASSERT (m_objPostStat.IsActive (), "CHttpClientT::_ReleasePostResponse: The post context is not active.") ;

	HINTERNET		hReleasedInternet = NULL ;
	HINTERNET		hReleasedConnection = NULL ;
	HINTERNET		hReleasedRequest = NULL ;

	_ReleasePostContext (hReleasedInternet, hReleasedConnection, hReleasedRequest) ;
	CHttpResponse *			pobjRes = NULL ;

	try {
		if ( NULL == (pobjRes = new CHttpResponse (hReleasedInternet, hReleasedConnection, hReleasedRequest)) )
			throw "Out of memory" ;

	} catch (...) {
		HttpTool::CloseRequest (hReleasedRequest) ;
		HttpTool::CloseConnection (hReleasedConnection) ;
		HttpTool::CloseInternet (hReleasedInternet) ;
		HttpTool::ThrowException (HTTPCLIENT_ERR_OUT_OF_MEMORY) ;
	}

	return pobjRes ;
}

// Releases the POST context when the upload task has been completed
template <typename HttpTool, typename HttpEncoder>
void CHttpClientT<HttpTool, HttpEncoder>::_ReleasePostContext (HINTERNET & hInternet, HINTERNET & hConnection, HINTERNET & hRequest)
	throw (Exception &)
{
	HTTPCLIENT_ASSERT (m_objPostStat.IsActive (), "CHttpClientT::_ReleasePostResponse: The post context is not active.") ;

	hInternet = m_bBorrowedInternet ? NULL : m_hInternet ;
	hConnection = m_bBorrowedConnection ? NULL : m_hConnection ;
	hRequest = m_hRequest ;

	m_hInternet = NULL ;
	m_bBorrowedInternet = FALSE ;
	m_hConnection = NULL ;
	m_bBorrowedConnection = FALSE ;
	m_hRequest = NULL ;

	// Closes all file handles and frees the MimeType
	if ( m_ahPostedFiles ) {
		for (DWORD i = 0; i < m_objPostStat.FileCount (); i++) {
			if ( m_ahPostedFiles[i] != INVALID_HANDLE_VALUE ) {
				::CloseHandle (m_ahPostedFiles[i]) ;
				m_ahPostedFiles[i] = INVALID_HANDLE_VALUE ;
			}

			if ( m_aszMimeTypes[i] )
				SAFEFREE ( (m_aszMimeTypes[i]) ) ;
		}

		SAFEFREE (m_ahPostedFiles) ;
		SAFEFREE (m_aszMimeTypes) ;
	}

	// Removes the posted value
	_SetPostedValue (NULL, FALSE) ;

	// Frees the buffer
	SAFEFREE (m_pbyCntxBuff) ;

	// Unactivate the Post context
	m_objPostStat._MakeUnActive () ;
}

template <typename HttpTool, typename HttpEncoder>
void CHttpClientT<HttpTool, HttpEncoder>::_EndPostContext (void)
	throw (Exception &)
{
	if ( !m_objPostStat.IsActive () )
		return ;

	HINTERNET		hInternet, hConnection, hRequest ;
	_ReleasePostContext (hInternet, hConnection, hRequest) ;

	// Closes internet handles
	HttpTool::CloseRequest (hRequest) ;
	HttpTool::CloseConnection (hConnection) ;
	HttpTool::CloseInternet (hInternet) ;
}
///////////////////////////////////////// CHttpClientT /////////////////////////////////////////

}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Software Developer
Korea (Republic of) Korea (Republic of)
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions