IIS 7.0용 네이티브 C\C++ 모듈 개발
소개
IIS 7.0 이상에서는 다음 두 가지 방법으로 개발된 모듈별로 서버를 확장할 수 있습니다.
- 관리 코드 및 ASP.NET 서버 확장성 API 사용
- 네이티브 코드 및 IIS 네이티브 서버 확장성 API 사용
이전 버전의 IIS와 달리 대부분의 서버 확장성 시나리오에는 네이티브(C++) 코드 개발이 필요하지 않으며 관리 코드 및 ASP.NET API를 사용하여 수용할 수 있습니다. ASP.NET 사용하여 서버를 확장하면 개발 시간을 크게 줄이고 ASP.NET 및 .NET Framework 풍부한 기능을 활용할 수 있습니다. ASP.NET 사용하여 IIS를 확장하는 방법에 대한 자세한 내용은 .NET을 사용하여 IIS 모듈 개발을 참조하세요.
또한 IIS는 이전 IIS 릴리스의 ISAPI 필터 및 확장 API를 대체하는 (C++) 네이티브 코어 서버 API를 제공합니다. 네이티브 코드 개발을 요구하거나 기존 네이티브 ISAPI 구성 요소를 변환하려는 특정 요구 사항이 있는 경우 이 API를 활용하여 서버 구성 요소를 빌드합니다. 새로운 네이티브 서버 API는 직관적인 개체 모델을 사용하여 개체 지향 개발을 특징으로 하며, 요청 처리를 보다 강력하게 제어하고, 더 간단한 디자인 패턴을 사용하여 강력한 코드를 작성하는 데 도움을 줍니다.
이 연습에서는 다음 작업을 살펴봅니다.
- 네이티브(C++) 서버 API를 사용하여 네이티브 모듈 개발
- 서버에 네이티브 모듈 배포
모듈을 컴파일하려면 IIS 헤더 파일이 포함된 플랫폼 SDK를 설치해야 합니다. 최신 Windows Vista 플랫폼 SDK는 여기에서 사용할 수 있습니다.
Visual Studio 2005에서 플랫폼 SDK를 사용하려면 SDK를 등록해야 합니다. SDK를 설치한 후 Visual Studio 등록 Visual Studio > 에 Windows SDK 디렉터리 등록을 Microsoft Windows SDK > 프로그램 시작을 >>통해 이 작업을 수행합니다.
이 모듈의 소스 코드는 Visual Studio IIS7 네이티브 모듈 샘플에서 사용할 수 있습니다.
네이티브 모듈 개발
이 작업에서는 새 네이티브(C++) 서버 API를 사용하여 네이티브 모듈의 개발을 살펴봅니다. 네이티브 모듈은 다음을 포함하는 Windows DLL입니다.
- RegisterModule 내보낸 함수입니다. 이 함수는 모듈 팩터리를 만들고 하나 이상의 서버 이벤트에 대한 모듈을 등록하는 작업을 담당합니다.
- CHttpModule 기본 클래스에서 상속되는 모듈 클래스의 구현입니다. 이 클래스는 모듈의 기본 기능을 제공합니다.
- IHttpModuleFactory 인터페이스를 구현하는 모듈 팩터리 클래스의 구현입니다. 클래스는 모듈의 인스턴스를 만드는 역할을 담당합니다.
참고
경우에 따라 요청 처리와 관련이 없는 일부 서버 기능을 확장하기 위해 IGlobalModule 인터페이스를 구현할 수도 있습니다. 이 항목은 고급 항목이며 이 연습에서는 다루지 않습니다.
네이티브 모듈의 수명 주기는 다음과 같습니다.
서버 작업자 프로세스가 시작되면 모듈이 포함된 DLL을 로드하고 내보낸 RegisterModule 함수를 호출합니다. 이 함수에서는 다음을 수행합니다.
a. 모듈 팩터리를 만듭니다.
b. 모듈이 구현하는 요청 파이프라인 이벤트에 대한 모듈 팩터리를 등록합니다.요청이 도착하면 서버는 다음을 수행합니다.
a. 제공한 팩터리를 사용하여 모듈 클래스의 instance 만듭니다.
b. 등록한 각 요청 이벤트에 대해 모듈 instance 적절한 이벤트 처리기 메서드를 호출합니다.
c. 요청 처리가 끝날 때 모듈의 instance 삭제합니다.
이제 빌드합니다.
모듈의 전체 소스 코드는 Visual Studio IIS7 네이티브 모듈 샘플에서 사용할 수 있습니다. 아래 단계는 모듈을 개발하는 데 가장 중요하며 지원 코드 및 오류 처리는 포함하지 않습니다.
모듈 DLL이 로드될 때 서버에서 호출하는 RegisterModule 함수를 구현합니다. 해당 서명 및 네이티브 API의 나머지 부분은 플랫폼 SDK의 일부인 httpserv.h 헤더 파일에 정의됩니다(플랫폼 SDK가 없는 경우 가져오기에 대한 자세한 내용은 소개 를 참조하세요.)
기본.cpp:
HRESULT
__stdcall
RegisterModule(
DWORD dwServerVersion,
IHttpModuleRegistrationInfo * pModuleInfo,
IHttpServer * pHttpServer
)
{
// step 1: save the IHttpServer and the module context id for future use
g_pModuleContext = pModuleInfo->GetId();
g_pHttpServer = pHttpServer;
// step 2: create the module factory
pFactory = new CMyHttpModuleFactory();
// step 3: register for server events
hr = pModuleInfo->SetRequestNotifications( pFactory,
RQ_ACQUIRE_REQUEST_STATE,
0 );
}
The RegisterModule
RegisterModule 내에서 수행해야 하는 세 가지 기본 작업이 있습니다.
전역 상태 저장
전역 변수에서 나중에 사용할 수 있도록 전역 서버 instance 및 모듈 컨텍스트 ID를 저장합니다. 이 예제에서는 이 정보를 사용하지 않지만 많은 모듈에서는 나중에 요청 처리 중에 저장하고 사용하는 것이 유용하다고 합니다. IHttpServer 인터페이스는 파일 열기 및 캐시 액세스와 같은 많은 서버 함수에 대한 액세스를 제공합니다. 모듈 컨텍스트 ID는 사용자 지정 모듈 상태를 요청 및 애플리케이션과 같은 여러 서버 개체와 연결하는 데 사용됩니다.
모듈 팩터리 만들기
이 연습의 뒷부분에서 팩터리 클래스 인 CMyHttpModuleFactory를 구현합니다. 이 팩터리는 각 요청에 대한 모듈의 제조 인스턴스를 담당합니다.
원하는 요청 처리 이벤트에 대한 모듈 팩터리 등록
등록은 SetRequestNotificatons 메서드를 통해 수행됩니다. 이 메서드는 지정된 팩터리를 사용하여 각 요청에 대한 모듈 instance 만들고 지정된 각 요청 처리 단계에 대해 적절한 이벤트 처리기를 호출하도록 서버에 지시합니다.
이 경우 RQ_ACQUIRE_REQUEST_STATE 단계에만 관심이 있습니다. 요청 처리 파이프라인을 구성하는 단계의 전체 목록은 httpserv.h에 정의되어 있습니다.
#define RQ_BEGIN_REQUEST 0x00000001 // request is beginning
#define RQ_AUTHENTICATE_REQUEST 0x00000002 // request is being authenticated
#define RQ_AUTHORIZE_REQUEST 0x00000004 // request is being authorized
#define RQ_RESOLVE_REQUEST_CACHE 0x00000008 // satisfy request from cache
#define RQ_MAP_REQUEST_HANDLER 0x00000010 // map handler for request
#define RQ_ACQUIRE_REQUEST_STATE 0x00000020 // acquire request state
#define RQ_PRE_EXECUTE_REQUEST_HANDLER 0x00000040 // pre-execute handler
#define RQ_EXECUTE_REQUEST_HANDLER 0x00000080 // execute handler
#define RQ_RELEASE_REQUEST_STATE 0x00000100 // release request state
#define RQ_UPDATE_REQUEST_CACHE 0x00000200 // update cache
#define RQ_LOG_REQUEST 0x00000400 // log request
#define RQ_END_REQUEST 0x00000800 // end request
또한 클라이언트에 대한 응답을 플러시하는 등 다른 모듈이 수행하는 작업으로 인해 요청 처리 중에 발생할 수 있는 몇 가지 비결정적 이벤트를 구독할 수 있습니다.
#define RQ_CUSTOM_NOTIFICATION 0x10000000 // custom notification
#define RQ_SEND_RESPONSE 0x20000000 // send response
#define RQ_READ_ENTITY 0x40000000 // read entity
#define RQ_MAP_PATH 0x80000000 // map a url to a physical path
RegisterModule 구현이 서버에 액세스할 수 있도록 하려면 내보내야 합니다. 를 사용합니다. RegisterModule 함수를 내보내는 EXPORTS 키워드(keyword) 포함된 DEF 파일입니다.
다음으로, 모듈 팩터리 클래스를 구현합니다.
mymodulefactory.h:
class CMyHttpModuleFactory : public IHttpModuleFactory
{
public:
virtual HRESULT GetHttpModule(
OUT CHttpModule **ppModule,
IN IModuleAllocator *
)
{
}
virtual void Terminate()
{
}
};
모듈 팩터리는 IHttpModuleFactory 인터페이스를 구현하고 각 요청에 대해 모듈의 인스턴스를 만드는 역할을 합니다.
서버는 모든 요청의 시작 부분에서 GetHttpModule 메서드를 호출하여 이 요청에 사용할 모듈의 instance 가져옵니다. 구현은 다음에 구현하는 모듈 클래스 CMyHttpModule의 새 instance 반환합니다. 곧 볼 수 있듯이 서버는 항상 각 요청에 대해 모듈의 새 instance 만들고 사용하기 때문에 스레드 안전에 대해 걱정하지 않고 요청 상태를 쉽게 저장할 수 있습니다.
고급 팩터리 구현에서는 매번 새 instance 만드는 대신 싱글톤 패턴을 사용하거나 제공된 IModuleAllocator 인터페이스를 사용하여 요청 풀에 모듈 메모리를 할당할 수 있습니다. 이러한 고급 패턴은 이 연습에서 설명하지 않습니다.
Terminate 메서드는 작업자 프로세스가 종료되어 모듈의 최종 정리를 수행할 때 서버에서 호출됩니다. RegisterModule에서 전역 상태를 초기화하는 경우 이 메서드에서 해당 정리를 구현합니다.
모듈 클래스 구현
이 클래스는 하나 이상의 서버 이벤트 중에 모듈의 기본 기능을 제공합니다.
myhttpmodule.h:
class CMyHttpModule : public CHttpModule
{
public:
REQUEST_NOTIFICATION_STATUS
OnAcquireRequestState(
IN IHttpContext * pHttpContext,
IN OUT IHttpEventProvider * pProvider
);
};
모듈 클래스는 앞에서 설명한 각 서버 이벤트에 대한 이벤트 처리기 메서드를 정의하는 CHttpModule 기본 클래스에서 상속됩니다. 요청 처리 파이프라인이 각 이벤트를 실행하면 해당 이벤트에 대해 등록된 각 모듈 인스턴스에서 연결된 이벤트 처리기 메서드를 호출합니다.
각 이벤트 처리기 메서드에는 다음과 같은 서명이 있습니다.
REQUEST_NOTIFICATION_STATUS
OnEvent(
IN IHttpContext * pHttpContext,
IN OUT IHttpEventProvider * pProvider
);
IHttpContext 인터페이스는 요청 컨텍스트 개체에 대한 액세스를 제공합니다. 이 개체는 요청 검사 및 응답 조작과 같은 요청 처리 작업을 수행하는 데 사용할 수 있습니다.
IHttpEventProvider 인터페이스는 모듈에 특정 기능을 제공하는 각 이벤트에 대한 보다 구체적인 인터페이스로 대체됩니다. 예를 들어 OnAuthenticateRequest 이벤트 처리기는 모듈이 인증된 사용자를 설정할 수 있도록 하는 IAuthenticationProvider 인터페이스를 받습니다.
각 이벤트 처리기 메서드의 반환은 REQUEST_NOTIFICATION_STATUS 열거형의 값 중 하나입니다. 모듈이 작업을 성공적으로 수행한 경우 RQ_NOTIFICATION_CONTINUE 반환해야 합니다. 파이프라인은 실행을 계속해야 합니다.
오류가 발생하여 오류로 요청 처리를 중단하려는 경우 오류 상태 설정하고 RQ_NOTIFICATION_FINISH_REQUEST 반환해야 합니다. RQ_NOTIFICATION_PENDING 반환을 사용하면 작업을 비동기적으로 수행하고 요청을 처리하는 스레드를 놓아 다른 요청에 다시 사용할 수 있습니다. 비동기 실행은 이 문서에서 설명하지 않습니다.
모듈 클래스는 OnAcquireRequestState 이벤트 처리기 메서드를 재정의합니다. 파이프라인 단계에서 기능을 제공하려면 모듈 클래스가 해당 이벤트 처리기 메서드를 재정의해야 합니다. RegisterModule에서 이벤트에 등록하지만 모듈 클래스에서 적절한 이벤트 처리기 메서드를 재정의하지 않으면 런타임에 모듈이 실패하고 디버그 모드에서 컴파일된 경우 디버그 시간 어설션을 트리거합니다. 재정의하는 메서드의 메서드 시그니처가 재정의하는 CHttpModule 클래스의 기본 클래스 메서드와 정확히 동일한지 확인합니다.
모듈 컴파일
컴파일하려면 플랫폼 SDK가 필요합니다. 이를 가져오고 Visual Studio에서 참조할 수 있도록 하는 방법에 대한 자세한 내용은 소개 를 참조하세요.
네이티브 모듈 배포
모듈을 컴파일한 후에는 서버에 배포해야 합니다. 모듈을 컴파일한 다음 IIS7NativeModule.dll(원하는 경우 IIS7NativeModule.pdb 디버깅 기호 파일)을 IIS를 실행하는 컴퓨터의 모든 위치에 복사합니다.
애플리케이션에 직접 추가할 수 있는 관리되는 모듈과 달리 네이티브 모듈은 먼저 서버에 설치해야 합니다. 이를 위해서는 관리 권한이 필요합니다.
네이티브 모듈을 설치하기 위해 다음과 같은 몇 가지 옵션이 있습니다.
- APPCMD.EXE 명령줄 도구 사용
APPCMD를 사용하면 모듈을 간단하게 설치할 수 있습니다. 프로그램 액세서리>시작으로>이동하여 명령줄 프롬프트를 마우스 오른쪽 단추로 클릭하고 관리자 권한으로 실행을 선택합니다. 명령줄 창에서 다음을 실행합니다.
%systemroot%\system32\inetsrv\appcmd.exe install module /name:MyModule /image:[FULL\_PATH\_TO\_DLL]
여기서 [FULL_PATH_TO_DLL]은 방금 빌드한 모듈이 포함된 컴파일된 DLL의 전체 경로입니다. - IIS 관리 도구 사용
이렇게 하면 GUI를 사용하여 모듈을 추가할 수 있습니다. 실행 시작으로>이동하여 inetmgr을 입력하고 Enter 키를 누릅니다. localhost에 연결하고 모듈 작업을 찾은 다음 두 번 클릭하여 엽니다. 그런 다음 오른쪽 창 에서 네이티브 모듈 추가 작업을 클릭합니다. - 수동으로 모듈 설치
모듈을 applicationHost.config 구성 파일의 system.webServer>/<globalModules> 구성 섹션에 추가하여 수동으로 설치하고, 이를 사용하도록 설정하기 위해 <동일한 파일의 system.webServer>/<modules> 구성 섹션에 참조를 추가<합니다. 구성을 직접 편집하는 대신 이전 두 옵션 중 하나를 사용하여 모듈을 설치하는 것이 좋습니다.
작업이 완료되었습니다. 새 네이티브 모듈 구성을 완료했습니다.
요약
이 연습에서는 새 네이티브(C++) 확장성 API를 사용하여 사용자 지정 네이티브 모듈을 개발하고 배포하는 방법을 알아보았습니다. 네이티브(C++) 서버 API에 대한 자세한 내용은 네이티브 코드 개발 개요 를 참조하세요.
관리 코드 및 .NET 프레임워크를 사용하여 IIS를 확장하는 방법에 대한 자세한 내용은 .NET을 사용하여 IIS 모듈 개발을 참조하세요. IIS 모듈 관리에 대한 자세한 내용은 모듈 개요 백서를 참조하세요.