Capítulo 24

Application Program Interface Del Servidor Del Internet (ISAPI)


CONTENIDO

Ahora que usted ha tenido la oportunidad de repasar los elementos del cgi así como estructura algunos ejemplos simples, las vueltas de este capítulo a los detalles de la arquitectura de ISAPI. El estándar del marco ISAPI del servidor de ActiveX ofrece algunas ventajas distintas sobre el cgi para ciertos tipos de usos. Así pues, en este capítulo le introducirán a los detalles de la arquitectura de ISAPI.

La Arquitectura de ISAPI

El acercamiento de ISAPI utiliza las bibliotecas de acoplamiento dinámicas (DLLs) para evitar algo del implicado de arriba en procesos concurrentes de freza en el estándar del cgi. Observe que un DLL se puede cargar por el servidor que entonces sigue habiendo una petición se recibe la primera vez para ese DLL y en la memoria lista mantener peticiones adicionales. Desemejante del modelo de proceso del cgi, ISAPI DLLs se cargan en el mismo espacio de dirección que el servidor del HTTP (véase fig. 24,1).

Cuadro 24,1: La arquitectura de ISAPI emplea ligarse dinámico para mejorar funcionamiento.

El DLLs puede persistir en memoria, y el servidor sabe que el DLLs está mintiendo alrededor de esperar para mantener una petición. Así, no hay virtualmente asociado de arriba con el mantenimiento de la petición siguiente. Por supuesto, este rendimiento más alto viene en un coste. Todos los usos de ISAPI deben ser caja fuerte multithread. Pero antes de que sumerjamos en los detalles de multithreaded y el desarrollo del código reentrante (un tema más allá del alcance de este libro), vamos tomar una mirada debajo de la capilla del desarrollo del uso de ISAPI.

Puntos De Entrada Requeridos Del Servidor

Un uso de ISAPI es realmente justo una biblioteca de acoplamiento dinámica que se conforme con algunas reglas simples. Cada uso de ISAPI se requiere para proporcionar dos puntos de entrada:

