공부/네트워크

Http

songhees 2026. 1. 25. 00:13

HyperText Transfer Protocol

note

definition : 웹브라우저와 웹서버 간의 메세지 교환 시 사용되는 규칙을 정의  

  • 요청과 응답 메세지의 형태를 규정하는 프로토콜
    • 요청(Request) : 브라우저인 클라이언트에 의해 전송되는 메세지
    • 응답(Response) : 서버에서 응답으로 전송되는 메세지
    • HTTP는 애플리케이션 계층의 프로토콜이며 전송 프로토콜인 TCP나 암호화된 TCP연결인 TLS를 통해 전송된다.
      • 클라이언트는 TCP 연결을 시작하는 주체

  1. HTTP 규칙으로 만든 요청 메세지를 네트워크 장치에서 tcp를 통해 패킷으로 쪼갠다.
  2. 클라이언트에서 보낸 메세지가 ip주소를 따라 서버에 도달 하고
  3. 네트워크 장치에서 tcp를 통해 패킷를 재 조립한다.
  4. 서버에 요청 메세지를 전달한다.
  5. 웹 애플리캐이션에서 메세지를 해석해서 응답 메세지를 만든다.

프로토콜 이란 : 각 계층에 대한 통신 기기나 프로그램이 알아볼 수 있는 대화/통신 규칙


HTTP 1.1

통신 과정 : 하나의 TCP 커넥션을 통해 요청을 보낼 때, 응답이 도착하고 나서야 다시 요청을 보낼 수 있다.

브라우저는 여러개의 요청을 동시에 보내기 위해 여러개의 TCP 커넥션을 만들어 동시에 요청을 보내는 방법을 사용

연결 전송 개수 : 하나의 TCP 연결 위에서 하나의 요청만 처리 할 수 있다. ⇒ 직렬 처리 ( 동시에 여러 요청을 보내기 위해 서버와 여러 개의 연결을 사용 )

Pipelining
여러개의 요청을 보낼때 처음 요청이 응답될 때까지 기다리지 않고 바로요청을 한꺼번에 보내는 것을 의미
단, 응답 순서를 지키기 위해 응답 처리를 미루기 때문에 Head Of Line Blocking 문제가 발생

전송 메세지 타입 : text ⇒ 파싱이 단순하지만, 성능/효율의 한계

메시지 전체가 한 덩어리(바이트로 인코딩)로 TCP 바이트 스트림 위에 올라감 ← 프레임 개념이 없기 때문

줄바꿈/공백 기준으로 문자열 파싱 ⇒ 구현체 마다 해석(파싱)이 달라질 수 있음

⇒ 오류 + 보안 이슈


HTTP 2

https://httpwg.org/specs/rfc7540.html

연결 전송 개수 : 멀티플랙싱 ⇒ 병렬 처리

   멀티플랙싱 : 하나의 커넥션으로 동시에 여러개의 메세지 스트림을 응답 순서에 상관없이 주고 받는 것

전송 메세지 타입 : header,body 바이트 덩어리 + 프레임 계층 + Stream ID

  binary frame로 인코딩 ( header 와 body가 layer로 구분된다. )

  고정된 바이너리 구조로 파싱 ⇒ 구현체 마다 동일한 해석

  ⇒ 오류 발생 가능성이 줄어듦

우선순위 : 클라이언트는 서버에게 스트림을 보낼때, 각 요청 자원에 가중치 우선순위를 지정하고 보낸다.

server push : 서버가 받은 요청 다음에 클라이언트가 어떤 요청을 보낼지를 예측함으로써, 그 요청들에 대해 한 번의 왕복을 제거하여 클라이언트가 체감하는 성능을 개선하기 위해 설계

  • 클라이언트: GET /index.html 가 필요
  • 서버: /index.html 응답 + 그 안에서 쓸 CSS, JS를 미리 PUSH해서 보냄
  • → 클라이언트가 HTML을 받고 나서 다시 CSS/JS를 요청하는 왕복(RTT)을 줄여서 체감 성능을 올리려는 목적

실제에서는 서버 푸시는 효과적으로 사용하기가 어렵다.

서버가 클라이언트가 추가로 어떤 요청을 보낼지를 정확히 예측해야 하고, 이때 캐시, 콘텐츠 협상(content negotiation), 사용자 행동과 같은 요소들을 고려해야 하기 때문이다.

  • 안 써도 되는 데이터를 네트워크로 보낸 꼴 → 대역폭 낭비 + 중요한 응답과 경쟁해서 오히려 성능 저하
  • 특히 푸시 데이터가 크면 더 문제.
  • 실무에선 Server Push는 비활성화하거나 매우 제한적으로만 쓰는 경우가 많다

HPACK

header용 압축 알고리즘

 

