HTTP Chunking 과 테스트 클라이언트 구현

2024. 6. 22. 01:24·HTTP

HTTP 1.1은 HTTP 메세지를 나눠서 보낼 수 있는 청크 인코딩을 지원 합니다.

 

HTTP 1.1은 기본적으로 TCP 세션을 지속적으로 유지하며 (Persistent Connection = keep-alive = connection reuse), 이는 새로운 TCP 세션을 생성하기 위한 오버헤드를 제거해 네트워크 대기시간을 줄이는 방법 중 하나 입니다.

 

소켓 프로그래밍을 해본 사람을 알겠지만, 소켓을 재사용하기 위해서는 보내는 메세지의 정확히 길이를 알거나 종료 조건을 알아야  합니다. 따라서 사전에  HTTP 메세지의 본문(body)의 길이를 Content-Length 헤더를 통해 알려 주고, 수신 받는 쪽에서는 해당 바이트수(길이) 만큼 읽어 동일한 소켓을 종료하지 않고 연결을 유지하며 재사용 할 수 있습니다.

 

그러나, 매우 큰 데이터와 같이 사전에 전송할 본문의 길이를 알 수 없다면, 전통적인 HTTP 구현체에서 HTTP 1.0은 Content-Length 헤더를 생략하고 종료 조건인 EOF 표식 -1 이 나올 때까지 읽었습니다.

HTTP 1.1 에서는 이 문제를 해결하기 위해 Transfer-Encoding 이라는 헤더를 사용했습니다. Transfer-Encoding 헤더에 chunked 라는 값을 사용하면, Content-Length 헤더가 생략되며, 각 청크의 앞부분에 현재 청크의 길이가 16진수 형태로 오고 그 뒤 '\r\n'이 오고 그 다음에 청크 (분할 된 본문)이 오며, 길이가 0인 청크로 큰을 나타냅니다. 경우에 따라, 이후 연속된 엔티티 헤더 필드로 구성된 트레일러가 옵니다.

 

다음은 POST 요청을 chunking 해서 테스트한 HTTP 메세지 입니다.

POST /api/test HTTP/1.1
Host: localhost:8080
Accept-Encoding: identity
Transfer-Encoding: chunked
Content-Type: application/json

2c\r\n
{"Person":[{"id":1,"name":"Jaxon","age":35},
21\r\n
{"id":2,"name":"Jhon","age":40}]}
0\r\n
\r\n

 

본문 부분의  첫번째 청크인 {"Person":[{"id":1,"name":"Jaxon","age":35}, 길이는 총 44바이트로 이를 16진수로 표기하면 2c 가 됩니다. 이어 두번째 청크인 {"id":2,"name":"Jhon","age":40}]} 은 길이가 33바이트며, 이를 16진수로 표기하면 21이 됩니다. 마지막으로 길이를 0으로 표기함으로써 종료 청크를 나타냅니다.

 

HTTP Chunking 된 요청의 경우 테스트 시 많이 사용하는 curl 이나 httpie 로는 원하는 대로 본문을 나눠서 전송하는 테스트가 불가능합니다.

 

HTTP Chunking 된 메세지를 보낼수 있는 파이썬 클라이언트 코드는 아래와 같이 간단히 작성할 수 있습니다.

import http.client
import time

if __name__ == "__main__":
    conn = http.client.HTTPConnection('localhost', 8080)
    conn.connect()
    conn.putrequest('POST', '/api/test')
    conn.putheader('Transfer-Encoding', 'chunked')
    conn.putheader('Content-Type', 'application/json')    
    conn.endheaders(encode_chunked=True)
    
    chunk1 = '{"Person":[{"id":1,"name":"Jaxon","age":35},'
    chunk2 = '{"id":2,"name":"Jhon","age":40}]}'

    print(f'{hex(len(chunk1))[2:]}\r\n')
    conn.send(f'{hex(len(chunk1))[2:]}\r\n'.encode())
    print(f'{chunk1}\r\n')
    conn.send(f'{chunk1}\r\n'.encode())
    time.sleep(1)
    
    print(f'{hex(len(chunk2))[2:]}\r\n')
    conn.send(f'{hex(len(chunk2))[2:]}\r\n'.encode())
    print(f'{chunk2}\r\n')
    conn.send(f'{chunk2}\r\n'.encode())
    time.sleep(1)
    
    print("0\r\n\r\n")
    conn.send("0\r\n\r\n".encode())

    r = conn.getresponse()
    print(r.status, r.reason, r.read())

 

요청을 간단히 출력해 주는 서버를 파이썬 플라스크로 작성한 코드 입니다.

from pprint import pprint
from flask import Flask, request, jsonify


app = Flask(__name__)

@app.route('/api/test', methods=['POST'])
def post_body():
    header = dict(request.headers)
    data = request.get_json()
    pprint (header)
    pprint (data)
    res_body = data["Person"]
    return jsonify(res_body)

if __name__ == '__main__':
    app.run(debug=False, host='0.0.0.0', port=8080)

 

위를 실행하면, 

Flask 구현체에서는 클라이언트에서 분할 전송한 바디를 하나의 json 으로 정확히 인식하는 것을 확인 할 수 있습니다.

 

 

[참조]

HTTP Chunking, Eric D. Larson, https://www.oracle.com/technical-resources/articles/javame/chunking.html
Transfer-Encoding, mdn web docs, https://developer.mozilla.org/ko/docs/Web/HTTP/Headers/Transfer-Encoding

'HTTP' 카테고리의 다른 글

IIS URL Rewrite 활용해서 파일명 없이 다운로드 받기  (0) 2024.01.22
'HTTP' 카테고리의 다른 글
  • IIS URL Rewrite 활용해서 파일명 없이 다운로드 받기
virbr0.net
virbr0.net
  • virbr0.net
    virbr0.net
    virbr0.net
  • 전체
    오늘
    어제
    • 분류 전체보기
      • SIP
      • HTTP
      • OS
      • 프로그래밍
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    다익스트라
    파일명
    URL Rewrite
    DFS
    HTTP
    시간복잡도
    rtp inject
    SBC
    최단경로
    파일명 없이 다운로드
    가상화
    SIP
    클라우드
    해외발신
    IIS
    RDP
    브로드웍스
    유기농 배추
    BFS
    윈도우서버
    딕셔너리
    셀프칭찬
    alteon
    인터넷전화
    회사생활
    chunked
    파이썬
    ADC
    RTP
    인증서
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
virbr0.net
HTTP Chunking 과 테스트 클라이언트 구현
상단으로

티스토리툴바