Это в основном из любопытства;скажем, у меня есть HTTP1.1-совместимый сервер. У меня нет ресурсов / невозможно добавить полную поддержку HTTP2 на этот сервер.
Тем не менее я хочу иметь возможность обслуживать клиентов, которые открывают соединение с предисловием соединения HTTP2 (PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n
), сказав им использовать HTTP1.1 вместо этого. Обратите внимание, что речь идет о клиентах, которые не запускают соединение с запросом HTTP1.1 с заголовком Upgrade
.
Я бы хотел добиться такого поведения:
CLIENT SERVER
| ------ http2 GET --> |
| <----need http1 ---- |
| ------ http1 GET --> |
... (normal http1 processing)
В спецификации содержится код ошибки HTTP_1_1_REQUIRED (0xd)
, который звучит примерно так, как мне нужно. Более того, из прочтения кажется, что этот код ошибки используется для понижения уровня соединений при необходимости (очевидно, повторное согласование TLS?). Тем не менее, я просто не могу заставить это работать ...
char const Http2EmptySettingsFrame[] =
{
// Frame Header
0, 0, 0,
0x4,
0,
0 & 0x7F, 0, 0, 0
};
char const Http2RstStreamFrame[] =
{
// Frame Header
0, 0, 4, // Length of frame content
0x3, // Type: RST_STREAM
0, // Flags
0 & 0x7F, 0, 0, 1, // Stream identifier 1 ; also tried with 0
// Frame content
0, 0, 0, 0xD // Error code (RST_STREAM frame)
};
char const Http2GoAwayFrame[] =
{
// Frame Header
0, 0, 8,
0x7,
0,
0 & 0x7F, 0, 0, 0,
0 & 0x7F, 0, 0, 0, // GO_AWAY stream id
0, 0, 0, 0xD // GO_AWAY error code: HTTP_1_1_REQUIRED
};
Используя вышеизложенное и fwrite(frame, sizeof(frame), 1, stdout)
в небольшой тестовой программе, я создаю кадры для отправки клиенту (./a.out > frame.bin
). Затем я запускаю свой «сервер»:
cat emptysettings.bin rststream.bin goaway.bin - | netcat -l -p 8888 -s 127.0.0.1
Используя клиент http2, я получаю ...
# this was when just sending empty SETTINGS and RST_STREAM frame
nghttp -v http://127.0.0.1:8888
[ 0.000] Connected
[ 0.000] recv SETTINGS frame <length=0, flags=0x00, stream_id=0>
(niv=0)
[ 0.000] [INVALID; error=Protocol error] recv RST_STREAM frame <length=4, flags=0x00, stream_id=1>
(error_code=HTTP_1_1_REQUIRED(0x0d))
[ 0.000] send SETTINGS frame <length=12, flags=0x00, stream_id=0>
(niv=2)
[SETTINGS_MAX_CONCURRENT_STREAMS(0x03):100]
[SETTINGS_INITIAL_WINDOW_SIZE(0x04):65535]
[ 0.000] send GOAWAY frame <length=34, flags=0x00, stream_id=0>
(last_stream_id=0, error_code=PROTOCOL_ERROR(0x01), opaque_data(26)=[RST_STREAM: stream in idle])
[ERROR] request http://127.0.0.1:8888 failed: request HEADERS is not allowed
Some requests were not processed. total=1, processed=0
... или используя curl ...
curl -v --http2-prior-knowledge http://127.0.0.1:8888
* Trying 127.0.0.1:8888...
* TCP_NODELAY set
* Connected to 127.0.0.1 (127.0.0.1) port 8888 (#0)
* Using HTTP2, server supports multi-use
* Connection state changed (HTTP/2 confirmed)
* Copying HTTP/2 data in stream buffer to connection buffer after upgrade: len=0
* Using Stream ID: 1 (easy handle 0x16887f0)
> GET / HTTP/2
> Host: 127.0.0.1:8888
> User-Agent: curl/7.66.0
> Accept: */*
>
* Connection state changed (MAX_CONCURRENT_STREAMS == 4294967295)!
* stopped the pause stream!
* Connection #0 to host 127.0.0.1 left intact
curl: (16) Error in the HTTP2 framing layer
... таким образом: Какой ответ мне нужно отправить клиенту, инициирующему соединение http2, чтобы он понизил соединение до HTTP1.1?
Отправка HTTP/1.1 505 hmmm\n\n
(505 = HTTP-версия не поддерживается ) тоже не помогает ...