ISAPI
Une application ISAPI est une DLL pour Windows NT. Le but principal de ISAPI est de fournir une interface alternative au CGI (Common Gateway Interface) plus couramment utilisé. Une application CGI est une application externe s'exécutant dans un processus distinct du serveur Web et est couramment utilisée pour fournir des services non fournis par le serveur HTTP (ou Web) ou définis par la spécification HTML. Vous pouvez utiliser une application CGI pour s'interfacer avec une base de données SQL Server, pour enregistrer des données à partir d'un formulaire HTML ou même pour insérer un compteur sur votre pager Web (comme les compteurs d'accès sur de nombreuses pages Web).
Étant donné que les applications CGI existent depuis plus longtemps, vous vous demandez peut-être pourquoi vous avez besoin (ou devriez vous soucier) d'une autre méthodologie pour fournir des fonctionnalités similaires. Vous vous demandez probablement quelle est la différence entre ISAPI et CGI. Après tout, les deux étendent les capacités de votre serveur Web en créant des extensions personnalisées, et les deux vous permettent d'écrire des extensions à l'aide de votre compilateur C ou C++ préféré. Eh bien, la principale différence entre ces deux interfaces peut se résumer en un mot : la performance !
Une application CGI versus une application ISAPI
Une application CGI s'exécute en tant que processus distinct. Cette situation signifie qu'à chaque fois que l'application est appelée, elle doit être chargée en mémoire, passée les paramètres client et exécutée. Ensuite, il doit renvoyer les données client au serveur HTTP. Si l'application est appelée plusieurs fois par différents clients simultanément, plusieurs copies de l'application résideront en mémoire en même temps, ce qui consomme des ressources système. Une application ISAPI, cependant, est assez différente d'une application CGI :
- Plutôt que de s'exécuter en tant qu'application externe dans un processus distinct, une application ISAPI est une application Windows NT native s'exécutant dans le même espace d'adressage que le serveur HTTP. Il est possible car l'application ISAPI est une bibliothèque de liens dynamiques plutôt qu'un programme externe et qu'elle s'exécute en tant que partie intégrante du serveur HTTP.
- Une bibliothèque de liens dynamiques peut être chargée ou déchargée à volonté. Cette situation signifie que si la fonctionnalité supplémentaire fournie par la DLL n'est plus requise, elle peut être déchargée de la mémoire pour conserver les ressources du système. Lorsque le service est à nouveau nécessaire, il sera rechargé.
- Une application ISAPI est une image partageable de multiple processus léger. Cette situation signifie qu'une seule copie de la DLL sera chargée en mémoire, et cette copie unique peut prendre en charge plusieurs demandes client simultanées. Encore une fois, cela convertit les ressources du système et améliore le temps de réponse.
Quelques considérations relatives aux applications ISAPI
Bien qu'une bibliothèque de liens dynamiques ait ses avantages, elle présente également un inconvénient que vous devez prendre en compte : étant donné qu'une bibliothèque de liens dynamiques s'exécute dans le même espace d'adressage que l'application parente, une bibliothèque de liens dynamiques errante (telle que celle faisant référence à la mémoire sans discernement) peut provoquer l'échec de l'application parente ! Cette situation signifie qu'une application ISAPI mal écrite peut provoquer l'étouffement, le plantage et la mort cruelle et anormale de votre serveur Web. Vous devez vous préparer à l'éventualité en suivant ces suggestions :
- N'effectuez jamais de développement ou de test sur un serveur de production. Il peut vous coûter plus cher de maintenir un double de votre environnement de production, mais c'est une condition nécessaire si la maintenance de votre serveur est essentielle pour votre entreprise.
- N'installez jamais une application ISAPI tierce sur votre serveur de production sans tester minutieusement l'application ISAPI sur un autre serveur (tel que votre environnement de test en double).
- Testez l'application ISAPI en utilisant plusieurs connexions à partir de plusieurs navigateurs Web (y compris Internet Explorer, Netscape, Firefox, Chrome et tous les autres dont vous disposez) pour vous assurer que votre application ISAPI fonctionne comme prévu.
- Si vous développez vos propres applications ISAPI, utilisez la gestion structurée des exceptions fournie par l'API de Win32. Ces API d'exception incluent les instructions try except et try finally. Les instructions try except sont utilisées pour capturer toute exception (telle qu'une référence mémoire non valide) se produisant dans le bloc try, puis exécuter les instructions dans le bloc except (afin que vous puissiez déterminer le type d'exception, puis remédier à la situation). Les instructions try finally sont utilisées pour garantir que peu importe ce qui se passe dans le bloc try (y compris une exception), votre code dans le bloc finally s'exécutera (vous pouvez donc nettoyer votre application en libérant la mémoire allouée).
L'interface ISAPI de base
Si vous avez effectué un développement Windows ou Windows NT, vous êtes peut-être déjà au courant de la multitude d'interfaces (ou des exigences pour implémenter une fonction souhaitée) définies par l'API de Windows ou de Windows NT. Une application ISAPI, aussi communément appelée ISA (Internet Server Application), n'est pas différente. Il nécessite également que vous utilisiez une interface spécifique. La première partie de cette interface, qui est facultative, est que l'application doit prendre en charge la définition de base d'une dynamique d'une bibliothèque de liens dynamiques. Par conséquent, il peut inclure une fonction appelée DLLMain étant appelée lorsque l'application est chargée ou déchargée par le système d'exploitation (Windows Server). Cette fonction fournit une initialisation et un nettoyage ISA spécifiques.
Une fois l'application chargée en mémoire, le serveur HTTP appelle la fonction GetExtensionVersion pour obtenir des informations spécifiques à la version. Si le numéro de version est acceptable, le serveur HTTP appelle ensuite la fonction HttpExtensionProc chaque fois qu'un client effectue une demande nécessitant cette ISA. Normalement, cette demande client est exécutée chaque fois qu'un client sélectionne une URL sous la forme de :
http:\\www.mondomaine.com/scripts/NomExtension.DLL?parametre |
Vous pouvez remplacer respectivement votre propre nom de domaine, nom ISA et paramètres pour les valeurs DomainName, NomExtension.DLL et parametre dans l'URL. Par exemple, votre URL peut apparaître comme ceci :
http://www.gladir.com/scripts/sequelle?PasDeFonction |
Pour vous donner une meilleure idée de cette interface requise, voici un schéma de l'architecture de base ISA :
Comme vous pouvez le voir dans le schéma précédent, il y a un espace d'adressage dans le serveur HTTP et l'application serveur Internet s'exécute. Le serveur HTTP communique avec l'ISA à l'aide d'un bloc de contrôle d'extension (ECB). Un pointeur vers l'ECB est passé à la fonction HttpExtensionProc chaque fois qu'un client effectue une demande. Il appartient à la fonction HttpExtensionProc d'analyser la structure ECB et de déterminer la demande du client et d'effectuer le traitement approprié. La fonction HttpExtensionProc est similaire à la fonction principale d'une application C utilisée comme extension CGI, mais au lieu de recevoir son entrée en utilisant stdin, comme le fait une application C, le stdout pour renvoyer les données traitées au client, la fonction HttpExtensionProc utilise les fonctions WriteClient ou ServerSupportFunction. Dans la fonction HttpExtensionProc, vous pouvez utiliser n'importe quelle API de Win32. Il fournit un moyen d'étendre le serveur HTTP pour effectuer presque toutes les tâches. Le Microsoft dbWeb, par exemple, est une application ISAPI. Le Microsoft dbWeb est utilisé pour fournir une interface ODBC au serveur HTTP afin que vos clients puissent insérer, supprimer, mettre à jour ou interroger vos bases de données compatibles ODBC à l'aide de leur navigateur Web. Voici les six fonctions définies pour un ISA :
Nom | Description |
---|---|
GetExtensionVersion | Cette fonction permet de fournir des informations spécifiques à la version au serveur HTTP. |
HttpExtensionProc | Cette fonction permet d'effectuer le vrai travail de l'extension ISA. |
GetServerVariable | Cette fonction permet de fournir un accès aux variables d'environnement prédéfinies. |
ReadClient | Cette fonction permet de demander des données supplémentaires du client lorsque le tampon de données ECB ne peut pas contenir toutes les données. |
WriteClient | Cette fonction permet de renvoyer les données au client. |
ServerSupportFunction | Cette fonction permet de fournir des fonctions générales ainsi que des fonctions spécifiques au serveur HTTP à l'ISA. |
La fonction GetExtensionVersion
La fonction GetExtensionVersion informe le serveur HTTP de l'adéquation de l'extension. En substance, lorsque la fonction est appelée par le serveur HTTP, elle est passée sous la forme d'un pointeur vers une structure HSE_VERSION_INFO. Cette structure contient les versions majeures et mineures du serveur HTTP que l'extension prend en charge, ainsi qu'une description textuelle de l'extension de serveur. L'ISA doit remplir la structure HSE_VERSION_INFO avec ces informations de version lorsqu'il est appelé par le serveur HTTP. La fonction est utilisée pour empêcher un ISA obsolète d'être chargé sur un serveur HTTP non compatible. La syntaxe de fonction est la suivante :
BOOL WINAPI GetExtensionVersion(HSE_VERSION_INFO *pVer); |
La structure HSE_VERSION_INFO dans httpext.h est définit selon la syntaxe suivante :
typedef struct _HSE_VERSION_INFO { DWORD struct dwExtensionVersion; CHAR lpszExtensionDesc[HSE_MAX_EXT_DLL_NAME_LEN]; } HSE_VERSION_INFO, *LPHSE_VERSION_INFO"; |
La définition HSE_MAX_EXT_DLL_NAME_LEN est définie sur une valeur de 256 caractères, ce qui vous fournit un espace de nom de 255 caractères car vous devez laisser de la place pour le terminateur nul de la chaîne de caractères. Lorsque cette fonction est appelée, elle doit remplir la structure HSE_VERSION_INFO et retourner TRUE (1) en cas de succès ou FALSE (0) en cas d'échec. L'élément dwExtensionVersion peut être rempli à l'aide des macros HIWORD et LOWORD. Le numéro de version majeur est contenu dans le mot bas de l'élément dwExtensionVersion.
La fonction HttpExtensionProc
Chaque fois que le client (le navigateur Web) effectue une requête nécessitant l'ISA, la fonction HttpExtensionProc de l'ISA est appelée et transmet un pointeur vers une structure ECB. La structure ECB est définie dans l'entête HTTPEXT.H comme EXTENSION_CONTROL_BLOCK et a les éléments résumés dans le tableau suivant la syntaxe. La syntaxe de la fonction HttpExtensionProc est la suivante :
DWORD WINAPI HttpExtensionProc(EXTENSION_CONTROL_BLOCK *pEcb); |
Voici les différents champs de la structure EXTENSION_CONTROL_BLOCK :
Champ | Type | Flux | Description |
---|---|---|---|
cbSize | DWORD | Entrée | Ce champ permet d'indiquer la taille d'une structure ECB. |
dwVersion | DWORD | Entrée | Ce champ permet d'indiquer le numéro de version IIS pris en charge par cette extension. Le numéro de version majeure est contenu dans le mot haut et le numéro de version mineure est contenu dans le mot bas. Vous pouvez utiliser les macros HIWORD et LOWORD pour obtenir les numéros de version majeure et mineure. |
ConnID | HCONN | Entrée | Ce champ permet d'indiquer un numéro unique attribué par le serveur HTTP. Ce numéro ne doit pas être modifié. |
dwHttpStatusCode | DWORD | Sortie | Ce champ permet d'indiquer l'état de la transaction terminée en cours à renvoyer au serveur HTTP. |
lpszLogData | CHAR | Sortie | Ce champ permet d'indiquer une chaîne de caractères terminée par un zéro de HSE_LOG_BUFFER_LEN (actuellement définie comme 80 caractères) à insérer dans le journal du serveur HTTP. |
lpszMethod | LPSTR | Entrée | Ce champ permet d'indiquer une chaîne de caractères terminée par null contenant la méthode utilisée par le client pour cette transaction. Il s'agit de la même valeur que la variable REQUEST_METHOD de CGI. |
lpszQueryString | LPSTR | Entrée | Ce champ permet d'indiquer une chaîne de caractères terminée par un caractère nul contenant les informations de la requête. C'est la même chose que la variable QUERY_STRING de CGI. |
lpszPathInfo | LPSTR | Entrée | Ce champ permet d'indiquer une chaîne de caractères à terminaison nulle, contenant les informations de chemin supplémentaires fournies au client. C'est la même chose que la variable PATH_INFO de CGI. |
lpszPathTranslated | LPSTR | Entrée | Ce champ permet d'indiquer une chaîne de caractères terminée par un caractère nul contenant le chemin traduit. C'est la même chose que la variable PATH_TRANSLATED de CGI. |
cbTotalBytes | DWORD | Entrée | Ce champ permet d'indiquer le nombre total d'octets reçus du client. C'est la même chose pour la variable CONTENT_LENGTH de CGI. |
cbAvailable | DWORD | Entrée | Ce champ permet d'indiquer le nombre d'octets disponibles, sur le total défini par cbTotalBytes, contenu dans la mémoire tampon pointée par lpdData. |
lpbData | LPBYTE | Entrée | Ce champ permet d'indiquer un pointeur vers un tampon, de taille cbAvailable, contenant les données envoyées par le client. |
lpszContentType | LPSTR | Entrée | Ce champ permet d'indiquer une chaîne de caractères terminée par un nul contenant le type de données envoyées par le client. C'est la même chose que la variable CONTENT_TYPE de CGI. |
Il appartient à la fonction HttpExtensionProc d'analyser le ECB pour déterminer ce que le client demande. Ces informations seront contenues dans l'élément ECB de lpszQueryString. Toutes les données supplémentaires seront contenues dans le tampon pointé par l'élément lpdData. Si la mémoire tampon est trop petite pour contenir toutes les données telles que définies par l'élément cbTotalBytes, la fonction HttpExtensionProc peut récupérer les données supplémentaires à l'aide de la fonction ReadClient. Lorsque le HttpExtensionProc a terminé son traitement, il doit envoyer les données résultantes (au format HTML) au client à l'aide de la fonction WriteClient ou ServerSupportFunction. Il doit renvoyer un code d'état au serveur HTTP à la fin. L'état peut être l'un des suivants :
Constante | Description |
---|---|
HSE_STATUS_SUCCESS | Le code d'état spécifie que l'ISA a terminé sa tâche avec succès et que le serveur HTTP peut se déconnecter et libérer des ressources système en déchargeant l'ISA. |
HSE_STATUS_SUCCESS_AND_KEEP_CONN | Ce code d'état spécifie que l'ISA a terminé sa tâche, mais que le serveur HTTP doit garder l'ISA actif en mémoire si le client prend en charge les connexions persistantes. Le code d'état HSE_STATUS_SUCCESS_AND_KEEP_CONN ne doit être utilisé que si l'ISA a envoyé un entête keep-alive au client. |
HSE_STATUS_PENDING | Le code d'état spécifie que l'ISA a mis la demande en file d'attente pour traitement et qu'il informera le serveur HTTP lorsque la demande sera terminée à l'aide de ServerSupportFunction avec le paramètre dwHSERequest défini sur HSE_REQ_DONE_WITH_SESSION. |
HSE_STATUS_ERROR | Ce code d'état spécifie que l'ISA a rencontré une erreur en essayant de terminer sa tâche et que le serveur HTTP peut se déconnecter et libérer des ressources système en téléversant l'ISA. |
La fonction GetServerVariable
La fonction GetServerVariable obtient des informations sur la connexion ou des informations sur le serveur HTTP. La fonction est définie comme suit :
BOOL WINAPI GetServerVariable(HCONN hConn, LPSTR lpszVariableName, LPVOID lpvBuffer, LPDWORD lpdwSizeofBuffer); |
Les paramètres de la fonction sont définis comme suit :
Nom | Description | |
---|---|---|
hConn | Ce paramètre permet d'indiquer la connexion à identificateur de descripteur Handle. | |
lpszVariableName | Ce paramètre permet d'indiquer le nom de la variable CGI souhaitée : | |
Nom | Description | |
ALL_HTTP | Ce nom de variable permet d'indiquer tous les entêtes HTTP n'étant pas analysés dans l'une des autres variables répertoriées dans ce tableau. | |
AUTH_PASS | Ce nom de variable permet d'indiquer le mot de passe fourni par le client. Le mot de passe sera une chaîne de caractères terminée par un nul. | |
AUTH_TYPE | Ce nom de variable permet d'indiquer le type d'autorisation utilisé. Si l'utilisateur a été authentifié par le serveur, la valeur de retour sera Basic. Sinon, l'entrée ne sera pas présente. | |
CONTENT_LENGTH | Ce nom de variable permet d'indiquer le nombre d'octets que le script peut sauf recevoir du client. | |
CONTENT_TYPE | Ce nom de variable permet d'indiquer le type de contenu des informations fournies dans la requête POST. | |
GATEWAY_INTERFACE | Ce nom de variable permet d'indiquer le niveau de révision de la spécification CGI. | |
HTTP_ACCEPT | Ce nom de variable permet d'indiquer les informations d'entête HTTP à usage spécial. | |
PATH_INFO | Ce nom de variable permet d'indiquer les informations de chemin d'ajout telles que données par le client. | |
PATH_TRANSLATED | Ce nom de variable est identique à PATH_INFO, mais sans traduction de chemin virtuel. | |
QUERY_STRING | Ce nom de variable permet d'indiquer les informations après le «?» dans une requête de script. | |
REMOTE_ADDR | Ce nom de variable permet d'indiquer l'adresse IP du client. | |
REMOTE_HOST | Ce nom de variable permet d'indiquer le nom d'hôte du client. | |
REMOTE_USER | Ce nom de variable permet d'indiquer le nom d'utilisateur fourni par le client et autorisé par le serveur. | |
REQUEST_METHOD | Ce nom de variable permet d'indiquer la méthode de requête HTTP. | |
SCRIPT_NAME | Ce nom de variable permet d'indiquer le nom du script en cours d'exécution. | |
SERVER_NAME | Ce nom de variable permet d'indiquer le nom du serveur (ou l'adresse IP) tel qu'il doit apparaître dans les URL auto-référencées. | |
SERVER_PORT | Ce nom de variable permet d'indiquer le port TCP/IP sur lequel la demande a été reçue. | |
SERVER_PROTOCOL | Ce nom de variable permet d'indiquer le nom et la version de la requête. | |
SERVER_SOFTWARE | Ce nom de variable permet d'indiquer le nom et le numéro de version du serveur Web sous lequel l'extension IIS s'exécutant. | |
lpvBuffer | Ce paramètre permet d'indiquer un pointeur vers un tampon pour contenir les données définies par la variable CGI. | |
lpdwSizeOfBuffer | Ce paramètre permet d'indiquer la taille du tampon vers lequel pointe lpvBuffer. Lorsque la fonction retourne, ce paramètre contient la longueur des données renvoyées, y compris le terminateur nul. |
La fonction ReadClient
La fonction ReadClient obtient des données supplémentaires du client lorsque le bloc entier de données ne rentrera pas dans le pointeur de tampon par lpdData dans le bloc ECB. La définition de la fonction a la syntaxe suivante :
BOOL ReadClient(HCONN hConn, LPVOID lpvBuffer, LPDWORD lpdwSize); |
Les paramètres de la fonction sont définis comme suit :
Nom | Description |
---|---|
hConn | Ce paramètre permet d'indiquer l'identificateur de descripteur de connexion. |
lpvBuffer | Ce paramètre permet d'indiquer un pointeur vers un tampon. Ce tampon sera utilisé pour contenir les données renvoyées par la requête de lecture. |
lpdwSizeOfBuffer | Ce paramètre permet d'indiquer la taille du tampon vers lequel pointe lpvBuffer. Lorsque la fonction retourne, ce paramètre contient la longueur des données renvoyées. |
La fonction WriteClient
La fonction WriteClient envoie des données au client. La définition de la fonction a la syntaxe suivante :
BOOL WriteClient(HCONN hConn, LPVOID lpvBuffer, LPDWORD lpdwSize, DWORD dwReserved); |
Les paramètres de la fonction sont définis comme suit :
Nom | Description |
---|---|
hConn | Ce paramètre permet d'indiquer l'identificateur de descripteur de connexion. |
lpvBuffer | Ce paramètre permet d'indiquer un pointeur vers un tampon. Ce tampon sera utilisé pour contenir les données envoyées au client. Normalement, ce sera une série de HTML, de balises et de données. |
lpdwSizeOfBuffer | Ce paramètre permet d'indiquer la taille du tampon vers lequel pointe lpvBuffer. Si une chaîne de caractères terminée par un caractère nul doit être envoyée au client dans son intégralité, cette valeur doit être définie à l'aide de la valeur renvoyée par l'appel de fonction strlen(lpvBuffer). Lorsque la fonction WriteClient retourne, ce paramètre contient la longueur des données envoyées au client. |
dwReserved | Ce paramètre permet d'indiquer que le paramètre est réservé pour une utilisation future. |
La fonction ServerSupportFunction
La fonction ServerSupportFunction renvoie les données à un client de la même manière que la fonction WriteClient. Cependant, il informe également le serveur HTTP de l'état de l'opération que la fonction WriteClient ne peut pas faire. La définition de la fonction a la syntaxe suivante :
BOOL ServerSupportFunction(HCONN hConn, DWORD dwHSERequest, LPVOID lpvBuffer, LPDWORD lpdwSizeOfBuffer, LPDWORD lpdwDataType); |
Les paramètres de la fonction sont définis comme suit :
Nom | Description | |
---|---|---|
hConn | Ce paramètre permet d'indiquer l'identificateur de descripteur de connexion. | |
dwHSERequest | Ce paramètre permet d'indiquer le type de demande à envoyer au client. Il peut s'agir d'un type de demande personnalisé, utilisant une valeur supérieure à celle définie par HSE_REQ_END_RESERVED, ou de l'une des valeurs prédéfinies à usage général suivantes : | |
Valeur | Description | |
HSE_REQ_SEND_URL_REDIRECT_RESP | Ce paramètre permet d'indiquer qu'un message 302 URL redirect doit être envoyé au client. Il revient à spécifier l'URI:URL dans un entête de script CGI. Si cette valeur est utilisée, le lpvBuffer doit pointer vers une chaîne de caractères terminée par null contenant l'URL. | |
HSE_REQ_SEND_URL | Ce paramètre permet d'indiquer que les données associées à une URL soient envoyées au client comme s'il avait explicitement sélectionné l'URL. Le lpvBuffer doit pointer vers une chaîne de caractères terminée par null contenant l'URL. | |
HSE_REQ_SEND_RESPONSE_HEADER | Ce paramètre permet d'indiquer qu'un entête de réponse de serveur HTTP complet doit être envoyé au client. L'application doit ajouter des informations supplémentaires, telles que le type de contenu, la longueur du contenu, un \r\n (paire retour chariot/saut de ligne) et toute autre information supplémentaire. | |
HSE_REQ_MAP_URL_TO_PATH | Ce paramètre permet d'indiquer que le lpdvBuffer contiendra un chemin d'accès logique lorsque le ServerSupportFunction est exécuté, et que le lpdvBuffer aura un chemin d'accès physique correspondant au nom logique à la fin. | |
lpvBuffer | Ce paramètre permet d'indiquer un pointeur vers une mémoire tampon contenant la chaîne de caractères d'état terminée par null (telle que 402-Payment required) à renvoyer au client. Si cette valeur est NULL, le code d'état par défaut (200 OK) sera envoyé au client. | |
lpdwSizeOfBuffer | Ce paramètre permet d'indiquer la taille du tampon vers lequel pointe lpvBuffer. Si une chaîne de caractères terminée par un caractère nul doit être envoyée au client dans son intégralité, cette valeur doit être définie à l'aide de la valeur renvoyée par l'appel de fonction strlen(lpvBuffer). Lorsque la fonction ServerSupportFunction retourne, lpdwSizeOfBuffer contient la longueur des données de l'ensemble envoyées au client. | |
lpDataType | Ce paramètre permet d'indiquer un pointeur vers une chaîne de caractères données terminée par un zéro à ajouter à l'entête. Il s'agit d'une chaîne de caractères facultative, et si ce champ est NULL, l'entête se terminera par un \r\n. |