Skip to content
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
3 changes: 2 additions & 1 deletion proxy/http2/Http2ClientSession.cc
Original file line number Diff line number Diff line change
Expand Up @@ -523,7 +523,8 @@ Http2ClientSession::do_complete_frame_read()
ink_release_assert(this->_read_buffer_reader->read_avail() >= this->current_hdr.length);

Http2Frame frame(this->current_hdr, this->_read_buffer_reader, this->cur_frame_from_early_data);
send_connection_event(&this->connection_state, HTTP2_SESSION_EVENT_RECV, &frame);
connection_state.rcv_frame(&frame);

// Check whether data is read from early data
if (this->read_from_early_data > 0) {
this->read_from_early_data -=
Expand Down
131 changes: 65 additions & 66 deletions proxy/http2/Http2ConnectionState.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1089,6 +1089,71 @@ Http2ConnectionState::destroy()
mutex = nullptr; // magic happens - assigning to nullptr frees the ProxyMutex
}

void
Http2ConnectionState::rcv_frame(const Http2Frame *frame)
{
REMEMBER(NO_EVENT, this->recursion);
const Http2StreamId stream_id = frame->header().streamid;
Http2Error error;

// [RFC 7540] 5.5. Extending HTTP/2
// Implementations MUST discard frames that have unknown or unsupported types.
if (frame->header().type >= HTTP2_FRAME_TYPE_MAX) {
Http2StreamDebug(ua_session, stream_id, "Discard a frame which has unknown type, type=%x", frame->header().type);
return;
}

// We need to be careful here, certain frame types are not safe over 0-rtt, tentative for now.
// DATA: NO
// HEADERS: YES (safe http methods only, can only be checked after parsing the payload).
// PRIORITY: YES
// RST_STREAM: NO
// SETTINGS: YES
// PUSH_PROMISE: NO
// PING: YES
// GOAWAY: NO
// WINDOW_UPDATE: YES
// CONTINUATION: YES (safe http methods only, same as HEADERS frame).
if (frame->is_from_early_data() &&
(frame->header().type == HTTP2_FRAME_TYPE_DATA || frame->header().type == HTTP2_FRAME_TYPE_RST_STREAM ||
frame->header().type == HTTP2_FRAME_TYPE_PUSH_PROMISE || frame->header().type == HTTP2_FRAME_TYPE_GOAWAY)) {
Http2StreamDebug(ua_session, stream_id, "Discard a frame which is received from early data and has type=%x",
frame->header().type);
return;
}

if (frame_handlers[frame->header().type]) {
error = frame_handlers[frame->header().type](*this, *frame);
} else {
error = Http2Error(Http2ErrorClass::HTTP2_ERROR_CLASS_CONNECTION, Http2ErrorCode::HTTP2_ERROR_INTERNAL_ERROR, "no handler");
}

if (error.cls != Http2ErrorClass::HTTP2_ERROR_CLASS_NONE) {
ip_port_text_buffer ipb;
const char *client_ip = ats_ip_ntop(ua_session->get_remote_addr(), ipb, sizeof(ipb));
if (error.cls == Http2ErrorClass::HTTP2_ERROR_CLASS_CONNECTION) {
if (error.msg) {
Error("HTTP/2 connection error code=0x%02x client_ip=%s session_id=%" PRId64 " stream_id=%u %s",
static_cast<int>(error.code), client_ip, ua_session->connection_id(), stream_id, error.msg);
}
this->send_goaway_frame(this->latest_streamid_in, error.code);
this->ua_session->set_half_close_local_flag(true);
if (fini_event == nullptr) {
fini_event = this_ethread()->schedule_imm_local((Continuation *)this, HTTP2_SESSION_EVENT_FINI);
}

// The streams will be cleaned up by the HTTP2_SESSION_EVENT_FINI event
// The Http2ClientSession will shutdown because connection_state.is_state_closed() will be true
} else if (error.cls == Http2ErrorClass::HTTP2_ERROR_CLASS_STREAM) {
if (error.msg) {
Error("HTTP/2 stream error code=0x%02x client_ip=%s session_id=%" PRId64 " stream_id=%u %s", static_cast<int>(error.code),
client_ip, ua_session->connection_id(), stream_id, error.msg);
}
this->send_rst_stream_frame(stream_id, error.code);
}
}
}

