#include #include #include #include #include #include namespace vanetza { namespace rpc { namespace { class KjBufferSequence { public: using value_type = boost::asio::const_buffer; class const_iterator { public: using value_type = boost::asio::const_buffer; using difference_type = std::ptrdiff_t; using pointer = const value_type*; using reference = value_type; using iterator_category = std::forward_iterator_tag; const_iterator() = default; explicit const_iterator(const kj::ArrayPtr* ptr) : ptr_(ptr) {} value_type operator*() const { return boost::asio::const_buffer(ptr_->begin(), ptr_->size()); } const_iterator& operator++() { ++ptr_; return *this; } const_iterator operator++(int) { auto tmp = *this; ++ptr_; return tmp; } bool operator==(const const_iterator& other) const { return ptr_ == other.ptr_; } bool operator!=(const const_iterator& other) const { return ptr_ != other.ptr_; } private: const kj::ArrayPtr* ptr_ = nullptr; }; explicit KjBufferSequence(kj::ArrayPtr> pieces) : pieces_(pieces) {} const_iterator begin() const { return const_iterator(pieces_.begin()); } const_iterator end() const { return const_iterator(pieces_.end()); } private: kj::ArrayPtr> pieces_; }; } // namespace AsioStream::AsioStream(boost::asio::ip::tcp::socket socket) : socket_(std::move(socket)) { } void AsioStream::shutdownWrite() { socket_.shutdown(boost::asio::ip::tcp::socket::shutdown_send); } kj::Promise AsioStream::write(const void* buffer, size_t size) { auto paf = kj::newPromiseAndFulfiller(); boost::asio::const_buffer buf(buffer, size); boost::asio::async_write(socket_, buf, [this, fulfiller = std::move(paf.fulfiller)]( const boost::system::error_code& ec, std::size_t bytes_transferred) mutable { mark_unused(bytes_transferred); if (ec) { signalDisconnect(ec); fulfiller->reject(KJ_EXCEPTION(FAILED, "write", ec.message())); } else { fulfiller->fulfill(); } }); return kj::mv(paf.promise); } kj::Promise AsioStream::write(kj::ArrayPtr> pieces) { auto paf = kj::newPromiseAndFulfiller(); boost::asio::async_write(socket_, KjBufferSequence(pieces), [this, fulfiller = std::move(paf.fulfiller)]( const boost::system::error_code& ec, std::size_t bytes_transferred) mutable { mark_unused(bytes_transferred); if (ec) { signalDisconnect(ec); fulfiller->reject(KJ_EXCEPTION(FAILED, "write", ec.message())); } else { fulfiller->fulfill(); } }); return kj::mv(paf.promise); } kj::Promise AsioStream::whenWriteDisconnected() { if (!socket_.is_open()) { return kj::READY_NOW; } KJ_IF_MAYBE(p, disconnect_promise_) { return p->addBranch(); } else { auto paf = kj::newPromiseAndFulfiller(); disconnect_fulfiller_ = kj::mv(paf.fulfiller); auto fork = paf.promise.fork(); auto result = fork.addBranch(); disconnect_promise_ = kj::mv(fork); return kj::mv(result); } } void AsioStream::signalDisconnect(const boost::system::error_code& ec) { if (ec != boost::asio::error::operation_aborted) { boost::system::error_code ignored; socket_.close(ignored); KJ_IF_MAYBE(f, disconnect_fulfiller_) { (*f)->fulfill(); disconnect_fulfiller_ = nullptr; } } } kj::Promise AsioStream::tryRead(void* buffer, size_t minBytes, size_t maxBytes) { auto paf = kj::newPromiseAndFulfiller(); boost::asio::async_read(socket_, boost::asio::buffer(buffer, maxBytes), boost::asio::transfer_at_least(minBytes), [this, fulfiller = std::move(paf.fulfiller)]( const boost::system::error_code& ec, std::size_t bytes_transferred) mutable { if (ec) { signalDisconnect(ec); fulfiller->reject(KJ_EXCEPTION(FAILED, "read", ec.message())); } else { fulfiller->fulfill(kj::mv(bytes_transferred)); } }); return kj::mv(paf.promise); } } // namespace rpc } // namespace vanetza