hooking 함수

M2 후킹(hooking) 함수를 이용하면 자유롭게 HTTP 트랜잭션을 제어할 수 있다.

  • 클라이언트 HTTP 요청에 대한 재정의

  • 캐싱키 정의

  • 원본 HTTP 요청에 대한 재정의

../_images/m2_34.png
  1. 클라이언트 요청을 M2의 hooking 함수로 보낸다.

  2. hooking 함수는 캐싱키와 TCP_MISS 시 원본에 보낼 요청을 정의하여 응답한다. 200 OK 가 아니라면 M2RT는 403 Forbidden 을 응답한다.

  3. 재정의된 캐싱키를 사용해 캐싱엔진에서 찾는다.

  4. 원본 요청 시 hooking 함수가 정의한 요청 모델을 참조해 원본서버에 요청한다.

Note

Purge의 경우 2, 3 항에서 사용하는 캐싱 키를 사용한다.

설정

hosting[].hook 설정을 통해 확장 커스터마이징을 구성한다.

{
  "hosting": [
    {
      "name": "www.foo.com",
      "hook": {
        "enable": true,
        "path": "https://example.com/sample.js"
      }
    }
  ]
}

예를 들어 다음과 같이 임의의 hooking 함수전용 설정이 필요할 수 있다.

{
  "name": "John",
  "age": 30,
  "car": null
}

설정은 hooking 함수에 파라미터로 전달되는데 hosting[].hook.session.param 에 값으로 설정한다.

See also

{
  "hosting": [
    {
      "name": "www.foo.com",
      "hook": {
        "enable": true,
        "path": "https://example.com/sample.js",
        "session": {
          "param": "{ \"name\": \"John\", \"age\": 30, \"car\": null }"
        }
      }
    }
  ]
}

Note

hosting[].hook.session.param 을 ESCAPE & 직렬화해야 하는 이유는 setting.json 의 표준 DTD로 관리가 불가능하기 때문이다. 다시 말해 setting.json 의 정규기능이 아닌 설정이 setting.json 설정구조에 들어가는 것을 방지하기 위함이다.

클라이언트 요청후킹

클라이언트가 보낸 HTTP 요청을 hooking 함수가 재정의 할 수 있도록 구조화하여 전달한다.

{
   "host": "foo.com",
   "clientIp": "181.43.1.33",
   "sessionId": 2,
   "protocol": "http",
   "message": "POST /itemimage/LO/12/37/50/02/80/vdo/LO1237500280_1.mpx3123 HTTP/1.1\r\nHost: foo.com\r\nContent-Type: text/plain\r\nUser-Agent: PostmanRuntime/7.26.8\r\nAccept: */*\r\nPostman-Token: 0bce4527-7d8b-4974-9c2c-742efb8a549c\r\nAccept-Encoding: gzip, deflate, br\r\nConnection: keep-alive\r\nContent-Length: 519\r\nX-Forwarded-For: 181.43.1.33\r\n\r\n",
   "body": "<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\">\r\n  <s:Body>\r\n    <serviceCall xmlns=\"http://webservice.B2BOnline.com\">\r\n      <AvailRQ>\r\n        <AgencyId>JJSEL13157</AgencyId>\r\n        <CarrierCode>7C</CarrierCode>\r\n        <DepApoCode>CJU</DepApoCode>\r\n        <DepApoName></DepApoName>\r\n        <ArrApoCode>PUS</ArrApoCode>\r\n        <ArrApoName></ArrApoName>\r\n        <FlightDate>20171228</FlightDate>\r\n        <PaxCount>1</PaxCount>\r\n      </AvailRQ>\r\n    </serviceCall>\r\n  </s:Body>\r\n</s:Envelope>"
   "param": { "name": "John", "age": 30, "car": null },
   "functions": null
}
host

요청을 처리하는 가상호스트 이름

clientIp

소켓 리모트 주소

sessionId

세션 고유번호 ( access.logsession-id 필드와 동일)

protocol

프로토콜. http 또는 https 또는 http2

message

클라이언트 요청의 HTTP 메시지

body

클라이언트 POST 요청의 HTTP Body. POST 요청이 아니라면 해당 필드가 없거나 값이 null

param

hosting[].hook.param 에 설정된 고객사 전용설정. Unescaped JSON으로 전달된다.

functions

연계될 함수체인 정보

 # 함수체인이 message에 명시되어 있다면 functions필드가 추가된다.
 {
   ...
   "message": "GET /sample.zip/unzip/files/logo.jpg/dims/resize/100x100 ... (생략)... ",
   ..
   "functions": [
     {
       "name": "unzip",
       "url": "/unzip/files/logo.jpg"
     },
     {
       "name": "dims",
       "url": "/dims/resize/100x100"
     }
   ]
}

Important

  • 개발 호환성을 위해 요청 헤더에 Content-Type: application/json 를 명시한다.

  • RT 는 Hooking함수를 호출할 때 로그분석을 위해 X-M2X-Forwarded-Context-Id 헤더를 포워딩한다. hooking 함수에서 외부통신을 위해 RT 를 이용하는 경우 반드시 X-M2X-Forwarded-Context-Id 헤더를 명시해주어야 같은 x-ctx-id 로 access.log와 origin.log에 기록된다.

에러 처리

다음과 같이 code 를 통해 요청이 더 진행되지 못하도록 에러처리할 수 있다.

{
   "name": "m2hook1",
   "code": 403,
   "body": "<HTML><TITLE>403 Forbidden</TITLE><BODY><H1>Forbidden</H1></BODY></HTML>"
}
name

m2hook1 (고정)

code

200 OK 를 보내지 않는다면 510 Not Extended 응답과 함께 오류 메시지를 보낸다.

body

응답 바디

로깅

hooking 함수도 함수체인에 속하기 때문에 origin.log 에 기록된다.

주요 로그필드는 다음과 같다.

  • session-type hook1 으로 기록된다.

  • x-sc-chain-error Hooking함수 에러 메시지

  • x-sc-chain-info Hooking함수 정보 메시지. 주로 캐싱키를 기록한다.

요청 재정의

hooking 함수의 응답에 클라이언트 HTTP 요청을 재정의한다.

{
   "sessionId": 2,
   "response": {
      "code": 100,
      "body": "blah blah",
      "headers": [
         { "key": "my-powered-by", "value": "winesoft" },
         { "key": "cookie", "value": "NNB=LS3KUV63E5RV6; NRTK=ag#all_gr#1_ma#-2_si#0_en#0_sp#0;" }
      ]
   },
   "request": null,
   "cacheKey": "/availity?key={a,b,1}",
   "vhost": "bar.com",
   "originRequest": {
      "method": "POST",
      "url": "/itemimage/LO/12/37/50/02/80/vdo/LO1237500280_1.mpx3123",
      "body": "<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\">\r\n  <s:Body>\r\n    <serviceCall xmlns=\"http://webservice.B2BOnline.com\">\r\n      <AvailRQ>\r\n        <AgencyId>JJSEL13157</AgencyId>\r\n        <CarrierCode>7C</CarrierCode>\r\n        <DepApoCode>CJU</DepApoCode>\r\n        <DepApoName></DepApoName>\r\n        <ArrApoCode>PUS</ArrApoCode>\r\n        <ArrApoName></ArrApoName>\r\n        <FlightDate>20171228</FlightDate>\r\n        <PaxCount>1</PaxCount>\r\n      </AvailRQ>\r\n    </serviceCall>\r\n  </s:Body>\r\n</s:Envelope>",
      "headers": [
         { "key": "x-custom-header", "value": "abcdefg" },
         { "key": "x-custom-header2", "value": "baq.com" },
         { "key": "cookie", "value": "NNB=LS3KUV63E5RV6; NRTK=ag#all_gr#1_ma#-2_si#0_en#0_sp#0;" }
      ]
   }
}

Note

요청을 재정의하지 않고 바이패스 시키고 싶다면 다음과 같이 응답한다.

{
   "sessionId": 2,
   "response": {
      "code": 100
   },
   "cacheKey": null
}
  • response.code100 으로 설정하여 요청을 진행시킨다.

  • cacheKeynull 로 설정하여 캐싱엔진을 우회시키도록 한다.

sessionId

세션 고유번호 (디버그 용, USERDATA 개념)

response

클라이언트 요청 처리응답

code

값이 100 인 경우 흐름을 지속한다. 그 외에는 트랜잭션을 더 진행하지 않고 정의된 code , body , headers 를 즉시 응답한다.

body

즉시 응답할 경우 HTTP Body 데이터.

headers

HTTP Headers. 바이패스를 제외한 모든 응답에 명시된다.

request

클라이언트 요청 재정의

headers

클라이언트 요청헤더를 modify 한다.

"request": {
   "headers": [
      { "key": "X-M2X-Hook", "value": "true", "mode": "set" },
      { "key": "cookie", "value": "mykey=myvalue;", "mode": "append" }
   ]
}

key , value 쌍으로 지정하며 mode 표현은 modify 모듈의 mode 와 동일하다. 단, rewrite 는 지원하지 않는다.

cacheKey

캐싱엔진에서 사용할 키

cacheTtl (단위: 초)

캐싱엔진에서 사용할 TTL(Time To Live). 원본이 2xx 계열로 응답할 때만 적용된다.

Note

이 값이 있다면 ttl 설정보다 우선한다. 캐싱엔진의 ttl 설정을 따르도록 구성하려면 이 필드를 명시하지 않는다.

cacheResult

access.logsc-cachehit 필드를 재정의한다.

Note

만약 Hook에서 클라이언트 트랜잭션을 종료할 경우 캐시는 요청거부로 판단하여 TCP_DENY 로 기록한다.

vhost

변경될 가상호스트. 이 값이 NULL 또는 빈문자열 이라면 가상호스트를 변경하지 않는다.

originRequest

원본에 요청해야 하는 경우 HTTP 요청 구조체

method

원본요청 method

Warning

클라이언트 HEAD 메소드 요청이라면 GET 으로 응답해야 한다.

url

원본요청 URL

body

원본요청 HTTP Body 데이터.

headers

HTTP Headers

Warning

주의사항

  • 서버로 보내는 HTTP 헤더를 설정할 뿐이지 클라이언트가 전송한 헤더가 덮어씌워지는 것은 아님에 주의한다. 만약 Hook을 통해 조작한 의도를 다른 기능과 연계하려면 cacheKey 에 그 의도를 담아야 한다.

  • host 헤더는 재정의되지 않는다. 정확히는 여기에 설정되어도 캐싱엔진에서 원본 요청시 항상 덮어 쓰기 때문에 동작하지 않는다.

    • 만약 임의의 서버로 보내고 싶다면 proxy 함수를 사용한다.

    • instant 바이패스로 동작할 경우 클라이언트 헤더보다 여기서 재정의한 헤더가 우선한다.

POST 요청 상세

POST 요청을 hooking 하려면 다음 설정이 선행되어야 한다.

# functions.network.http.frontEnd.bypass
"postMethod": {
  "enable": true
}

# functions.network.cache.cachingKey
"postMethod": {
  "enable": true,
  "bodySensitive": true,
  "bodyContentLengthMax": 102400
}
  • 바이패스하는 POST 요청이라면 Hooking을 통한 재정의는 불필요하다.

  • hooking 모듈을 동작시키려면 POST요청을 캐싱해야 한다.

캐싱엔진은 POST 요청에 대해 URL과 Body의 조합으로 캐싱키를 생성한다. hooking 모듈에 의해 요청이 재정의될 경우 다음과 같은 규칙을 따른다.

  • 캐싱키는 hooking 응답의 cacheKey 만을 사용한다. 클라이언트 POST 요청의 URL 및 Body는 무시된다.

  • 캐싱엔진이 원본에 요청을 보낼 때는 hooking 응답의 originRequest 만을 사용한다. 캐싱키 및 클라이언트 요청은 모두 무시된다.

정리하면 hooking 모듈은 요청을 완전히 재정의하는 개념이기 때문에 POST 요청은 hooking 모듈로 전달되기 전까지만 의미를 가진다고 볼 수 있다.