int
Http2ConnectionState::main_event_handler(int event, void *edata)
{
Expand Down Expand Up @@ -1146,72 +1211,6 @@ Http2ConnectionState::main_event_handler(int event, void *edata)
_scheduled = false;
} break;

// Parse received HTTP/2 frames
case HTTP2_SESSION_EVENT_RECV: {
REMEMBER(event, this->recursion);
const Http2Frame *frame = static_cast<Http2Frame *>(edata);
const Http2StreamId stream_id = frame->header().streamid;
Http2Error error;

// [RFC 7540] 5.5. Extending HTTP/2
// Implementations MUST discard frames that have unknown or unsupported types.
if (frame->header().type >= HTTP2_FRAME_TYPE_MAX) {
Http2StreamDebug(ua_session, stream_id, "Discard a frame which has unknown type, type=%x", frame->header().type);
break;
}

// We need to be careful here, certain frame types are not safe over 0-rtt, tentative for now.
// DATA: NO
// HEADERS: YES (safe http methods only, can only be checked after parsing the payload).
// PRIORITY: YES
// RST_STREAM: NO
// SETTINGS: YES
// PUSH_PROMISE: NO
// PING: YES
// GOAWAY: NO
// WINDOW_UPDATE: YES
// CONTINUATION: YES (safe http methods only, same as HEADERS frame).
if (frame->is_from_early_data() &&
(frame->header().type == HTTP2_FRAME_TYPE_DATA || frame->header().type == HTTP2_FRAME_TYPE_RST_STREAM ||
frame->header().type == HTTP2_FRAME_TYPE_PUSH_PROMISE || frame->header().type == HTTP2_FRAME_TYPE_GOAWAY)) {
Http2StreamDebug(ua_session, stream_id, "Discard a frame which is received from early data and has type=%x",
frame->header().type);
break;
}

if (frame_handlers[frame->header().type]) {
error = frame_handlers[frame->header().type](*this, *frame);
} else {
error = Http2Error(Http2ErrorClass::HTTP2_ERROR_CLASS_CONNECTION, Http2ErrorCode::HTTP2_ERROR_INTERNAL_ERROR, "no handler");
}

if (error.cls != Http2ErrorClass::HTTP2_ERROR_CLASS_NONE) {
ip_port_text_buffer ipb;
const char *client_ip = ats_ip_ntop(ua_session->get_remote_addr(), ipb, sizeof(ipb));
if (error.cls == Http2ErrorClass::HTTP2_ERROR_CLASS_CONNECTION) {
if (error.msg) {
Error("HTTP/2 connection error code=0x%02x client_ip=%s session_id=%" PRId64 " stream_id=%u %s",
static_cast<int>(error.code), client_ip, ua_session->connection_id(), stream_id, error.msg);
}
this->send_goaway_frame(this->latest_streamid_in, error.code);
this->ua_session->set_half_close_local_flag(true);
if (fini_event == nullptr) {
fini_event = this_ethread()->schedule_imm_local((Continuation *)this, HTTP2_SESSION_EVENT_FINI);
}

// The streams will be cleaned up by the HTTP2_SESSION_EVENT_FINI event
// The Http2ClientSession will shutdown because connection_state.is_state_closed() will be true
} else if (error.cls == Http2ErrorClass::HTTP2_ERROR_CLASS_STREAM) {
if (error.msg) {
Error("HTTP/2 stream error code=0x%02x client_ip=%s session_id=%" PRId64 " stream_id=%u %s", static_cast<int>(error.code),
client_ip, ua_session->connection_id(), stream_id, error.msg);
}
this->send_rst_stream_frame(stream_id, error.code);
}
}

} break;

// Initiate a gracefull shutdown
case HTTP2_SESSION_EVENT_SHUTDOWN_INIT: {
REMEMBER(event, this->recursion);
Expand Down
2 changes: 2 additions & 0 deletions proxy/http2/Http2ConnectionState.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
#include "Http2FrequencyCounter.h"

class Http2ClientSession;
class Http2Frame;

enum class Http2SendDataFrameResult {
NO_ERROR = 0,
Expand Down Expand Up @@ -91,6 +92,7 @@ class Http2ConnectionState : public Continuation

void init();
void destroy();
void rcv_frame(const Http2Frame *frame);

// Event handlers
int main_event_handler(int, void *);
Expand Down