Post

HTTP/2 421 Misdirected Request

목차


HTTP/2 에서 프록시를 경유하는 네트워크 환경 구축 시 발생하는 문제에 대한 내용 및 해결한 방법을 기술한다.

Config Network Setting

  1. 도메인 serv1.iasdf.comserv2.iasdf.com을 HAProxy에 연결한다.
  2. HAProxy를 다음과 같이 설정한다.
    • 아래 호스트의 연결은 HTTP/2 L7 Load Balancing으로 설정한다.
    • serv1.iasdf.com 호스트는 10.0.1.43 서버로 접속
    • serv2.iasdf.com 호스트는 10.0.1.48 서버로 접속
  3. 웹 서버 두 대를 HTTP/2 프로토콜 통신이 가능하도록 설정한다.
  4. 요청을 크롬 브라우저의 Secret Tab을 이용하여 탭을 열어 각각의 서버에 접속한다.
      1. 탭 하나 (tab1)을 열어서 serv1.iasdf.com에 접속한다.
      1. 탭을 하나 더 열어서 (tab2) serv2.iasdf.com에 접속한다.

HAProxy 설정 내용

HAProxy 설정

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
frontend webserver_front_http2_connection_test
    mode http
    bind 10.0.1.49:443 ssl crt /path/to/cert.pem alpn h2
    use_backend     webserver_backend_http2_1   if { ssl_fc_sni serv1.iasdf.com }
    use_backend     webserver_backend_http2_2   if { ssl_fc_sni serv2.iasdf.com }
    # use_backend       webserver_backend_http2 if { ssl_fc_alpn -i h2 }
    default_backend webserver_backend_http2_1

backend webserver_backend_http2_1
    mode http
    balance roundrobin
    server server1 10.0.1.43:443 ssl verify none alpn h2

backend webserver_backend_http2_2
    mode http
    balance roundrobin
    server server1 10.0.1.48:443  ssl verify none alpn h2
설정 완료 후 크롬 접속 스크린샷

serv1.iasdf.com

Chrome serv1.iasdf.com -> HAProxy 10.0.1.49 -> Server 10.0.1.43

  • server1 은 title 바가 server 1, 10.0.1.43 내용이 담긴 페이지를 응답한다.
  • favicon이 있다.

serv2.iasdf.com

Chrome serv2.iasdf.com -> HAProxy 10.0.1.49 -> Server 10.0.1.48

  • server2 는 title 바가 server 2, 10.0.1.48 내용이 담긴 페이지를 응답한다.
  • favicon이 없다.

Expected Action

  1. serv1.iasdf.com에 접속한 탭(tab1)은 serv1.iasdf.com에 대한 커넥션을 만들고 유지한다.
  2. serv2.iasdf.com에 접속한 탭(tab2)은 serv2.iasdf.com에 대한 커넥션을 만들고 유지한다.

시퀀스 다이어그램

serv2serv1HAProxyChromeserv2serv1HAProxyChrome tab1 - serv1.iasdf.comtab1 - TLS HandShakeserv1.iasdf.comserv1 - TLS HandShakeHTTP/2 200 OKHTTP/2 200 OKtab2 - serv2.iasdf.comtab2 - TLS HandShakeserv2.iasdf.comserv2 - TLS HandShakeHTTP/2 200 OKHTTP/2 200 OK

Current Action

탭을 열어 serv1 접속 후, serv2 접속 시에도 serv1 커넥션을 재사용하여 serv1 서버로 요청된다.
이러한 문제로 serv2.iasdf.com 도메인으로 요청했으나 serv1 페이지가 응답된다.

serv2serv1HAProxyChromeserv2serv1HAProxyChrome tab1 Connection reuseso, tab2(tab1) connect to serv1tab1 - serv1.iasdf.comtab1 - TLS HandShakeserv1.iasdf.comserv1 - TLS HandShakeHTTP/2 200 OKHTTP/2 200 OKtab2 - serv2.iasdf.comserv2.iasdf.comHTTP/2 200 OKHTTP/2 200 OK

크롬에서 탭을 2개 열어서 서로 다른 도메인에 접속했지만,
serv2.iasdf.com 도메인 요청 시 serv2.iasdf.com 스크린샷 과 달리 serv1.iasdf.com 스크린샷 페이지가 응답된다.

