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
63 changes: 39 additions & 24 deletions proxy/http2/Http2ConnectionState.cc
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ rcv_data_frame(Http2ConnectionState &cstate, const Http2Frame &frame)

stream->increment_data_length(payload_length - pad_length - nbytes);
if (frame.header().flags & HTTP2_FLAGS_DATA_END_STREAM) {
stream->recv_end_stream = true;
if (!stream->change_state(frame.header().type, frame.header().flags)) {
cstate.send_rst_stream_frame(id, HTTP2_ERROR_STREAM_CLOSED);
return Http2Error(HTTP2_ERROR_CLASS_NONE);
Expand Down Expand Up @@ -222,7 +223,7 @@ rcv_headers_frame(Http2ConnectionState &cstate, const Http2Frame &frame)
uint32_t header_block_fragment_length = payload_length;

if (frame.header().flags & HTTP2_FLAGS_HEADERS_END_STREAM) {
stream->end_stream = true;
stream->recv_end_stream = true;
}

// NOTE: Strip padding if exists
Expand Down Expand Up @@ -966,6 +967,10 @@ Http2ConnectionState::delete_stream(Http2Stream *stream)
}
}

if (stream->get_state() == HTTP2_STREAM_STATE_HALF_CLOSED_LOCAL) {
send_rst_stream_frame(stream->get_id(), HTTP2_ERROR_NO_ERROR);
}

stream_list.remove(stream);
stream->initiating_close();

Expand Down Expand Up @@ -1035,28 +1040,29 @@ Http2ConnectionState::send_data_frames_depends_on_priority()
size_t len = 0;
Http2SendADataFrameResult result = send_a_data_frame(stream, len);

