/*!
* \file RyeolHttpClient.h
* \brief classes which helps to interact with a HTTP server.
* \author Jo Hyeong-ryeol
* \since 2004.04.12
* \version $LastChangedRevision: 101 $
* $LastChangedDate: 2006-02-04 01:28:18 +0900 (토, 04 2 2006) $
*
*
* - Requirements:
* - Requires Internet Explorer 4.0 or later.
* - Unicode version class support on Windows Me/98/95 requires Microsoft Layer for Unicode.
* - UTF-8 encoding support on Windows 95 requires Microsoft Layer for Unicode.
*
* \n
Introduction
This file contains classes which help to interact with a HTTP server.
The codes contained in this file depends on the documentation of STLPort 4.6.1
(describing about exception safety).
How to use RyeolHttpClient
In your project, include the following files.
- RyeolException.h
- RyeolException.cpp
- RyeolHttpClient.h
- RyeolHttpClient.cpp
- SafeInt.hpp
In your stdafx.h file, add the following line.
\code
#include "RyeolHttpClient.h"
\endcode
How to send a request using HTTP GET
CHttpClient supports RequestGet method which sends a request using HTTP GET.
\code
// Retrieves the resource specified by the szUrl using HTTP GET request.
// szUrl [in] A HTTP URL.
// bUseCache [in] Specifies whether to use cache.
CHttpResponse * CHttpClient::RequestGet (PCSZ szUrl, BOOL bUseCache = FALSE) throw (Exception &) ;
\endcode
The following code demonstrates the basic usage of the RequestGet method.
\code
using namespace Ryeol ;
CHttpClient objHttpReq ;
CHttpResponse * pobjHttpRes = NULL ;
try {
// Initialize the User Agent
objHttpReq.SetInternet (_T ("My User Agent v1.0")) ;
// Specifies whether to use UTF-8 encoding. (This uses ANSI encoding)
// Default is FALSE
objHttpReq.SetUseUtf8 (FALSE) ;
// Specifies a code page for ANSI strings. (This uses Korean)
// Default is CP_ACP
objHttpReq.SetAnsiCodePage (949) ;
// Add user's custom HTTP headers
objHttpReq.AddHeader (_T ("Ryeol-Magic"), _T ("My Magic Header")) ;
objHttpReq.AddHeader (_T ("User-Magic"), _T ("User's Magic Header")) ;
// Add user's parameters
objHttpReq.AddParam (_T ("where"), _T ("nexearch")) ;
objHttpReq.AddParam (_T ("frm"), _T ("t1")) ;
objHttpReq.AddParam (_T ("query"), _T ("%C3%D6%C1%F6%BF%EC"), CHttpClient::ParamEncodedValue) ;
// Send a request
pobjHttpRes = objHttpReq.RequestGet (_T ("http://search.naver.com/search.naver")) ;
... // Place codes to handle the returned CHttpResponse object.
} catch (httpclientexception & e) {
... // Place exception handling codes here.
}
\endcode
How to send a request using HTTP POST
The HTTP POST method is used in two ways. One is to post simple text, the other is to upload file.
To post simple text, CHttpClient provides BeginPost method.
\code
// Starts a new HTTP POST request
// szUrl [in] A HTTP URL.
// bUseCache [in] Specifies whether to use cache.
void CHttpClient::BeginPost (PCSZ szUrl, BOOL bUseCache = FALSE) throw (Exception &) ;
\endcode
The following code demonstrates the basic usage of the BeginPost method.
\code
using namespace Ryeol ;
CHttpClient objHttpReq ;
CHttpResponse * pobjHttpRes = NULL ;
try {
// Initialize the User Agent
objHttpReq.SetInternet (_T ("My User Agent v1.0")) ;
// Add user's custom HTTP headers
objHttpReq.AddHeader (_T ("Ryeol-Magic"), _T ("My Magic Header")) ;
objHttpReq.AddHeader (_T ("User-Magic"), _T ("User's Magic Header")) ;
// Add user's parameter
objHttpReq.AddParam (_T ("st"), _T ("kw")) ;
objHttpReq.AddParam (_T ("target"), _T ("WinInet")) ;
// Start a new request
objHttpReq.BeginPost (_T ("http://www.codeproject.com/info/search.asp")) ;
// Specifies the number of bytes to send when the Proceed method is called.
const DWORD cbProceed = 1024 ; // 1K
do {
... // Place codes to report progress information to user.
} while ( !(pobjHttpRes = objHttpReq.Proceed (cbProceed)) ) ;
... // Place codes to handle the returned CHttpResponse object.
} catch (httpclientexception & e) {
... // Place exception handling codes here.
}
\endcode
To upload file, CHttpClient provides BeginUpload method.
\code
// Starts a new UPLOAD request
// szUrl [in] A HTTP URL.
// bUseCache [in] Specifies whether to use cache.
void CHttpClient::BeginUpload (PCSZ szUrl, BOOL bUseCache = FALSE) throw (Exception &) ;
\endcode
The following code demonstrates the basic usage of the BeginUpload method.
\code
using namespace Ryeol ;
CHttpClient objHttpReq ;
CHttpResponse * pobjHttpRes = NULL ;
try {
// Initialize the User Agent
objHttpReq.SetInternet (_T ("My User Agent v1.0")) ;
// Add user's custom HTTP headers
objHttpReq.AddHeader (_T ("Ryeol-Magic"), _T ("My Magic Header")) ;
objHttpReq.AddHeader (_T ("User-Magic"), _T ("User's Magic Header")) ;
// Add user's parameters
objHttpReq.AddParam (_T ("nohtml"), _T ("1")) ;
objHttpReq.AddParam (_T ("title"), _T ("The K-NET photo")) ;
objHttpReq.AddParam (_T ("content"), _T ("A photo of the K-NET")) ;
// Specifies a file to upload
objHttpReq.AddParam (_T ("ufile01"), _T ("D:\\My Photo\\K-NET\\photo1.jpg"), CHttpClient::ParamFile) ;
// Start a new request
objHttpReq.BeginUpload (_T ("http://club.hooriza.com/cmd/box.html?clubid=1&boxid=53&action=store&link=")) ;
// Specifies the number of bytes to send when the Proceed method is called.
const DWORD cbProceed = 1024 ; // 1K
do {
... // Place codes to report progress information to user.
} while ( !(pobjHttpRes = objHttpReq.Proceed (cbProceed)) ) ;
... // Place codes to handle the returned CHttpResponse object.
} catch (httpclientexception && e) {
... // Place exception handling codes here.
}
\endcode
How to handle the returned CHttpResponse object
When you send a request using CHttpClient, all method will return CHttpResponse object.
CHttpResponse represents the response returned by a HTTP web server.
CHttpResponse provides following methods.
\code
// Returns the number of headers of which name is the szName
DWORD CHttpResponse::GetHeaderCount (PCSZ szName) throw (Exception &) ;
// Returns the header of which name is the szName
PCSZ CHttpResponse::GetHeader (PCSZ szName, DWORD nIdx = 0) throw (Exception &) ;
// Returns the HTTP status code returned by a HTTP server
DWORD CHttpResponse::GetStatus (void) throw (Exception &) ;
// Returns the HTTP status text returned by a HTTP server
PCSZ CHttpResponse::GetStatusText (void) throw (Exception &) ;
// Retrieves the length of the content returned by a HTTP server
BOOL CHttpResponse::GetContentLength (DWORD & cbContLen) throw (Exception &) ;
// Reads the content returned by a HTTP server
DWORD CHttpResponse::ReadContent (BYTE * pbyBuff, DWORD cbBuff) throw (Exception &) ;
\endcode
The following code demonstrates the basic usage of the CHttpResponse object.
\code
using namespace Ryeol ;
CHttpResponse * pobjHttpRes = NULL ;
try {
// Get the CHttpResponse object
pobjHttpRes = ... ;
// Reads the HTTP status code
_tprintf (_T ("%u"), pobjHttpRes->GetStatus ()) ;
// Reads the HTTP status text
_tprintf (_T (" %s\n"), pobjHttpRes->GetStatusText ()) ;
// Reads HTTP headers using an array of header names
static LPCTSTR szHeaders[] =
{ _T ("Server"), _T ("Date"), _T ("X-Powered-By"), _T ("Content-Length"), _T ("Set-Cookie")
, _T ("Expires"), _T ("Cache-control"), _T ("Connection"), _T ("Transfer-Encoding")
, _T ("Content-Type") } ;
LPCTSTR szHeader ;
for (size_t i = 0; i < sizeof (szHeaders) / sizeof (LPCTSTR); i++) {
if ( szHeader = pobjHttpRes->GetHeader (szHeaders[i]) )
_tprintf (_T ("%s: %s\n"), szHeaders[i], szHeader) ;
else
// If the header is not found..
_tprintf (_T ("'%s' header does not exist..\r\n"), szHeaders[i]) ;
}
_tprintf (_T ("\r\n")) ;
// Checks whether the returned stream is a text
BOOL bIsText = FALSE ;
if ( szHeader = pobjHttpRes->GetHeader (_T ("Content-Type")) )
bIsText = (0 == ::_tcsncicmp (szHeader, _T ("text/"), 5)) ;
// Reads the length of the stream
DWORD dwContSize ;
// If the length is not specified
if ( !pobjHttpRes->GetContentLength (dwContSize) )
dwContSize = 0 ;
const DWORD cbBuff = 1024 * 10 ;
BYTE byBuff[cbBuff] ;
DWORD dwRead ;
size_t cbTotal = 0 ;
// Reads the data stream returned by the HTTP server.
while ( dwRead = pobjHttpRes->ReadContent (byBuff, cbBuff - 1) ) {
cbTotal += dwRead ;
if ( bIsText ) {
byBuff[dwRead] = '\0' ;
printf ("%s", reinterpret_cast<LPCSTR> (byBuff)) ;
}
}
if ( !bIsText )
_tprintf (_T ("%u bytes skipped..\n"), cbTotal) ;
} catch (httpclientexception & e) {
... // Place exception handling codes here.
}
delete pobjHttpRes ;
pobjHttpRes = NULL ;
\endcode
How to handle exception
When an error occurred, httpclientexception object is thrown.
\code
class httpclientexception {
public:
// Returns the last error code. The error codes is defined in RyeolHttpClient.h
DWORD LastError (void) const throw () ;
// Returns the last error message.
LPCTSTR errmsg (void) const throw () ;
// Returns the last win32 error code retrieved by using ::GetLastError when an error occurred.
DWORD Win32LastError (void) const throw () ;
} ;
\endcode
Before throwing an exception, most methods restore their internal states. (all or nothing like transaction)
But if you call BeginPost or BeginUpload method, the POST context is automatically canceled.
You should write the following try-catch block to handle the exception.
\code
using namespace Ryeol ;
try {
... // Place codes which throw a httpclientexception exception
} catch (httpclientexception & e) {
_tprintf (_T ("An error has been occured\n")) ;
_tprintf (_T ("ErrCode: 0x%x\n"), e.LastError ()) ;
_tprintf (_T ("ErrMsg: %s\n"), e.errmsg ()) ;
if ( e.Win32LastError () != NO_ERROR ) {
TCHAR szErrMsg[512] ;
GetWinInetErrMsg (szErrMsg, 512, e.Win32LastError ()) ;
_tprintf (_T ("Win32ErrCode: 0x%x\n"), e.Win32LastError ()) ;
_tprintf (_T ("Win32ErrMsg: %s\n"), szErrMsg) ;
}
}
\endcode
How to show progress information to user
If you call BeginPost or BeginUpload method, you can retrieve progress information using Query method.
\code
// Queries progress information of the current POST context
// objPostStat [out] A CHttpPostStat object.
void CHttpClient::Query (CHttpPostStat & objPostStat) throw () ;
\endcode
CHttpPostStat represents progress information of the current POST context.
The following code demonstrates the basic usage of the CHttpPostStat object.
\code
using namespace Ryeol ;
CHttpClient objHttpReq ;
CHttpResponse * pobjHttpRes = NULL ;
size_t cbProceed = 1024 ; // 1k
try {
... ; // Intialize the CHttpClient object
// Starts a new POST request
objHttpReq.BeginPost (...) or objHttpReq.BeginUpload (...) ;
// Displays progress information
CHttpPostStat objPostStat ;
do {
// Retrieves progress information
objHttpReq.Query (objPostStat) ;
_tprintf (_T ("\nPost in progress... %2u/%2u\n")
, objPostStat.PostedCount () // The number of posted parameters
, objPostStat.TotalCount ()) ; // The total number of parameters
_tprintf (_T ("%s: %10u/%10u %10u/%10u %10u/%10u\n")
, objPostStat.CurrParam () // The name of the current parameter
, objPostStat.CurrParamPostedByte () // The number of posted bytes of the current parameter
, objPostStat.CurrParamTotalByte () // The total number of bytes of the current parameter
, objPostStat.PostedByte () // The number of posted bytes of the request
, objPostStat.TotalByte () // The total number of bytes of the request
, objPostStat.ActualPostedByte () // The actual number of posted bytes of the request
, objPostStat.ActualTotalByte ()) ; // The actual total number of bytes of the request
// If the current parameter is a file parameter, displays the file path
if ( objPostStat.CurrParamIsFile () )
_tprintf (_T ("-->%s\n")
, objPostStat.CurrFile ()) ;
// Sends the number of bytes specified by cbProceed to the server
} while ( !(pobjHttpRes = objHttpReq.Proceed (cbProceed)) ) ;
... ; // Handles the returned CHttpResponse object
} catch (httpclientexception & e) {
... // Place exception handling codes here.
}
\endcode
* Copyright © 2006 by Jo Hyeong-ryeol\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.
*/
#pragma once
#include // for generic types, .. etc
#include // for the windows internet functions
#include // for the _ASSERTE macro
#include // for ::CoCreateGuid
#include // for the STL pair type
#include