이러한 문제는 프록시가 중개하여 발생하는데, 프록시에서 호스트에 따라 커넥션을 분배하기 때문에
serv1.iasdf.com, serv2.iasdf.com 모두 목적지가 프록시 (10.0.1.49) 를 가리킨다.

즉 클라이언트(크롬)은 serv1, serv2 모두 목적지가 같다고 판단하고 커넥션을 재사용한다.

WireShark 패킷 분석 내용

serv1 - 연결, 클라이언트 TCP 포트 53554

serv2 - 연결, 클라이언트 TCP 포트 53554 (serv1 커넥션 재사용)

응답 데이터가 server 1 페이지임.


Modify Action

sni를 비교하여 호스트가 다를 경우 HTTP/2 421 Misdirected Request 응답을 하도록 소스를 변경했다.

나와 같은 문제를 겪는 사람이 이미 있었는데, 링크를 첨부한다.

In the rare case where a server gets a resource request for an authority (or scheme) it can’t serve, there’s a dedicated error code 421 in HTTP/2 that it can respond with and the browser can then go back and retry that request on another connection.

이 링크를 보면, 잘못된 요청을 받았을 때 HTTP/2 421 Misdirected Request 응답을 하도록 하라고 한다.

우선은 HAProxy의 코드를 수정해서 HTTP/2 421 Misdirected Request 응답을 하도록 했는데, 421 에러코드의 내용을 보면 “이 에러코드는 프록시에 의해 생성되어서는 안된다.” 라고 한다. (따라서 PR은 못 할듯)

The 421 (Misdirected Request) status code indicates that the request was directed at a server that is not able to produce a response. This can be sent by a server that is not configured to produce responses for the combination of scheme and authority that are included in the request URI.

Clients receiving a 421 (Misdirected Request) response from a server MAY retry the request — whether the request method is idempotent or not — over a different connection. This is possible if a connection is reused (Section 9.1.1) or if an alternative service is selected [ALT-SVC].

This status code MUST NOT be generated by proxies.

A 421 response is cacheable by default, i.e., unless otherwise indicated by the method definition or explicit cache controls (see Section 4.2.2 of [RFC7234]).

동작 과정은 아래와 같다.

serv2serv1HAProxyChromeserv2serv1HAProxyChrome tab1 커넥션을 끊으면 안된다.tab1 커넥션을 끊어버리면,tab1에서 페이지 이동이 있을 경우(serv1 도메인으로 추가 요청을 진행할 경우)Client Hello 부터 세션을 다시 맺어야 한다.HTTP/2 에서 421 에러코드가 있는 이유이며301 Redirect 등으로 커넥션을 끊어버리면serv1 (tab1)커넥션이 끊겨해당 탭에서 다시 세션을 맺어야 하기 때문에 TLS HandShaking으로 인한 부하가 발생할 수 있다.tab1 - serv1.iasdf.comtab1 - TLS HandShakeserv1.iasdf.comserv1 - TLS HandShakeHTTP/2 200 OKHTTP/2 200 OKtab2 - serv2.iasdf.comHTTP/2 421 Misdirected Requesttab2 - RST_STREAM421 에러코드로 인해 스트림 종료ACK (FIN 아님, tab1 커넥션 끊지 않음.)tab2 - serv2.iasdf.comtab2 - TLS HandShake(New Connection)serv2.iasdf.comserv2 - TLS HandShakeHTTP/2 200 OKHTTP/2 200 OK

새로운 커넥션으로 serv2.iasdf.com 에 접속하게 되어, serv2.iasdf.com 페이지가 응답된다.

WireShark 패킷 분석 내용

serv1 - 연결, 클라이언트 TCP 포트 54250

serv2 - 연결, 클라이언트 TCP 포트 54250 으로 커넥션 재사용 시,
HAProxy에서 HTTP/2 421 Misdirected Request 응답
클라이언트는 421 응답을 받고, RST_STREAM을 보내 해당 탭의 HTTP/2 스트림을 중지(종료 아님)한다.

이후 serv2.iasdf.com 으로 새로운 커넥션을 맺어 통신한다. (TCP 포트 54253)

301 Redirect 와 달리, serv1 커넥션을 끊지 않는다.

This post is licensed under CC BY 4.0 by the author.