/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ #include "cmServerConnection.h" #include "cmConfigure.h" #include "cmServer.h" #include "cmServerDictionary.h" #ifdef _WIN32 #include "io.h" #else #include #endif #include cmStdIoConnection::cmStdIoConnection( cmConnectionBufferStrategy* bufferStrategy) : cmEventBasedConnection(bufferStrategy) { } void cmStdIoConnection::SetupStream(uv_stream_t*& stream, int file_id) { assert(stream == nullptr); switch (uv_guess_handle(file_id)) { case UV_TTY: { auto tty = new uv_tty_t(); uv_tty_init(this->Server->GetLoop(), tty, file_id, file_id == 0); uv_tty_set_mode(tty, UV_TTY_MODE_NORMAL); stream = reinterpret_cast(tty); break; } case UV_FILE: if (file_id == 0) { return; } // Intentional fallthrough; stdin can _not_ be treated as a named // pipe, however stdout can be. CM_FALLTHROUGH; case UV_NAMED_PIPE: { auto pipe = new uv_pipe_t(); uv_pipe_init(this->Server->GetLoop(), pipe, 0); uv_pipe_open(pipe, file_id); stream = reinterpret_cast(pipe); break; } default: assert(false && "Unable to determine stream type"); return; } stream->data = static_cast(this); } void cmStdIoConnection::SetServer(cmServerBase* s) { cmConnection::SetServer(s); if (!s) { return; } SetupStream(this->ReadStream, 0); SetupStream(this->WriteStream, 1); } void shutdown_connection(uv_prepare_t* prepare) { cmStdIoConnection* connection = reinterpret_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) { 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); } void cmStdIoConnection::ShutdownStream(uv_stream_t*& stream) { if (!stream) { return; } switch (stream->type) { case UV_TTY: { assert(!uv_is_closing(reinterpret_cast(stream))); if (!uv_is_closing(reinterpret_cast(stream))) { uv_close(reinterpret_cast(stream), &on_close_delete); } break; } case UV_FILE: case UV_NAMED_PIPE: { assert(!uv_is_closing(reinterpret_cast(stream))); if (!uv_is_closing(reinterpret_cast(stream))) { uv_close(reinterpret_cast(stream), &on_close_delete); } break; } default: assert(false && "Unable to determine stream type"); } stream = nullptr; } bool cmStdIoConnection::OnConnectionShuttingDown() { if (ReadStream) { uv_read_stop(ReadStream); } ShutdownStream(ReadStream); ShutdownStream(WriteStream); cmEventBasedConnection::OnConnectionShuttingDown(); return true; } cmServerPipeConnection::cmServerPipeConnection(const std::string& name) : cmPipeConnection(name, new cmServerBufferStrategy) { } cmServerStdIoConnection::cmServerStdIoConnection() : cmStdIoConnection(new cmServerBufferStrategy) { } cmConnectionBufferStrategy::~cmConnectionBufferStrategy() { } 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"; } }