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(const std::shared_ptr<Reader>& reader) |
28 | : reader(reader) {} |
29 | |
30 | ContentReader& ContentReader::operator=(ContentReader&& rhs) noexcept { |
31 | buf = std::move(rhs.buf); |
32 | reader = std::move(rhs.reader); |
33 | return *this; |
34 | } |
35 | |
36 | bool ContentReader::isOpen() { |
37 | return reader ? reader->isOpen() : false; |
38 | } |
39 | |
40 | void ContentReader::close() { |
41 | if (reader) { |
42 | reader->close(); |
43 | } |
44 | } |
45 | |
46 | std::string ContentReader::read() { |
47 | matched_idx = 0; |
48 | |
49 | // Find Content-Length header prefix |
50 | if (!scan("Content-Length:" )) { |
51 | return "" ; |
52 | } |
53 | |
54 | // Skip whitespace and tabs |
55 | while (matchAny(" \t" )) { |
56 | } |
57 | |
58 | // Parse length |
59 | size_t len = 0; |
60 | while (true) { |
61 | auto c = matchAny("0123456789" ); |
62 | if (c == 0) { |
63 | break; |
64 | } |
65 | len *= 10; |
66 | len += size_t(c) - size_t('0'); |
67 | } |
68 | if (len == 0) { |
69 | return "" ; |
70 | } |
71 | // Expect \r\n\r\n |
72 | if (!match("\r\n\r\n" )) { |
73 | return "" ; |
74 | } |
75 | |
76 | // Read message |
77 | if (!buffer(len + matched_idx)) { |
78 | return "" ; |
79 | } |
80 | |
81 | for (size_t i = 0; i < matched_idx; i++) { |
82 | buf.pop_front(); |
83 | } |
84 | |
85 | std::string out; |
86 | out.reserve(len); |
87 | for (size_t i = 0; i < len; i++) { |
88 | out.push_back(static_cast<char>(buf.front())); |
89 | buf.pop_front(); |
90 | } |
91 | return out; |
92 | } |
93 | |
94 | bool ContentReader::scan(const uint8_t* seq, size_t len) { |
95 | while (buffer(len)) { |
96 | if (match(seq, len)) { |
97 | return true; |
98 | } |
99 | buf.pop_front(); |
100 | } |
101 | return false; |
102 | } |
103 | |
104 | bool ContentReader::scan(const char* str) { |
105 | auto len = strlen(str); |
106 | return scan(reinterpret_cast<const uint8_t*>(str), len); |
107 | } |
108 | |
109 | bool ContentReader::match(const uint8_t* seq, size_t len) { |
110 | if (!buffer(len + matched_idx)) { |
111 | return false; |
112 | } |
113 | auto it = matched_idx; |
114 | for (size_t i = 0; i < len; i++, it++) { |
115 | if (buf[it] != seq[i]) { |
116 | return false; |
117 | } |
118 | } |
119 | |
120 | matched_idx += len; |
121 | return true; |
122 | } |
123 | |
124 | bool ContentReader::match(const char* str) { |
125 | auto len = strlen(str); |
126 | return match(reinterpret_cast<const uint8_t*>(str), len); |
127 | } |
128 | |
129 | char ContentReader::matchAny(const char* chars) { |
130 | if (!buffer(1 + matched_idx)) { |
131 | return false; |
132 | } |
133 | int c = buf[matched_idx]; |
134 | if (auto p = strchr(chars, c)) { |
135 | matched_idx++; |
136 | return *p; |
137 | } |
138 | return 0; |
139 | } |
140 | |
141 | bool ContentReader::buffer(size_t bytes) { |
142 | if (bytes < buf.size()) { |
143 | return true; |
144 | } |
145 | bytes -= buf.size(); |
146 | while (bytes > 0) { |
147 | uint8_t chunk[256]; |
148 | auto numWant = std::min(sizeof(chunk), bytes); |
149 | auto numGot = reader->read(chunk, numWant); |
150 | if (numGot == 0) { |
151 | return false; |
152 | } |
153 | for (size_t i = 0; i < numGot; i++) { |
154 | buf.push_back(chunk[i]); |
155 | } |
156 | bytes -= numGot; |
157 | } |
158 | return true; |
159 | } |
160 | |
161 | //////////////////////////////////////////////////////////////////////////////// |
162 | // ContentWriter |
163 | //////////////////////////////////////////////////////////////////////////////// |
164 | ContentWriter::ContentWriter(const std::shared_ptr<Writer>& rhs) |
165 | : writer(rhs) {} |
166 | |
167 | ContentWriter& ContentWriter::operator=(ContentWriter&& rhs) noexcept { |
168 | writer = std::move(rhs.writer); |
169 | return *this; |
170 | } |
171 | |
172 | bool ContentWriter::isOpen() { |
173 | return writer ? writer->isOpen() : false; |
174 | } |
175 | |
176 | void ContentWriter::close() { |
177 | if (writer) { |
178 | writer->close(); |
179 | } |
180 | } |
181 | |
182 | bool ContentWriter::write(const std::string& msg) const { |
183 | auto = |
184 | std::string("Content-Length: " ) + std::to_string(msg.size()) + "\r\n\r\n" ; |
185 | return writer->write(header.data(), header.size()) && |
186 | writer->write(msg.data(), msg.size()); |
187 | } |
188 | |
189 | } // namespace dap |
190 | |