Coverage Report

Created: 2026-02-09 06:05

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/CMake/Utilities/cmcppdap/src/content_stream.cpp
Line
Count
Source
1
// Copyright 2019 Google LLC
2
//
3
// Licensed under the Apache License, Version 2.0 (the "License");
4
// you may not use this file except in compliance with the License.
5
// You may obtain a copy of the License at
6
//
7
//     https://www.apache.org/licenses/LICENSE-2.0
8
//
9
// Unless required by applicable law or agreed to in writing, software
10
// distributed under the License is distributed on an "AS IS" BASIS,
11
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
// See the License for the specific language governing permissions and
13
// limitations under the License.
14
15
#include "content_stream.h"
16
17
#include "dap/io.h"
18
19
#include <string.h>   // strlen
20
#include <algorithm>  // std::min
21
22
namespace dap {
23
24
////////////////////////////////////////////////////////////////////////////////
25
// ContentReader
26
////////////////////////////////////////////////////////////////////////////////
27
ContentReader::ContentReader(
28
    const std::shared_ptr<Reader>& reader,
29
    OnInvalidData on_invalid_data /* = OnInvalidData::kIgnore */)
30
0
    : reader(reader), on_invalid_data(on_invalid_data) {}
31
32
0
ContentReader& ContentReader::operator=(ContentReader&& rhs) noexcept {
33
0
  buf = std::move(rhs.buf);
34
0
  reader = std::move(rhs.reader);
35
0
  on_invalid_data = std::move(rhs.on_invalid_data);
36
0
  return *this;
37
0
}
38
39
0
bool ContentReader::isOpen() {
40
0
  return reader ? reader->isOpen() : false;
41
0
}
42
43
0
void ContentReader::close() {
44
0
  if (reader) {
45
0
    reader->close();
46
0
  }
47
0
}
48
49
0
std::string ContentReader::read() {
50
  // Find Content-Length header prefix
51
0
  if (on_invalid_data == kClose) {
52
0
    if (!match("Content-Length:")) {
53
0
      return badHeader();
54
0
    }
55
0
  } else {
56
0
    if (!scan("Content-Length:")) {
57
0
      return "";
58
0
    }
59
0
  }
60
  // Skip whitespace and tabs
61
0
  while (matchAny(" \t")) {
62
0
  }
63
  // Parse length
64
0
  size_t len = 0;
65
0
  while (true) {
66
0
    auto c = matchAny("0123456789");
67
0
    if (c == 0) {
68
0
      break;
69
0
    }
70
0
    len *= 10;
71
0
    len += size_t(c) - size_t('0');
72
0
  }
73
0
  if (len == 0) {
74
0
    return "";
75
0
  }
76
77
  // Expect \r\n\r\n
78
0
  if (!match("\r\n\r\n")) {
79
0
    return badHeader();
80
0
  }
81
82
  // Read message
83
0
  if (!buffer(len)) {
84
0
    return "";
85
0
  }
86
0
  std::string out;
87
0
  out.reserve(len);
88
0
  for (size_t i = 0; i < len; i++) {
89
0
    out.push_back(static_cast<char>(buf.front()));
90
0
    buf.pop_front();
91
0
  }
92
0
  return out;
93
0
}
94
95
0
bool ContentReader::scan(const uint8_t* seq, size_t len) {
96
0
  while (buffer(len)) {
97
0
    if (match(seq, len)) {
98
0
      return true;
99
0
    }
100
0
    buf.pop_front();
101
0
  }
102
0
  return false;
103
0
}
104
105
0
bool ContentReader::scan(const char* str) {
106
0
  auto len = strlen(str);
107
0
  return scan(reinterpret_cast<const uint8_t*>(str), len);
108
0
}
109
110
0
bool ContentReader::match(const uint8_t* seq, size_t len) {
111
0
  if (!buffer(len)) {
112
0
    return false;
113
0
  }
114
0
  auto it = buf.begin();
115
0
  for (size_t i = 0; i < len; i++, it++) {
116
0
    if (*it != seq[i]) {
117
0
      return false;
118
0
    }
119
0
  }
120
0
  for (size_t i = 0; i < len; i++) {
121
0
    buf.pop_front();
122
0
  }
123
0
  return true;
124
0
}
125
126
0
bool ContentReader::match(const char* str) {
127
0
  auto len = strlen(str);
128
0
  return match(reinterpret_cast<const uint8_t*>(str), len);
129
0
}
130
131
0
char ContentReader::matchAny(const char* chars) {
132
0
  if (!buffer(1)) {
133
0
    return false;
134
0
  }
135
0
  int c = buf.front();
136
0
  if (auto p = strchr(chars, c)) {
137
0
    buf.pop_front();
138
0
    return *p;
139
0
  }
140
0
  return 0;
141
0
}
142
143
0
bool ContentReader::buffer(size_t bytes) {
144
0
  if (bytes < buf.size()) {
145
0
    return true;
146
0
  }
147
0
  bytes -= buf.size();
148
0
  while (bytes > 0) {
149
0
    uint8_t chunk[256];
150
0
    auto numWant = std::min(sizeof(chunk), bytes);
151
0
    auto numGot = reader->read(chunk, numWant);
152
0
    if (numGot == 0) {
153
0
      return false;
154
0
    }
155
0
    for (size_t i = 0; i < numGot; i++) {
156
0
      buf.push_back(chunk[i]);
157
0
    }
158
0
    bytes -= numGot;
159
0
  }
160
0
  return true;
161
0
}
162
163
0
std::string ContentReader::badHeader() {
164
0
  if (on_invalid_data == kClose) {
165
0
    close();
166
0
  }
167
0
  return "";
168
0
}
169
170
////////////////////////////////////////////////////////////////////////////////
171
// ContentWriter
172
////////////////////////////////////////////////////////////////////////////////
173
ContentWriter::ContentWriter(const std::shared_ptr<Writer>& rhs)
174
0
    : writer(rhs) {}
175
176
0
ContentWriter& ContentWriter::operator=(ContentWriter&& rhs) noexcept {
177
0
  writer = std::move(rhs.writer);
178
0
  return *this;
179
0
}
180
181
0
bool ContentWriter::isOpen() {
182
0
  return writer ? writer->isOpen() : false;
183
0
}
184
185
0
void ContentWriter::close() {
186
0
  if (writer) {
187
0
    writer->close();
188
0
  }
189
0
}
190
191
0
bool ContentWriter::write(const std::string& msg) const {
192
0
  auto header =
193
0
      std::string("Content-Length: ") + std::to_string(msg.size()) + "\r\n\r\n";
194
0
  return writer->write(header.data(), header.size()) &&
195
0
         writer->write(msg.data(), msg.size());
196
0
}
197
198
}  // namespace dap