Microsoft MVP성태의 닷넷 이야기
VC++: 48. 윈도우에서 Apache Module - Content Handler 컴파일 [링크 복사], [링크+제목 복사],
조회: 28308
글쓴 사람
정성태 (techsharer at outlook.com)
홈페이지
첨부 파일
(연관된 글이 3개 있습니다.)
윈도우에서 Apache Module - Content Handler 컴파일 (VC++)

지난번에 만들어 본 모듈은 다음과 같이 httpd.conf에 SetHandler로 설정되는 유형이었습니다.

<Location /example-info>
    SetHandler example-handler
</Location>

그런데, 여기서 궁금함이 생겼습니다. 예전에 알아본 PHP의 경우에는 다음과 같이 추가를 AddType으로 추가를 했었는데, 그럼 그 둘 간에는 어떤 차이가 있냐는 것인데요.

AddType application/x-httpd-php .php

검색해 보니, PHP와 같은 경우를 별도로 "Content Handler"라고 부르는 것 같습니다. 궁금하니 어쩔 수 없습니다, 한번 만들어 봐야지. ^^ 이를 위해 검색해 보니 다음의 글이 눈에 띕니다.

2. Apache Content Handler
; http://woorie.net/entry/2-Apache-Content-Handler

위의 글에 Content Handler에 대한 소스 코드가 실려 있는데, 기존 모듈의 소스 코드와 차이점이 없어 보입니다. 안 좋은 소식이라면, 이 예제는 아파치 2.0 미만의 버전과 호환이 되는 것 같았습니다. 어쨌든 중요한 것은 뭔가 크게 다르지 않다는 것! ^^

#include "httpd.h" 
#include "http_config.h" 
#include "http_protocol.h" 
#include "ap_config.h" 

/* The sample content handler */ 
static int turbo08_handler(request_rec *r) 
{ 
    r->content_type = "text/html"; 
    ap_send_http_header(r); 

    if (!r->header_only) 
        ap_rputs("The sample page from mod_turbo08.c\n", r); 

    return OK; 
} 

/* Dispatch list of content handlers */ 
static const handler_rec turbo08_handlers[] = 
    { 
        { "turbo08", turbo08_handler }, 
        { NULL, NULL } 
    }; 

/* Dispatch list for API hooks */ 
module MODULE_VAR_EXPORT turbo08_module = 
    { 
        STANDARD_MODULE_STUFF, 
        NULL, /* module initializer */ 
        NULL, /* create per-dir config structures */ 
        NULL, /* merge per-dir config structures */ 
        NULL, /* create per-server config structures */ 
        NULL, /* merge per-server config structures */ 
        NULL, /* table of config file commands */ 
        turbo08_handlers, /* [#8] MIME-typed-dispatched handlers */ 
        NULL, /* [#1] URI to filename translation */ 
        NULL, /* [#4] validate user id from request */ 
        NULL, /* [#5] check if the user is ok _here_ */ 
        NULL, /* [#3] check access by host address */ 
        NULL, /* [#6] determine MIME type */ 
        NULL, /* [#7] pre-run fixups */ 
        NULL, /* [#9] log a transaction */ 
        NULL, /* [#2] header parser */ 
        NULL, /* child_init */ 
        NULL, /* child_exit */ 
        NULL /* [#0] post read-request */ 
    };

이해도를 높이기 위해 직접 제작을 해볼텐데요. ^^ 예전 글에서는 아파치에서 기본 제공되는 experimental 예제를 복사해서 했지만, 이번에는 Visual Studio 2008을 이용해서 바닥부터 프로젝트를 만들어 컴파일해 보겠습니다.

우선, Win32 Project를 생성하고 (제 경우에는 프로젝트 이름을 MyContentHandler로 명명) DLL 응용 프로그램 유형으로 시작했습니다.

how_to_build_apache_content_handler_1.png

물론, 프로젝트 속성을 몇 가지 바꿔주어야 합니다.

"General" / "Character Set"
==> Not Set

"Debugging" / "Command"
==> D:\httpd_build\Apache22\bin\httpd.exe

"C/C++" / "Additional Include Directories" - 아파치 헤더 파일 경로 설정
==> ../../include,../../srclib/apr/include,../../srclib/apr-util/include

"Linker" / "Output File" - 디버그하기 편리하도록 아파치 모듈 폴더에 곧바로 출력
==> ..\..\..\Apache22\modules\mycontenthandler_module.so

그다음, 기본 생성된 파일을 변경합니다.

  • stdafx.h 파일의 "#include windows.h" 라인을 주석 처리
  • dllmain.cpp 파일 제거

생성되어 있던 MyContentHandler.h 파일에 다음과 같이 헤더 파일 및 export 심벌 선언을 추가해 주고,

#include "httpd.h"
#include "http_config.h"
#include "http_core.h"
#include "http_log.h"
#include "http_main.h"
#include "http_protocol.h"
#include "http_request.h"
#include "util_script.h"
#include "http_connection.h"

#include "apr_strings.h"

#include 

#pragma comment(lib, "../lib/libhttpd.lib")
#pragma comment(lib, "../lib/libapr-1.lib")
#pragma comment(lib, "../lib/libaprutil-1.lib")

