| // Copyright 2019 Google LLC |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // https://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| #include "content_stream.h" |
| |
| #include "dap/io.h" |
| |
| #include <string.h> // strlen |
| #include <algorithm> // std::min |
| |
| namespace dap { |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // ContentReader |
| //////////////////////////////////////////////////////////////////////////////// |
| ContentReader::ContentReader(const std::shared_ptr<Reader>& reader) |
| : reader(reader) {} |
| |
| ContentReader& ContentReader::operator=(ContentReader&& rhs) noexcept { |
| buf = std::move(rhs.buf); |
| reader = std::move(rhs.reader); |
| return *this; |
| } |
| |
| bool ContentReader::isOpen() { |
| return reader ? reader->isOpen() : false; |
| } |
| |
| void ContentReader::close() { |
| if (reader) { |
| reader->close(); |
| } |
| } |
| |
| std::string ContentReader::read() { |
| matched_idx = 0; |
| |
| // Find Content-Length header prefix |
| if (!scan("Content-Length:")) { |
| return ""; |
| } |
| |
| // Skip whitespace and tabs |
| while (matchAny(" \t")) { |
| } |
| |
| // Parse length |
| size_t len = 0; |
| while (true) { |
| auto c = matchAny("0123456789"); |
| if (c == 0) { |
| break; |
| } |
| len *= 10; |
| len += size_t(c) - size_t('0'); |
| } |
| if (len == 0) { |
| return ""; |
| } |
| // Expect \r\n\r\n |
| if (!match("\r\n\r\n")) { |
| return ""; |
| } |
| |
| // Read message |
| if (!buffer(len + matched_idx)) { |
| return ""; |
| } |
| |
| for (size_t i = 0; i < matched_idx; i++) { |
| buf.pop_front(); |
| } |
| |
| std::string out; |
| out.reserve(len); |
| for (size_t i = 0; i < len; i++) { |
| out.push_back(static_cast<char>(buf.front())); |
| buf.pop_front(); |
| } |
| return out; |
| } |
| |
| bool ContentReader::scan(const uint8_t* seq, size_t len) { |
| while (buffer(len)) { |
| if (match(seq, len)) { |
| return true; |
| } |
| buf.pop_front(); |
| } |
| return false; |
| } |
| |
| bool ContentReader::scan(const char* str) { |
| auto len = strlen(str); |
| return scan(reinterpret_cast<const uint8_t*>(str), len); |
| } |
| |
| bool ContentReader::match(const uint8_t* seq, size_t len) { |
| if (!buffer(len + matched_idx)) { |
| return false; |
| } |
| auto it = matched_idx; |
| for (size_t i = 0; i < len; i++, it++) { |
| if (buf[it] != seq[i]) { |
| return false; |
| } |
| } |
| |
| matched_idx += len; |
| return true; |
| } |
| |
| bool ContentReader::match(const char* str) { |
| auto len = strlen(str); |
| return match(reinterpret_cast<const uint8_t*>(str), len); |
| } |
| |
| char ContentReader::matchAny(const char* chars) { |
| if (!buffer(1 + matched_idx)) { |
| return false; |
| } |
| int c = buf[matched_idx]; |
| if (auto p = strchr(chars, c)) { |
| matched_idx++; |
| return *p; |
| } |
| return 0; |
| } |
| |
| bool ContentReader::buffer(size_t bytes) { |
| if (bytes < buf.size()) { |
| return true; |
| } |
| bytes -= buf.size(); |
| while (bytes > 0) { |
| uint8_t chunk[256]; |
| auto numWant = std::min(sizeof(chunk), bytes); |
| auto numGot = reader->read(chunk, numWant); |
| if (numGot == 0) { |
| return false; |
| } |
| for (size_t i = 0; i < numGot; i++) { |
| buf.push_back(chunk[i]); |
| } |
| bytes -= numGot; |
| } |
| return true; |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // ContentWriter |
| //////////////////////////////////////////////////////////////////////////////// |
| ContentWriter::ContentWriter(const std::shared_ptr<Writer>& rhs) |
| : writer(rhs) {} |
| |
| ContentWriter& ContentWriter::operator=(ContentWriter&& rhs) noexcept { |
| writer = std::move(rhs.writer); |
| return *this; |
| } |
| |
| bool ContentWriter::isOpen() { |
| return writer ? writer->isOpen() : false; |
| } |
| |
| void ContentWriter::close() { |
| if (writer) { |
| writer->close(); |
| } |
| } |
| |
| bool ContentWriter::write(const std::string& msg) const { |
| auto header = |
| std::string("Content-Length: ") + std::to_string(msg.size()) + "\r\n\r\n"; |
| return writer->write(header.data(), header.size()) && |
| writer->write(msg.data(), msg.size()); |
| } |
| |
| } // namespace dap |