Extracting Information from IIS

The ECB is commonly used to extract information about the HTTP request and the server instance of IIS so that the ISAPI extension can perform some programmatic effort based on the request event. In Listing 17-3 from the ISAPI extension SEUX (Simple Extension Using XML), the HttpExtensionProc function is used to perform the following tasks:

  • Build an XML document with many of the common server variables obtained from the GetServerVariable function.

  • Add properties of the ECB into the XML document.

  • Return the XML document to the requester.

Listing 17-3: HttpExtensionProc Function Building XML Document in SEUX ISAPI Extension

start example
 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Name:HttpExtensionProc In:    pECB - pointer to the Extension control block structure Out:  DWORD - HSE status code  Purpose:   main entry point for HTTP request   the possible return codes are:    HSE_STATUS_SUCCESS  - everything worked great   HSE_STATUS_SUCCESS_AND_KEEP_CONN - same as HSE_STATUS_SUCCESS                      since IIS 4   HSE_STATUS_PENDING - wait until effort completed    HSE_STATUS_ERROR  - sends a 500 error code /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ DWORD WINAPI HttpExtensionProc(EXTENSION_CONTROL_BLOCK *pECB) {     string sDoc;   //start our XML document   sDoc = string(HEAD) + string(NEW_LINE) + string(XML_L) +          string(MAIN_ELEMENT_NAME) + string(XML_R) +          string(NEW_LINE);   //START THE ECBServerVariable VARIABLES   sDoc += string(XML_L) +      string("ECBServerVariable") + string(XML_R) +      string(NEW_LINE);   //GET the ALL_HTTP   sDoc += string(XML_L) + string("ALL_HTTP");   GetALLHTTPHeader(pECB, &sDoc);     sDoc += string(XML_R_END) +      string(NEW_LINE);//end the first main element   GetECBElement(pECB, string("AUTH_TYPE"), &sDoc);   GetECBElement(pECB, string("APPL_MD_PATH"), &sDoc);   GetECBElement(pECB, string("APPL_PHYSICAL_PATH"), &sDoc);   GetECBElement(pECB, string("CONTENT_LENGTH"), &sDoc);   GetECBElement(pECB, string("CONTENT_TYPE"), &sDoc);   GetECBElement(pECB, string("GATEWAY_INTERFACE"), &sDoc);   GetECBElement(pECB, string("HTTP_ACCEPT"), &sDoc);   GetECBElement(pECB, string("HTTPS"), &sDoc);   GetECBElement(pECB, string("HTTP_AUTHORIZATION"), &sDoc);   GetECBElement(pECB, string("LOGON_USER"), &sDoc);   GetECBElement(pECB, string("AUTH_PASSWORD"), &sDoc);   GetECBElement(pECB, string("AUTH_TYPE"), &sDoc);   GetECBElement(pECB, string("AUTH_USER"), &sDoc);   GetECBElement(pECB, string("APPL_PHYSICAL_PATH"), &sDoc);   GetECBElement(pECB, string("INSTANCE_ID"), &sDoc);   GetECBElement(pECB, string("INSTANCE_META_PATH"), &sDoc);   GetECBElement(pECB, string("PATH_INFO"), &sDoc);   GetECBElement(pECB, string("PATH_TRANSLATED"), &sDoc);   GetECBElement(pECB, string("QUERY_STRING"), &sDoc);   GetECBElement(pECB, string("REMOTE_ADDR"), &sDoc);   GetECBElement(pECB, string("REMOTE_HOST"), &sDoc);   GetECBElement(pECB, string("REMOTE_USER"), &sDoc);   GetECBElement(pECB, string("REQUEST_METHOD"), &sDoc);   GetECBElement(pECB, string("SCRIPT_NAME"), &sDoc);   GetECBElement(pECB, string("SERVER_NAME"), &sDoc);   GetECBElement(pECB, string("SERVER_PORT"), &sDoc);   GetECBElement(pECB, string("SERVER_PORT_SECURE"), &sDoc);   GetECBElement(pECB, string("SERVER_PROTOCOL"), &sDoc);   GetECBElement(pECB, string("SERVER_SOFTWARE"), &sDoc);   GetECBElement(pECB, string("URL"), &sDoc);   //End THE ECBServerVariable VARIABLES   sDoc += string(XML_L_END) +      string("ECBServerVariable") + string(XML_R) +      string(NEW_LINE);   //START THE ECBProperties   sDoc += string(XML_L) +      string("ECBProperties") + string(XML_R) +      string(NEW_LINE);      GetElement(string("lpszLogData"),         string(pECB->>lpszLogData),&sDoc);   GetElement(string("lpszMethod"),         string(pECB->>lpszMethod),&sDoc);   GetElement(string("lpszQueryString"),         string(pECB->>lpszQueryString),&sDoc);   GetElement(string("lpszPathInfo"),         string(pECB->>lpszPathInfo),&sDoc);   GetElement(string("lpszContentType"),         string(pECB->>lpszContentType),&sDoc);   //end THE ECBProperties   sDoc += string(XML_L_END) +      string("ECBProperties") + string(XML_R) +      string(NEW_LINE);     //end our XML document   sDoc += string(XML_L_END) +      string(MAIN_ELEMENT_NAME) + string(XML_R) +      string(NEW_LINE);   //write it!   SendResponse(pECB,sDoc);   return HSE_STATUS_SUCCESS; }
end example

Note 

The source code for SEUX is available on the author's web site as mentioned in the book's Introduction.

Building XML Representing the Server Variables Values

The first task that the code in Listing 17-3 does is to start the XML document by appending some constants that represent parts of an XML document. Since the XML is being built manually, constants are declared for common parts of the XML document because they are used many times in many places. The XML document is placed in a string variable named sDoc. The XML constants are as follows:

  //xml parts   const char* MAIN_ELEMENT_NAME = "HTTPRequestRaw";   const char* QUOTE    ="\"";   const char* XML_L    ="<<";   const char* XML_R    =">>";   const char* XML_L_END  ="<</";   const char* XML_R_END  ="/>>";   const char* NEW_LINE  ="\n";   const char* HEAD = "<<?xml version=\"1.0\"?>>";

Because this particular ISAPI extension builds XML manually, the parts of an XML document that are represented by the constant char pointers (const char*) are commonly used in many places. This XML document is also simple, so building it manually using string concatenation is not difficult. If the document were more complex, the use of an XML parsing library, such as MSXML, might be considered.

After the XML document is started, the effort of grabbing all of the server variables and placing them into XML elements will begin. The XML document that is produced has a parent element called HTTPRequestRaw. Within HTTPRequestRaw are two child elements called ECBServerVariable and ECBProperties. For each server variable value requested from the ECB, a child element will be created under the ECBServerVariable element, regardless of whether or not a value is obtained for the respective server variable. For each ECB property requested, a child element will be created under the ECBProperties element to hold the value, regardless of whether or not a value for the property was obtained. When the ISAPI extension SEUX is called using Internet Explorer (IE) version 6.0, the XML document will be displayed, as shown in Figure 17-12. Other browser versions may display the XML differently or not at all.

click to expand
Figure 17-12: IE 6.0 displaying the XML document of server variables from the SEUX ISAPI extension

Special Case of ALL_HTTP Server Variable

The first server variable that a value is obtained for is the ALL_HTTP variable that represents all of the HTTP headers that were passed in the HTTP request. All the other server variables requested result in a number or a string that fits into XML quite easily, but the ALL_HTTP server variable returns all of the headers in the same server variable. Since the headers in the value returned from the ALL_HTTP server variable are delimited using linefeeds, some additional parsing is required to make each header fit into an XML element as an attribute. The function GetALLHTTPHeader performs this task by using the ECB's GetServerVariable function, as shown in Listing 17-4.

Listing 17-4: GetALLHTTPHeader Function Building XML Element in SEUX ISAPI Extension

start example
 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Name:GetALLHTTPHeader In:  pECB - pointer to the Extension control block structure   psElement - string pointer to XML document being built that          will be updated with the element for the ALL_HTTP         server variable value. Out:  nothing returned but the string psElement points to      will be updated. Purpose:   Updates the XML document string by adding an element    for the ALL_HTTP server variable. This variable contains    all of the http headers so some additional parsing must    take place on the headers to format them into XML.  /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ void GetALLHTTPHeader(EXTENSION_CONTROL_BLOCK *pECB,             string *psElement) {   TCHAR szTempBuffer[BUFFER_LENGTH];   DWORD dwBufferSize = BUFFER_LENGTH;   const string EQUAL("=");   const string SPACE(" ");   string sAllHeaders;  //used to cut up 'all headers' returned   int nNewLinePos = 0;   int nEndLinePos = 0;   string sName;   string sValue;   //pull the whole HTTP header    if (pECB->>GetServerVariable(  pECB->>ConnID,                    "ALL_HTTP",                    szTempBuffer,                    &dwBufferSize))   {     //if the whole http header was pulled then parse it     sAllHeaders.assign(szTempBuffer);     //get the name / value pairs     while (GetHeaderValuePair(  sAllHeaders,                    nNewLinePos,                    &sName,                    &sValue,                    &nEndLinePos))     {       //add the attribute to the element       psElement->>append(  SPACE + sName + EQUAL +                  QUOTE +                  ValidateValue(sValue) +                  QUOTE);        //reset the newline to the last endline       nNewLinePos = nEndLinePos;                        //dump the values       sName.erase();       sValue.erase();     }   } }
end example

GetServerVariable Function

The GetServerVariable function returns true on a successful execution and returns false if an error was encountered, as shown in the following function prototype:

BOOL WINAPI GetServerVariable(  HCONN hConn,     LPSTR lpszVariableName,   LPVOID lpvBuffer,    LPDWORD lpdwSizeofBuffer  );

The prototype of GetServerVariable as shown here requires that four parameters be passed in:

  • hConn Connection handle obtained from the ECB

  • lpszVariableName Null terminated string of the server variable that is being requested

  • lpvBuffer Void pointer to a buffer that should be filled with the resulting value of the variable name and 1 null terminating byte

  • lpdwSizeofBuffer Pointer to a DWORD that indicates the size of the buffer

If GetServerVariable is successful, the function returns a value of true. The pointer lpvBuffer should be pointed to a value of the requested server variable, and lpdwSizeofBuffer should be pointed to a new DWORD value that indicates the actual size of the value lpvBuffer points to including the null terminating byte. If the GetServerVariable function is not successful, it will return a false value. If false is returned, the GetLastError function may be called. GetLastError returns a DWORD value representing an error code that may be referenced against a library to determine the error description. Four possible errors are thrown by GetServerVariable, as shown in Table 17-2. The values in Table 17-2 are constants that are defined in the ISAPI extension support header file.

Table 17-2: Summary of GetServerVariable Errors

Error Constant

Description of Error

ERROR_INVALID_PARAMETER

The value of hConn is no good or closed or the server variable parameters are invalid.

ERROR_INVALID_INDEX

The server variable being requested is not a supported variable.

ERROR_INSUFFICIENT_BUFFER

The size of lpdwSizeofBuffer is too small to contain the value of the server variable being requested.

ERROR_NO_DATA

The server variable being requested is not available.

Server Variable Values

Many of the possible server variables that could be requested are shown in Listing 17-3, the HttpExtensionProc function in the SEUX ISAPI extension, as arguments in the GetECBElement calls. The values of server variables can change at various points during a given HTTP request event in IIS, and often no value will be assigned to a variable. Server variables may have values only under certain IIS configurations as well. Table 17-3 summarizes many of the server variables that may be queried using the GetServerVariable function.

Table 17-3: Summary of Server Variables that May Be Queried Using the GetServerVariable Function

Variable

Description

ALL_HTTP

All of the HTTP headers (separated by linefeeds) that were passed in the HTTP request in a null- terminated string. The headers are in the form of <header name> : <value>.

ALL_RAW

All of the HTTP headers in an unaltered form as they were sent from the HTTP requester. The result will be similar to the value of ALL_HTTP.

APPL_MD_PATH

Metabase path of the web application. For example, /LM/W3SVC/1/Root/SimpleISAPI.

APPL_PHYSICAL_PATH

Physical path of the web root for the web application. For example, C:\ISAPI\.

AUTH_PASSWORD

Password entered by the web end user in the generic authentication dialog box spawned by the web browser if Basic Authentication is set.

AUTH_TYPE

Type of authentication used. Empty for no authentication or returns a value that correlates to Kerberos, user, SSL/PCT, Basic, or integrated Windows authentication.

AUTH_USER

User name entered by the end user in the generic authentication dialog box spawned by the web browser if Basic Authentication is set.

CERT_COOKIE

Unique ID of the client certificate.

CERT_FLAGS

Bit flags about the certification authority (CA) of the client certificate. If bit0 is set to 1, a client certificate is present. If bit1 is set to 1, the CA is not on this server's list of recognized CAs so it is considered invalid.

CERT_ISSUER

Contains the issuer's distinguished name of the client certificate. For example, O=Schmidlaps, OU=House, CN=user name, C=USA.

CERT_KEYSIZE

Key size of the Secure Sockets Layer (SSL) connection in bits.

CERT_SECRETKEYSIZE

Key size of the server certificate private key
in bits.

CERT_SERIALNUMBER

Serial number of the client certificate.

CERT_SERVER_ISSUER

Issuer's distinguished name of issuer of the server certificate.

CERT_SERVER_SUBJECT

Issuer's distinguished name of subject of the server certificate.

CERT_SUBJECT

Subject of the client certificate.

CONTENT_LENGTH

The number of bytes, not including headers for the HTTP request.

CONTENT_TYPE

The content type of a POST HTTP request.

LOGON_USER

If the end user successfully authenticated to Windows, the login account used.

HTTPS

Returns the value off if HTTPS is not being used via SSL and returns a value of on otherwise.

HTTPS_KEYSIZE

Key size of the SSL connection in bits.

HTTPS_SECRETKEYSIZE

Key size of the server certificate private key in bits.

HTTPS_SERVER_ISSUER

Issuer's distinguished name of issuer of the server certificate.

HTTPS_SERVER_SUBJECT

Issuer's distinguished name of subject of the server certificate.

INSTANCE_ID

Server instance number. Value of the server ID in the metabase. For example, 1.

INSTANCE_META_PATH

Metabase path for web instance. For example: LM/W3SVC/1.

PATH_INFO

Part of the URL that is between the ISAPI DLL and the start of the extra-info section of the URL. Normally nothing is between the ISAPI DLL and the extra-info section of the URL unless the requesting software put a value in place.

PATH_TRANSLATED

Path of the web instance as it is mapped to the physical hard drive and the value of PATH_INFO concatenated.

QUERY_STRING

String of the characters that follow the ? in the extra-info section of the URL.

REMOTE_ADDR

IP address of the requesting software's host or gateway.

REMOTE_HOST

Host name of the requesting software's host or gateway if reverse DNS is enabled; otherwise, the value of the IP address of the requesting software's host or gateway is returned.

REMOTE_USER

User name supplied HTTP requester and authenticated by the host server. This value is an empty string when the end user is anonymous.

REQUEST_METHOD

HTTP request method verb.

SCRIPT_NAME

Name of the binary being executed. For example, the ISAPI DLL or the CGI executable.

SERVER_NAME

Host's server name, or IP address.

SERVER_PORT

TCP/IP port on which the request was received.

SERVER_PORT_SECURE

Value of either 0 or 1. Secure port requests return a value of 1; otherwise a value of 0 will be returned.

SERVER_PROTOCOL

Name and version of the request protocol.
For example, HTTP/1.1.

SERVER_SOFTWARE

Name and version of IIS under which the ISAPI extension DLL program is running. For example, Microsoft-IIS/6.0.

URL

Value of the url-path part of the URL
excluding the PATH_INFO value. For example, /simpleisapi/folder1/folder2/SEUX.dll.

To demonstrate the values described in Table 17-3, Listing 17-5 shows an XML document produced from the SEUX.DLL ISAPI extension. In this example, the host server and IIS were configured so that many of the values for server variables would be obtained during the HTTP request. The hosting server is named amd1700v2. IIS 6 on amd1700v2 was configured with the following settings and file locations:

  • Physical location of the ISAPI extension SEUX.DLL C:\ISAPI\folder1\folder2 \SEUX.dll

  • Web instance root C:\inetpub\wwwroot

  • Virtual directory mapped to C:\ISAPI

  • Anonymous access Not enabled for the virtual directory

  • Basic authentication Checked for the virtual directory

The SEUX.DLL ISAPI extension was requested from amd1700v2 using the following data using a web browser hosted on a separate computer:

  • User logged into web site with user name normaluser

  • User logged into web site with password normaluser

  • URL placed in browser: http://amd1700v2/simpleisapi/folder1/folder2/SEUX.dll/PATH_INFO?parm1=value1&parm2=value

Listing 17-5: SEUX.DLL Output from http://amd1700v2/simpleisapi/folder1/folder2/SEUX.dll/PATH_INFO?parm1=value1&parm2=value

start example
<<?xml version="1.0" ?>>  <<HTTPRequestRaw>>  <<ECBServerVariable>>  <<ALL_HTTP HTTP_CONNECTION="Keep-Alive"    HTTP_ACCEPT=  "image/gif, image/x-xbitmap, image/jpeg, image/pjpeg,  application/vnd.ms-powerpoint, application/vnd.ms-excel,  application/msword, */*"    HTTP_ACCEPT_ENCODING="gzip, deflate"    HTTP_ACCEPT_LANGUAGE="en-us"    HTTP_AUTHORIZATION="Basic bm9ybWFsdXNlcjpub3JtYWx1c2Vy"   HTTP_COOKIE="ASPCLIENTDEBUG=1" HTTP_HOST="amd1700v2"    HTTP_USER_AGENT= "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0; .NET CLR  1.0.3705)" />>   <<AUTH_TYPE>>Basic<</AUTH_TYPE>>   <<APPL_MD_PATH>>/LM/W3SVC/1/Root/SimpleISAPI<</APPL_MD_PATH>>   <<APPL_PHYSICAL_PATH>>C:\ISAPI\<</APPL_PHYSICAL_PATH>>   <<CONTENT_LENGTH>>0<</CONTENT_LENGTH>>   <<CONTENT_TYPE />>   <<GATEWAY_INTERFACE>>CGI/1.1<</GATEWAY_INTERFACE>>   <<HTTP_ACCEPT>>image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/vnd.ms-powerpoint, application/vnd.ms-excel,  application/msword, */*<</HTTP_ACCEPT>>   <<HTTPS>>off<</HTTPS>>   <<HTTP_AUTHORIZATION>> Basic bm9ybWFsdXNlcjpub3JtYWx1c2Vy  <</HTTP_AUTHORIZATION>>   <<LOGON_USER>>normaluser<</LOGON_USER>>   <<AUTH_PASSWORD>>normaluser<</AUTH_PASSWORD>>   <<AUTH_TYPE>>Basic<</AUTH_TYPE>>   <<AUTH_USER>>normaluser<</AUTH_USER>>   <<APPL_PHYSICAL_PATH>>C:\ISAPI\<</APPL_PHYSICAL_PATH>>   <<INSTANCE_ID>>1<</INSTANCE_ID>>   <<INSTANCE_META_PATH>>/LM/W3SVC/1<</INSTANCE_META_PATH>>   <<PATH_INFO>>/PATH_INFO<</PATH_INFO>>   <<PATH_TRANSLATED>>c:\inetpub\wwwroot\PATH_INFO<</PATH_TRANSLATED>>   <<QUERY_STRING>>parm1=value1&parm2=value<</QUERY_STRING>>   <<REMOTE_ADDR>>169.254.176.147<</REMOTE_ADDR>>   <<REMOTE_HOST>>169.254.176.147<</REMOTE_HOST>>   <<REMOTE_USER>>normaluser<</REMOTE_USER>>   <<REQUEST_METHOD>>GET<</REQUEST_METHOD>>   <<SCRIPT_NAME>>/simpleisapi/folder1/folder2/SEUX.dll<</SCRIPT_NAME>>   <<SERVER_NAME>>amd1700v2<</SERVER_NAME>>   <<SERVER_PORT>>80<</SERVER_PORT>>   <<SERVER_PORT_SECURE>>0<</SERVER_PORT_SECURE>>   <<SERVER_PROTOCOL>>HTTP/1.1<</SERVER_PROTOCOL>>   <<SERVER_SOFTWARE>>Microsoft-IIS/6.0<</SERVER_SOFTWARE>>   <<URL>>/simpleisapi/folder1/folder2/SEUX.dll<</URL>>   <</ECBServerVariable>>  <<ECBProperties>>  <<lpszLogData />>   <<lpszMethod>>GET<</lpszMethod>>   <<lpszQueryString>>parm1=value1&parm2=value<</lpszQueryString>>   <<lpszPathInfo>>/PATH_INFO<</lpszPathInfo>>   <<lpszContentType />>   <</ECBProperties>> <</HTTPRequestRaw>>
end example

Parsing the Header-Value Pair

After the ALL_HTTP header is obtained from the GetServerVariable function in Listing 17-4, the string must be parsed for SEUX to be able to display the contents in XML. The headers are delineated by a linefeed and a carriage return. The colon (:) is used to separate the header name from the value. GetHeaderValuePair is designed to begin searching in a given position and return the name and associated value for a given header along with the position that the function stopped looking for headers. GetHeaderValuePair searches for one header value pair at a time.

As shown in Listing 17-6, the function GetHeaderValuePair takes the entire HTTP header and looks for a colon starting at position nStart in the string sHeader. The value of nStart is a counter that starts at zero. If a colon does not exist after the position nStart in the HTTP header, the function exits, returning a value of false. If a colon is found, this means a header exists, and sHeader is searched for a new line starting from the position at which the colon was found. The search is performed using the find function of the Standard Template Library (STL) string with the newline escape character \n as an argument along with the colon location as the start position. The newline position becomes the end position that is returned in the parameter pnEnd pointer, so that the calling function will know where the search stopped. Using all of the position parameters that are discovered from the find function calls to the sHeader string, the header name and value are extracted into the memory locations that are associated with pointers psName and psValue.

Listing 17-6: Source Code for Function GetHeaderValuePair

start example
 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Name: GetHeaderValuePair In: sHeader - string HTTP Header    nStart - integer search start location   psName - pointer to name of header that is being sought    psValue - pointer to string that will be filled if          value found   pnEnd - pointer to integer of the final search position  Out: bool  true returned if the header was found,        false returned otherwise Purpose:     Searches through the HTTP header passed in for a      header value. Returns data about the search      parameters if found or not. /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ bool GetHeaderValuePair(const string &sHeader,              const int &nStart,              string *psName,              string *psValue,              int *pnEnd) {   const string sColon(":");   //determine if header is a post header   string::size_type idxColonPosition = nStart;   //start looking at beginning    idxColonPosition = sHeader.find(sColon, idxColonPosition);   if (idxColonPosition == string::npos)//no more headers found     return false;//this is failure   //get the name   psName->>assign(sHeader.substr           (nStart, idxColonPosition - nStart));   //find next newline   string::size_type idxNewLine;   idxNewLine = sHeader.find('\n', idxColonPosition);   //get the end even if it means not found   *pnEnd = idxNewLine;   if (idxNewLine == string::npos)  //a newline was not found     return true;//not a failure - might be the last header   //get the value   //adjust colon position so we do not assign colon in value   idxColonPosition = idxColonPosition +1;    psValue->>assign(sHeader.substr(idxColonPosition,      idxNewLine - idxColonPosition));   return true; }
end example

Assembling the Remaining XML Elements

After the HttpExtensionProc function parses the ALL_HTTP header value, the remaining server variables are all processed using GetECBElement, as shown in Listing 17-7. Each server variable passed into GetECBElement is extracted using the GetServerVariable function and serialized into an XML element that is appended to a string pointed to by psElement. The pointer psElement points to the XML document that is being constructed in HttpExtensionProc.

Listing 17-7: Function GetECBElement

start example
 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Name:GetECBElement In:  pECB - Pointer to the extension control block for the    purposes of calling the GetServerVariable function.      sName - string name of the server variable that is        being sought.   psElement - string pointer to XML document being built that          will be updated with the name and value for the          server variable extracted from the extension          control block. Out:  nothing returned but the string psElement points to      will be updated. Purpose:   appends a string of an XML element to the string psElement    points to. The XML element that is created is in the form of <<Server Variable Name>>Server Variable Value<</Server Variable Name>>   for example:  <<GATEWAY_INTERFACE>>CGI/1.1<</GATEWAY_INTERFACE>> + newline /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/   void GetECBElement(  EXTENSION_CONTROL_BLOCK *pECB,                    const string &sName,                    string *psElement) { TCHAR szTempBuffer[BUFFER_LENGTH]; DWORD dwBufferSize = BUFFER_LENGTH;   //get the server variable value   if (pECB->>GetServerVariable(  pECB->>ConnID,            (LPSTR)sName.c_str(),            szTempBuffer,            &dwBufferSize))   {     //build the XML element and      //add it to the XML document passed in     psElement->>append(string(XML_L) + sName + string(XML_R));     psElement->>append(ValidateValue((string)szTempBuffer));     psElement->>append(string(XML_L_END) + sName + string(XML_R) +        string(NEW_LINE));   } }
end example

The ValidateValue function is used to make certain that special characters are escaped. The function is applied to a string before the string is set as an element value. ValidateValue could also be used to validate an attribute value. XML cannot tolerate certain special characters in a value position unless they are escaped. As shown in Listing 17-8, the characters that are used to make an XML structure, such as the equal sign or the greater than or less than symbols, are replaced with an acceptable equivalent escape version of the character.

Listing 17-8: Function ValidateValue

start example
 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Name: ValidateValue In: Constant reference to a string variable sValue. sValue is the    value being checked to see if it has a character requiring    escaping Out: returns a string with the escaped characters in place Purpose:   blindly replaces all special characters    with the escape sequence character so that XML will   be valid.  /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ string ValidateValue(const string &sValue) {   string sReturn;   sReturn = sValue;   FindAndReplace(&sReturn, &string("&"),&string("&amp;"));   FindAndReplace(&sReturn, &string("="),&string("&#61;"));   FindAndReplace(&sReturn, &string("<<"),&string("&lt;"));   FindAndReplace(&sReturn, &string(">>"),&string("&gt;"));   FindAndReplace(&sReturn, &string("'"),&string("&apos;"));   FindAndReplace(&sReturn, &string("\""),&string("&quot;"));   return sReturn; }
end example

The FindAndReplace function is a utility function that may be used to replace all the occurrences of a string. In the case of the SEUX ISAPI extension, it acts as a perfect mechanism to replace a phrase inside a string with another phrase. The arguments are pointers to the strings that represent the following:

  • The containing string that is being edited, otherwise known as the container

  • The string that needs to be replaced within the container, otherwise known as the target

  • The string that will be replacing the target within the container, otherwise known as the replacement

The STL string provides a find function and a replace function that the FindAndReplace function uses to search the container for all occurrences of the target, as shown in Listing 17-9. Each time the target is found in the container, it is replaced and a new search begins at the end of the replacement. When FindAndReplace completes, the container is updated with the replacements if any exist.

Listing 17-9

start example
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Name: FindAndReplace In: psContainer - pointer to a string that will be searched           and edited if a value is discovered.    psTarget - pointer to a string that is being sought for          replacement.    psReplacement - pointer to a string that will replace the             the string pointed to in psTarget. Out: nothing - but psContainer will be changed Purpose:    searches string psContainer pointer for the string that     psTarget points to and replaces it with the string that     psReplacement points to. /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ void FindAndReplace(string *psContainer,               string *psTarget,               string *psReplacement) {    string::size_type idx;    idx = psContainer->>find(*psTarget);    while (idx != string::npos)//an instance was found    {           //are we at the end of the string      if (psContainer->>size() == idx)      {         *psContainer += *psReplacement;         break;      }      else      {         psContainer->>replace              (idx, psTarget->>size() , *psReplacement);                  //advance beyond the current character         idx += psReplacement->>size();      }      //look for next occurance      idx = psContainer->>find(*psTarget, idx);    } }
end example

The GetElement function works almost exactly the same as GetECBElement. It is called from the HttpExtensionProc function to concatenate elements from the properties of the ECB. The properties are extracted from the ECB, and then they are passed along with their names and the XML document to the function GetElement. GetElement places the ECB property and the respective value placed into an XML element and concatenates it to the XML document pointer passed into GetElement. The following properties are queried:

  • lpszLogData Buffer the size of HSE_LOG_BUFFER_LEN that can be used to place information that will be added to the log file for the given HTTP request transaction.

  • lpszMethod Property that contains a string value of the HTTP method used. For example, GET or PUT or HEAD.

  • lpszQueryString Property that contains a string value of the characters in the extra-info section of the URL excluding the ? character. This is the same value as the server variable QUERY_STRING.

  • lpszPathInfo Property that contains a string value of the part of the URL that is between the ISAPI DLL and the start of the extra-info section of the URL. Normally there is nothing between the ISAPI DLL and the extra-info section of the URL unless the requesting software put a value in place. This is the same value as the server variable PATH_INFO.

  • lpszContentType Property that contains a string value of the content type of an HTTP Post. This is the same value as the server variable CONTENT_TYPE.

When the HttpExtensionProc function finishes contributing the content for all of the possible server variables and properties of the ECB, the XML document is capped off with the proper enclosing XML tags and sent to the SendResponse function. SendResponse writes the string value passed into the function to the requesting software. As shown in Listing 17-10, the content type header is sent to the requesting client using the ECB ServerSupportFunction. The header that is sent is represented by the constant BASIC_HEADER, which equals the following string: Content-type: text/html\r\n\r\n . HTTP headers are to be followed by two carriage returns and linefeeds so the text/HTML header is specified in the header since the return data is text. XML could be specified, but if the Multipurpose Internet Mail Extensions (MIME) types on the requesting web browser were registered to XML, the registered program may open to display the XML.

Listing 17-10: Function SendResponse

start example
 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Name: SendResponse In:  pECB - pointer to the extension control block   sValue - string reference to the value to be        written to the HTTP response Out:  nothing Purpose:     writes the intended value to the HTTP response  /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ void SendResponse(EXTENSION_CONTROL_BLOCK *pECB, string &sValue) {   TCHAR szTempBuffer[BUFFER_LENGTH];   DWORD dwBufferSize = BUFFER_LENGTH;   // set content-type header   strcpy(szTempBuffer, BASIC_HEADER);   DWORD dwHeaderSize = strlen(szTempBuffer);   pECB->>ServerSupportFunction(pECB->>ConnID,                  HSE_REQ_SEND_RESPONSE_HEADER,                  NULL,                  &dwHeaderSize,                  (LPDWORD) szTempBuffer);   //write value to http response   DWORD dwLength=sValue.length();   pECB->>WriteClient(  pECB->>ConnID,              (PVOID)sValue.c_str(),              &dwLength,              HSE_IO_SYNC); }
end example

After the return header is sent, the actual content passed is sent using the WriteClient function. As shown in the following function prototype, the handle to the connection ConnID is passed during the write to the client. The existing value obtained from the current instance of the ECB pointer was used in Listing 17-10. The content is sent with the WriteClient function using a void pointer in the Buffer parameter. The content to which the Buffer pointer refers must be sized in terms of the number of bytes being passed to the client and referenced in the lpdwBytes parameter. After the call is completed, lpdwBytes will contain the number of bytes that were sent unless the write was performed asynchronously. The value of the dwSync parameter determines how the write will occur to the client. Listing 17-10 specifies a value of 0x00000001 using the HSE_IO_SYNC macro, which means that the write will be performed synchronously and the memory space referenced by the pointer lpdwBytes will be updated after the WriteClient completes with the number of bytes written to the client. If the macro HSE_IO_ASYNC, which represents the value 0x00000002, were used, the data written to the client and a callback function would capture the client write event data. Using WriteClient asynchronously requires the callback function to be declared and the ServerSupportFunction also must send a HSE_REQ_IO_COMPLETION value to set up the asynchronous write transaction with the client.

The WriteClient function is a member function of the ECB. As such, it might seem odd that the handle to the ECB being used must be passed into the function. Since IIS is a multi-threaded application, however, there could be many ECB instances in use at any given time, and it is conceivable that you might want to write in an instance of the ECB to another instance of the ECB. The prototype for WriteClient is as follows:

BOOL WriteClient(  HCONN ConnID,    LPVOID Buffer,    LPDWORD lpdwBytes,   DWORD dwSync   );




IIS 6(c) The Complete Reference
IIS 6: The Complete Reference
ISBN: 0072224959
EAN: 2147483647
Year: 2005
Pages: 193

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net