This is an automated email from the ASF dual-hosted git repository. maskit pushed a commit to branch quic-latest in repository https://gitbox.apache.org/repos/asf/trafficserver.git
commit 5f46f5c50a8093536fc242da03b82f46c3035fc6 Author: Masakazu Kitajo <[email protected]> AuthorDate: Wed Aug 30 11:38:47 2017 +0900 Update stream state after sending / receiving frames --- iocore/net/quic/QUICFrame.cc | 20 ++++- iocore/net/quic/QUICFrame.h | 12 ++- iocore/net/quic/QUICStream.cc | 2 +- iocore/net/quic/QUICStreamState.cc | 126 +++++++++++++++++++++++++-- iocore/net/quic/test/test_QUICStreamState.cc | 119 ++++++++++++++++++++++--- 5 files changed, 256 insertions(+), 23 deletions(-) diff --git a/iocore/net/quic/QUICFrame.cc b/iocore/net/quic/QUICFrame.cc index f0c0b5b..e99eda6 100644 --- a/iocore/net/quic/QUICFrame.cc +++ b/iocore/net/quic/QUICFrame.cc @@ -70,12 +70,13 @@ QUICFrame::reset(const uint8_t *buf, size_t len) // STREAM Frame // -QUICStreamFrame::QUICStreamFrame(const uint8_t *data, size_t data_len, QUICStreamId stream_id, QUICOffset offset) +QUICStreamFrame::QUICStreamFrame(const uint8_t *data, size_t data_len, QUICStreamId stream_id, QUICOffset offset, bool last) { this->_data = data; this->_data_len = data_len; this->_stream_id = stream_id; this->_offset = offset; + this->_fin = last; } QUICFrameType @@ -108,6 +109,11 @@ QUICStreamFrame::store(uint8_t *buf, size_t *len, bool include_length_field) con buf[0] = static_cast<uint8_t>(QUICFrameType::STREAM); *len = 1; + // "F" of "11FSSOOD" + if (this->has_fin_flag()) { + buf[0] += (0x01 << 5); + } + // "SS" of "11FSSOOD" // use 32 bit length for now buf[0] += (0x03 << 3); @@ -1278,10 +1284,10 @@ QUICFrameFactory::fast_create(const uint8_t *buf, size_t len) } std::unique_ptr<QUICStreamFrame, QUICFrameDeleterFunc> -QUICFrameFactory::create_stream_frame(const uint8_t *data, size_t data_len, QUICStreamId stream_id, QUICOffset offset) +QUICFrameFactory::create_stream_frame(const uint8_t *data, size_t data_len, QUICStreamId stream_id, QUICOffset offset, bool last) { QUICStreamFrame *frame = quicStreamFrameAllocator.alloc(); - new (frame) QUICStreamFrame(data, data_len, stream_id, offset); + new (frame) QUICStreamFrame(data, data_len, stream_id, offset, last); return std::unique_ptr<QUICStreamFrame, QUICFrameDeleterFunc>(frame, &QUICFrameDeleter::delete_stream_frame); } @@ -1333,6 +1339,14 @@ QUICFrameFactory::create_stream_blocked_frame(QUICStreamId stream_id) return std::unique_ptr<QUICStreamBlockedFrame, QUICFrameDeleterFunc>(frame, &QUICFrameDeleter::delete_stream_blocked_frame); } +std::unique_ptr<QUICRstStreamFrame, QUICFrameDeleterFunc> +QUICFrameFactory::create_rst_stream_frame(QUICStreamId stream_id, QUICErrorCode error_code, QUICOffset final_offset) +{ + QUICRstStreamFrame *frame = quicRstStreamFrameAllocator.alloc(); + new (frame) QUICRstStreamFrame(error_code, stream_id, final_offset); + return std::unique_ptr<QUICRstStreamFrame, QUICFrameDeleterFunc>(frame, &QUICFrameDeleter::delete_rst_stream_frame); +} + std::unique_ptr<QUICRetransmissionFrame, QUICFrameDeleterFunc> QUICFrameFactory::create_retransmission_frame(std::unique_ptr<QUICFrame, QUICFrameDeleterFunc> original_frame, const QUICPacket &original_packet) diff --git a/iocore/net/quic/QUICFrame.h b/iocore/net/quic/QUICFrame.h index 11fcf03..0272a82 100644 --- a/iocore/net/quic/QUICFrame.h +++ b/iocore/net/quic/QUICFrame.h @@ -58,7 +58,7 @@ class QUICStreamFrame : public QUICFrame public: QUICStreamFrame() : QUICFrame() {} QUICStreamFrame(const uint8_t *buf, size_t len) : QUICFrame(buf, len) {} - QUICStreamFrame(const uint8_t *buf, size_t len, QUICStreamId streamid, QUICOffset offset); + QUICStreamFrame(const uint8_t *buf, size_t len, QUICStreamId streamid, QUICOffset offset, bool last = false); virtual QUICFrameType type() const override; virtual size_t size() const override; virtual void store(uint8_t *buf, size_t *len) const override; @@ -589,7 +589,8 @@ public: * You have to make sure that the data size won't exceed the maximum size of QUIC packet. */ static std::unique_ptr<QUICStreamFrame, QUICFrameDeleterFunc> create_stream_frame(const uint8_t *data, size_t data_len, - QUICStreamId stream_id, QUICOffset offset); + QUICStreamId stream_id, QUICOffset offset, + bool last = false); /* * Creates a ACK frame. * You shouldn't call this directly but through QUICAckFrameCreator because QUICAckFrameCreator manages packet numbers that we @@ -625,6 +626,13 @@ public: static std::unique_ptr<QUICStreamBlockedFrame, QUICFrameDeleterFunc> create_stream_blocked_frame(QUICStreamId stream_id); /* + * Creates a RST_STREAM frame. + */ + static std::unique_ptr<QUICRstStreamFrame, QUICFrameDeleterFunc> create_rst_stream_frame(QUICStreamId stream_id, + QUICErrorCode error_code, + QUICOffset final_offset); + + /* * Creates a retransmission frame, which is very special. * This retransmission frame will be used only for retransmission and it's not a standard frame type. */ diff --git a/iocore/net/quic/QUICStream.cc b/iocore/net/quic/QUICStream.cc index e77375f..79fd4e8 100644 --- a/iocore/net/quic/QUICStream.cc +++ b/iocore/net/quic/QUICStream.cc @@ -396,7 +396,7 @@ QUICStream::_send() total_len += len; if (!this->_state.is_allowed_to_send(*frame)) { - // FIXME: What should we do? + Debug(tag, "Canceled sending %s frame due to the stream state", QUICDebugNames::frame_type(frame->type())); break; } this->_state.update_with_sent_frame(*frame); diff --git a/iocore/net/quic/QUICStreamState.cc b/iocore/net/quic/QUICStreamState.cc index eb43130..2f78c3c 100644 --- a/iocore/net/quic/QUICStreamState.cc +++ b/iocore/net/quic/QUICStreamState.cc @@ -33,12 +33,52 @@ QUICStreamState::get() const bool QUICStreamState::is_allowed_to_send(const QUICFrame &frame) const { + switch (this->_state) { + case State::idle: + break; + case State::open: + break; + case State::half_closed_local: + if (frame.type() == QUICFrameType::STREAM) { + return false; + } + break; + case State::half_closed_remote: + break; + case State::closed: + // Once a stream reaches this state, no frames can be sent that mention the stream + if (frame.type() == QUICFrameType::STREAM) { + return false; + } else if (frame.type() == QUICFrameType::RST_STREAM) { + return false; + } else if (frame.type() == QUICFrameType::MAX_STREAM_DATA) { + return false; + } + break; + case State::illegal: + return false; + } return true; } bool QUICStreamState::is_allowed_to_receive(const QUICFrame &frame) const { + switch (this->_state) { + case State::idle: + break; + case State::open: + break; + case State::half_closed_local: + break; + case State::half_closed_remote: + break; + case State::closed: + // Reordering might cause frames to be received after closing + break; + case State::illegal: + return false; + } return true; } @@ -47,18 +87,46 @@ QUICStreamState::update_with_received_frame(const QUICFrame &frame) { switch (this->_state) { case State::idle: - this->_set_state(State::open); - // fall through - case State::open: { - if (frame.type() == QUICFrameType::STREAM && dynamic_cast<const QUICStreamFrame &>(frame).has_fin_flag()) { + if (frame.type() == QUICFrameType::STREAM) { + if (static_cast<const QUICStreamFrame &>(frame).has_fin_flag()) { + this->_set_state(State::half_closed_remote); + } else { + this->_set_state(State::open); + } + } else if (frame.type() == QUICFrameType::RST_STREAM) { this->_set_state(State::half_closed_remote); + } else if (frame.type() == QUICFrameType::MAX_STREAM_DATA || frame.type() == QUICFrameType::STREAM_BLOCKED) { + this->_set_state(State::open); + } else { + this->_set_state(State::illegal); + } + break; + case State::open: + if (frame.type() == QUICFrameType::STREAM) { + if (static_cast<const QUICStreamFrame &>(frame).has_fin_flag()) { + this->_set_state(State::half_closed_remote); + } } else if (frame.type() == QUICFrameType::RST_STREAM) { - this->_set_state(State::closed); + this->_set_state(State::half_closed_remote); + } else { + this->_set_state(State::illegal); } - } break; + break; case State::half_closed_local: + if (frame.type() == QUICFrameType::STREAM) { + if (static_cast<const QUICStreamFrame &>(frame).has_fin_flag()) { + this->_set_state(State::closed); + } + } else if (frame.type() == QUICFrameType::RST_STREAM) { + this->_set_state(State::closed); + } else { + this->_set_state(State::illegal); + } + break; case State::half_closed_remote: + break; case State::closed: + break; case State::illegal: // Once we get illegal state, no way to recover it break; @@ -70,6 +138,52 @@ QUICStreamState::update_with_received_frame(const QUICFrame &frame) void QUICStreamState::update_with_sent_frame(const QUICFrame &frame) { + switch (this->_state) { + case State::idle: + if (frame.type() == QUICFrameType::STREAM) { + if (static_cast<const QUICStreamFrame &>(frame).has_fin_flag()) { + this->_set_state(State::half_closed_local); + } else { + this->_set_state(State::open); + } + } else if (frame.type() == QUICFrameType::RST_STREAM) { + this->_set_state(State::half_closed_local); + } else { + this->_set_state(State::illegal); + } + break; + case State::open: + if (frame.type() == QUICFrameType::STREAM) { + if (static_cast<const QUICStreamFrame &>(frame).has_fin_flag()) { + this->_set_state(State::half_closed_local); + } + } else if (frame.type() == QUICFrameType::RST_STREAM) { + this->_set_state(State::half_closed_local); + } else { + this->_set_state(State::illegal); + } + break; + case State::half_closed_local: + break; + case State::half_closed_remote: + if (frame.type() == QUICFrameType::STREAM) { + if (static_cast<const QUICStreamFrame &>(frame).has_fin_flag()) { + this->_set_state(State::closed); + } + } else if (frame.type() == QUICFrameType::RST_STREAM) { + this->_set_state(State::closed); + } else { + this->_set_state(State::illegal); + } + break; + case State::closed: + break; + case State::illegal: + // Once we get illegal state, no way to recover it + break; + default: + break; + } } void diff --git a/iocore/net/quic/test/test_QUICStreamState.cc b/iocore/net/quic/test/test_QUICStreamState.cc index 353cffd..a771f66 100644 --- a/iocore/net/quic/test/test_QUICStreamState.cc +++ b/iocore/net/quic/test/test_QUICStreamState.cc @@ -28,20 +28,117 @@ #include "quic/QUICFrame.h" #include "quic/QUICStreamState.h" -TEST_CASE("QUICStreamState_Update", "[quic]") +TEST_CASE("QUICStreamState_Idle", "[quic]") { - QUICStreamState ss; + auto stream_frame = QUICFrameFactory::create_stream_frame(reinterpret_cast<const uint8_t *>("foo"), 4, 1, 0); + auto rst_stream_frame = QUICFrameFactory::create_rst_stream_frame(0, QUICErrorCode::QUIC_TRANSPORT_ERROR, 0); + auto max_stream_data_frame = QUICFrameFactory::create_max_stream_data_frame(0, 0); + auto stream_blocked_frame = QUICFrameFactory::create_stream_blocked_frame(0); - std::shared_ptr<const QUICStreamFrame> streamFrame = - std::make_shared<const QUICStreamFrame>(reinterpret_cast<const uint8_t *>("foo"), 4, 1, 0); - std::shared_ptr<const QUICRstStreamFrame> rstStreamFrame = - std::make_shared<const QUICRstStreamFrame>(QUICErrorCode::QUIC_TRANSPORT_ERROR, 0, 0); + // Case1. Send STREAM + QUICStreamState ss1; + ss1.update_with_sent_frame(*stream_frame); + CHECK(ss1.get() == QUICStreamState::State::open); - CHECK(ss.get() == QUICStreamState::State::idle); + // Case2. Send RST_STREAM + QUICStreamState ss2; + ss2.update_with_sent_frame(*rst_stream_frame); + CHECK(ss2.get() == QUICStreamState::State::half_closed_local); - ss.update_with_received_frame(*streamFrame); - CHECK(ss.get() == QUICStreamState::State::open); + // Case3. Recv STREAM + QUICStreamState ss3; + ss3.update_with_received_frame(*stream_frame); + CHECK(ss3.get() == QUICStreamState::State::open); - ss.update_with_received_frame(*rstStreamFrame); - CHECK(ss.get() == QUICStreamState::State::closed); + // Case4. Recv RST_STREAM + QUICStreamState ss4; + ss4.update_with_received_frame(*rst_stream_frame); + CHECK(ss4.get() == QUICStreamState::State::half_closed_remote); + + // Case5. Recv MAX_STREAM_DATA + QUICStreamState ss5; + ss5.update_with_received_frame(*max_stream_data_frame); + CHECK(ss5.get() == QUICStreamState::State::open); + + // Case6. Recv STREAM_BLOCKED + QUICStreamState ss6; + ss6.update_with_received_frame(*stream_blocked_frame); + CHECK(ss6.get() == QUICStreamState::State::open); +} + +TEST_CASE("QUICStreamState_Open", "[quic]") +{ + auto stream_frame = QUICFrameFactory::create_stream_frame(reinterpret_cast<const uint8_t *>("foo"), 4, 1, 0); + auto stream_frame_with_fin = QUICFrameFactory::create_stream_frame(reinterpret_cast<const uint8_t *>("bar"), 4, 1, 0, true); + auto rst_stream_frame = QUICFrameFactory::create_rst_stream_frame(0, QUICErrorCode::QUIC_TRANSPORT_ERROR, 0); + + // Case1. Send FIN in a STREAM + QUICStreamState ss1; + ss1.update_with_sent_frame(*stream_frame); // OPEN + CHECK(ss1.get() == QUICStreamState::State::open); + ss1.update_with_sent_frame(*stream_frame_with_fin); + CHECK(ss1.get() == QUICStreamState::State::half_closed_local); + + // Case2. Send RST_STREAM + QUICStreamState ss2; + ss2.update_with_sent_frame(*stream_frame); // OPEN + CHECK(ss2.get() == QUICStreamState::State::open); + ss2.update_with_sent_frame(*rst_stream_frame); + CHECK(ss2.get() == QUICStreamState::State::half_closed_local); + + // Case3. Recv FIN in a STREAM + QUICStreamState ss3; + ss3.update_with_received_frame(*stream_frame); // OPEN + CHECK(ss3.get() == QUICStreamState::State::open); + ss3.update_with_received_frame(*stream_frame_with_fin); + CHECK(ss3.get() == QUICStreamState::State::half_closed_remote); + + // Case4. Recv RST_STREAM + QUICStreamState ss4; + ss4.update_with_received_frame(*stream_frame); // OPEN + CHECK(ss4.get() == QUICStreamState::State::open); + ss4.update_with_received_frame(*rst_stream_frame); + CHECK(ss4.get() == QUICStreamState::State::half_closed_remote); +} + +TEST_CASE("QUICStreamState_Half_Closed_Remote", "[quic]") +{ + auto stream_frame = QUICFrameFactory::create_stream_frame(reinterpret_cast<const uint8_t *>("foo"), 4, 1, 0); + auto stream_frame_with_fin = QUICFrameFactory::create_stream_frame(reinterpret_cast<const uint8_t *>("bar"), 4, 1, 0, true); + auto rst_stream_frame = QUICFrameFactory::create_rst_stream_frame(0, QUICErrorCode::QUIC_TRANSPORT_ERROR, 0); + + // Case1. Send FIN in a STREAM + QUICStreamState ss1; + ss1.update_with_received_frame(*stream_frame_with_fin); // HALF CLOSED REMOTE + CHECK(ss1.get() == QUICStreamState::State::half_closed_remote); + ss1.update_with_sent_frame(*stream_frame_with_fin); + CHECK(ss1.get() == QUICStreamState::State::closed); + + // Case2. Send RST + QUICStreamState ss2; + ss2.update_with_received_frame(*stream_frame_with_fin); // HALF CLOSED REMOTE + CHECK(ss2.get() == QUICStreamState::State::half_closed_remote); + ss2.update_with_sent_frame(*rst_stream_frame); + CHECK(ss2.get() == QUICStreamState::State::closed); +} + +TEST_CASE("QUICStreamState_Half_Closed_Local", "[quic]") +{ + auto stream_frame = QUICFrameFactory::create_stream_frame(reinterpret_cast<const uint8_t *>("foo"), 4, 1, 0); + auto stream_frame_with_fin = QUICFrameFactory::create_stream_frame(reinterpret_cast<const uint8_t *>("bar"), 4, 1, 0, true); + auto rst_stream_frame = QUICFrameFactory::create_rst_stream_frame(0, QUICErrorCode::QUIC_TRANSPORT_ERROR, 0); + + // Case1. Recv FIN in a STREAM + QUICStreamState ss1; + ss1.update_with_sent_frame(*stream_frame_with_fin); // HALF CLOSED LOCAL + CHECK(ss1.get() == QUICStreamState::State::half_closed_local); + ss1.update_with_received_frame(*stream_frame_with_fin); + CHECK(ss1.get() == QUICStreamState::State::closed); + + // Case2. Recv RST + QUICStreamState ss2; + ss2.update_with_sent_frame(*stream_frame_with_fin); // HALF CLOSED LOCAL + CHECK(ss2.get() == QUICStreamState::State::half_closed_local); + ss2.update_with_received_frame(*rst_stream_frame); + CHECK(ss2.get() == QUICStreamState::State::closed); } -- To stop receiving notification emails like this one, please contact "[email protected]" <[email protected]>.
