AJP 프로토콜 모든 것을 분석 해보자
개요
AJP(Apache JServ Protocol)은 Web Server에서 받은 요청을 WAS로 전달해주는 프로토콜이다.
해당 프로토콜은 Apache HTTP Server, Apahce Tomcat, 웹스피어, 웹로직, JBOSS, JEUS, 등 다양한 WAS에서 지원한다.
패킷 구조 분석
개요
- AJP는 HTTP의 내용을 포워드 용도에만 있다.
- AJP는 8K 이상을 전송하지 못한다(헤더는, Body Chunk는 64K 를 보낸다.).
- 그래서 짤라서 여러 번 보내는 것 같다(시퀀셜하게, Body Chunk 또한 64K 씩 잘라서 보낸다.).
- Secure하지 않다. 단지 포워드 용도이기 때문이다. Secure하기 위해서는 HTTPS를 포워딩 하면 된다( 또는 TLS를 입히고 보낸다? ㅋㅋㅋ).
페킷의 데이터 타입
- AJP는 Byte/Boolean/Integer/String 타입이 존재한다.
- Byte는 말그대로 Byte array를 얘기한다.
- Boolean은 말그대로이다. 크기는 1바이트이다.
- Integer는 말그대로이다. 크기는 2바이트이다.
- String은 말그대로이다.
- String을 표현하는 방법은 다음과 같다. String의 구조는 (길이)/(String)/(end) 이다. 길이의 경우, Integer 값이다. 예를 들어 HTTP/1.1 표현할 경우, 길이: 0x0008 이며, String은 아스키 값으로 표현하고 end padding은 0x00을 표기한다.
AJP Request 패킷 구조
-Header: Request: 0x1234 /길이 / 데이터 길이
- Body:
-Prefix_code의 경우, 생략될 수 있다. Request_body값을 보낼 때(헤더를 보낸 후)
Request Prefix Code는 아래와 같다. 주로 쓰는 것은 HTTP 헤더 2번과 HTTP Body none이다.
AJP Response 패킷 구조
prefix_code_4: HTTP Response Header 전송
prefix_code_3: HTTP Response Body 전송
prefix_code_6: AJP 클라이언트의 Body 전송 수신 후, 더 보낼 것이 있는지 확인을 위한 구성
prefix_code_5: prefix_code_6다음으로 AJP 클라이언트가 더이상 보낼 것 이 없으면, 해당 구성을 사용
그 외 사용 패킷
HTTP-Method 표현 방법
- 1~27 Enum 값으로 표현한다.
HTTP-Header 표현 방법
- 헤더는 0xa001 ~ 0xa00E 으로 표현한다.
- Enum 값에 없는 헤더는 String 타입으로 구성한다.
Attribute 표현 방법
- 0x0a 부터 시작 이하 String 타입으로 구성한다.
AJP 플로우
AJP CPing/CPong
Ping 같은 것, AJP를 제대로 사용할 수 있는지 확인한다.
Cping(like ping, like http method option)
0x1234 -> AJP Request
0x0001 -> Data 길이
0x0a -> cping
Cpong
0x4142(ab) -> AJP Response
0x0001 -> Data 길이
0x09 -> Cpong(Cping response)
AJP Request 플로우
- 아래는 AJP의 Request 중 prefix 2번에 해당하는 것이다(HTTP 헤더 전송 부분).
- 도메인 부분은 가렸다.
※위의 그림, 0xff 관련된 것은 버그로 판명
github.com/jrialland/ajp-client/issues/8
(사정상 아래 이미지에서 위의 2개 Reqeust Header는 이미지 삭제)
아래는 prefix_code: none에 해당하는 HTTP Request Body 부분이다.
AJP Response 플로우
아래는 HTTP Response 헤더 전송 부분이다.
아래는 HTTP Response 바디 전송 부분이다.
AJP HTTP 응답의 끝을 알림
AJP Request/Response 전체 플로우
1. Server -> Container: AJP CPing( prefix_code: 10)
2. Container -> Server: AJP Cpong Reply(prefix_code: 9)
3. Server -> Container: AJP Request(헤더 전송: prefix_code: 2)
4. Server -> Container: AJP Request(Body 전송: prefix_code: 없음)
5. Container -> Server
- GET_BODY_CHUNK: 만약에 더 보낼 데이터가 있으면 보내라.(prefix_code:6)
- ff fa(Integer) : 근데 AJP 최대 전송 패킷 크기(200K 가까이 보내면 Apahce HTTPD 에서 fffa씩 짤라서 보냄)
6. Server -> Container
- 1안: 더 보낼 데이터가 있는 경우
-> 반복하다 더 이상 보낼 데이터가 없을 경우, 6-2로 빠진다.
- 2안: 더 보낼 데이터가 없는 경우
-> 00 00 을 보낸다.
7. Container -> Server: AJP Response(헤더 전송: prefix_code: 4)
8. Container -> Server: AJP Response(Body 전송: prefix_code: 3)
9. Container -> Server: AJP Response(끝을 알림: prefix_code: 5)
- reuse: true: (reuse: TCP 커넥션은 새로운 요청을 받을 수 있음을 알림 )
오픈 소스
해당 오픈 소스 테스트 완료 잘 통신됨, 그러나, 8K 이상의 HTTP Body에 대해서는 8K 씩 짤라서 보냄 << 64K 씩 짤라서 보낼 수 있도록 수정 필요
https://github.com/jrialland/ajp-client
마치며
해당 AJP 프로토콜은 중계 서버 또는 신뢰된 서버간의 서버 투 서버 통신에 많이 활용될 수 있다.
- HTTP 보다 더빠른 프로토콜(바이너리 프로토콜)
- TCP Connection Pool의 사용(HTTP는 Stateless 프로토콜, Conection Pool을 구성해도 URI 한정)
- 확장성 용이( 많은 WAS에서 지원)
- 재밌다
레퍼런스
https://tomcat.apache.org/connectors-doc/ajp/ajpv13a.html
https://developer.jboss.org/blogs/mladen.turk/2007/07/16/comparing-modproxy-and-modjk?_sscc=t
https://github.com/jrialland/ajp-client
'개발관련 > 삽질' 카테고리의 다른 글
SQLite 개념/구조/멀티 DB 실사용기 (0) | 2020.01.08 |
---|---|
AOP(SpringAOP/AspectJ) (0) | 2019.12.02 |
Insert Multiple - Oracle(Tibero) 포팅 삽질기 (0) | 2019.09.11 |
JWT를 분석하고 사용해보자 (3/3) (0) | 2019.08.13 |
JWT를 분석하고 사용해보자 (2/3) (0) | 2019.08.13 |