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;" }
      ]
   },
   "userData": null
}

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 (기본: null)

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

Warning

이 필드는 아래 경우처럼 명확한 사유가 있는 경우만 정의한다.

  • API캐싱처럼 POST 기반 요청의 캐싱 최적화를 수행해야 하는 경우

  • hook을 통해 원본 HTTP 요청 전체를 변조해야 하는 경우

이 필드를 정의하지 않으면 캐싱엔진은 cacheKey 에 기반해 함수체인등을 알아서 잘 처리한다.

method

GETPOST 만 지원한다. 그 외는 GET 으로 간주된다.

Warning

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

url

원본요청 URL

body

원본요청 HTTP Body 데이터.

headers

HTTP Headers

Warning

주의사항

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

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

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

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

userData

사용자 정의 access.log 기록을 위한 필드. 하위 1 depth까지만 인식한다.

"userData": {
  "myfield": "custom-field",
  "mystatus": "custom-status"
}

POST 요청 상세

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

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

# 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 모듈로 전달되기 전까지만 의미를 가진다고 볼 수 있다.