extern "C"
{
    extern module AP_MODULE_DECLARE_DATA mycontenthandler_module;
}

MyContentHandler.cpp 파일에는 다음과 같이 코드를 추가해줍니다.

#include "stdafx.h"
#include "MyContentHandler.h"

static void x_register_hooks(apr_pool_t *p)
{
}

module AP_MODULE_DECLARE_DATA mycontenthandler_module = 
{ 
    STANDARD20_MODULE_STUFF, 
    NULL, /* create per-dir config structures */ 
    NULL, /* merge per-dir config structures */ 
    NULL, /* create per-server config structures */ 
    NULL, /* merge per-server config structures */ 
    NULL, /* table of config file commands */ 
    x_register_hooks, /* dispatched handlers */ 
};

여기까지 일단 빌드를 하고, depends.exe로 결과를 확인해 봅니다. 반드시 아래와 같이 mycontenthandler_module이 export 되어 있어야 합니다.

how_to_build_apache_content_handler_2.png




돌아가는 분위기를 보아하니, Content Handler라고 해서 특별한 점은 없어 보입니다. 즉, 지난번에 알아본 핸들러와도 소스 코드 구조 면에서 완전히 일치합니다. 단지, httpd.conf 파일에 어떻게 등록하느냐에 따라서 그것이 Content Handler라고 불리는 것 같습니다. 어디... 확인을 해봐야겠지요. ^^ 이를 위해 간단한 x_handler 함수를 만들어 두고 x_register_hooks에서 등록시켜 줍니다.

static int x_handler(request_rec *r)
{
    return OK;
}

static void x_register_hooks(apr_pool_t *p)
{
    ap_hook_handler(x_handler, NULL, NULL, APR_HOOK_MIDDLE);
}

남은 작업은 [아파치 설치 폴더]/conf/httpd.conf 파일에 다음과 같이 Content Handler를 등록해 주는 것입니다.

LoadModule mycontenthandler_module modules/mycontenthandler_module.so

AddType mycontenthandler_module myext

위와 같이 해주고, (Shift + F2 디버깅 상태에서) 웹 브라우저로 "http://localhost:65000/test.txt"라고 방문했더니 제가 예상했던 것과는 다르게, 원하는 확장자(myext)가 아님(txt)에도 불구하고 mycontenthandler_module.so 모듈에 정의된 x_handler가 호출되는 것이 확인되었습니다.

비효율적이라고 생각되었지만, 어쨌든 이제 다시 한번 궁금한 점이 생깁니다. 도대체 그럼, x_handler에서는 test.txt로 요청이 들어왔는지, test.myext로 요청이 들어왔는지 어떻게 구분하고 반응을 해야 하는 걸까요?

아파치는 AddType 정의가 추가된 경우, 그곳에 등록된 확장자(위의 예에서는 myext)로 요청이 들어오면 AddType의 첫 번째 인자(위의 예에서는 mycontenthandler)로 주어진 문자열을 x_handler 메서드의 첫 번째 파라미터인 request_rec의 handler 멤버에 설정을 해줍니다.

아래는, 제가 "Shift + F2" 디버깅상에서 확인한 결과입니다.

[그림 1: test.txt로 방문한 경우, r->handler == "text/plain"]
how_to_build_apache_content_handler_3.png

[그림 2: test.myext로 방문한 경우, r->handler == "mycontenthandler_module"]
how_to_build_apache_content_handler_4.png

따라서, 자신이 담당해야 할 확장자로 들어온 요청은 AddType에 지정된 첫 번째 문자열 값이 r->handler에 들어왔을 때를 기준으로 구분을 하면 되므로, 다음과 같이 코드를 작성해 줄 수 있습니다.

static int x_handler(request_rec *r)
{
    if (strcmp(r->handler, "mycontenthandler_module"))
    {
        return DECLINED;
    }

    ap_set_content_type(r, "text/html");

    if (r->header_only) 
    {
        return OK;
    }

    ap_rputs("test is good!", r); 

    return OK;
}

이쯤에서 다시 PHP 모듈 등록 방식으로 돌아가 볼까요?

AddType application/x-httpd-php .php

아하~~~ 이제야 눈에 들어오는 군요. 아파치는 .php 확장자로 요청이 들어온 경우에는 "LoadModule"로 등록된 모든 모듈의 x_handler를 호출하면서 r->handler에 "application/x-httpd-php" 문자열을 전달해 줄 것이고, 그중에서 php5apache2_2.dll 안에 정의된 x_handler 동격의 함수에서 다음과 같은 처리 코드를 가지고 시작할 거라고 예상해 볼 수 있습니다.

static int x_handler(request_rec *r)
{
    if (strcmp(r->handler, "application/x-httpd-php"))
    {
        return DECLINED;
    }

... [생략] ...
    return OK;
}




마지막으로 정리해 보면, 결국 일반 모듈이나 Content Handler라고 불리는 모듈이나 제작 방식은 동일하고, 단순히 httpd.conf 파일에 SetHandler로 등록하느냐, AddType으로 등록하느냐에 대한 차이만 있는 것입니다. (아마도, 다른 AddEncoding/AddHandler 등의 모듈도 제작 방식은 동일할 것 같습니다.)