HPACK은 헤더 이름/값을 다음 두 종류의 테이블를 통해 인덱스 기반으로 표현

  • Static Table
    • 고정으로 정의된 헤더 목록 ( 어떤 요청, 연결, 구현이든 동일 )
    • 예: :method, :path, :status, content-type, cache-control …
    • 자주 쓰는 헤더들을 인덱스 1,2,3,… 으로 미리 정의해 둔 일종의 공유 사전(dictionary)
  • Dynamic Table
    • 통신을 하면서 점점 쌓아 가는 테이블
    • 어떤 헤더를 한 번 보냈다면, 그 헤더를 Dynamic Table에 넣고
    • 다음부터는 그걸 인덱스 번호로만 참조해서 전송할 수 있음

헤더에 중복 값이 존재하는 경우, Static / Dynamic Header Table 사용하여 중복 헤더를 검출

중복된 헤더는 index값만 전송(바이트)하고 중복되지 않은 Header 정보의 값인 리터럴 값은 그대로 인코딩되거나 정적 허프만 코드를 사용할 수 있다.

 

통신과정

하나의 커넥션에 여러개의 Stream이 동시에 요청/응답 한다.

HTTP/2의 동시 요청은, 하나의 TCP 연결 위에 여러 개의 논리적인 stream을 두고, 각 stream에 고유한 Stream ID를 부여한 뒤, 요청/응답 데이터를 프레임 단위로 쪼개어 서로 다른 stream의 프레임들을 TCP 바이트 스트림 위에서 인터리빙해서 전송하는 방식

  • stream 하나가 하나의 HTTP 요청
  • 동시 스트림 개수는 SETTINGS_MAX_CONCURRENT_STREAMS 로 제어 할 수 있다.

 

구성

Frame : HTTP2 통신 최소 단위 ( 프로토콜 단위 )

  • HEADERS/DATA 프레임 → HTTP 요청과 응답의 기초를 형성
  • SETTINGS/WINDOW_UPDATE/PUSH_PROMISE 프레임 → HTTP/2의 다른 기능들을 지원하는 데 사용

Stream : 연결된 Connection(TCP)내에서 양방향으로 메세지(HTTP 요청/응답)를 주고 받는 하나의 독립적인 논리 채널

StreamId : 이러한 각각의 Stream을 구분하기 위한 id 값

 

통신 흐름

  • 애플리케이션 / HTTP 레벨
    • GET /users, 헤더, 바디(JSON 등)를 만듦
  • HTTP/2 레벨
    • 1.1과 같이 텍스트 줄을 만들지 않고
    • 헤더를 HPACK으로 압축해서 header block(바이트 덩어리)로 만들고
    • 바이트를 HEADERS/ PUSH_PROMISE /CONTINUATION 프레임, 바디부분은 여러개의 DATA 프레임으로 쪼갬
    • 각 프레임에 프레임 헤더(9바이트) 를 붙임
    • 프레임 헤더에 Stream ID / Type / Flags / Length 가 존재
 +-----------------------------------------------+
 |                 Length (24)                   |
 +---------------+---------------+---------------+
 |   Type (8)    |   Flags (8)   |
 +-+-------------+---------------+-------------------------------+
 |R|                 Stream Identifier (31)                      |
 +=+=============================================================+
 |                   Frame Payload (0...)                      ...
 +---------------------------------------------------------------+

  • TCP 레벨
    • 그 프레임들을 그냥 바이트 스트림으로 직렬 전송
    • 오로지 바이트를 순서대로(직렬로) 보내는 역할

HTTP 응답 버퍼링

클라이언트와 서버 사이의 중간 계층(미들웨어/프록시/게이트 웨이 등)에서 HTTP 응답을 버퍼링하여 일정량 모은 뒤 한 번에 전달하는 현상

⇒ 따라서 클라이언트는 실시간으로 와야 할 데이터가 늦게 몰아서 도착하는 것처럼 느껴질 수 있음

 

SSE(Server-Sent Events) 에서 문제됨

서버와 클라이언트를 HTTP 연결로 유지하면서 실시간 데이터를 서버 → 클라이언트로 보내는게 목적인데

nginx 와 같은 중간계층으로 인한 proxy buffering 기능으로 인해 실시간성이 떨어질 수 있다.

 


  • HTTP2에서 tcp 연결 1개만 사용하는가?

: TCP 연결 1개 안에서도 Stream 수 제한이 있다. ⇀ 서버에서 제한 가능

 

ex ) 

- nginx - http2_max_concurrent_streams : 128 개

- tomcat - max_concurrent_streams : 100 개

- 크롬 자체 제한 - 256개 (kMaxConcurrentStreamLimit = 256)

 

Stream 수 제한을 넘게 되면? → 브라우저 마다 처리 방식이 다르다.

⇒ 해당 값을 넘게 되면 추가적인 TCP 연결을 하나 더 열어서 분산 처리 할 수 있다.? 

⇒ 혹은 순차 처리

 

  • HTTP/2 Rapid Reset(CVE-2023-44487) ? 

 

 

끝이없어.....................................