Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix HTTP2 max_frame_size error #4394

Merged
merged 1 commit into from
Sep 7, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 8 additions & 7 deletions ext-src/php_swoole_http.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@
#pragma once

#include "swoole_http.h"
#ifdef SW_USE_HTTP2
#include "swoole_http2.h"
#endif
#include "thirdparty/swoole_http_parser.h"
#include "thirdparty/multipart_parser.h"

Expand Down Expand Up @@ -208,8 +211,8 @@ class Stream {
// uint8_t priority; // useless now
uint32_t id;
// flow control
uint32_t send_window;
uint32_t recv_window;
uint32_t remote_window_size;
uint32_t local_window_size;
Coroutine *waiting_coroutine = nullptr;

Stream(Session *client, uint32_t _id);
Expand All @@ -230,11 +233,9 @@ class Session {
nghttp2_hd_inflater *inflater = nullptr;
nghttp2_hd_deflater *deflater = nullptr;

uint32_t header_table_size;
uint32_t send_window;
uint32_t recv_window;
uint32_t max_concurrent_streams;
uint32_t max_frame_size;
http2::Settings local_settings = {};
http2::Settings remote_settings = {};

uint32_t last_stream_id;
bool shutting_down;
bool is_coro;
Expand Down
16 changes: 7 additions & 9 deletions ext-src/swoole_http2_client_coro.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1068,13 +1068,11 @@ ssize_t Client::build_header(zval *zobject, zval *zrequest, char *buffer) {
}

size_t buflen = nghttp2_hd_deflate_bound(h2c->deflater, headers.get(), headers.len());
/*
if (buflen > h2c->remote_settings.max_header_list_size)
{
php_swoole_error(E_WARNING, "header cannot bigger than remote max_header_list_size %u",
h2c->remote_settings.max_header_list_size); return -1;
}
*/
// if (buflen > h2c->remote_settings.max_header_list_size) {
// php_swoole_error(E_WARNING, "header cannot bigger than remote max_header_list_size %u",
// h2c->remote_settings.max_header_list_size);
// return -1;
// }
ssize_t rv = nghttp2_hd_deflate_hd(h2c->deflater, (uchar *) buffer, buflen, headers.get(), headers.len());
if (rv < 0) {
h2c->nghttp2_error(rv, "nghttp2_hd_deflate_hd() failed");
Expand Down Expand Up @@ -1125,8 +1123,8 @@ bool Client::send_data(uint32_t stream_id, const char *p, size_t len, int flag)
uint32_t send_len;
char header[SW_HTTP2_FRAME_HEADER_SIZE];
while (len > 0) {
if (len > remote_settings.max_frame_size) {
send_len = remote_settings.max_frame_size;
if (len > local_settings.max_frame_size) {
send_len = local_settings.max_frame_size;
send_flag = 0;
} else {
send_len = len;
Expand Down
90 changes: 46 additions & 44 deletions ext-src/swoole_http2_server.cc
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,8 @@ Http2Stream::Stream(Http2Session *client, uint32_t _id) {
ctx->stream = this;
ctx->keepalive = true;
id = _id;
send_window = SW_HTTP2_DEFAULT_WINDOW_SIZE;
recv_window = SW_HTTP2_DEFAULT_WINDOW_SIZE;
local_window_size = SW_HTTP2_DEFAULT_WINDOW_SIZE;
remote_window_size = SW_HTTP2_DEFAULT_WINDOW_SIZE;
}

Http2Stream::~Stream() {
Expand All @@ -66,11 +66,9 @@ void Http2Stream::reset(uint32_t error_code) {

Http2Session::Session(SessionId _fd) {
fd = _fd;
header_table_size = SW_HTTP2_DEFAULT_HEADER_TABLE_SIZE;
send_window = SW_HTTP2_DEFAULT_WINDOW_SIZE;
recv_window = SW_HTTP2_DEFAULT_WINDOW_SIZE;
max_concurrent_streams = SW_HTTP2_MAX_MAX_CONCURRENT_STREAMS;
max_frame_size = SW_HTTP2_MAX_MAX_FRAME_SIZE;
Http2::init_settings(&local_settings);
// [init]: we must set default value, peer is not always send all the settings
Http2::init_settings(&remote_settings);
last_stream_id = 0;
shutting_down = false;
is_coro = false;
Expand Down Expand Up @@ -350,7 +348,7 @@ static ssize_t http2_build_header(HttpContext *ctx, uchar *buffer, size_t body_l
Http2Session *client = http2_sessions[ctx->fd];
nghttp2_hd_deflater *deflater = client->deflater;
if (!deflater) {
ret = nghttp2_hd_deflate_new2(&deflater, client->header_table_size, php_nghttp2_mem());
ret = nghttp2_hd_deflate_new2(&deflater, client->local_settings.header_table_size, php_nghttp2_mem());
if (ret != 0) {
swoole_warning("nghttp2_hd_deflate_new2() failed with error: %s", nghttp2_strerror(ret));
return -1;
Expand Down Expand Up @@ -531,10 +529,10 @@ static bool swoole_http2_server_respond(HttpContext *ctx, String *body) {

// If send_yield is not supported, ignore flow control
if (ctx->co_socket || !((Server *) ctx->private_data)->send_yield || !swoole_coroutine_is_in()) {
if (body->length > client->send_window) {
swoole_warning("The data sent exceeded send_window");
if (body->length > client->remote_settings.window_size) {
swoole_warning("The data sent exceeded remote_window_size");
}
if (!stream->send_body(body, end_stream, client->max_frame_size)) {
if (!stream->send_body(body, end_stream, client->local_settings.max_frame_size)) {
error = true;
}
} else {
Expand All @@ -545,31 +543,31 @@ static bool swoole_http2_server_respond(HttpContext *ctx, String *body) {
break;
}

if (stream->send_window == 0) {
if (stream->remote_window_size == 0) {
stream->waiting_coroutine = Coroutine::get_current();
stream->waiting_coroutine->yield();
stream->waiting_coroutine = nullptr;
continue;
}

bool _end_stream;
if (send_len > stream->send_window) {
send_len = stream->send_window;
if (send_len > stream->remote_window_size) {
send_len = stream->remote_window_size;
_end_stream = false;
} else {
_end_stream = true && end_stream;
}

error = !stream->send_body(body, _end_stream, client->max_frame_size, body->offset, send_len);
error = !stream->send_body(body, _end_stream, client->local_settings.max_frame_size, body->offset, send_len);
if (!error) {
swoole_trace_log(
SW_TRACE_HTTP2, "body: send length=%zu, stream->send_window=%u", send_len, stream->send_window);
SW_TRACE_HTTP2, "body: send length=%zu, stream->remote_window_size=%u", send_len, stream->remote_window_size);

body->offset += send_len;
if (send_len > stream->send_window) {
stream->send_window = 0;
if (send_len > stream->remote_window_size) {
stream->remote_window_size = 0;
} else {
stream->send_window -= send_len;
stream->remote_window_size -= send_len;
}
}
}
Expand Down Expand Up @@ -642,10 +640,10 @@ static bool http2_context_sendfile(HttpContext *ctx, const char *file, uint32_t
bool error = false;

if (body->length > 0) {
if (!stream->send_body(body.get(), end_stream, client->max_frame_size, offset, length)) {
if (!stream->send_body(body.get(), end_stream, client->local_settings.max_frame_size, offset, length)) {
error = true;
} else {
client->send_window -= length; // TODO: flow control?
client->remote_settings.window_size -= length; // TODO: flow control?
}
}

Expand Down Expand Up @@ -825,8 +823,8 @@ int swoole_http2_server_parse(Http2Session *client, const char *buf) {
swoole_http2_frame_trace_log(recv, "id=%d, value=%d", id, value);
switch (id) {
case SW_HTTP2_SETTING_HEADER_TABLE_SIZE:
if (value != client->header_table_size) {
client->header_table_size = value;
if (value != client->remote_settings.header_table_size) {
client->remote_settings.header_table_size = value;
if (client->deflater) {
int ret = nghttp2_hd_deflate_change_table_size(client->deflater, value);
if (ret != 0) {
Expand All @@ -840,19 +838,19 @@ int swoole_http2_server_parse(Http2Session *client, const char *buf) {
swoole_trace_log(SW_TRACE_HTTP2, "setting: header_table_size=%u", value);
break;
case SW_HTTP2_SETTINGS_MAX_CONCURRENT_STREAMS:
client->max_concurrent_streams = value;
client->remote_settings.max_concurrent_streams = value;
swoole_trace_log(SW_TRACE_HTTP2, "setting: max_concurrent_streams=%u", value);
break;
case SW_HTTP2_SETTINGS_INIT_WINDOW_SIZE:
client->send_window = value;
swoole_trace_log(SW_TRACE_HTTP2, "setting: init_send_window=%u", value);
client->remote_settings.window_size = value;
swoole_trace_log(SW_TRACE_HTTP2, "setting: init_window_size=%u", value);
break;
case SW_HTTP2_SETTINGS_MAX_FRAME_SIZE:
client->max_frame_size = value;
client->remote_settings.max_frame_size = value;
swoole_trace_log(SW_TRACE_HTTP2, "setting: max_frame_size=%u", value);
break;
case SW_HTTP2_SETTINGS_MAX_HEADER_LIST_SIZE:
// client->max_header_list_size = value; // useless now
client->remote_settings.max_header_list_size = value; // useless now
swoole_trace_log(SW_TRACE_HTTP2, "setting: max_header_list_size=%u", value);
break;
default:
Expand Down Expand Up @@ -915,17 +913,17 @@ int swoole_http2_server_parse(Http2Session *client, const char *buf) {
buffer->append(buf, length);

// flow control
client->recv_window -= length;
stream->recv_window -= length;
client->local_settings.window_size -= length;
stream->local_window_size -= length;

if (length > 0) {
if (client->recv_window < (SW_HTTP2_MAX_WINDOW_SIZE / 4)) {
http2_server_send_window_update(ctx, 0, SW_HTTP2_MAX_WINDOW_SIZE - client->recv_window);
client->recv_window = SW_HTTP2_MAX_WINDOW_SIZE;
if (client->local_settings.window_size < (SW_HTTP2_MAX_WINDOW_SIZE / 4)) {
http2_server_send_window_update(ctx, 0, SW_HTTP2_MAX_WINDOW_SIZE - client->local_settings.window_size);
client->local_settings.window_size = SW_HTTP2_MAX_WINDOW_SIZE;
}
if (stream->recv_window < (SW_HTTP2_MAX_WINDOW_SIZE / 4)) {
http2_server_send_window_update(ctx, stream_id, SW_HTTP2_MAX_WINDOW_SIZE - stream->recv_window);
stream->recv_window = SW_HTTP2_MAX_WINDOW_SIZE;
if (stream->local_window_size < (SW_HTTP2_MAX_WINDOW_SIZE / 4)) {
http2_server_send_window_update(ctx, stream_id, SW_HTTP2_MAX_WINDOW_SIZE - stream->local_window_size);
stream->local_window_size = SW_HTTP2_MAX_WINDOW_SIZE;
}
}

Expand Down Expand Up @@ -967,14 +965,18 @@ int swoole_http2_server_parse(Http2Session *client, const char *buf) {
}
case SW_HTTP2_TYPE_WINDOW_UPDATE: {
value = ntohl(*(uint32_t *) buf);
client->send_window += value;
if (stream_id > 0 && !client->is_coro && client->streams.find(stream_id) != client->streams.end()) {
stream = client->streams[stream_id];
Server *serv = (Server *) stream->ctx->private_data;

stream->send_window += value;
if (serv->send_yield && stream->waiting_coroutine) {
stream->waiting_coroutine->resume();
if (stream_id == 0) {
client->remote_settings.window_size += value;
} else {
if (client->streams.find(stream_id) != client->streams.end()) {
stream = client->streams[stream_id];
stream->remote_window_size += value;
if (!client->is_coro) {
Server *serv = (Server *) stream->ctx->private_data;
if (serv->send_yield && stream->waiting_coroutine) {
stream->waiting_coroutine->resume();
}
}
}
}
swoole_http2_frame_trace_log(recv, "window_size_increment=%d", value);
Expand Down