|
|
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
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 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; }
Note | The source code for SEUX is available on the author's web site as mentioned in the book's Introduction. |
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.
Figure 17-12: IE 6.0 displaying the XML document of server variables from the SEUX ISAPI extension
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
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 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(); } } }
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.
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. |
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.
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 |
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. |
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 |
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
<<?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>>
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
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 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; }
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
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 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)); } }
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
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 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("&")); FindAndReplace(&sReturn, &string("="),&string("=")); FindAndReplace(&sReturn, &string("<<"),&string("<")); FindAndReplace(&sReturn, &string(">>"),&string(">")); FindAndReplace(&sReturn, &string("'"),&string("'")); FindAndReplace(&sReturn, &string("\""),&string(""")); return sReturn; }
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
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 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); } }
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
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 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); }
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 );
|
|