FILTER 절

Filter 모듈을 사용할 FILTER 절을 정의하여, NODE 절이나 SERVER 절에서 filter 항목을 설정합니다.

설정 항목

다음은 FILTER 절의 환경 설정 형식입니다.

#"filter": {
    #"filters": [
        {
            "name": string,
            "path": string,
            #"event": [string]
        }
    ]
}

절과 설정 항목의 구성에 대한 기호나 내용에 대한 자세한 내용은 설정 항목 값의 형식 및 설정 방법을 참고합니다.

filters

FILTER 설정에 대한 목록입니다.

구분 설명

자료형

array(object)

filters/name (필수 항목)

FILTER 설정의 이름입니다. 다른 절에서 FILTER 절의 기능을 사용할 때는 이 'name’을 설정해야 합니다.

구분 설명

자료형

string

범위

31자 이내

filters/url (필수 항목)

서버 안의 물리적 디렉터리의 Filter 모듈이 위치한 경로명을 설정합니다.

구분 설명

자료형

string

범위

255자 이내

filters/event

Filter 모듈이 동작하는 타이밍을 설정합니다.

구분 설명

자료형

array(string)

범위

15개 이내(255자 이내)

다음은 종류별 필터 이벤트에 대한 설명입니다. 필터 이벤트 종류에 따라 설정하는 위치가 NODE 절 또는 SERVER 절로 구분됩니다.

  • SYSTEM 필터 이벤트

    SYSTEM 필터 이벤트를 포함하는 필터는 NODE 절에 설정합니다.

    필터 이벤트 설명

    ON_STOP

    WebtoB 엔진 종료 시점에 동작합니다.

    ON_START

    WebtoB 엔진 기동 시점에 동작합니다.

  • HTTP 필터 이벤트

    HTTP 필터 이벤트를 포함하는 필터는 SERVER 절에 설정합니다.

    필터 이벤트 설명

    RECEIVE_REQUEST

    클라이언트에게 HTTP 요청을 받은 시점에 동작합니다.

    BEFORE_SEND_REQUEST

    클라이언트에게 받은 HTTP 요청을 내부 서버에 전달하기 전 시점에 동작합니다.

    AFTER_SEND_REQUEST

    클라이언트에게 받은 HTTP 요청을 내부 서버에 전달한 후 시점에 동작합니다.

    RECEIVE_RESPONSE

    내부 서버에게 HTTP 응답을 받은 시점에 동작합니다.

    BEFORE_SEND_RESPONSE

    내부 서버에게 받은 HTTP 응답을 클라이언트에 전달하기 전 시점에 동작합니다.

    AFTER_SEND_RESPONSE

    내부 서버에게 받은 HTTP 응답을 클라이언트에 전달한 후 점에 동작합니다.

설정 예시

