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.
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.
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.
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.
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.
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.
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 }
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).
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 ...
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:
Listing 24.11 GETECBNOMFC.DEF-Definition File for the ISAPI Application
LIBRARY "GETECBNOMFC" EXPORTS HttpExtensionProc GetExtensionVersion
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);
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); }
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 ---> %u<BR>", pECB->cbSize); SendHTML(pECB, szBuffer); wsprintf(szBuffer, "dwVersion ---> %ul<BR>", pECB->dwVersion); SendHTML(pECB, szBuffer); wsprintf(szBuffer, "ConnID -> %u<BR>", pECB->ConnID); SendHTML(pECB, szBuffer); wsprintf(szBuffer, "lpszMethod ---> %s<BR>", pECB->lpszMethod); SendHTML(pECB, szBuffer); wsprintf(szBuffer, "lpszQueryString ---> %s<BR>", pECB->lpszQueryString); SendHTML(pECB, szBuffer); wsprintf(szBuffer, "lpszPathInfo ---> %s<BR>", pECB->lpszPathInfo); SendHTML(pECB, szBuffer); wsprintf(szBuffer, "lpszPathTranslated ---> %s<BR>", pECB >lpszPathTranslated); SendHTML(pECB, szBuffer); wsprintf(szBuffer, "cbTotalBytes ---> %u<BR>", pECB->cbTotalBytes); SendHTML(pECB, szBuffer); wsprintf(szBuffer, "cbAvailable ---> %u<BR>", pECB->cbAvailable); SendHTML(pECB, szBuffer); if (pECB->lpbData == NULL) { SendHTML(pECB, "lpbData ---> (null)<BR>"); } else { wsprintf(szBuffer, "lpbData ---> %s<BR>", pECB->lpbData); SendHTML(pECB, szBuffer); } wsprintf(szBuffer, "lpszContentType ---> %s<BR>", pECB->lpszContentType); SendHTML(pECB, szBuffer); return TRUE; }
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; }
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: