/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ #include "cmConfigure.h" #include "cmServerConnection.h" #include "cm_uv.h" #include "cmServer.h" #include "cmServerDictionary.h" #ifdef _WIN32 # include "io.h" #else # include #endif #include #include cmStdIoConnection::cmStdIoConnection( cmConnectionBufferStrategy* bufferStrategy) : cmEventBasedConnection(bufferStrategy) { } cm::uv_stream_ptr cmStdIoConnection::SetupStream(int file_id) { switch (uv_guess_handle(file_id)) { case UV_TTY: { cm::uv_tty_ptr tty; tty.init(*this->Server->GetLoop(), file_id, file_id == 0, static_cast(this)); uv_tty_set_mode(tty, UV_TTY_MODE_NORMAL); return { std::move(tty) }; } case UV_FILE: if (file_id == 0) { return nullptr; } // Intentional fallthrough; stdin can _not_ be treated as a named // pipe, however stdout can be. CM_FALLTHROUGH; case UV_NAMED_PIPE: { cm::uv_pipe_ptr pipe; pipe.init(*this->Server->GetLoop(), 0, static_cast(this)); uv_pipe_open(pipe, file_id); return { std::move(pipe) }; } default: assert(false && "Unable to determine stream type"); return nullptr; } } void cmStdIoConnection::SetServer(cmServerBase* s) { cmConnection::SetServer(s); if (!s) { return; } this->ReadStream = SetupStream(0); this->WriteStream = SetupStream(1); } void shutdown_connection(uv_prepare_t* prepare) { cmStdIoConnection* connection = static_cast(prepare->data); if (!uv_is_closing(reinterpret_cast(prepare))) { uv_close(reinterpret_cast(prepare), &cmEventBasedConnection::on_close_delete); } connection->OnDisconnect(0); } bool cmStdIoConnection::OnServeStart(std::string* pString) { Server->OnConnected(this); if (this->ReadStream.get()) { uv_read_start(this->ReadStream, on_alloc_buffer, on_read); } else if (uv_guess_handle(0) == UV_FILE) { char buffer[1024]; while (auto len = read(0, buffer, sizeof(buffer))) { ReadData(std::string(buffer, buffer + len)); } // We can't start the disconnect from here, add a prepare hook to do that // for us auto prepare = new uv_prepare_t(); prepare->data = this; uv_prepare_init(Server->GetLoop(), prepare); uv_prepare_start(prepare, shutdown_connection); } return cmConnection::OnServeStart(pString); } bool cmStdIoConnection::OnConnectionShuttingDown() { if (ReadStream.get()) { uv_read_stop(ReadStream); ReadStream->data = nullptr; } this->ReadStream.reset(); cmEventBasedConnection::OnConnectionShuttingDown(); return true; } cmServerPipeConnection::cmServerPipeConnection(const std::string& name) : cmPipeConnection(name, new cmServerBufferStrategy) { } cmServerStdIoConnection::cmServerStdIoConnection() : cmStdIoConnection(new cmServerBufferStrategy) { } cmConnectionBufferStrategy::~cmConnectionBufferStrategy() = default; void cmConnectionBufferStrategy::clear() { } std::string cmServerBufferStrategy::BufferOutMessage( const std::string& rawBuffer) const { return std::string("\n") + kSTART_MAGIC + std::string("\n") + rawBuffer + kEND_MAGIC + std::string("\n"); } std::string cmServerBufferStrategy::BufferMessage(std::string& RawReadBuffer) { for (;;) { auto needle = RawReadBuffer.find('\n'); if (needle == std::string::npos) { return ""; } std::string line = RawReadBuffer.substr(0, needle); const auto ls = line.size(); if (ls > 1 && line.at(ls - 1) == '\r') { line.erase(ls - 1, 1); } RawReadBuffer.erase(RawReadBuffer.begin(), RawReadBuffer.begin() + static_cast(needle) + 1); if (line == kSTART_MAGIC) { RequestBuffer.clear(); continue; } if (line == kEND_MAGIC) { std::string rtn; rtn.swap(this->RequestBuffer); return rtn; } this->RequestBuffer += line; this->RequestBuffer += "\n"; } }