if (result != HTTP2_SEND_A_DATA_FRAME_NO_ERROR) {
// When no stream level window left, deactivate node once and wait window_update frame
dependency_tree->deactivate(node, len);
this_ethread()->schedule_imm_local((Continuation *)this, HTTP2_SESSION_EVENT_XMIT);
return;
switch (result) {
case HTTP2_SEND_A_DATA_FRAME_NO_ERROR: {
// No response body to send
if (len == 0 && !stream->is_body_done()) {
dependency_tree->deactivate(node, len);
} else {
dependency_tree->update(node, len);
}
break;
}

// No response body to send
if (len == 0 && !stream->is_body_done()) {
case HTTP2_SEND_A_DATA_FRAME_DONE: {
dependency_tree->deactivate(node, len);
this_ethread()->schedule_imm_local((Continuation *)this, HTTP2_SESSION_EVENT_XMIT);
return;
delete_stream(stream);
break;
}

if (stream->get_state() == HTTP2_STREAM_STATE_CLOSED) {
default:
// When no stream level window left, deactivate node once and wait window_update frame
dependency_tree->deactivate(node, len);
delete_stream(stream);
} else {
dependency_tree->update(node, len);
break;
}

this_ethread()->schedule_imm_local((Continuation *)this, HTTP2_SESSION_EVENT_XMIT);
return;
}

Http2SendADataFrameResult
Expand Down Expand Up @@ -1115,37 +1121,45 @@ Http2ConnectionState::send_a_data_frame(Http2Stream *stream, size_t &payload_len

stream->update_sent_count(payload_length);

// Change state to 'closed' if its end of DATAs.
// xmit event
SCOPED_MUTEX_LOCK(lock, this->ua_session->mutex, this_ethread());
this->ua_session->handleEvent(HTTP2_SESSION_EVENT_XMIT, &data);

if (flags & HTTP2_FLAGS_DATA_END_STREAM) {
DebugHttp2Stream(ua_session, stream->get_id(), "End of DATA frame");
stream->send_end_stream = true;
// Setting to the same state shouldn't be erroneous
stream->change_state(data.header().type, data.header().flags);
}

// xmit event
SCOPED_MUTEX_LOCK(lock, this->ua_session->mutex, this_ethread());
this->ua_session->handleEvent(HTTP2_SESSION_EVENT_XMIT, &data);
return HTTP2_SEND_A_DATA_FRAME_DONE;
}

return HTTP2_SEND_A_DATA_FRAME_NO_ERROR;
}

void
Http2ConnectionState::send_data_frames(Http2Stream *stream)
{
if (stream->get_state() == HTTP2_STREAM_STATE_CLOSED) {
if (stream->get_state() == HTTP2_STREAM_STATE_CLOSED || stream->get_state() == HTTP2_STREAM_STATE_HALF_CLOSED_LOCAL) {
return;
}

size_t len = 0;
while (send_a_data_frame(stream, len) == HTTP2_SEND_A_DATA_FRAME_NO_ERROR) {
if (stream->get_state() == HTTP2_STREAM_STATE_CLOSED) {
while (true) {
Http2SendADataFrameResult result = send_a_data_frame(stream, len);

if (result == HTTP2_SEND_A_DATA_FRAME_DONE) {
// Delete a stream immediately
// TODO its should not be deleted for a several time to handling
// RST_STREAM and WINDOW_UPDATE.
// See 'closed' state written at [RFC 7540] 5.1.
DebugSsn(this->ua_session, "http2_cs", "Shutdown stream %d", stream->get_id());
this->delete_stream(stream);
break;
} else if (result == HTTP2_SEND_A_DATA_FRAME_NO_ERROR) {
continue;
} else {
break;
}
}
}
Expand Down Expand Up @@ -1186,6 +1200,7 @@ Http2ConnectionState::send_headers_frame(Http2Stream *stream)
flags |= HTTP2_FLAGS_HEADERS_END_HEADERS;
if (h2_hdr.presence(MIME_PRESENCE_CONTENT_LENGTH) && h2_hdr.get_content_length() == 0) {
flags |= HTTP2_FLAGS_HEADERS_END_STREAM;
stream->send_end_stream = true;
}
} else {
payload_length = BUFFER_SIZE_FOR_INDEX(buffer_size_index[HTTP2_FRAME_TYPE_HEADERS]);
Expand Down
1 change: 1 addition & 0 deletions proxy/http2/Http2ConnectionState.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ enum Http2SendADataFrameResult {
HTTP2_SEND_A_DATA_FRAME_NO_ERROR = 0,
HTTP2_SEND_A_DATA_FRAME_NO_WINDOW = 1,
HTTP2_SEND_A_DATA_FRAME_NO_PAYLOAD = 2,
HTTP2_SEND_A_DATA_FRAME_DONE = 3,
};

class Http2ConnectionSettings
Expand Down
44 changes: 32 additions & 12 deletions proxy/http2/Http2Stream.cc
Original file line number Diff line number Diff line change
Expand Up @@ -173,14 +173,18 @@ Http2Stream::change_state(uint8_t type, uint8_t flags)
switch (_state) {
case HTTP2_STREAM_STATE_IDLE:
if (type == HTTP2_FRAME_TYPE_HEADERS) {
if (end_stream && flags & HTTP2_FLAGS_HEADERS_END_HEADERS) {
if (recv_end_stream) {
_state = HTTP2_STREAM_STATE_HALF_CLOSED_REMOTE;
} else if (send_end_stream) {
_state = HTTP2_STREAM_STATE_HALF_CLOSED_LOCAL;
} else {
_state = HTTP2_STREAM_STATE_OPEN;
}
} else if (type == HTTP2_FRAME_TYPE_CONTINUATION) {
if (end_stream && flags & HTTP2_FLAGS_CONTINUATION_END_HEADERS) {
if (recv_end_stream) {
_state = HTTP2_STREAM_STATE_HALF_CLOSED_REMOTE;
} else if (send_end_stream) {
_state = HTTP2_STREAM_STATE_HALF_CLOSED_LOCAL;
} else {
_state = HTTP2_STREAM_STATE_OPEN;
}
Expand All @@ -194,46 +198,63 @@ Http2Stream::change_state(uint8_t type, uint8_t flags)
case HTTP2_STREAM_STATE_OPEN:
if (type == HTTP2_FRAME_TYPE_RST_STREAM) {
_state = HTTP2_STREAM_STATE_CLOSED;
} else if (type == HTTP2_FRAME_TYPE_DATA && flags & HTTP2_FLAGS_DATA_END_STREAM) {
_state = HTTP2_STREAM_STATE_HALF_CLOSED_REMOTE;
} else if (type == HTTP2_FRAME_TYPE_DATA) {
if (recv_end_stream) {
_state = HTTP2_STREAM_STATE_HALF_CLOSED_REMOTE;
} else if (send_end_stream) {
_state = HTTP2_STREAM_STATE_HALF_CLOSED_LOCAL;
} else {
// Do not change state
}
} else {
// Currently ATS supports only HTTP/2 server features
return false;
// A stream in the "open" state may be used by both peers to send frames of any type.
return true;
}
break;

case HTTP2_STREAM_STATE_RESERVED_LOCAL:
// Currently ATS supports only HTTP/2 server features
ink_assert(false);
return false;

case HTTP2_STREAM_STATE_RESERVED_REMOTE:
// XXX Server Push have been supported yet.
ink_assert(false);
return false;

case HTTP2_STREAM_STATE_HALF_CLOSED_LOCAL:
// Currently ATS supports only HTTP/2 server features
return false;
if (type == HTTP2_FRAME_TYPE_RST_STREAM || recv_end_stream) {
_state = HTTP2_STREAM_STATE_CLOSED;
} else {
// Error, set state closed
_state = HTTP2_STREAM_STATE_CLOSED;
return false;
}
break;

case HTTP2_STREAM_STATE_HALF_CLOSED_REMOTE:
if (type == HTTP2_FRAME_TYPE_RST_STREAM || (type == HTTP2_FRAME_TYPE_HEADERS && flags & HTTP2_FLAGS_HEADERS_END_STREAM) ||
(type == HTTP2_FRAME_TYPE_DATA && flags & HTTP2_FLAGS_DATA_END_STREAM)) {
if (type == HTTP2_FRAME_TYPE_RST_STREAM || send_end_stream) {
_state = HTTP2_STREAM_STATE_CLOSED;
} else if (type == HTTP2_FRAME_TYPE_HEADERS) { // w/o END_STREAM flag
// No state change here. Expect a following DATA frame with END_STREAM flag.
return true;
} else {
// Error, set state closed
_state = HTTP2_STREAM_STATE_CLOSED;
return false;
}
break;

case HTTP2_STREAM_STATE_CLOSED:
// No state changing
return false;
return true;

default:
return false;
}

Debug("http2_stream", "%s", Http2DebugNames::get_state_name(_state));

return true;
}

Expand Down Expand Up @@ -740,7 +761,6 @@ Http2Stream::cancel_inactivity_timeout()
{
set_inactivity_timeout(0);
}

void
Http2Stream::clear_inactive_timer()
{
Expand Down
6 changes: 4 additions & 2 deletions proxy/http2/Http2Stream.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,8 @@ class Http2Stream : public ProxyClientTransaction
header_blocks(NULL),
header_blocks_length(0),
request_header_length(0),
end_stream(false),
recv_end_stream(false),
send_end_stream(false),
sent_request_header(false),
response_header_done(false),
request_sent(false),
Expand Down Expand Up @@ -190,7 +191,8 @@ class Http2Stream : public ProxyClientTransaction
// Padding or other fields)
uint32_t request_header_length; // total length of payload (include Padding
// and other fields)
bool end_stream;
bool recv_end_stream;
bool send_end_stream;

bool sent_request_header;
bool response_header_done;
Expand Down