Я пытаюсь написать формальное расширение ISAPI, чтобы лучше понять IIS. У меня есть следующий код для расширения:
#include <httpext.h>
#include <cstring>
#include <cstdio>
#include <fstream>
#include <iostream>
#include <windows.h>
BOOL WINAPI GetExtensionVersion(HSE_VERSION_INFO * versionInfo)
{
versionInfo->dwExtensionVersion = HSE_VERSION;
strcpy_s( versionInfo->lpszExtensionDesc, "ISAPIExtension.dll");
return TRUE;
}
DWORD WINAPI HttpExtensionProc(EXTENSION_CONTROL_BLOCK* ecb)
{
char * msg = "<html><head/><body>Hello world</body></html>";
DWORD msg_length = strlen(msg) * sizeof(char);
DWORD sent_length = msg_length;
std::cout << sent_length;
auto result = ecb->WriteClient(ecb->ConnID, msg, &sent_length, 0);
if (!result || sent_length != msg_length)
{
std::fstream output("errors.txt");
output << "Could not write to client. Last error was " << GetLastError();
output.flush();
output.close();
return HSE_STATUS_ERROR;
}
std::cout << sent_length;
return HSE_STATUS_SUCCESS;
}
IIS Express отвечает HTTP 200, но нулевые байты были отправлены обратно клиенту. Что-то в вышесказанном не так? Для справки, это мой applicationhost.config:
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<configSections>
<sectionGroup name="system.applicationHost">
<section name="applicationPools"/>
<section name="sites"/>
</sectionGroup>
<sectionGroup name="system.webServer">
<section name="globalModules"/>
<section name="handlers"/>
<section name="caching"/>
<section name="modules"/>
<section name="staticContent"/>
<section name="httpErrors"/>
<sectionGroup name="tracing">
<section name="traceFailedRequests" overrideModeDefault="Allow" />
<section name="traceProviderDefinitions" overrideModeDefault="Deny" />
</sectionGroup>
<!-- The following section elements are not specified in this file, but without these
declarations, IIS will 500 requests -->
<sectionGroup name="security">
<section name="isapiCgiRestriction"/>
<section name="access"/>
<sectionGroup name="authentication">
<section name="anonymousAuthentication"/>
</sectionGroup>
</sectionGroup>
<section name="serverRuntime"/>
</sectionGroup>
</configSections>
<system.applicationHost>
<applicationPools>
<add name="UnmanagedClassicAppPool" managedRuntimeVersion="" managedPipelineMode="Classic" autoStart="true" />
</applicationPools>
<sites>
<site name="Default Web Site" id="1" serverAutoStart="true">
<application path="/" applicationPool="UnmanagedClassicAppPool">
<virtualDirectory path="/" physicalPath="%IIS_USER_HOME%\blog" />
</application>
<bindings>
<binding protocol="http" bindingInformation=":8080:localhost" />
</bindings>
<traceFailedRequestsLogging directory="%IIS_USER_HOME%\TraceLogFiles" enabled="true" maxLogFileSizeKB="1024" />
</site>
</sites>
</system.applicationHost>
<system.webServer>
<security>
<isapiCgiRestriction notListedIsapisAllowed="true"/>
</security>
<globalModules>
<add name="StaticFileModule" image="%IIS_BIN%\static.dll" />
<add name="AnonymousAuthenticationModule" image="%IIS_BIN%\authanon.dll" />
<add name="IsapiModule" image="%IIS_BIN%\isapi.dll" />
<add name="FailedRequestsTracingModule" image="%IIS_BIN%\iisfreb.dll" />
</globalModules>
<staticContent>
<mimeMap fileExtension=".html" mimeType="text/html" />
</staticContent>
<modules>
<add name="StaticFileModule"/>
<add name="AnonymousAuthenticationModule"/>
<add name="IsapiModule"/>
<add name="FailedRequestsTracingModule"/>
</modules>
<handlers accessPolicy="Read, Execute, Script">
<add name="ISAPI-dll" path="*.dll" verb="*" modules="IsapiModule" resourceType="File" requireAccess="Execute" allowPathInfo="true" />
<add name="StaticFile" path="*.html" verb="*" modules="StaticFileModule" resourceType="Either" requireAccess="Read" />
</handlers>
<tracing>
<traceProviderDefinitions>
<add name="ISAPI Extension" guid="{a1c2040e-8840-4c31-ba11-9871031a19ea}">
<areas>
<clear />
</areas>
</add>
</traceProviderDefinitions>
<traceFailedRequests>
<add path="*">
<traceAreas>
<add provider="ISAPI Extension" verbosity="Verbose" />
</traceAreas>
<failureDefinitions statusCodes="200-999" />
</add>
</traceFailedRequests>
</tracing>
</system.webServer>
</configuration>
Добавление заголовка Content-Type устраняет проблему:
DWORD WINAPI HttpExtensionProc(EXTENSION_CONTROL_BLOCK* ecb)
{
char msg [] = "<html><head/><body>Hello world</body></html>";
DWORD msg_length = strlen(msg);
DWORD sent_length = msg_length;
HSE_SEND_HEADER_EX_INFO HeaderExInfo;
HeaderExInfo.pszStatus = "200 OK";
HeaderExInfo.pszHeader = "Content-type: text/html\r\n\r\n";
HeaderExInfo.cchStatus = strlen(HeaderExInfo.pszStatus);
HeaderExInfo.cchHeader = strlen(HeaderExInfo.pszHeader);
HeaderExInfo.fKeepConn = FALSE;
ecb->ServerSupportFunction(ecb->ConnID, HSE_REQ_SEND_RESPONSE_HEADER_EX, &HeaderExInfo, NULL, NULL);
auto result = ecb->WriteClient(ecb->ConnID, msg, &sent_length, 0);
if (!result || sent_length != msg_length)
{
return HSE_STATUS_ERROR;
}
return HSE_STATUS_SUCCESS;
}
Microsoft опубликовала аналогичный пример расширения ISAPI в своей учетной записи GitHub: https://github.com/Microsoft/Windows-classic-samples/tree/master/Samples/Win7Samples/web/iis/extensions/simple
Других решений пока нет …