| #include "http2/adapter/oghttp2_adapter.h" |
| |
| #include <list> |
| |
| #include "absl/memory/memory.h" |
| #include "http2/adapter/window_manager.h" |
| #include "http2/platform/api/http2_string_utils.h" |
| #include "spdy/core/spdy_framer.h" |
| #include "spdy/core/spdy_protocol.h" |
| |
| namespace http2 { |
| namespace adapter { |
| |
| namespace { |
| |
| using spdy::SpdyFrameIR; |
| using spdy::SpdyGoAwayIR; |
| using spdy::SpdyPingIR; |
| using spdy::SpdyPriorityIR; |
| using spdy::SpdySettingsIR; |
| using spdy::SpdyWindowUpdateIR; |
| |
| spdy::SpdyErrorCode TranslateErrorCode(Http2ErrorCode code) { |
| switch (code) { |
| case Http2ErrorCode::NO_ERROR: |
| return spdy::ERROR_CODE_NO_ERROR; |
| case Http2ErrorCode::PROTOCOL_ERROR: |
| return spdy::ERROR_CODE_PROTOCOL_ERROR; |
| case Http2ErrorCode::INTERNAL_ERROR: |
| return spdy::ERROR_CODE_INTERNAL_ERROR; |
| case Http2ErrorCode::FLOW_CONTROL_ERROR: |
| return spdy::ERROR_CODE_FLOW_CONTROL_ERROR; |
| case Http2ErrorCode::SETTINGS_TIMEOUT: |
| return spdy::ERROR_CODE_SETTINGS_TIMEOUT; |
| case Http2ErrorCode::STREAM_CLOSED: |
| return spdy::ERROR_CODE_STREAM_CLOSED; |
| case Http2ErrorCode::FRAME_SIZE_ERROR: |
| return spdy::ERROR_CODE_FRAME_SIZE_ERROR; |
| case Http2ErrorCode::REFUSED_STREAM: |
| return spdy::ERROR_CODE_REFUSED_STREAM; |
| case Http2ErrorCode::CANCEL: |
| return spdy::ERROR_CODE_CANCEL; |
| case Http2ErrorCode::COMPRESSION_ERROR: |
| return spdy::ERROR_CODE_COMPRESSION_ERROR; |
| case Http2ErrorCode::CONNECT_ERROR: |
| return spdy::ERROR_CODE_CONNECT_ERROR; |
| case Http2ErrorCode::ENHANCE_YOUR_CALM: |
| return spdy::ERROR_CODE_ENHANCE_YOUR_CALM; |
| case Http2ErrorCode::INADEQUATE_SECURITY: |
| return spdy::ERROR_CODE_INADEQUATE_SECURITY; |
| case Http2ErrorCode::HTTP_1_1_REQUIRED: |
| return spdy::ERROR_CODE_HTTP_1_1_REQUIRED; |
| } |
| } |
| |
| } // namespace |
| |
| struct StreamState { |
| WindowManager window_manager; |
| }; |
| |
| class OgHttp2Adapter::OgHttp2Session : public Http2Session { |
| public: |
| OgHttp2Session(Http2VisitorInterface& /*visitor*/, Options /*options*/) {} |
| ~OgHttp2Session() override {} |
| |
| ssize_t ProcessBytes(absl::string_view bytes) override { |
| SPDY_BUG(oghttp2_process_bytes) << "Not implemented"; |
| return 0; |
| } |
| |
| int Consume(Http2StreamId stream_id, size_t num_bytes) override { |
| auto it = stream_map_.find(stream_id); |
| if (it == stream_map_.end()) { |
| // TODO(b/181586191): LOG_ERROR rather than SPDY_BUG. |
| SPDY_BUG(stream_consume_notfound) |
| << "Stream " << stream_id << " not found"; |
| } else { |
| it->second.window_manager.MarkDataFlushed(num_bytes); |
| } |
| return 0; // Remove? |
| } |
| |
| bool want_read() const override { return false; } |
| bool want_write() const override { |
| return !frames_.empty() || !serialized_prefix_.empty(); |
| } |
| int GetRemoteWindowSize() const override { |
| SPDY_BUG(peer_window_not_updated) << "Not implemented"; |
| return peer_window_; |
| } |
| |
| void EnqueueFrame(std::unique_ptr<spdy::SpdyFrameIR> frame) { |
| frames_.push_back(std::move(frame)); |
| } |
| |
| std::string GetBytesToWrite(absl::optional<size_t> max_bytes) { |
| const size_t serialized_max = |
| max_bytes ? max_bytes.value() : std::numeric_limits<size_t>::max(); |
| std::string serialized = std::move(serialized_prefix_); |
| while (serialized.size() < serialized_max && !frames_.empty()) { |
| spdy::SpdySerializedFrame frame = |
| framer_.SerializeFrame(*frames_.front()); |
| Http2StrAppend(&serialized, absl::string_view(frame)); |
| frames_.pop_front(); |
| } |
| if (serialized.size() > serialized_max) { |
| serialized_prefix_ = serialized.substr(serialized_max); |
| serialized.resize(serialized_max); |
| } |
| return serialized; |
| } |
| |
| private: |
| spdy::SpdyFramer framer_{spdy::SpdyFramer::ENABLE_COMPRESSION}; |
| absl::flat_hash_map<Http2StreamId, StreamState> stream_map_; |
| std::list<std::unique_ptr<SpdyFrameIR>> frames_; |
| std::string serialized_prefix_; |
| int peer_window_ = 65535; |
| }; |
| |
| /* static */ |
| std::unique_ptr<OgHttp2Adapter> OgHttp2Adapter::Create( |
| Http2VisitorInterface& visitor, |
| Options options) { |
| // Using `new` to access a non-public constructor. |
| return absl::WrapUnique(new OgHttp2Adapter(visitor, std::move(options))); |
| } |
| |
| OgHttp2Adapter::~OgHttp2Adapter() {} |
| |
| ssize_t OgHttp2Adapter::ProcessBytes(absl::string_view bytes) { |
| return session_->ProcessBytes(bytes); |
| } |
| |
| void OgHttp2Adapter::SubmitSettings(absl::Span<const Http2Setting> settings) { |
| auto settings_ir = absl::make_unique<SpdySettingsIR>(); |
| for (const Http2Setting& setting : settings) { |
| settings_ir->AddSetting(setting.id, setting.value); |
| } |
| session_->EnqueueFrame(std::move(settings_ir)); |
| } |
| |
| void OgHttp2Adapter::SubmitPriorityForStream(Http2StreamId stream_id, |
| Http2StreamId parent_stream_id, |
| int weight, |
| bool exclusive) { |
| session_->EnqueueFrame(absl::make_unique<SpdyPriorityIR>( |
| stream_id, parent_stream_id, weight, exclusive)); |
| } |
| |
| void OgHttp2Adapter::SubmitPing(Http2PingId ping_id) { |
| session_->EnqueueFrame(absl::make_unique<SpdyPingIR>(ping_id)); |
| } |
| |
| void OgHttp2Adapter::SubmitGoAway(Http2StreamId last_accepted_stream_id, |
| Http2ErrorCode error_code, |
| absl::string_view opaque_data) { |
| session_->EnqueueFrame(absl::make_unique<SpdyGoAwayIR>( |
| last_accepted_stream_id, TranslateErrorCode(error_code), |
| std::string(opaque_data))); |
| } |
| void OgHttp2Adapter::SubmitWindowUpdate(Http2StreamId stream_id, |
| int window_increment) { |
| session_->EnqueueFrame( |
| absl::make_unique<SpdyWindowUpdateIR>(stream_id, window_increment)); |
| } |
| |
| void OgHttp2Adapter::SubmitMetadata(Http2StreamId stream_id, bool fin) { |
| SPDY_BUG(oghttp2_submit_metadata) << "Not implemented"; |
| } |
| |
| std::string OgHttp2Adapter::GetBytesToWrite(absl::optional<size_t> max_bytes) { |
| return session_->GetBytesToWrite(max_bytes); |
| } |
| |
| int OgHttp2Adapter::GetPeerConnectionWindow() const { |
| return session_->GetRemoteWindowSize(); |
| } |
| |
| void OgHttp2Adapter::MarkDataConsumedForStream(Http2StreamId stream_id, |
| size_t num_bytes) { |
| session_->Consume(stream_id, num_bytes); |
| } |
| |
| const Http2Session& OgHttp2Adapter::session() const { |
| return *session_; |
| } |
| |
| OgHttp2Adapter::OgHttp2Adapter(Http2VisitorInterface& visitor, Options options) |
| : Http2Adapter(visitor), |
| session_(absl::make_unique<OgHttp2Session>(visitor, std::move(options))) { |
| } |
| |
| } // namespace adapter |
| } // namespace http2 |