저 같은 아파치 초보자는 여기까지 개념을 잡는 것도 오래 걸리는 군요. ^^

(첨부한 파일은 위의 예제 코드를 담고 있는 VC++ 프로젝트입니다.)



[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]

[연관 글]






[최초 등록일: ]
[최종 수정일: 6/28/2021]

Creative Commons License
이 저작물은 크리에이티브 커먼즈 코리아 저작자표시-비영리-변경금지 2.0 대한민국 라이센스에 따라 이용하실 수 있습니다.
by SeongTae Jeong, mailto:techsharer at outlook.com

비밀번호

댓글 작성자
 




... 61  62  63  64  65  66  67  68  69  70  [71]  72  73  74  75  ...
NoWriterDateCnt.TitleFile(s)
12161정성태2/26/202017703오류 유형: 597. manifest - The value "x64" of attribute "processorArchitecture" in element "assemblyIdentity" is invalid.
12160정성태2/26/202018202개발 환경 구성: 469. Reg-free COM 개체 사용을 위한 manifest 파일 생성 도구 - COMRegFreeManifest
12159정성태2/26/202015258오류 유형: 596. Visual Studio - The project needs to include ATL support
12158정성태2/25/202017839디버깅 기술: 165. C# - Marshal.GetIUnknownForObject/GetIDispatchForObject 사용 시 메모리 누수(Memory Leak) 발생파일 다운로드1
12157정성태2/25/202017904디버깅 기술: 164. C# - Marshal.GetNativeVariantForObject 사용 시 메모리 누수(Memory Leak) 발생 및 해결 방법파일 다운로드1
12156정성태2/25/202016456오류 유형: 595. LINK : warning LNK4098: defaultlib 'nafxcw.lib' conflicts with use of other libs; use /NODEFAULTLIB:library
12155정성태2/25/202015911오류 유형: 594. Warning NU1701 - This package may not be fully compatible with your project
12154정성태2/25/202015271오류 유형: 593. warning LNK4070: /OUT:... directive in .EXP differs from output filename
12153정성태2/23/202019404.NET Framework: 898. Trampoline을 이용한 후킹의 한계파일 다운로드1
12152정성태2/23/202018477.NET Framework: 897. 실행 시에 메서드 가로채기 - CLR Injection: Runtime Method Replacer 개선 - 세 번째 이야기(Trampoline 후킹)파일 다운로드1
12151정성태2/22/202019253.NET Framework: 896. C# - Win32 API를 Trampoline 기법을 이용해 C# 메서드로 가로채는 방법 - 두 번째 이야기 (원본 함수 호출)파일 다운로드1
12150정성태2/21/202019488.NET Framework: 895. C# - Win32 API를 Trampoline 기법을 이용해 C# 메서드로 가로채는 방법 [1]파일 다운로드1
12149정성태2/20/202018310.NET Framework: 894. eBEST C# XingAPI 래퍼 - 연속 조회 처리 방법 [1]
12148정성태2/19/202020730디버깅 기술: 163. x64 환경에서 구현하는 다양한 Trampoline 기법 [1]
12147정성태2/19/202018490디버깅 기술: 162. x86/x64의 기계어 코드 최대 길이
12146정성태2/18/202019266.NET Framework: 893. eBEST C# XingAPI 래퍼 - 로그인 처리파일 다운로드1
12145정성태2/18/202019086.NET Framework: 892. eBEST C# XingAPI 래퍼 - Sqlite 지원 추가파일 다운로드1
12144정성태2/13/202019268.NET Framework: 891. 실행 시에 메서드 가로채기 - CLR Injection: Runtime Method Replacer 개선 - 두 번째 이야기파일 다운로드1
12143정성태2/13/202015906.NET Framework: 890. 상황별 GetFunctionPointer 반환값 정리 - x64파일 다운로드1
12142정성태2/12/202018287.NET Framework: 889. C# 코드로 접근하는 MethodDesc, MethodTable파일 다운로드1
12141정성태2/10/202017284.NET Framework: 888. C# - ASP.NET Core 웹 응용 프로그램의 출력 가로채기 [2]파일 다운로드1
12140정성태2/10/202017771.NET Framework: 887. C# - ASP.NET 웹 응용 프로그램의 출력 가로채기파일 다운로드1
12139정성태2/9/202019055.NET Framework: 886. C# - Console 응용 프로그램에서 UI 스레드 구현 방법
12138정성태2/9/202023036.NET Framework: 885. C# - 닷넷 응용 프로그램에서 SQLite 사용 [6]파일 다운로드1
12137정성태2/9/202016329오류 유형: 592. [AhnLab] 경고 - 디버거 실행을 탐지했습니다.
12136정성태2/6/202017270Windows: 168. Windows + S(또는 Q)로 뜨는 작업 표시줄의 검색 바가 동작하지 않는 경우
... 61  62  63  64  65  66  67  68  69  70  [71]  72  73  74  75  ...