다음은 Filter를 구현하기 위한 설정 예시입니다.

  • Filter 프로젝트 구조

    다음은 Filter를 구현하기 위한 프로젝트의 구조 예시입니다. 각 파일은 Filter를 정의하고 구현하는 데 필요한 요소들을 포함하고 있습니다.

    .
    ├── CMakeLists.txt
    ├── SampleSystemFilter.h
    ├── SampleSystemFilter.cpp
    ├── SampleHttpFilter.h
    └── SampleHttpFilter.cpp

    각 파일의 설정 예시는 다음과 같습니다.

    • CMakeLists.txt

      cmake_minimum_required(VERSION 3.15)
      
      project(sampleFilter)
      set(CMAKE_VERBOSE_MAKEFILE true)
      set(CMAKE_CXX_STANDARD 17)
      
      message("\t MODULE: ${PROJECT_NAME}")
      
      # Webtob Filter Include Directory
      include_directories(/WEBTOB_BINARY_PATH/include)
      
      # Library Add
      add_library(sampleSystemFilter SHARED SampleSystemFilter.cpp)
      add_library(sampleHttpFilter SHARED SampleHttpFilter.cpp)
    • SampleSystemFilter.h

      /**
       * @File     : SampleFilter.h
       * @Author   : Webtob
       * @Year     : 2024
       */
      
      #ifndef WEBTOB6_SAMPLE_SYSTEM_FILTER_H
      #define WEBTOB6_SAMPLE_SYSTEM_FILTER_H
      
      #include <iostream>
      #include <string>
      #include <sstream>
      #include <memory>
      #include <vector>
      #include "webtob-filter/filter-api/WebtobFilter.h"
      
      namespace webtob {
          class SampleSystemFilter : public WebtobFilter {
          public:
              static std::vector<std::string> calledEvents;
      
              FilterResult onStart() override;
      
              FilterResult onStop() override;
      
              FilterResult onReceiveRequest(const std::shared_ptr<FilterRequest>& request,
                                            const std::shared_ptr<FilterResponse>& response) override;
      
              FilterResult onReceiveResponse(const std::shared_ptr<FilterRequest>& request,
                                             const std::shared_ptr<FilterResponse>& response) override;
      
          private:
              static std::string getStaticVector();
      
              void printCalledInfo(const char* calledEvent);
      
              std::string filterName = "SampleSystemFilter";
          };
      } // namespace webtob
      
      #endif //WEBTOB6_SAMPLE_SYSTEM_FILTER_H
    • SampleSystemFilter.cpp

      #include "SampleSystemFilter.h"
      
      using namespace webtob;
      
      std::vector<std::string> SampleSystemFilter::calledEvents;
      
      // Filter Creator
      extern "C" [[maybe_unused]] std::shared_ptr<WebtobFilter> createFilter() {
          return std::make_shared<SampleSystemFilter>();
      }
      
      std::string SampleSystemFilter::getStaticVector() {
          std::ostringstream description;
      
          description << "Sample SystemFilter Event Called Vector= [\n";
          for (const std::string& event : calledEvents) {
              description << "\t" << event << "\n";
          }
          description << "]";
          return description.str();
      }
      
      void SampleSystemFilter::printCalledInfo(const char* calledEvent) {
          calledEvents.emplace_back(calledEvent);
          std::ostringstream message;
          message << "\n=============================================";
          message << "\n" << filterName << " : " << calledEvent;
          message << "\n---------------------------------------------";
          message << "\n" << getStaticVector();
          message << "\n=============================================";
          std::cout << message.str() << std::endl;
      }
      
      FilterResult SampleSystemFilter::onStart() {
          printCalledInfo(__FUNCTION__);
          return FilterResult::SUCCESS;
      }
      
      
      FilterResult SampleSystemFilter::onStop() {
          printCalledInfo(__FUNCTION__);
          return FilterResult::SUCCESS;
      }
      
      FilterResult SampleSystemFilter::onReceiveRequest(const std::shared_ptr<FilterRequest>& request,
                                                        const std::shared_ptr<FilterResponse>& response) {
          printCalledInfo(__FUNCTION__);
          return FilterResult::SUCCESS;
      }
      
      
      FilterResult SampleSystemFilter::onReceiveResponse(const std::shared_ptr<FilterRequest>& request,
                                                         const std::shared_ptr<FilterResponse>& response) {
          printCalledInfo(__FUNCTION__);
          return FilterResult::SUCCESS;
      }
    • SampleHttpFilter.h

      /**
       * @File     : SampleHttpFilter.h
       * @Author   : Webtob
       * @Year     : 2024
       */
      #ifndef WEBTOB6_SAMPLE_HTTP_FILTER_H
      #define WEBTOB6_SAMPLE_HTTP_FILTER_H
      
      #include <iostream>
      #include <sstream>
      #include "webtob-filter/filter-api/WebtobFilter.h"
      
      namespace webtob {
          class SampleHttpFilter : public WebtobFilter {
          public:
              FilterResult onReceiveRequest(const std::shared_ptr<FilterRequest>& request,
                                            const std::shared_ptr<FilterResponse>& response) override;
      
              FilterResult onBeforeSendRequest(const std::shared_ptr<FilterRequest>& request,
                                               const std::shared_ptr<FilterResponse>& response) override;
      
              FilterResult onAfterSendRequest(const std::shared_ptr<FilterRequest>& request,
                                              const std::shared_ptr<FilterResponse>& response) override;
      
              FilterResult onReceiveResponse(const std::shared_ptr<FilterRequest>& request,
                                             const std::shared_ptr<FilterResponse>& response) override;
      
              FilterResult onBeforeSendResponse(const std::shared_ptr<FilterRequest>& request,
                                                const std::shared_ptr<FilterResponse>& response) override;
      
              FilterResult onAfterSendResponse(const std::shared_ptr<FilterRequest>& request,
                                               const std::shared_ptr<FilterResponse>& response) override;
      
              std::string filterName = "SampleHttpFilter";
          private:
              void printCalledInfo(const char* calledEvent);
          };
      } // namespace webtob
      
      #endif //WEBTOB6_SAMPLE_HTTP_FILTER_H
    • SampleHttpFilter.cpp

      #include "SampleHttpFilter.h"
      #include <string_view>
      
      using namespace webtob;
      
      // Filter Creator
      extern "C" [[maybe_unused]] std::shared_ptr<WebtobFilter> createFilter() {
          return std::make_shared<SampleHttpFilter>();
      }
      
      
      bool startsWith(const std::string_view& str, const std::string_view& prefix) {
          return str.substr(0, prefix.size()) == prefix;
      }
      
      void SampleHttpFilter::printCalledInfo(const char* calledEvent) {
          std::ostringstream message;
          message << "\n=============================================";
          message << "\n" << filterName << " : " << calledEvent;
          message << "\n=============================================";
          std::cout << message.str() << std::endl;
      }
      
      FilterResult SampleHttpFilter::onReceiveRequest(const std::shared_ptr<FilterRequest>& request,
                                                      const std::shared_ptr<FilterResponse>& response) {
      
          printCalledInfo(__FUNCTION__);
      
          std::string path = request->getPath();
          if (startsWith(path, "/service/")) {
              if (startsWith(path, "/service/A")) {
                  response->setStatusCode(200);
                  response->setBody("Filter makes service A response");
              } else if (startsWith(path, "/service/B")) {
                  response->setStatusCode(200);
                  response->setBody("Service B response");
              } else if (startsWith(path, "/service/C")) {
                  response->setStatusCode(400);
                  response->setBody("BAD Response Makes");
              }
              return FilterResult::TERMINATE;
          }
      
          auto queryString = request->getQueryString();
      
          if (queryString == "test=reject1") {
              response->setStatusCode(200);
              response->setBody("I decide to reject");
              response->addHeader("ErrorCheck", "Reject1_Detected");
              return FilterResult::TERMINATE;
          }
          request->addHeader("Filter", "InsertOn");
          request->addCookie("FilterCookieKey", "OK_Inserted");
          return FilterResult::SUCCESS;
      }
      
      FilterResult SampleHttpFilter::onBeforeSendRequest(const std::shared_ptr<FilterRequest>& request,
                                                         const std::shared_ptr<FilterResponse>& response) {
          printCalledInfo(__FUNCTION__);
          auto queryString = request->getQueryString();
          if (queryString == "test=reject2") {
              response->setStatusCode(200);
              response->setBody("I decide to reject onBeforeSendRequest");
              response->addHeader("ErrorCheck", "onBeforeSendRequest");
              return FilterResult::TERMINATE;
          }
      
          return FilterResult::SUCCESS;
      }
      
      FilterResult SampleHttpFilter::onAfterSendRequest(const std::shared_ptr<FilterRequest>& request,
                                                        const std::shared_ptr<FilterResponse>& response) {
          printCalledInfo(__FUNCTION__);
          auto queryString = request->getQueryString();
          if (queryString == "test=reject3") {
              response->addHeader("ErrorCheck", "onAfterSendRequest");
              return FilterResult::TERMINATE;
          }
          return FilterResult::SUCCESS;
      }
      
      FilterResult SampleHttpFilter::onReceiveResponse(const std::shared_ptr<FilterRequest>& request,
                                                       const std::shared_ptr<FilterResponse>& response) {
          printCalledInfo(__FUNCTION__);
          auto cookieBuilder = response->getCookieBuilder();
          auto cookie = cookieBuilder->newCookie().setName("Cookie_ReceiveResponse").setValue("AddFromResponse")
              .setHttpOnly(true).setPath("/").setSameSite(FilterCookie::SameSite::OFF).build();
          response->addCookie(cookie);
          response->addHeader("Filter_response", "OK_ADDED");
          return FilterResult::SUCCESS;
      }
      
      FilterResult SampleHttpFilter::onBeforeSendResponse(const std::shared_ptr<FilterRequest>& request,
                                                          const std::shared_ptr<FilterResponse>& response) {
          printCalledInfo(__FUNCTION__);
          auto cookieBuilder = response->getCookieBuilder();
          auto cookie = cookieBuilder->newCookie().setName("MY_COOKIE").setValue("AddFromResponse")
              .setHttpOnly(true).setPath("/").setSameSite(FilterCookie::SameSite::OFF).build();
          response->addCookie(cookie);
      
          return FilterResult::SUCCESS;
      }
      
      FilterResult SampleHttpFilter::onAfterSendResponse(const std::shared_ptr<FilterRequest>& request,
                                                         const std::shared_ptr<FilterResponse>& response) {
          printCalledInfo(__FUNCTION__);
          return FilterResult::SUCCESS;
      }
  • 빌드

    ~/workspace/myTestFilter> cmake . -B build
    -- The C compiler identification is AppleClang 15.0.0.15000309
    -- The CXX compiler identification is AppleClang 15.0.0.15000309
    -- Detecting C compiler ABI info
    -- Detecting C compiler ABI info - done
    -- Check for working C compiler: /Library/Developer/CommandLineTools/usr/bin/cc - skipped
    -- Detecting C compile features
    -- Detecting C compile features - done
    -- Detecting CXX compiler ABI info
    -- Detecting CXX compiler ABI info - done
    -- Check for working CXX compiler: /Library/Developer/CommandLineTools/usr/bin/c++ - skipped
    -- Detecting CXX compile features
    -- Detecting CXX compile features - done
    	 MODULE: sampleFilter
    -- Configuring done (0.5s)
    -- Generating done (0.0s)
    -- Build files have been written to: /Users/seungwook/workspace/myTestFilter/build
    ~/workspace/myTestFilter>
    
    ~/workspace/myTestFilter> cd build && make
  • 환경 설정

    다음 환경 설정 파일에서 FILTER 절을 설정하는 예시입니다.

    {
        "filter": {
            "filters": [
                {
                    "name": "filter1",
                    "path": "TargetRealPath/myTestFilter/libsampleSystemFilter.so",
                    "event": [ "ON_START", "ON_STOP" ]
                },
                {
                    "name": "filter2",
                    "path": "TargetRealPath/myTestFilter/libsampleHttpFilter.so",
                    "event": [ "RECEIVE_REQUEST", "RECEIVE_RESPONSE" ]
                },
            ]
        }
    }