Cuando el web server carga un uso de ISAPI para la primera vez, llama la función de GetExtensionVersion. Falta de hacer esta función causa disponible de la voluntad el uso de ISAPI al fall. Una puesta en práctica típica de la función de GetExtensionVersion (la que esta' recomendada por Microsoft) se demuestra en el listado 24,1.


Enumerar la puesta en práctica recomendada 24,1 de GetExtensionVersion
   BOOL WINAPI GetExtensionVersion(HSE_VERSION_INFO * pVer) { pVer->dwExtensionVersion = MAKELONG(HSE_VERSION_MINOR, HSE_VERSION_MAJOR);  el pVer->lpszExtensionDesc del lstrcpyn(, "esto es un uso del web server de la muestra", HSE_MAX_EXT_DLL_NAME_LEN);  de vuelta VERDAD;  }  

La función de GetExtensionVersion es llamada por el web server cuando carga el DLL de ISAPI. El propósito de este paso es permitir que el uso de ISAPI identifique la versión del ISAPI en el cual se basa el uso.

El servidor también depende de un segundo punto de entrada en su uso de ISAPI llamado el HttpExtensionProc . El HttpExtensionProc es básicamente la función principal para su uso. El prototipo para la función de HttpExtensionProc se demuestra en el listado 24,2.


Enumerar El Prototipo De la Función De 24,2 HttpExtensionProc
   DWORD HttpExtensionProc(LPEXTENSION_CONTROL_BLOCK * lpEcb);  

El HttpExtensionProc es responsable de volver un valor del estado. Los valores de vuelta posibles son los siguientes:

Note que el HttpExtensionProc recibe un indicador largo a una estructura llamada un EXTENSION_CONTROL_BLOCK . El EXTENSION_CONTROL_BLOCK se utiliza para proveer cualquier información o dato del contexto referente a la petición del web browser. Le expondrán a esta estructura importante detalladamente en apenas un momento. Pero primero heche una ojeada cada uno de los varios valores del estado que HttpExtensionProc puede volver.

Usted divulgará HSE_STATUS_SUCCESS cuando el uso de ISAPI ha venido a una conclusión feliz. El servidor después cierra abajo de la sesión con el cliente y libera recursos para arriba asignados.

A veces, usted deseará divulgar éxito sin hacer el servidor cerrar la conexión del cliente. Después de enviar una subsistencia del HTTP el jefe vivo, vuelva HSE_STATUS_SUCCESS_AND_KEEP_CONN para solicitar que la subsistencia del servidor la conexión del cliente abierta. En este caso, el servidor puede esperar la petición siguiente del HTTP (si se asume que el cliente apoya conexiones persistentes). Observe que el servidor no está requerido para mantener la sesión abierta.

Los usos de algún ISAPI pueden necesitar tiempo de transformación extendido así que se proporciona el estado de HSE_STATUS_PENDING. Este estado se utiliza conjuntamente con la función de ServerSupportFunction.

Si el DLL de ISAPI ha encontrado un error durante el proceso, vuelve HSE_STATUS_ERROR . Cuando el servidor coge este estado, cierra a cliente que la sesión libre asignó recursos.

Usar Bloques De Control De la Extensión

Memoria en el interfaz de entrada común que la información sobre la petición del cliente se pasa con una serie de variables de entorno. Estas variables de entorno son fijadas por el servidor basado por el requerimiento del cliente cuando es el proceso de uso instantiated. ISAPI tiene un concepto similar llamado el EXTENSION_CONTROL_BLOCK . Como en el cgi, la mayoría de la información que sus usos de ISAPI necesitarán conseguir comenzados se encuentran en el EXTENSION_CONTROL_BLOCK . Como usted vio en enumerar 24,2, HttpExtensionProc se pasa un indicador al llenado la estructura de EXTENSION_CONTROL_BLOCK. Demuestran los miembros de una estructura de EXTENSION_CONTROL_BLOCK en la tabla 24,1.

Miembros de la tabla 24,1 de la estructura de EXTENSION_CONTROL_BLOCK

Nombre Descripción
cbSize Indica el tamaño de la estructura actual de EXTENSION_CONTROL_BLOCK.
dwVersion El HIWORD de este valor lleva a cabo el número de versión principal y el LOWORD almacena el número de versión de menor importancia.
ConnID Identifica únicamente la conexión. Utilizado en llamadas a ServerSupportFunction .
dwHttpStatusCode Fije este valor para indicar el estado de la transacción cuando se termina la petición.
lpszLogData Indica la secuencia para entrar en el fichero de diario.
lpszMethod Contiene un método de la petición del HTTP como CONSIGUEN o FIJAN . Utilice este campo apenas como la variable de entorno de REQUEST_METHOD en el cgi.
el lpszQueryString Éste es el equivalente del cgi QUERY_STRING variable .
lpszPathInfo Éste es el equivalente al cgi PATH_INFO variable .
lpszPathTranslated Éste es el equivalente al cgi PATH_TRANSLATED variable .
cbTotalBytes Indica el número total de los octetos que se recibirán del cliente. Es igual que el cgi CONTENT_LENGTH variable .
cbAvailable Indica el número disponible de octetos (fuera de un total de cbTotalBytes ) en el almacenador intermediario señalado por al lpbData .
lpbData Indica los datos enviados por el cliente.
lpszContentType Indica el tipo contento de los datos enviados por el cliente. Está el equivalente al cgi CONTENT_TYPE variable .

Muchos de estos artículos están pasando simplemente a lo largo de la información del interfaz de entrada común. Sin embargo, hay pares de éstos que lleven la discusión adicional.

El miembro del lpszLogData ofrece una capacidad no disponible a los usos del cgi directamente. Los usos de ISAPI se permiten para proveer (o no fuente) una secuencia de texto para la entrada en el registro de la transacción del servidor de la tela. Los usos del cgi han estado siempre libres guardar registros privados. Sin embargo, el acercamiento del bloque de control de la extensión en ISAPI permite la registración integrada simple de la transacción.

El miembro de los cbTotalBytes indica el número total de los octetos que se recibirán del cliente. Según lo mencionado en la tabla 24,1, esto es equivalente al cgi CONTENT_LENGTH variable . Usted utilizará la función de ReadClient conjuntamente con el miembro cbAvailable para obtener la información pasada por el web browser a través del miembro del lpbData.

La variable cbAvailable demuestra el número disponible de octetos (fuera de un total de cbTotalBytes ) en el almacenador intermediario señalado por al lpbData . Si los cbTotalBytes son iguales que cbAvailable , el lpbData señalará a todos los datos según lo enviado por el cliente.

El EXTENSION_CONTROL_BLOCK tiene varias funciones del miembro también. Estas funciones se resumen brevemente en la tabla 24,2 y después se discuten detalladamente como necesarias.

Funciones Del Miembro De la Tabla 24,2 EXTENSION_CONTROL_BLOCK

Nombre Descripción
GetServerVariable Recuperaban las variables del servidor no pasadas como miembros de los datos en el EXTENSION_CONTROL_BLOCK .
WriteClient Envía la información al cliente.
ReadClient Lee la información del cliente.
ServerSupportFunction Ejecutaban ciertas transacciones del servidor y del específico del HTTP.

Usando GetServerVariable la función de GetServerVariable es práctica para recuperar la otra información referente a la sesión del uso con el web browser que no se pasa con el EXTENSION_CONTROL_BLOCK . Todos estos artículos originan en la especificación del cgi. Enumerando 24,3 demostraciones el prototipo de la función para el GetServerVariable funciona.


Enumerar El Prototipo De la Función De 24,3 GetServerVariable
   El hConn de BOOL WINAPI GetServerVariable(HCONN, lpszVariableName de LPSTR, lpvBuffer de LPVOID, LPDWORD lpdwSize);  

Usar esta función es bastante directo. Le pasarán el valor apropiado para el hConn en el parámetro de lpECB de HttpExtensionProc . Usted proveerá un almacenador intermediario y su tamaño que el servidor pase detrás completado de los datos apropiados. El lpszVariableName puede adquirir un número de valores según lo demostrado en la tabla 24,3.

Valores posibles de la tabla 24,3 para el lpszVariableName

Valor Descripción
ALL_HTTP Utilice este valor para obtener todas las variables que no se pasan con un EXTENSION_CONTROL_BLOCK .
AUTH_PASS Lleva a cabo la contraseña.
AUTH_TYPE Contiene el tipo de autentificación usado.
CONTENT_LENGTH Indica el tamaño de los datos en los octetos recibidos del cliente.
CONTENT_TYPE Indica el tipo contento de datos del POSTE enviados del cliente.
GATEWAY_INTERFACE Indica la versión de espec. del cgi que el servidor apoya.
HTTP_ACCEPT Indica un jefe especial del HTTP del caso.
PATH_INFO Indica la parte del URL que aparece de siguiente del Domain Name.
PATH_TRANSLATED Almacena el PATH_INFO con cualquier nombre virtual de la trayectoria substituido por la trayectoria completamente cualificada.
QUERY_STRING Contiene los pares del valor del campo provistos de una petición del CONSEGUIR.
REMOTE_ADDR Indica el IP ADDRESS del cliente.
REMOTE_HOST Indica el hostname del cliente.
REMOTE_USER Contiene el nombre del usuario provisto durante la autentificación.
REQUEST_METHOD Contiene el método de la petición del HTTP. Generalmente, esto es CONSIGUE o FIJA .
SCRIPT_NAME Indica el nombre del uso que es ejecutado.
SERVER_NAME Indica el nombre de anfitrión o el IP ADDRESS del servidor.
SERVER_PORT Contiene el puerto de TCP/IP en el cual la petición fue recibida.
SERVER_PROTOCOL Contiene el protocolo apoyado por el servidor.
SERVER_SOFTWARE Indica el nombre del software del servidor que procesa el uso.

A call to GetServerVariable is really quite straightforward. Take a look at Listing 24.4.


Listing 24.4  Calling GetServerVariable
...



...



char szBuffer[BUFSIZE];











if ( lpECB->GetServerVariable(lpECB->ConnID, AUTH_TYPE, (LPVOID) szBuffer, BUFSIZE) )



{



     if ( strcmp(szBuffer, "NTLM") ) 



     {



          //Do something for NT security authenticated users only



     }



}



...



...




Using ReadClient  ReadClient is used for POST operations. Rather than reading data from stdin as you would in a CGI application, you'll use the ReadClient function. The prototype for ReadClient is displayed in Listing 24.5.


Listing 24.5  ReadClient Function Prototype
BOOL WINAPI ReadClient( HCONN ConnID, LPVOID lpvBuffer, LPDWORD lpdwSize );




As with GetServerVariable, you'll have the appropriate value for hConn in the lpECB parameter from HttpExtensionProc. You'll supply a buffer and its size which the server will pass back filled out with the appropriate data.

Calling ReadClient is simple. Take a look at Listing 24.6.


Listing 24.6  Calling ReadClient
...



...



char szBuffer[BUFSIZE];



DWORD dwErrorCode;











if ( lpECB->ReadClient(lpECB->ConnID, (LPVOID) szBuffer, BUFSIZE) )



{



     //Process the data



}



else



{



     dwErrorCode = GetLastError();







    //Handle Error



    ...



}



...



...




Using WriteClient  WriteClient is used to send data to the client. Rather than writing your responses to stdout as you would in a CGI application, you'll use the WriteClient function. The prototype for WriteClient is displayed in Listing 24.7.


Listing 24.7  WriteClient Function Prototype
BOOL WINAPI WriteClient( HCONN ConnID, LPVOID Buffer, LPDWORD lpdwBytes,



DWORD dwReserved );




Calling WriteClient is simple. Take a look at Listing 24.8.


Listing 24.8  Calling WriteClient
...



DWORD dwSize;



char szBuffer[255];







wsprintf(szBuffer, "The ConnID is %u<BR>", lpECB->ConnID);



dwSize = strlen(szBuffer);







if ( lpECB->WriteClient(lpECB->ConnID, szBuffer, &dwSize, 0L) )



{



     //Do something on success



}




Using ServerSupportFunction

The ServerSupportFunction is used to send various HTTP headers to the Web browser. The prototype for the ServerSupportFunction is shown in Listing 24.9.


Listing 24.9  ServerSupportFunction Function Prototype
BOOL WINAPI ServerSupportFunction( HCONN hConn, 



                             DWORD dwHSERRequest, 



                             LPVOID lpvBuffer, 



                             LPDWORD lpdwSize,



                             LPDWORD lpdwDataType );




You can use ServerSupportFunction to pass various constants to the server. These constants typically provide status and request action from the server (see Table 24.4).

Table 24.4  dwHSERRequest Constants to Send Via ServerSupportFunction

Constant Meaning
HSE_REQ_SEND_URL_REDIRECT_RESP Used to send Redirect (302) message to the client. Use lpvBuffer to point at the URL to which the client should be redirected.
HSE_REQ_SEND_URL Sends a local URL (pointed to by lbvBuffer) to the client as if the client had requested that URL. The URL must be on this server.
HSE_REQ_SEND_RESPONSE_HEADER Sends a complete HTTP server response header. Your ISAPI application must append other HTTP headers such as the Content-Type.
HSE_REQ_DONE_WITH_SESSION ISAPI application that require significant processing time may want to hold on to the connection. Use this constant to signal the server that the session can be closed.
HSE_REQ_END_RESERVED Used to mark the end of space reserved for requests. Special server specific requests are above this number.
HSE_REQ_MAP_URL_TO_PATH Used to map URLs to physical path on the server.

Take a look at the code snippet in Listing 24.10 for an example of using ServerSupportFunction.


Listing 24.10  Using ServerSupportFunction
strcpy(szBuffer,"Content-Length: 1023");



lpECB->ServerSupportFunction ( pECB->ConnID,



                               HSE_REQ_SEND_RESPONSE_HEADER,



                               "200 OK",



                               NULL,



                               (LPDWORD) szBuffer);



//Send other headers and HTML



...




Building a Simple ISAPI Application

To demonstrate the use of some of the methods and data members of the EXTENSION_CONTROL_BLOCK, you'll build a simple ISAPI DLL to display the values stored in the EXTENSION_CONTROL_BLOCK. This example ISAPI application was compiled and constructed using Microsoft Visual C++ 4.1. This application is called GetECBNoMFC. The NoMFC part of the name is included because you'll repeat this exercise again using the ISAPI Wizard and Microsoft Foundation Classes. However, it is quite educational to try it without MFCs first so that you clearly understand what's going on under the covers. To build this simple ISAPI application, follow these steps:

  1. Start Microsoft Visual C++ 4.1 (or higher) and begin a new project. Select File, New and choose Project Workspace. ISAPI applications are DLLs so choose Dynamic Link Library from the list of project types (see fig. 24.2). Now, press the Create button.
    Figure 24.2 : Starting the ISAPI project.

  2. The first thing you'll need is the definition (.def) file that indicates the functions that will be exported by this DLL. So create a GetECBNoMFC.def file and add exports for GetExtensionVersion and HttpExtensionProc. Your definition file should look like Listing 24.11.

Listing 24.11  GETECBNOMFC.DEF-Definition File for the ISAPI Application
LIBRARY          "GETECBNOMFC"







EXPORTS



     HttpExtensionProc



     GetExtensionVersion




  1. Go ahead and build a small header file for the application. You'll be con-structing four functions: SendHTML, SendECB, GetExtensionVersion, and HttpExtensionProc. Your code should look like Listing 24.12.

Listing 24.12  GETECBNOMFC.H-Header File for the ISAPI Application
//



//     Function Prototypes



//



BOOL SendHTML(EXTENSION_CONTROL_BLOCK *pECB, LPSTR lpData);



BOOL SendECB(EXTENSION_CONTROL_BLOCK *pECB);



BOOL WINAPI GetExtensionVersion(HSE_VERSION_INFO *pVer);



DWORD WINAPI HttpExtensionProc(EXTENSION_CONTROL_BLOCK *pECB);





    Note that GetExtensionVersion and HttpExtensionProc are required interfaces for the DLL.
  1. Now start the GetECBNoMFC.cpp file. The first function you'll want to build is SendHTML. This function uses WriteClient to send strings of HTML back to the Web browser. Your code should look like Listing 24.13.

Listing 24.13  GETECBNOMFC.C-SendHTML Function
#include "windows.h"



#include "windowsx.h"



#include "httpext.h"



#include "GetECBNoMFC.h"











/*-------------------------------------------



 *--   Used to Send HTML to the browser



 *-------------------------------------------*/



BOOL SendHTML(EXTENSION_CONTROL_BLOCK *pECB, LPSTR lpData)



{



     DWORD     dwSize;







     dwSize = strlen(lpData);



     return pECB->WriteClient(pECB->ConnID, lpData, &dwSize, 0L);



}





    Remember to include required header files. Note that many of the constants applicable to an ISAPI application are included in HttpExt.h. The call to WriteClient is really quite simple.
  1. The next function to build is SendECB. SendECB employs the SendHTML function you just wrote to display the values of each of the data members of the EXTENSION_CONTROL_BLOCK referenced by pECB. Your code should look like Listing 24.14.

Listing 24.14  2GETECBNOMFC.C-SendECB Function
/*-------------------------------------------



 *--   Used to Send the ECB values



 *-------------------------------------------*/



BOOL SendECB(EXTENSION_CONTROL_BLOCK *pECB)



{



     char szBuffer[4096];







     wsprintf(szBuffer, "cbSize             ---&GT %u<BR>", pECB->cbSize);



     SendHTML(pECB, szBuffer);







     wsprintf(szBuffer, "dwVersion          ---&GT %ul<BR>", pECB->dwVersion);



     SendHTML(pECB, szBuffer);



     



     wsprintf(szBuffer, "ConnID             -&GT %u<BR>", pECB->ConnID);



     SendHTML(pECB, szBuffer);



     



     wsprintf(szBuffer, "lpszMethod   ---&GT %s<BR>", pECB->lpszMethod);



     SendHTML(pECB, szBuffer);



     



     wsprintf(szBuffer, "lpszQueryString    ---&GT %s<BR>", pECB->lpszQueryString);



     SendHTML(pECB, szBuffer);



     



     wsprintf(szBuffer, "lpszPathInfo       ---&GT %s<BR>", pECB->lpszPathInfo);



     SendHTML(pECB, szBuffer);



     



     wsprintf(szBuffer, "lpszPathTranslated ---&GT %s<BR>", pECB >lpszPathTranslated);



     SendHTML(pECB, szBuffer);



     



     wsprintf(szBuffer, "cbTotalBytes       ---&GT %u<BR>", pECB->cbTotalBytes);



     SendHTML(pECB, szBuffer);



     



     wsprintf(szBuffer, "cbAvailable        ---&GT %u<BR>", pECB->cbAvailable);



     SendHTML(pECB, szBuffer);



     



     if (pECB->lpbData == NULL)



     {



          SendHTML(pECB, "lpbData ---&GT (null)<BR>");



     }



     else



     {



          wsprintf(szBuffer, "lpbData            ---&GT %s<BR>", pECB->lpbData);



          SendHTML(pECB, szBuffer);



     }







     wsprintf(szBuffer, "lpszContentType    ---&GT %s<BR>", pECB->lpszContentType);



     SendHTML(pECB, szBuffer);







     return TRUE;







}




  1. The GetExtensionVersion function is a required interface with a straightforward implementation. Your code should look like Listing 24.15.

Listing 24.15  3GETECBNOMFC.C-Implementation of the GetExtensioVersion Interface
/*-------------------------------------------



 *--   Required interface 



 *-------------------------------------------*/



BOOL WINAPI GetExtensionVersion(HSE_VERSION_INFO *pVer)



{



     // Set the version number



     pVer->dwExtensionVersion = MAKELONG(1,0);







     // Set the description field



     strcpy(pVer->lpszExtensionDesc,"Get Extension Control Block");







     return TRUE;



}





    Recall that the dwExtensionVersion member is a HIWORD/LOWORD style variable representing the major and minor versions of ISAPI for which this application was written. A short, simple description is customary for the lpszExtensionDesc member.
  1. The HttpExtensionProc function is the main function for an ISAPI DLL. For this simple application, you'll print out the values of each of the relevant EXTENSION_CONTROL_BLOCK data members. Your code should look like Listing 24.16.

Listing 24.16  4GETECBNOMFC.C-Implementation of the HttpExtensionProc Interface
/*-------------------------------------------------



 *--   The Main of this ISAPI (required function)



 *------------------------------------------------*/



DWORD WINAPI HttpExtensionProc(EXTENSION_CONTROL_BLOCK *pECB)



{



     char     szBuffer[4096]; //Buffer size is arbitrary



     



     // Set the return code to success



     pECB->dwHttpStatusCode = HSE_STATUS_SUCCESS;







     // Send HTTP Headers



     strcpy(szBuffer,"Content-Type: text/html\r\n\r\n");



     pECB->ServerSupportFunction ( pECB->ConnID,



                                     HSE_REQ_SEND_RESPONSE_HEADER,



                                     "200 OK",



                                     NULL,



                                     (LPDWORD) szBuffer);







     // Send HTML Document



     SendHTML(pECB,"<HTML><HEAD>\n");



     SendHTML(pECB,"<HEAD><TITLE>Extension Control Block</TITLE>\n");



     SendHTML(pECB,"</HEAD><BODY>\n");



     SendHTML(pECB,"<H1>Extension Control Block</H1>\n");



     SendECB(pECB);



     SendHTML(pECB,"</BODY></HTML>");











     return HSE_STATUS_SUCCESS;



}




You are required to append application-specific HTTP headers (such as the Content-Type directive followed by a pair of carriage returns and linefeeds) to an HSE_REQ_SEND_RESPONSE_HEADER. Next, you'll send out the HTML for your document and finally return an HSE_STATUS_SUCCESS to the caller.

Now you're ready to test the application. You'll need access to a Windows NT server running Microsoft's IIS or another ISAPI-compatible Web server. Follow these steps to test your ISAPI application:

  1. Place the compiled version of your ISAPI DLL in the appropriate "scripts" directory on the Web server.
  2. You can make a simple form to execute the DLL as a GET from a form. You'll find a simple form on the enclosed CD for this purpose, but you can use any form you choose. The example form on the disk looks like figure 24.3.
    Figure 24.3 : Use a form like this to execute GetECBNoMFC.

  3. Enter some information into the form and then press the Submit button. When executed as the action of a simple form, the output should look like figure 24.4.
    Figure 24.4 : Execute GetECBNoMFC.dll as a GET from a form.

  4. You can use GetECBNoMFC.dll with a POST form as well. When executed as the action of a simple form using POST, the output should look like figure 24.5.
    Figure 24.5 : Execute GetECBNoMFC.dll as a POST from a form.