1 /* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /*
3 * Copyright 2016 Couchbase, Inc.
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17 #include "client_mcbp_commands.h"
18 #include "frameinfo.h"
19 #include <mcbp/mcbp.h>
20 #include <memcached/tracer.h>
21 #include <array>
22 #include <gsl/gsl>
23
24 /**
25 * Append a 16 bit integer to the buffer in network byte order
26 *
27 * @param buf the buffer to add the data to
28 * @param value The value (in host local byteorder) to add.
29 */
append(std::vector<uint8_t>& buf, uint16_t value)30 static void append(std::vector<uint8_t>& buf, uint16_t value) {
31 uint16_t vallen = ntohs(value);
32 auto p = reinterpret_cast<const char*>(&vallen);
33 buf.insert(buf.end(), p, p + 2);
34 }
35
36 /**
37 * Append a 32 bit integer to the buffer in network byte order
38 *
39 * @param buf the buffer to add the data to
40 * @param value The value (in host local byteorder) to add.
41 */
append(std::vector<uint8_t>& buf, uint32_t value)42 static void append(std::vector<uint8_t>& buf, uint32_t value) {
43 uint32_t vallen = ntohl(value);
44 auto p = reinterpret_cast<const char*>(&vallen);
45 buf.insert(buf.end(), p, p + 4);
46 }
47
encode(std::vector<uint8_t>& buf) const48 void BinprotCommand::encode(std::vector<uint8_t>& buf) const {
49 writeHeader(buf);
50 }
51
encode() const52 BinprotCommand::Encoded BinprotCommand::encode() const {
53 Encoded bufs;
54 encode(bufs.header);
55 return bufs;
56 }
57
fillHeader(cb::mcbp::Request& header, size_t payload_len, size_t extlen) const58 void BinprotCommand::fillHeader(cb::mcbp::Request& header,
59 size_t payload_len,
60 size_t extlen) const {
61 header.setMagic(cb::mcbp::Magic::ClientRequest);
62 header.setOpcode(opcode);
63 // The server refuse to use a key > 0xff so just narrow it here to make
64 // sure that we don't use the frame extras field if we want to inject
65 // that at a later time
66 header.setKeylen(gsl::narrow<uint8_t>(key.size()));
67 header.setExtlen(gsl::narrow<uint8_t>(extlen));
68 header.setDatatype(cb::mcbp::Datatype::Raw);
69 header.setVBucket(vbucket);
70 header.setBodylen(gsl::narrow<uint32_t>(key.size() + extlen + payload_len +
71 frame_info.size()));
72 header.setOpaque(opaque);
73 // @todo fix this to use the setter. There is still some dependency
74 // in other tests which use expects this to be in the same byte order
75 // as the server sent it..
76 header.cas = cas;
77 }
78
writeHeader(std::vector<uint8_t>& buf, size_t payload_len, size_t extlen) const79 void BinprotCommand::writeHeader(std::vector<uint8_t>& buf,
80 size_t payload_len,
81 size_t extlen) const {
82 buf.resize(sizeof(cb::mcbp::Request));
83 auto& hdr = *reinterpret_cast<cb::mcbp::Request*>(buf.data());
84 fillHeader(hdr, payload_len, extlen);
85
86 if (!frame_info.empty()) {
87 hdr.setMagic(cb::mcbp::Magic::AltClientRequest);
88 hdr.setFramingExtraslen(gsl::narrow<uint8_t>(frame_info.size()));
89 std::copy(frame_info.cbegin(),
90 frame_info.cend(),
91 std::back_inserter(buf));
92 }
93 }
94
setKey(std::string key_)95 BinprotCommand& BinprotCommand::setKey(std::string key_) {
96 key = std::move(key_);
97 return *this;
98 }
99
setCas(uint64_t cas_)100 BinprotCommand& BinprotCommand::setCas(uint64_t cas_) {
101 cas = cas_;
102 return *this;
103 }
104
setOp(cb::mcbp::ClientOpcode cmd_)105 BinprotCommand& BinprotCommand::setOp(cb::mcbp::ClientOpcode cmd_) {
106 opcode = cmd_;
107 return *this;
108 }
109
clear()110 void BinprotCommand::clear() {
111 opcode = cb::mcbp::ClientOpcode::Invalid;
112 key.clear();
113 cas = 0;
114 vbucket = Vbid(0);
115 }
116
getCas() const117 uint64_t BinprotCommand::getCas() const {
118 return cas;
119 }
120
getKey() const121 const std::string& BinprotCommand::getKey() const {
122 return key;
123 }
124
getOp() const125 cb::mcbp::ClientOpcode BinprotCommand::getOp() const {
126 return opcode;
127 }
128
setVBucket(Vbid vbid)129 BinprotCommand& BinprotCommand::setVBucket(Vbid vbid) {
130 vbucket = vbid;
131 return *this;
132 }
133
setOpaque(uint32_t opaq)134 BinprotCommand& BinprotCommand::setOpaque(uint32_t opaq) {
135 opaque = opaq;
136 return *this;
137 }
138
addFrameInfo(const FrameInfo& fi)139 BinprotCommand& BinprotCommand::addFrameInfo(const FrameInfo& fi) {
140 auto encoded = fi.encode();
141 return addFrameInfo({encoded.data(), encoded.size()});
142 }
143
addFrameInfo(cb::const_byte_buffer section)144 BinprotCommand& BinprotCommand::addFrameInfo(cb::const_byte_buffer section) {
145 std::copy(section.cbegin(), section.cend(), std::back_inserter(frame_info));
146 return *this;
147 }
148
assign(uint32_t value_)149 void BinprotCommand::ExpiryValue::assign(uint32_t value_) {
150 value = value_;
151 set = true;
152 }
153
clear()154 void BinprotCommand::ExpiryValue::clear() {
155 set = false;
156 }
157
isSet() const158 bool BinprotCommand::ExpiryValue::isSet() const {
159 return set;
160 }
161
getValue() const162 uint32_t BinprotCommand::ExpiryValue::getValue() const {
163 return value;
164 }
165
encode(std::vector<uint8_t>& buf) const166 void BinprotGenericCommand::encode(std::vector<uint8_t>& buf) const {
167 writeHeader(buf, value.size(), extras.size());
168 buf.insert(buf.end(), extras.begin(), extras.end());
169 buf.insert(buf.end(), key.begin(), key.end());
170 buf.insert(buf.end(), value.begin(), value.end());
171 }
172
BinprotGenericCommand(cb::mcbp::ClientOpcode opcode, const std::string& key_, const std::string& value_)173 BinprotGenericCommand::BinprotGenericCommand(cb::mcbp::ClientOpcode opcode,
174 const std::string& key_,
175 const std::string& value_)
176 : BinprotCommandT() {
177 setOp(opcode);
178 setKey(key_);
179 setValue(value_);
180 }
181
BinprotGenericCommand(cb::mcbp::ClientOpcode opcode, const std::string& key_)182 BinprotGenericCommand::BinprotGenericCommand(cb::mcbp::ClientOpcode opcode,
183 const std::string& key_)
184 : BinprotCommandT() {
185 setOp(opcode);
186 setKey(key_);
187 }
188
BinprotGenericCommand(cb::mcbp::ClientOpcode opcode)189 BinprotGenericCommand::BinprotGenericCommand(cb::mcbp::ClientOpcode opcode)
190 : BinprotCommandT() {
191 setOp(opcode);
192 }
193
BinprotGenericCommand()194 BinprotGenericCommand::BinprotGenericCommand() : BinprotCommandT() {
195 }
196
setValue(std::string value_)197 BinprotGenericCommand& BinprotGenericCommand::setValue(std::string value_) {
198 value = std::move(value_);
199 return *this;
200 }
201
setExtras( const std::vector<uint8_t>& buf)202 BinprotGenericCommand& BinprotGenericCommand::setExtras(
203 const std::vector<uint8_t>& buf) {
204 extras.assign(buf.begin(), buf.end());
205 return *this;
206 }
207
clear()208 void BinprotGenericCommand::clear() {
209 BinprotCommand::clear();
210 value.clear();
211 extras.clear();
212 }
213
BinprotSubdocCommand( cb::mcbp::ClientOpcode cmd_, const std::string& key_, const std::string& path_, const std::string& value_, protocol_binary_subdoc_flag pathFlags_, mcbp::subdoc::doc_flag docFlags_, uint64_t cas_)214 BinprotSubdocCommand::BinprotSubdocCommand(
215 cb::mcbp::ClientOpcode cmd_,
216 const std::string& key_,
217 const std::string& path_,
218 const std::string& value_,
219 protocol_binary_subdoc_flag pathFlags_,
220 mcbp::subdoc::doc_flag docFlags_,
221 uint64_t cas_)
222 : BinprotCommandT() {
223 setOp(cmd_);
224 setKey(key_);
225 setPath(path_);
226 setValue(value_);
227 addPathFlags(pathFlags_);
228 addDocFlags(docFlags_);
229 setCas(cas_);
230 }
231
setPath(std::string path_)232 BinprotSubdocCommand& BinprotSubdocCommand::setPath(std::string path_) {
233 if (path_.size() > std::numeric_limits<uint16_t>::max()) {
234 throw std::out_of_range("BinprotSubdocCommand::setPath: Path too big");
235 }
236 path = std::move(path_);
237 return *this;
238 }
239
encode(std::vector<uint8_t>& buf) const240 void BinprotSubdocCommand::encode(std::vector<uint8_t>& buf) const {
241 if (key.empty()) {
242 throw std::logic_error("BinprotSubdocCommand::encode: Missing a key");
243 }
244
245 // Expiry (optional) is encoded in extras. Only include if non-zero or
246 // if explicit encoding of zero was requested.
247 const bool include_expiry = (expiry.getValue() != 0 || expiry.isSet());
248 const bool include_doc_flags = !isNone(doc_flags);
249
250 // Populate the header.
251 const size_t extlen = sizeof(uint16_t) + // Path length
252 1 + // flags
253 (include_expiry ? sizeof(uint32_t) : 0) +
254 (include_doc_flags ? sizeof(uint8_t) : 0);
255
256 writeHeader(buf, path.size() + value.size(), extlen);
257
258 // Add extras: pathlen, flags, optional expiry
259 append(buf, gsl::narrow<uint16_t>(path.size()));
260 buf.push_back(flags);
261
262 if (include_expiry) {
263 // As expiry is optional (and immediately follows subdoc_flags,
264 // i.e. unaligned) there's no field in the struct; so use low-level
265 // memcpy to populate it.
266 append(buf, expiry.getValue());
267 }
268
269 if (include_doc_flags) {
270 buf.push_back(
271 std::underlying_type<mcbp::subdoc::doc_flag>::type(doc_flags));
272 }
273
274 // Add Body: key; path; value if applicable.
275 buf.insert(buf.end(), key.begin(), key.end());
276 buf.insert(buf.end(), path.begin(), path.end());
277 buf.insert(buf.end(), value.begin(), value.end());
278 }
BinprotSubdocCommand()279 BinprotSubdocCommand::BinprotSubdocCommand() : BinprotCommandT() {
280 }
BinprotSubdocCommand(cb::mcbp::ClientOpcode cmd_)281 BinprotSubdocCommand::BinprotSubdocCommand(cb::mcbp::ClientOpcode cmd_) {
282 setOp(cmd_);
283 }
BinprotSubdocCommand(cb::mcbp::ClientOpcode cmd_, const std::string& key_, const std::string& path_)284 BinprotSubdocCommand::BinprotSubdocCommand(cb::mcbp::ClientOpcode cmd_,
285 const std::string& key_,
286 const std::string& path_)
287 : BinprotSubdocCommand(cmd_,
288 key_,
289 path_,
290 "",
291 SUBDOC_FLAG_NONE,
292 mcbp::subdoc::doc_flag::None,
293 0) {
294 }
setValue(std::string value_)295 BinprotSubdocCommand& BinprotSubdocCommand::setValue(std::string value_) {
296 value = std::move(value_);
297 return *this;
298 }
addPathFlags( protocol_binary_subdoc_flag flags_)299 BinprotSubdocCommand& BinprotSubdocCommand::addPathFlags(
300 protocol_binary_subdoc_flag flags_) {
301 static protocol_binary_subdoc_flag validFlags = SUBDOC_FLAG_XATTR_PATH |
302 SUBDOC_FLAG_MKDIR_P |
303 SUBDOC_FLAG_EXPAND_MACROS;
304 if ((flags_ & ~validFlags) == 0) {
305 flags = flags | flags_;
306 } else {
307 throw std::invalid_argument("addPathFlags: flags_ (which is " +
308 std::to_string(flags_) +
309 ") is not a path flag");
310 }
311 return *this;
312 }
addDocFlags( mcbp::subdoc::doc_flag flags_)313 BinprotSubdocCommand& BinprotSubdocCommand::addDocFlags(
314 mcbp::subdoc::doc_flag flags_) {
315 using namespace mcbp::subdoc;
316 constexpr doc_flag validFlags = doc_flag::Mkdoc | doc_flag::AccessDeleted |
317 doc_flag::Add | doc_flag::CreateAsDeleted;
318 if ((flags_ & ~validFlags) == mcbp::subdoc::doc_flag::None) {
319 doc_flags = doc_flags | flags_;
320 } else {
321 throw std::invalid_argument("addDocFlags: flags_ (which is " +
322 to_string(flags_) + ") is not a doc flag");
323 }
324 return *this;
325 }
setExpiry(uint32_t value_)326 BinprotSubdocCommand& BinprotSubdocCommand::setExpiry(uint32_t value_) {
327 expiry.assign(value_);
328 return *this;
329 }
getPath() const330 const std::string& BinprotSubdocCommand::getPath() const {
331 return path;
332 }
getValue() const333 const std::string& BinprotSubdocCommand::getValue() const {
334 return value;
335 }
getFlags() const336 protocol_binary_subdoc_flag BinprotSubdocCommand::getFlags() const {
337 return flags;
338 }
339
isSuccess() const340 bool BinprotResponse::isSuccess() const {
341 return cb::mcbp::isStatusSuccess(getStatus());
342 }
343
assign(std::vector<uint8_t>&& srcbuf)344 void BinprotResponse::assign(std::vector<uint8_t>&& srcbuf) {
345 payload = std::move(srcbuf);
346 }
347
getTracingData() const348 boost::optional<std::chrono::microseconds> BinprotResponse::getTracingData()
349 const {
350 auto framingExtrasLen = getFramingExtraslen();
351 if (framingExtrasLen == 0) {
352 return boost::optional<std::chrono::microseconds>{};
353 }
354 const auto& framingExtras = getResponse().getFramingExtras();
355 const auto& data = framingExtras.data();
356 size_t offset = 0;
357
358 // locate the tracing info
359 while (offset < framingExtrasLen) {
360 const uint8_t id = data[offset] & 0xF0;
361 const uint8_t len = data[offset] & 0x0F;
362 if (0 == id) {
363 uint16_t micros = ntohs(
364 reinterpret_cast<const uint16_t*>(data + offset + 1)[0]);
365 return cb::tracing::Tracer::decodeMicros(micros);
366 }
367 offset += 1 + len;
368 }
369
370 return boost::optional<std::chrono::microseconds>{};
371 }
372
getOp() const373 cb::mcbp::ClientOpcode BinprotResponse::getOp() const {
374 return cb::mcbp::ClientOpcode(getResponse().getClientOpcode());
375 }
376
getStatus() const377 cb::mcbp::Status BinprotResponse::getStatus() const {
378 return getResponse().getStatus();
379 }
380
getExtlen() const381 size_t BinprotResponse::getExtlen() const {
382 return getResponse().getExtlen();
383 }
384
getBodylen() const385 size_t BinprotResponse::getBodylen() const {
386 return getResponse().getBodylen();
387 }
388
getFramingExtraslen() const389 size_t BinprotResponse::getFramingExtraslen() const {
390 return getResponse().getFramingExtraslen();
391 }
392
getHeaderLen()393 size_t BinprotResponse::getHeaderLen() {
394 return sizeof(cb::mcbp::Response);
395 }
396
getCas() const397 uint64_t BinprotResponse::getCas() const {
398 return getResponse().cas;
399 }
400
getDatatype() const401 protocol_binary_datatype_t BinprotResponse::getDatatype() const {
402 return protocol_binary_datatype_t(getResponse().getDatatype());
403 }
404
getKey() const405 cb::const_char_buffer BinprotResponse::getKey() const {
406 const auto buf = getResponse().getKey();
407 return {reinterpret_cast<const char*>(buf.data()), buf.size()};
408 }
409
getKeyString() const410 std::string BinprotResponse::getKeyString() const {
411 const auto buf = getKey();
412 return {buf.data(), buf.size()};
413 }
414
getData() const415 cb::const_byte_buffer BinprotResponse::getData() const {
416 return getResponse().getValue();
417 }
418
getDataString() const419 std::string BinprotResponse::getDataString() const {
420 const auto buf = getData();
421 return {reinterpret_cast<const char*>(buf.data()), buf.size()};
422 }
423
getResponse() const424 const cb::mcbp::Response& BinprotResponse::getResponse() const {
425 return getHeader().getResponse();
426 }
427
clear()428 void BinprotResponse::clear() {
429 payload.clear();
430 }
431
getHeader() const432 const cb::mcbp::Header& BinprotResponse::getHeader() const {
433 return *reinterpret_cast<const cb::mcbp::Header*>(payload.data());
434 }
435
assign(std::vector<uint8_t>&& srcbuf)436 void BinprotSubdocResponse::assign(std::vector<uint8_t>&& srcbuf) {
437 BinprotResponse::assign(std::move(srcbuf));
438 if (getBodylen() - getExtlen() - getFramingExtraslen() > 0) {
439 value.assign(payload.data() + sizeof(cb::mcbp::Response) + getExtlen() +
440 getFramingExtraslen(),
441 payload.data() + payload.size());
442 }
443 }
getValue() const444 const std::string& BinprotSubdocResponse::getValue() const {
445 return value;
446 }
clear()447 void BinprotSubdocResponse::clear() {
448 BinprotResponse::clear();
449 value.clear();
450 }
operator ==( const BinprotSubdocResponse& other) const451 bool BinprotSubdocResponse::operator==(
452 const BinprotSubdocResponse& other) const {
453 bool rv = getStatus() == other.getStatus();
454
455 if (getStatus() == cb::mcbp::Status::Success) {
456 rv = getValue() == other.getValue();
457 }
458 return rv;
459 }
460
encode(std::vector<uint8_t>& buf) const461 void BinprotSaslAuthCommand::encode(std::vector<uint8_t>& buf) const {
462 if (key.empty()) {
463 throw std::logic_error("BinprotSaslAuthCommand: Missing mechanism (setMechanism)");
464 }
465
466 writeHeader(buf, challenge.size(), 0);
467 buf.insert(buf.end(), key.begin(), key.end());
468 buf.insert(buf.end(), challenge.begin(), challenge.end());
469 }
setMechanism(const std::string& mech_)470 void BinprotSaslAuthCommand::setMechanism(const std::string& mech_) {
471 setKey(mech_);
472 }
setChallenge(cb::const_char_buffer data)473 void BinprotSaslAuthCommand::setChallenge(cb::const_char_buffer data) {
474 challenge.assign(data.begin(), data.size());
475 }
476
encode(std::vector<uint8_t>& buf) const477 void BinprotSaslStepCommand::encode(std::vector<uint8_t>& buf) const {
478 if (key.empty()) {
479 throw std::logic_error("BinprotSaslStepCommand::encode: Missing mechanism (setMechanism");
480 }
481 if (challenge.empty()) {
482 throw std::logic_error("BinprotSaslStepCommand::encode: Missing challenge response");
483 }
484
485 writeHeader(buf, challenge.size(), 0);
486 buf.insert(buf.end(), key.begin(), key.end());
487 buf.insert(buf.end(), challenge.begin(), challenge.end());
488 }
setMechanism(const std::string& mech)489 void BinprotSaslStepCommand::setMechanism(const std::string& mech) {
490 setKey(mech);
491 }
setChallenge(cb::const_char_buffer data)492 void BinprotSaslStepCommand::setChallenge(cb::const_char_buffer data) {
493 challenge.assign(data.begin(), data.size());
494 }
495
setConfig(const std::string& module, const std::string& config)496 void BinprotCreateBucketCommand::setConfig(const std::string& module,
497 const std::string& config) {
498 module_config.assign(module.begin(), module.end());
499 module_config.push_back(0x00);
500 module_config.insert(module_config.end(), config.begin(), config.end());
501 }
502
encode(std::vector<uint8_t>& buf) const503 void BinprotCreateBucketCommand::encode(std::vector<uint8_t>& buf) const {
504 if (module_config.empty()) {
505 throw std::logic_error("BinprotCreateBucketCommand::encode: Missing bucket module and config");
506 }
507 writeHeader(buf, module_config.size(), 0);
508 buf.insert(buf.end(), key.begin(), key.end());
509 buf.insert(buf.end(), module_config.begin(), module_config.end());
510 }
BinprotCreateBucketCommand(const char* name)511 BinprotCreateBucketCommand::BinprotCreateBucketCommand(const char* name) {
512 setKey(name);
513 }
514
encode(std::vector<uint8_t>& buf) const515 void BinprotGetCommand::encode(std::vector<uint8_t>& buf) const {
516 writeHeader(buf, 0, 0);
517 buf.insert(buf.end(), key.begin(), key.end());
518 }
519
encode(std::vector<uint8_t>& buf) const520 void BinprotGetAndLockCommand::encode(std::vector<uint8_t>& buf) const {
521 writeHeader(buf, 0, sizeof(lock_timeout));
522 cb::mcbp::request::GetLockedPayload payload;
523 payload.setExpiration(lock_timeout);
524 auto extras = payload.getBuffer();
525 buf.insert(buf.end(), extras.begin(), extras.end());
526 buf.insert(buf.end(), key.begin(), key.end());
527 }
BinprotGetAndLockCommand()528 BinprotGetAndLockCommand::BinprotGetAndLockCommand()
529 : BinprotCommandT(), lock_timeout(0) {
530 }
setLockTimeout( uint32_t timeout)531 BinprotGetAndLockCommand& BinprotGetAndLockCommand::setLockTimeout(
532 uint32_t timeout) {
533 lock_timeout = timeout;
534 return *this;
535 }
536
encode(std::vector<uint8_t>& buf) const537 void BinprotGetAndTouchCommand::encode(std::vector<uint8_t>& buf) const {
538 writeHeader(buf, 0, sizeof(expirytime));
539 cb::mcbp::request::GatPayload extras;
540 extras.setExpiration(expirytime);
541 auto buffer = extras.getBuffer();
542 buf.insert(buf.end(), buffer.begin(), buffer.end());
543 buf.insert(buf.end(), key.begin(), key.end());
544 }
BinprotGetAndTouchCommand(std::string key, uint32_t exp)545 BinprotGetAndTouchCommand::BinprotGetAndTouchCommand(std::string key,
546 uint32_t exp)
547 : BinprotCommandT(), expirytime(exp) {
548 setKey(std::move(key));
549 }
isQuiet() const550 bool BinprotGetAndTouchCommand::isQuiet() const {
551 return getOp() == cb::mcbp::ClientOpcode::Gatq;
552 }
setQuiet(bool quiet)553 BinprotGetAndTouchCommand& BinprotGetAndTouchCommand::setQuiet(bool quiet) {
554 setOp(quiet ? cb::mcbp::ClientOpcode::Gatq : cb::mcbp::ClientOpcode::Gat);
555 return *this;
556 }
setExpirytime( uint32_t timeout)557 BinprotGetAndTouchCommand& BinprotGetAndTouchCommand::setExpirytime(
558 uint32_t timeout) {
559 expirytime = timeout;
560 return *this;
561 }
562
BinprotTouchCommand(std::string key, uint32_t exp)563 BinprotTouchCommand::BinprotTouchCommand(std::string key, uint32_t exp)
564 : BinprotCommandT(), expirytime(exp) {
565 setKey(std::move(key));
566 }
567
encode(std::vector<uint8_t>& buf) const568 void BinprotTouchCommand::encode(std::vector<uint8_t>& buf) const {
569 writeHeader(buf, 0, sizeof(expirytime));
570 cb::mcbp::request::TouchPayload extras;
571 extras.setExpiration(expirytime);
572 auto buffer = extras.getBuffer();
573 buf.insert(buf.end(), buffer.begin(), buffer.end());
574 buf.insert(buf.end(), key.begin(), key.end());
575 }
setExpirytime(uint32_t timeout)576 BinprotTouchCommand& BinprotTouchCommand::setExpirytime(uint32_t timeout) {
577 expirytime = timeout;
578 return *this;
579 }
580
encode(std::vector<uint8_t>& buf) const581 void BinprotUnlockCommand::encode(std::vector<uint8_t>& buf) const {
582 writeHeader(buf, 0, 0);
583 buf.insert(buf.end(), key.begin(), key.end());
584 }
585
getDocumentFlags() const586 uint32_t BinprotGetResponse::getDocumentFlags() const {
587 if (!isSuccess()) {
588 return 0;
589 }
590
591 auto extras = getResponse().getExtdata();
592 if (extras.size() < sizeof(uint32_t)) {
593 throw std::runtime_error(
594 "BinprotGetResponse::getDocumentFlags(): extras does not "
595 "contain flags");
596 }
597
598 return ntohl(*reinterpret_cast<const uint32_t*>(extras.data()));
599 }
600
setMutationType( MutationType type)601 BinprotMutationCommand& BinprotMutationCommand::setMutationType(
602 MutationType type) {
603 switch (type) {
604 case MutationType::Add:
605 setOp(cb::mcbp::ClientOpcode::Add);
606 return *this;
607 case MutationType::Set:
608 setOp(cb::mcbp::ClientOpcode::Set);
609 return *this;
610 case MutationType::Replace:
611 setOp(cb::mcbp::ClientOpcode::Replace);
612 return *this;
613 case MutationType::Append:
614 setOp(cb::mcbp::ClientOpcode::Append);
615 return *this;
616 case MutationType::Prepend:
617 setOp(cb::mcbp::ClientOpcode::Prepend);
618 return *this;
619 }
620
621 throw std::invalid_argument(
622 "BinprotMutationCommand::setMutationType: Mutation type not supported: " +
623 std::to_string(int(type)));
624 }
625
to_string(MutationType type)626 std::string to_string(MutationType type) {
627 switch (type) {
628 case MutationType::Add: return "ADD";
629 case MutationType::Set: return "SET";
630 case MutationType::Replace: return "REPLACE";
631 case MutationType::Append: return "APPEND";
632 case MutationType::Prepend: return "PREPEND";
633 }
634
635 return "to_string(MutationType type) Unknown type: " +
636 std::to_string(int(type));
637 }
638
setDocumentInfo( const DocumentInfo& info)639 BinprotMutationCommand& BinprotMutationCommand::setDocumentInfo(
640 const DocumentInfo& info) {
641 if (!info.id.empty()) {
642 setKey(info.id);
643 }
644
645 setDocumentFlags(info.flags);
646 setCas(info.cas);
647 setExpiry(info.expiration);
648
649 datatype = uint8_t(info.datatype);
650 return *this;
651 }
652
encodeHeader(std::vector<uint8_t>& buf) const653 void BinprotMutationCommand::encodeHeader(std::vector<uint8_t>& buf) const {
654 if (key.empty()) {
655 throw std::invalid_argument("BinprotMutationCommand::encode: Key is missing!");
656 }
657 if (!value.empty() && !value_refs.empty()) {
658 throw std::invalid_argument("BinprotMutationCommand::encode: Both value and value_refs have items!");
659 }
660
661 uint8_t extlen = 8;
662 if (getOp() == cb::mcbp::ClientOpcode::Append ||
663 getOp() == cb::mcbp::ClientOpcode::Prepend) {
664 if (expiry.getValue() != 0) {
665 throw std::invalid_argument("BinprotMutationCommand::encode: Expiry invalid with append/prepend");
666 }
667 extlen = 0;
668 }
669
670 size_t value_size = value.size();
671 for (const auto& vbuf : value_refs) {
672 value_size += vbuf.size();
673 }
674
675 writeHeader(buf, value_size, extlen);
676 auto* header = reinterpret_cast<cb::mcbp::Request*>(buf.data());
677 header->setDatatype(cb::mcbp::Datatype(datatype));
678
679 if (extlen != 0) {
680 // Write the extras:
681 cb::mcbp::request::MutationPayload mp;
682 mp.setFlags(flags);
683 mp.setExpiration(expiry.getValue());
684 auto buffer = mp.getBuffer();
685 buf.insert(buf.end(), buffer.begin(), buffer.end());
686 }
687 }
688
encode(std::vector<uint8_t>& buf) const689 void BinprotMutationCommand::encode(std::vector<uint8_t>& buf) const {
690 encodeHeader(buf);
691 buf.insert(buf.end(), key.begin(), key.end());
692 buf.insert(buf.end(), value.begin(), value.end());
693 for (const auto& vbuf : value_refs) {
694 buf.insert(buf.end(), vbuf.begin(), vbuf.end());
695 }
696 }
697
encode() const698 BinprotCommand::Encoded BinprotMutationCommand::encode() const {
699 Encoded ret;
700 auto& hdrbuf = ret.header;
701 encodeHeader(hdrbuf);
702 hdrbuf.insert(hdrbuf.end(), key.begin(), key.end());
703 hdrbuf.insert(hdrbuf.end(), value.begin(), value.end());
704
705 ret.bufs.assign(value_refs.begin(), value_refs.end());
706 return ret;
707 }
setValue( std::vector<uint8_t>&& value_)708 BinprotMutationCommand& BinprotMutationCommand::setValue(
709 std::vector<uint8_t>&& value_) {
710 value = std::move(value_);
711 return *this;
712 }
713 template <typename T>
setValue(const T& value_)714 BinprotMutationCommand& BinprotMutationCommand::setValue(const T& value_) {
715 value.assign(value_.begin(), value_.end());
716 return *this;
717 }
addValueBuffer( cb::const_byte_buffer buf)718 BinprotMutationCommand& BinprotMutationCommand::addValueBuffer(
719 cb::const_byte_buffer buf) {
720 value_refs.emplace_back(buf);
721 return *this;
722 }
setDatatype(uint8_t datatype_)723 BinprotMutationCommand& BinprotMutationCommand::setDatatype(uint8_t datatype_) {
724 datatype = datatype_;
725 return *this;
726 }
setDatatype( cb::mcbp::Datatype datatype_)727 BinprotMutationCommand& BinprotMutationCommand::setDatatype(
728 cb::mcbp::Datatype datatype_) {
729 return setDatatype(uint8_t(datatype_));
730 }
setDocumentFlags( uint32_t flags_)731 BinprotMutationCommand& BinprotMutationCommand::setDocumentFlags(
732 uint32_t flags_) {
733 flags = flags_;
734 return *this;
735 }
setExpiry(uint32_t expiry_)736 BinprotMutationCommand& BinprotMutationCommand::setExpiry(uint32_t expiry_) {
737 expiry.assign(expiry_);
738 return *this;
739 }
740
BinprotMutationResponse(BinprotResponse&& other)741 BinprotMutationResponse::BinprotMutationResponse(BinprotResponse&& other)
742 : BinprotResponse(other) {
743 decode();
744 }
745
assign(std::vector<uint8_t>&& buf)746 void BinprotMutationResponse::assign(std::vector<uint8_t>&& buf) {
747 BinprotResponse::assign(std::move(buf));
748 decode();
749 }
750
getMutationInfo() const751 const MutationInfo& BinprotMutationResponse::getMutationInfo() const {
752 return mutation_info;
753 }
754
decode()755 void BinprotMutationResponse::decode() {
756 if (!isSuccess()) {
757 // No point parsing the other info..
758 return;
759 }
760
761 mutation_info.cas = getCas();
762 mutation_info.size = 0; // TODO: what's this?
763
764 const auto extras = getResponse().getExtdata();
765
766 if (extras.empty()) {
767 mutation_info.vbucketuuid = 0;
768 mutation_info.seqno = 0;
769 } else if (extras.size() == 16) {
770 auto const* bufs = reinterpret_cast<const uint64_t*>(extras.data());
771 mutation_info.vbucketuuid = ntohll(bufs[0]);
772 mutation_info.seqno = ntohll(bufs[1]);
773 } else {
774 throw std::runtime_error(
775 "BinprotMutationResponse::decode: Bad extras length");
776 }
777 }
778
encode(std::vector<uint8_t>& buf) const779 void BinprotHelloCommand::encode(std::vector<uint8_t>& buf) const {
780 writeHeader(buf, features.size() * 2, 0);
781 buf.insert(buf.end(), key.begin(), key.end());
782
783 for (auto f : features) {
784 uint16_t enc = htons(f);
785 const char* p = reinterpret_cast<const char*>(&enc);
786 buf.insert(buf.end(), p, p + 2);
787 }
788 }
BinprotHelloCommand(const std::string& client_id)789 BinprotHelloCommand::BinprotHelloCommand(const std::string& client_id)
790 : BinprotCommandT() {
791 setKey(client_id);
792 }
enableFeature( cb::mcbp::Feature feature, bool enabled)793 BinprotHelloCommand& BinprotHelloCommand::enableFeature(
794 cb::mcbp::Feature feature, bool enabled) {
795 if (enabled) {
796 features.insert(static_cast<uint8_t>(feature));
797 } else {
798 features.erase(static_cast<uint8_t>(feature));
799 }
800 return *this;
801 }
802
BinprotHelloResponse(BinprotResponse&& other)803 BinprotHelloResponse::BinprotHelloResponse(BinprotResponse&& other)
804 : BinprotResponse(other) {
805 decode();
806 }
807
assign(std::vector<uint8_t>&& buf)808 void BinprotHelloResponse::assign(std::vector<uint8_t>&& buf) {
809 BinprotResponse::assign(std::move(buf));
810 decode();
811 }
getFeatures() const812 const std::vector<cb::mcbp::Feature>& BinprotHelloResponse::getFeatures()
813 const {
814 return features;
815 }
816
decode()817 void BinprotHelloResponse::decode() {
818 if (isSuccess()) {
819 // Ensure body length is even
820 auto value = getResponse().getValue();
821
822 if ((value.size() & 1) != 0) {
823 throw std::runtime_error(
824 "BinprotHelloResponse::assign: "
825 "Invalid response returned. "
826 "Uneven body length");
827 }
828
829 auto const* end =
830 reinterpret_cast<const uint16_t*>(value.data() + value.size());
831 auto const* cur = reinterpret_cast<const uint16_t*>(value.data());
832
833 for (; cur != end; ++cur) {
834 features.push_back(cb::mcbp::Feature(htons(*cur)));
835 }
836 }
837 }
838
encode(std::vector<uint8_t>& buf) const839 void BinprotIncrDecrCommand::encode(std::vector<uint8_t>& buf) const {
840 writeHeader(buf, 0, 20);
841 if (getOp() != cb::mcbp::ClientOpcode::Decrement &&
842 getOp() != cb::mcbp::ClientOpcode::Increment) {
843 throw std::invalid_argument(
844 "BinprotIncrDecrCommand::encode: Invalid opcode. Need INCREMENT or DECREMENT");
845 }
846
847 // Write the delta
848 for (auto n : std::array<uint64_t, 2>{{delta, initial}}) {
849 uint64_t tmp = htonll(n);
850 auto const* p = reinterpret_cast<const char*>(&tmp);
851 buf.insert(buf.end(), p, p + 8);
852 }
853
854 uint32_t exptmp = htonl(expiry.getValue());
855 auto const* p = reinterpret_cast<const char*>(&exptmp);
856 buf.insert(buf.end(), p, p + 4);
857 buf.insert(buf.end(), key.begin(), key.end());
858 }
setDelta(uint64_t delta_)859 BinprotIncrDecrCommand& BinprotIncrDecrCommand::setDelta(uint64_t delta_) {
860 delta = delta_;
861 return *this;
862 }
setInitialValue( uint64_t initial_)863 BinprotIncrDecrCommand& BinprotIncrDecrCommand::setInitialValue(
864 uint64_t initial_) {
865 initial = initial_;
866 return *this;
867 }
setExpiry(uint32_t expiry_)868 BinprotIncrDecrCommand& BinprotIncrDecrCommand::setExpiry(uint32_t expiry_) {
869 expiry.assign(expiry_);
870 return *this;
871 }
872
BinprotIncrDecrResponse(BinprotResponse&& other)873 BinprotIncrDecrResponse::BinprotIncrDecrResponse(BinprotResponse&& other)
874 : BinprotMutationResponse(std::move(other)) {
875 decode();
876 }
877
assign(std::vector<uint8_t>&& buf)878 void BinprotIncrDecrResponse::assign(std::vector<uint8_t>&& buf) {
879 BinprotMutationResponse::assign(std::move(buf));
880 decode();
881 }
882
getValue() const883 uint64_t BinprotIncrDecrResponse::getValue() const {
884 return value;
885 }
886
decode()887 void BinprotIncrDecrResponse::decode() {
888 if (isSuccess()) {
889 value = htonll(*reinterpret_cast<const uint64_t*>(getData().data()));
890 } else {
891 value = 0;
892 }
893 }
894
encode(std::vector<uint8_t>& buf) const895 void BinprotRemoveCommand::encode(std::vector<uint8_t>& buf) const {
896 writeHeader(buf, 0, 0);
897 buf.insert(buf.end(), key.begin(), key.end());
898 }
899
encode(std::vector<uint8_t>& buf) const900 void BinprotGetErrorMapCommand::encode(std::vector<uint8_t>& buf) const {
901 writeHeader(buf, 2, 0);
902 uint16_t encversion = htons(version);
903 const char* p = reinterpret_cast<const char*>(&encversion);
904 buf.insert(buf.end(), p, p + 2);
905 }
setVersion(uint16_t version_)906 void BinprotGetErrorMapCommand::setVersion(uint16_t version_) {
907 version = version_;
908 }
909
encode(std::vector<uint8_t>& buf) const910 void BinprotSubdocMultiMutationCommand::encode(std::vector<uint8_t>& buf) const {
911 // Calculate the size of the payload
912 size_t total = 0;
913 for (const auto& spec : specs) {
914 // Accorrding to the spec the payload should be encoded as:
915 // 1 @0 : Opcode
916 // 1 @1 : Flags
917 // 2 @2 : Path Length
918 // 4 @4 : Value Length
919 // pathlen @8 : Path
920 // vallen @8+pathlen : Value
921 total += 1 + 1 + 2 + 4 + spec.path.size() + spec.value.size();
922 }
923
924 const uint8_t extlen =
925 (expiry.isSet() ? 4 : 0) + (!isNone(docFlags) ? 1 : 0);
926 writeHeader(buf, total, extlen);
927 if (expiry.isSet()) {
928 uint32_t expbuf = htonl(expiry.getValue());
929 const char* p = reinterpret_cast<const char*>(&expbuf);
930 buf.insert(buf.end(), p, p + 4);
931 }
932 if (!isNone(docFlags)) {
933 const uint8_t* doc_flag_ptr =
934 reinterpret_cast<const uint8_t*>(&docFlags);
935 buf.insert(buf.end(), doc_flag_ptr, doc_flag_ptr + sizeof(uint8_t));
936 }
937
938 buf.insert(buf.end(), key.begin(), key.end());
939
940 // Time to add the data:
941 for (const auto& spec : specs) {
942 buf.push_back(uint8_t(spec.opcode));
943 buf.push_back(uint8_t(spec.flags));
944 uint16_t pathlen = ntohs(gsl::narrow<uint16_t>(spec.path.size()));
945 const char* p = reinterpret_cast<const char*>(&pathlen);
946 buf.insert(buf.end(), p, p + 2);
947 uint32_t vallen = ntohl(gsl::narrow<uint32_t>(spec.value.size()));
948 p = reinterpret_cast<const char*>(&vallen);
949 buf.insert(buf.end(), p, p + 4);
950 buf.insert(buf.end(), spec.path.begin(), spec.path.end());
951 buf.insert(buf.end(), spec.value.begin(), spec.value.end());
952 }
953 }
BinprotSubdocMultiMutationCommand()954 BinprotSubdocMultiMutationCommand::BinprotSubdocMultiMutationCommand()
955 : BinprotCommandT<BinprotSubdocMultiMutationCommand,
956 cb::mcbp::ClientOpcode::SubdocMultiMutation>(),
957 docFlags(mcbp::subdoc::doc_flag::None) {
958 }
959
BinprotSubdocMultiMutationCommand( std::string key, std::vector<MutationSpecifier> specs, mcbp::subdoc::doc_flag docFlags, const boost::optional<cb::durability::Requirements>& durReqs)960 BinprotSubdocMultiMutationCommand::BinprotSubdocMultiMutationCommand(
961 std::string key,
962 std::vector<MutationSpecifier> specs,
963 mcbp::subdoc::doc_flag docFlags,
964 const boost::optional<cb::durability::Requirements>& durReqs)
965 : specs(std::move(specs)), docFlags(docFlags) {
966 setKey(key);
967 if (durReqs) {
968 setDurabilityReqs(*durReqs);
969 }
970 }
971
972 BinprotSubdocMultiMutationCommand&
addDocFlag(mcbp::subdoc::doc_flag docFlag)973 BinprotSubdocMultiMutationCommand::addDocFlag(mcbp::subdoc::doc_flag docFlag) {
974 using namespace mcbp::subdoc;
975 constexpr doc_flag validFlags = doc_flag::Mkdoc | doc_flag::AccessDeleted |
976 doc_flag::Add | doc_flag::CreateAsDeleted;
977 if ((docFlag & ~validFlags) == doc_flag::None) {
978 docFlags = docFlags | docFlag;
979 } else {
980 throw std::invalid_argument("addDocFlag: docFlag (Which is " +
981 to_string(docFlag) + ") is not a doc flag");
982 }
983 return *this;
984 }
985 BinprotSubdocMultiMutationCommand&
addMutation( const BinprotSubdocMultiMutationCommand::MutationSpecifier& spec)986 BinprotSubdocMultiMutationCommand::addMutation(
987 const BinprotSubdocMultiMutationCommand::MutationSpecifier& spec) {
988 specs.push_back(spec);
989 return *this;
990 }
991 BinprotSubdocMultiMutationCommand&
addMutation( cb::mcbp::ClientOpcode opcode, protocol_binary_subdoc_flag flags, const std::string& path, const std::string& value)992 BinprotSubdocMultiMutationCommand::addMutation(
993 cb::mcbp::ClientOpcode opcode,
994 protocol_binary_subdoc_flag flags,
995 const std::string& path,
996 const std::string& value) {
997 specs.emplace_back(MutationSpecifier{opcode, flags, path, value});
998 return *this;
999 }
setExpiry( uint32_t expiry_)1000 BinprotSubdocMultiMutationCommand& BinprotSubdocMultiMutationCommand::setExpiry(
1001 uint32_t expiry_) {
1002 expiry.assign(expiry_);
1003 return *this;
1004 }
1005
1006 BinprotSubdocMultiMutationCommand&
setDurabilityReqs( const cb::durability::Requirements& durReqs)1007 BinprotSubdocMultiMutationCommand::setDurabilityReqs(
1008 const cb::durability::Requirements& durReqs) {
1009 addFrameInfo(DurabilityFrameInfo(durReqs.getLevel(), durReqs.getTimeout()));
1010 return *this;
1011 }
1012
1013 BinprotSubdocMultiMutationCommand::MutationSpecifier&
at(size_t index)1014 BinprotSubdocMultiMutationCommand::at(size_t index) {
1015 return specs.at(index);
1016 }
1017 BinprotSubdocMultiMutationCommand::MutationSpecifier&
operator [](size_t index)1018 BinprotSubdocMultiMutationCommand::operator[](size_t index) {
1019 return specs[index];
1020 }
empty() const1021 bool BinprotSubdocMultiMutationCommand::empty() const {
1022 return specs.empty();
1023 }
size() const1024 size_t BinprotSubdocMultiMutationCommand::size() const {
1025 return specs.size();
1026 }
clearMutations()1027 void BinprotSubdocMultiMutationCommand::clearMutations() {
1028 specs.clear();
1029 }
clearDocFlags()1030 void BinprotSubdocMultiMutationCommand::clearDocFlags() {
1031 docFlags = mcbp::subdoc::doc_flag::None;
1032 }
1033
assign(std::vector<uint8_t>&& buf)1034 void BinprotSubdocMultiMutationResponse::assign(std::vector<uint8_t>&& buf) {
1035 BinprotResponse::assign(std::move(buf));
1036 switch (getStatus()) {
1037 case cb::mcbp::Status::Success:
1038 case cb::mcbp::Status::SubdocMultiPathFailure:
1039 break;
1040 default:
1041 return;
1042 }
1043
1044 const uint8_t* bufcur = getData().data();
1045 const uint8_t* bufend = getData().data() + getData().size();
1046
1047 // Result spec is:
1048 // 1@0 : Request Index
1049 // 2@1 : Status
1050 // 4@3 : Value length -- ONLY if status is success
1051 // $ValueLen@7 : Value
1052
1053 while (bufcur < bufend) {
1054 uint8_t index = *bufcur;
1055 bufcur += 1;
1056
1057 auto cur_status = cb::mcbp::Status(ntohs(*reinterpret_cast<const uint16_t*>(bufcur)));
1058 bufcur += 2;
1059
1060 if (cur_status == cb::mcbp::Status::Success) {
1061 uint32_t cur_len =
1062 ntohl(*reinterpret_cast<const uint32_t*>(bufcur));
1063 bufcur += 4;
1064 if (cur_len > bufend - bufcur) {
1065 throw std::runtime_error(
1066 "BinprotSubdocMultiMutationResponse::assign(): "
1067 "Invalid value length received");
1068 }
1069 results.emplace_back(MutationResult{
1070 index,
1071 cur_status,
1072 std::string(reinterpret_cast<const char*>(bufcur),
1073 cur_len)});
1074 bufcur += cur_len;
1075 } else {
1076 results.emplace_back(MutationResult{index, cur_status});
1077 }
1078 }
1079 }
clear()1080 void BinprotSubdocMultiMutationResponse::clear() {
1081 BinprotResponse::clear();
1082 results.clear();
1083 }
1084 const std::vector<BinprotSubdocMultiMutationResponse::MutationResult>&
getResults() const1085 BinprotSubdocMultiMutationResponse::getResults() const {
1086 return results;
1087 }
1088
encode(std::vector<uint8_t>& buf) const1089 void BinprotSubdocMultiLookupCommand::encode(std::vector<uint8_t>& buf) const {
1090 size_t total = 0;
1091 // Payload is to be encoded as:
1092 // 1 @0 : Opcode
1093 // 1 @1 : Flags
1094 // 2 @2 : Path Length
1095 // $pathlen @4 : Path
1096 for (const auto& spec : specs) {
1097 total += 1 + 1 + 2 + spec.path.size();
1098 }
1099
1100 const uint8_t extlen =
1101 (expiry.isSet() ? 4 : 0) + (!isNone(docFlags) ? 1 : 0);
1102 writeHeader(buf, total, extlen);
1103
1104 // Note: Expiry isn't supported for multi lookups, but we specifically
1105 // test for it, and therefore allowed at the API level
1106 if (expiry.isSet()) {
1107 uint32_t expbuf = htonl(expiry.getValue());
1108 const char* p = reinterpret_cast<const char*>(&expbuf);
1109 buf.insert(buf.end(), p, p + 4);
1110 }
1111 if (!isNone(docFlags)) {
1112 const uint8_t* doc_flag_ptr =
1113 reinterpret_cast<const uint8_t*>(&docFlags);
1114 buf.insert(buf.end(), doc_flag_ptr, doc_flag_ptr + sizeof(uint8_t));
1115 }
1116
1117 buf.insert(buf.end(), key.begin(), key.end());
1118
1119 // Add the lookup specs themselves:
1120 for (const auto& spec : specs) {
1121 buf.push_back(uint8_t(spec.opcode));
1122 buf.push_back(uint8_t(spec.flags));
1123
1124 uint16_t pathlen = ntohs(gsl::narrow<uint16_t>(spec.path.size()));
1125 const char* p = reinterpret_cast<const char*>(&pathlen);
1126 buf.insert(buf.end(), p, p + 2);
1127 buf.insert(buf.end(), spec.path.begin(), spec.path.end());
1128 }
1129 }
BinprotSubdocMultiLookupCommand()1130 BinprotSubdocMultiLookupCommand::BinprotSubdocMultiLookupCommand()
1131 : BinprotCommandT<BinprotSubdocMultiLookupCommand,
1132 cb::mcbp::ClientOpcode::SubdocMultiLookup>(),
1133 docFlags(mcbp::subdoc::doc_flag::None) {
1134 }
1135
BinprotSubdocMultiLookupCommand( std::string key, std::vector<LookupSpecifier> specs, mcbp::subdoc::doc_flag docFlags)1136 BinprotSubdocMultiLookupCommand::BinprotSubdocMultiLookupCommand(
1137 std::string key,
1138 std::vector<LookupSpecifier> specs,
1139 mcbp::subdoc::doc_flag docFlags)
1140 : specs(std::move(specs)), docFlags(docFlags) {
1141 setKey(key);
1142 }
1143
addLookup( const BinprotSubdocMultiLookupCommand::LookupSpecifier& spec)1144 BinprotSubdocMultiLookupCommand& BinprotSubdocMultiLookupCommand::addLookup(
1145 const BinprotSubdocMultiLookupCommand::LookupSpecifier& spec) {
1146 specs.push_back(spec);
1147 return *this;
1148 }
addLookup( const std::string& path, cb::mcbp::ClientOpcode opcode, protocol_binary_subdoc_flag flags)1149 BinprotSubdocMultiLookupCommand& BinprotSubdocMultiLookupCommand::addLookup(
1150 const std::string& path,
1151 cb::mcbp::ClientOpcode opcode,
1152 protocol_binary_subdoc_flag flags) {
1153 return addLookup({opcode, flags, path});
1154 }
addGet( const std::string& path, protocol_binary_subdoc_flag flags)1155 BinprotSubdocMultiLookupCommand& BinprotSubdocMultiLookupCommand::addGet(
1156 const std::string& path, protocol_binary_subdoc_flag flags) {
1157 return addLookup(path, cb::mcbp::ClientOpcode::SubdocGet, flags);
1158 }
addExists( const std::string& path, protocol_binary_subdoc_flag flags)1159 BinprotSubdocMultiLookupCommand& BinprotSubdocMultiLookupCommand::addExists(
1160 const std::string& path, protocol_binary_subdoc_flag flags) {
1161 return addLookup(path, cb::mcbp::ClientOpcode::SubdocExists, flags);
1162 }
addGetcount( const std::string& path, protocol_binary_subdoc_flag flags)1163 BinprotSubdocMultiLookupCommand& BinprotSubdocMultiLookupCommand::addGetcount(
1164 const std::string& path, protocol_binary_subdoc_flag flags) {
1165 return addLookup(path, cb::mcbp::ClientOpcode::SubdocGetCount, flags);
1166 }
addDocFlag( mcbp::subdoc::doc_flag docFlag)1167 BinprotSubdocMultiLookupCommand& BinprotSubdocMultiLookupCommand::addDocFlag(
1168 mcbp::subdoc::doc_flag docFlag) {
1169 static const mcbp::subdoc::doc_flag validFlags =
1170 mcbp::subdoc::doc_flag::Mkdoc |
1171 mcbp::subdoc::doc_flag::AccessDeleted | mcbp::subdoc::doc_flag::Add;
1172 if ((docFlag & ~validFlags) == mcbp::subdoc::doc_flag::None) {
1173 docFlags = docFlags | docFlag;
1174 } else {
1175 throw std::invalid_argument("addDocFlag: docFlag (Which is " +
1176 to_string(docFlag) + ") is not a doc flag");
1177 }
1178 return *this;
1179 }
clearLookups()1180 void BinprotSubdocMultiLookupCommand::clearLookups() {
1181 specs.clear();
1182 }
1183 BinprotSubdocMultiLookupCommand::LookupSpecifier&
at(size_t index)1184 BinprotSubdocMultiLookupCommand::at(size_t index) {
1185 return specs.at(index);
1186 }
1187 BinprotSubdocMultiLookupCommand::LookupSpecifier&
operator [](size_t index)1188 BinprotSubdocMultiLookupCommand::operator[](size_t index) {
1189 return specs[index];
1190 }
empty() const1191 bool BinprotSubdocMultiLookupCommand::empty() const {
1192 return specs.empty();
1193 }
size() const1194 size_t BinprotSubdocMultiLookupCommand::size() const {
1195 return specs.size();
1196 }
clearDocFlags()1197 void BinprotSubdocMultiLookupCommand::clearDocFlags() {
1198 docFlags = mcbp::subdoc::doc_flag::None;
1199 }
1200 BinprotSubdocMultiLookupCommand&
setExpiry_Unsupported(uint32_t expiry_)1201 BinprotSubdocMultiLookupCommand::setExpiry_Unsupported(uint32_t expiry_) {
1202 expiry.assign(expiry_);
1203 return *this;
1204 }
1205
assign(std::vector<uint8_t>&& buf)1206 void BinprotSubdocMultiLookupResponse::assign(std::vector<uint8_t>&& buf) {
1207 BinprotResponse::assign(std::move(buf));
1208 decode();
1209 }
1210 const std::vector<BinprotSubdocMultiLookupResponse::LookupResult>&
getResults() const1211 BinprotSubdocMultiLookupResponse::getResults() const {
1212 return results;
1213 }
clear()1214 void BinprotSubdocMultiLookupResponse::clear() {
1215 BinprotResponse::clear();
1216 results.clear();
1217 }
1218
decode()1219 void BinprotSubdocMultiLookupResponse::decode() {
1220 // Check if this is a success - either full or partial.
1221 switch (getStatus()) {
1222 case cb::mcbp::Status::Success:
1223 case cb::mcbp::Status::SubdocSuccessDeleted:
1224 case cb::mcbp::Status::SubdocMultiPathFailure:
1225 case cb::mcbp::Status::SubdocMultiPathFailureDeleted:
1226 break;
1227 default:
1228 return;
1229 }
1230
1231 const uint8_t* bufcur = getData().data();
1232 const uint8_t* bufend = getData().data() + getData().size();
1233
1234 // Result spec is:
1235 // 2@0 : Status
1236 // 4@0 : Value Length
1237 // $ValueLen@6 : Value
1238
1239 while (bufcur < bufend) {
1240 uint16_t cur_status = ntohs(*reinterpret_cast<const uint16_t*>(bufcur));
1241 bufcur += 2;
1242
1243 uint32_t cur_len = ntohl(*reinterpret_cast<const uint32_t*>(bufcur));
1244 bufcur += 4;
1245
1246 results.emplace_back(LookupResult{
1247 cb::mcbp::Status(cur_status),
1248 std::string(reinterpret_cast<const char*>(bufcur), cur_len)});
1249 bufcur += cur_len;
1250 }
1251 }
1252
BinprotSubdocMultiLookupResponse( BinprotResponse&& other)1253 BinprotSubdocMultiLookupResponse::BinprotSubdocMultiLookupResponse(
1254 BinprotResponse&& other)
1255 : BinprotResponse(other) {
1256 decode();
1257 }
1258
encode(std::vector<uint8_t>& buf) const1259 void BinprotGetCmdTimerCommand::encode(std::vector<uint8_t>& buf) const {
1260 writeHeader(buf, 0, 1);
1261 buf.push_back(std::underlying_type<cb::mcbp::ClientOpcode>::type(opcode));
1262 buf.insert(buf.end(), key.begin(), key.end());
1263 }
BinprotGetCmdTimerCommand( cb::mcbp::ClientOpcode opcode)1264 BinprotGetCmdTimerCommand::BinprotGetCmdTimerCommand(
1265 cb::mcbp::ClientOpcode opcode)
1266 : BinprotCommandT(), opcode(opcode) {
1267 }
BinprotGetCmdTimerCommand( const std::string& bucket, cb::mcbp::ClientOpcode opcode)1268 BinprotGetCmdTimerCommand::BinprotGetCmdTimerCommand(
1269 const std::string& bucket, cb::mcbp::ClientOpcode opcode)
1270 : BinprotCommandT(), opcode(opcode) {
1271 setKey(bucket);
1272 }
setOpcode(cb::mcbp::ClientOpcode opcode)1273 void BinprotGetCmdTimerCommand::setOpcode(cb::mcbp::ClientOpcode opcode) {
1274 BinprotGetCmdTimerCommand::opcode = opcode;
1275 }
setBucket(const std::string& bucket)1276 void BinprotGetCmdTimerCommand::setBucket(const std::string& bucket) {
1277 setKey(bucket);
1278 }
1279
assign(std::vector<uint8_t>&& buf)1280 void BinprotGetCmdTimerResponse::assign(std::vector<uint8_t>&& buf) {
1281 BinprotResponse::assign(std::move(buf));
1282 if (isSuccess()) {
1283 try {
1284 timings = nlohmann::json::parse(getDataString());
1285 } catch(nlohmann::json::exception& e) {
1286 std::string msg("BinprotGetCmdTimerResponse::assign: Invalid payload returned");
1287 msg + " Reason: " + e.what();
1288 throw std::runtime_error(msg);
1289 }
1290 }
1291 }
getTimings() const1292 nlohmann::json BinprotGetCmdTimerResponse::getTimings() const {
1293 return timings;
1294 }
1295
encode(std::vector<uint8_t>& buf) const1296 void BinprotVerbosityCommand::encode(std::vector<uint8_t>& buf) const {
1297 writeHeader(buf, 0, 4);
1298 uint32_t value = ntohl(level);
1299 auto const* p = reinterpret_cast<const char*>(&value);
1300 buf.insert(buf.end(), p, p + 4);
1301 }
setLevel(int level)1302 void BinprotVerbosityCommand::setLevel(int level) {
1303 BinprotVerbosityCommand::level = level;
1304 }
1305
netToHost(uint8_t x)1306 static uint8_t netToHost(uint8_t x) {
1307 return x;
1308 }
1309
netToHost(uint64_t x)1310 static uint64_t netToHost(uint64_t x) {
1311 return ntohll(x);
1312 }
1313
netToHost(Vbid x)1314 static Vbid netToHost(Vbid x) {
1315 return x.ntoh();
1316 }
1317
1318 /**
1319 * Extract the specified type from the buffer position. Returns an iterator
1320 * to the next element after the type extracted.
1321 */
1322 template <typename T>
extract( std::vector<uint8_t>::iterator pos, T& value)1323 static std::vector<uint8_t>::iterator extract(
1324 std::vector<uint8_t>::iterator pos, T& value) {
1325 auto* p = reinterpret_cast<T*>(&*pos);
1326 value = netToHost(*p);
1327 return pos + sizeof(T);
1328 }
1329
1330 /**
1331 * Append a 64 bit integer to the buffer in network byte order
1332 *
1333 * @param buf the buffer to add the data to
1334 * @param value The value (in host local byteorder) to add.
1335 */
append(std::vector<uint8_t>& buf, uint64_t value)1336 void append(std::vector<uint8_t>& buf, uint64_t value) {
1337 uint64_t vallen = htonll(value);
1338 auto p = reinterpret_cast<const char*>(&vallen);
1339 buf.insert(buf.end(), p, p + 8);
1340 }
1341
encode(std::vector<uint8_t>& buf) const1342 void BinprotDcpOpenCommand::encode(std::vector<uint8_t>& buf) const {
1343 if (payload.empty()) {
1344 writeHeader(buf, 0, 8);
1345 append(buf, seqno);
1346 append(buf, flags);
1347 buf.insert(buf.end(), key.begin(), key.end());
1348 } else {
1349 const auto json = payload.dump();
1350 writeHeader(buf, json.size(), 8);
1351 append(buf, seqno);
1352 append(buf, flags);
1353 buf.insert(buf.end(), key.begin(), key.end());
1354 buf.insert(buf.end(), json.cbegin(), json.cend());
1355 auto& req = *reinterpret_cast<cb::mcbp::Request*>(buf.data());
1356 req.setDatatype(cb::mcbp::Datatype::JSON);
1357 }
1358 }
1359
setConsumerName(std::string name)1360 void BinprotDcpOpenCommand::setConsumerName(std::string name) {
1361 payload["consumer_name"] = name;
1362 }
1363
BinprotDcpOpenCommand(const std::string& name, uint32_t seqno_, uint32_t flags_)1364 BinprotDcpOpenCommand::BinprotDcpOpenCommand(const std::string& name,
1365 uint32_t seqno_,
1366 uint32_t flags_)
1367 : BinprotGenericCommand(cb::mcbp::ClientOpcode::DcpOpen, name, {}),
1368 seqno(seqno_),
1369 flags(flags_) {
1370 }
makeProducer()1371 BinprotDcpOpenCommand& BinprotDcpOpenCommand::makeProducer() {
1372 if (flags & cb::mcbp::request::DcpOpenPayload::Notifier) {
1373 throw std::invalid_argument(
1374 "BinprotDcpOpenCommand::makeProducer: a stream can't be both a "
1375 "consumer and producer");
1376 }
1377 flags |= cb::mcbp::request::DcpOpenPayload::Producer;
1378 return *this;
1379 }
makeConsumer()1380 BinprotDcpOpenCommand& BinprotDcpOpenCommand::makeConsumer() {
1381 if (flags & cb::mcbp::request::DcpOpenPayload::Producer) {
1382 throw std::invalid_argument(
1383 "BinprotDcpOpenCommand::makeConsumer: a stream can't be both a "
1384 "consumer and producer");
1385 }
1386 flags |= cb::mcbp::request::DcpOpenPayload::Notifier;
1387 return *this;
1388 }
makeIncludeXattr()1389 BinprotDcpOpenCommand& BinprotDcpOpenCommand::makeIncludeXattr() {
1390 flags |= cb::mcbp::request::DcpOpenPayload::IncludeXattrs;
1391 return *this;
1392 }
makeNoValue()1393 BinprotDcpOpenCommand& BinprotDcpOpenCommand::makeNoValue() {
1394 flags |= cb::mcbp::request::DcpOpenPayload::NoValue;
1395 return *this;
1396 }
setFlags(uint32_t flags)1397 BinprotDcpOpenCommand& BinprotDcpOpenCommand::setFlags(uint32_t flags) {
1398 BinprotDcpOpenCommand::flags = flags;
1399 return *this;
1400 }
1401
encode(std::vector<uint8_t>& buf) const1402 void BinprotDcpStreamRequestCommand::encode(std::vector<uint8_t>& buf) const {
1403 writeHeader(buf, 0, 48);
1404 append(buf, dcp_flags);
1405 append(buf, dcp_reserved);
1406 append(buf, dcp_start_seqno);
1407 append(buf, dcp_end_seqno);
1408 append(buf, dcp_vbucket_uuid);
1409 append(buf, dcp_snap_start_seqno);
1410 append(buf, dcp_snap_end_seqno);
1411 }
BinprotDcpStreamRequestCommand()1412 BinprotDcpStreamRequestCommand::BinprotDcpStreamRequestCommand()
1413 : BinprotGenericCommand(cb::mcbp::ClientOpcode::DcpStreamReq, {}, {}),
1414 dcp_flags(0),
1415 dcp_reserved(0),
1416 dcp_start_seqno(std::numeric_limits<uint64_t>::min()),
1417 dcp_end_seqno(std::numeric_limits<uint64_t>::max()),
1418 dcp_vbucket_uuid(0),
1419 dcp_snap_start_seqno(std::numeric_limits<uint64_t>::min()),
1420 dcp_snap_end_seqno(std::numeric_limits<uint64_t>::max()) {
1421 }
setDcpFlags( uint32_t value)1422 BinprotDcpStreamRequestCommand& BinprotDcpStreamRequestCommand::setDcpFlags(
1423 uint32_t value) {
1424 BinprotDcpStreamRequestCommand::dcp_flags = value;
1425 return *this;
1426 }
setDcpReserved( uint32_t value)1427 BinprotDcpStreamRequestCommand& BinprotDcpStreamRequestCommand::setDcpReserved(
1428 uint32_t value) {
1429 BinprotDcpStreamRequestCommand::dcp_reserved = value;
1430 return *this;
1431 }
1432 BinprotDcpStreamRequestCommand&
setDcpStartSeqno(uint64_t value)1433 BinprotDcpStreamRequestCommand::setDcpStartSeqno(uint64_t value) {
1434 BinprotDcpStreamRequestCommand::dcp_start_seqno = value;
1435 return *this;
1436 }
setDcpEndSeqno( uint64_t value)1437 BinprotDcpStreamRequestCommand& BinprotDcpStreamRequestCommand::setDcpEndSeqno(
1438 uint64_t value) {
1439 BinprotDcpStreamRequestCommand::dcp_end_seqno = value;
1440 return *this;
1441 }
1442 BinprotDcpStreamRequestCommand&
setDcpVbucketUuid(uint64_t value)1443 BinprotDcpStreamRequestCommand::setDcpVbucketUuid(uint64_t value) {
1444 BinprotDcpStreamRequestCommand::dcp_vbucket_uuid = value;
1445 return *this;
1446 }
1447 BinprotDcpStreamRequestCommand&
setDcpSnapStartSeqno(uint64_t value)1448 BinprotDcpStreamRequestCommand::setDcpSnapStartSeqno(uint64_t value) {
1449 BinprotDcpStreamRequestCommand::dcp_snap_start_seqno = value;
1450 return *this;
1451 }
1452 BinprotDcpStreamRequestCommand&
setDcpSnapEndSeqno(uint64_t value)1453 BinprotDcpStreamRequestCommand::setDcpSnapEndSeqno(uint64_t value) {
1454 BinprotDcpStreamRequestCommand::dcp_snap_end_seqno = value;
1455 return *this;
1456 }
1457
encode(std::vector<uint8_t>& buf) const1458 void BinprotSetParamCommand::encode(std::vector<uint8_t>& buf) const {
1459 writeHeader(buf, value.size(), 4);
1460 cb::mcbp::request::SetParamPayload payload;
1461 payload.setParamType(type);
1462 auto extra = payload.getBuffer();
1463 buf.insert(buf.end(), extra.begin(), extra.end());
1464 buf.insert(buf.end(), key.begin(), key.end());
1465 buf.insert(buf.end(), value.begin(), value.end());
1466 }
BinprotSetParamCommand( cb::mcbp::request::SetParamPayload::Type type_, const std::string& key_, const std::string& value_)1467 BinprotSetParamCommand::BinprotSetParamCommand(
1468 cb::mcbp::request::SetParamPayload::Type type_,
1469 const std::string& key_,
1470 const std::string& value_)
1471 : BinprotGenericCommand(cb::mcbp::ClientOpcode::SetParam),
1472 type(type_),
1473 value(value_) {
1474 setKey(key_);
1475 }
1476
encode(std::vector<uint8_t>& buf) const1477 void BinprotSetWithMetaCommand::encode(std::vector<uint8_t>& buf) const {
1478 size_t extlen = 24;
1479 if (options) {
1480 extlen += 4;
1481 }
1482
1483 if (!meta.empty()) {
1484 extlen += 2;
1485 }
1486
1487 writeHeader(buf, doc.value.size(), extlen);
1488 auto& request = *reinterpret_cast<cb::mcbp::Request*>(buf.data());
1489 request.setDatatype(doc.info.datatype);
1490 append(buf, getFlags());
1491 append(buf, getExptime());
1492 append(buf, seqno);
1493 append(buf, getMetaCas());
1494
1495 if (options) {
1496 append(buf, options);
1497 }
1498
1499 if (!meta.empty()) {
1500 append(buf, uint16_t(htons(gsl::narrow<uint16_t>(meta.size()))));
1501 }
1502
1503 buf.insert(buf.end(), key.begin(), key.end());
1504 buf.insert(buf.end(), doc.value.begin(), doc.value.end());
1505 buf.insert(buf.end(), meta.begin(), meta.end());
1506 }
1507
BinprotSetWithMetaCommand(const Document& doc, Vbid vbucket, uint64_t operationCas, uint64_t seqno, uint32_t options, std::vector<uint8_t>& meta)1508 BinprotSetWithMetaCommand::BinprotSetWithMetaCommand(const Document& doc,
1509 Vbid vbucket,
1510 uint64_t operationCas,
1511 uint64_t seqno,
1512 uint32_t options,
1513 std::vector<uint8_t>& meta)
1514 : BinprotGenericCommand(cb::mcbp::ClientOpcode::SetWithMeta),
1515 doc(doc),
1516 seqno(seqno),
1517 operationCas(operationCas),
1518 options(options),
1519 meta(meta) {
1520 setVBucket(vbucket);
1521 setCas(operationCas);
1522 setKey(doc.info.id);
1523 setMeta(meta);
1524 }
setQuiet(bool quiet)1525 BinprotSetWithMetaCommand& BinprotSetWithMetaCommand::setQuiet(bool quiet) {
1526 if (quiet) {
1527 setOp(cb::mcbp::ClientOpcode::SetqWithMeta);
1528 } else {
1529 setOp(cb::mcbp::ClientOpcode::SetWithMeta);
1530 }
1531 return *this;
1532 }
getFlags() const1533 uint32_t BinprotSetWithMetaCommand::getFlags() const {
1534 return doc.info.flags;
1535 }
setFlags(uint32_t flags)1536 BinprotSetWithMetaCommand& BinprotSetWithMetaCommand::setFlags(uint32_t flags) {
1537 doc.info.flags = flags;
1538 return *this;
1539 }
getExptime() const1540 uint32_t BinprotSetWithMetaCommand::getExptime() const {
1541 return doc.info.expiration;
1542 }
setExptime( uint32_t exptime)1543 BinprotSetWithMetaCommand& BinprotSetWithMetaCommand::setExptime(
1544 uint32_t exptime) {
1545 doc.info.expiration = exptime;
1546 return *this;
1547 }
getSeqno() const1548 uint64_t BinprotSetWithMetaCommand::getSeqno() const {
1549 return seqno;
1550 }
setSeqno(uint64_t seqno)1551 BinprotSetWithMetaCommand& BinprotSetWithMetaCommand::setSeqno(uint64_t seqno) {
1552 BinprotSetWithMetaCommand::seqno = seqno;
1553 return *this;
1554 }
getMetaCas() const1555 uint64_t BinprotSetWithMetaCommand::getMetaCas() const {
1556 return doc.info.cas;
1557 }
setMetaCas(uint64_t cas)1558 BinprotSetWithMetaCommand& BinprotSetWithMetaCommand::setMetaCas(uint64_t cas) {
1559 doc.info.cas = cas;
1560 return *this;
1561 }
getMeta()1562 const std::vector<uint8_t>& BinprotSetWithMetaCommand::getMeta() {
1563 return meta;
1564 }
setMeta( const std::vector<uint8_t>& meta)1565 BinprotSetWithMetaCommand& BinprotSetWithMetaCommand::setMeta(
1566 const std::vector<uint8_t>& meta) {
1567 std::copy(meta.begin(),
1568 meta.end(),
1569 std::back_inserter(BinprotSetWithMetaCommand::meta));
1570 return *this;
1571 }
1572
encode(std::vector<uint8_t>& buf) const1573 void BinprotSetControlTokenCommand::encode(std::vector<uint8_t>& buf) const {
1574 writeHeader(buf, 0, sizeof(token));
1575 append(buf, token);
1576 }
BinprotSetControlTokenCommand(uint64_t token_, uint64_t oldtoken)1577 BinprotSetControlTokenCommand::BinprotSetControlTokenCommand(uint64_t token_,
1578 uint64_t oldtoken)
1579 : BinprotGenericCommand(cb::mcbp::ClientOpcode::SetCtrlToken),
1580 token(token_) {
1581 setCas(htonll(oldtoken));
1582 }
1583
encode(std::vector<uint8_t>& buf) const1584 void BinprotSetClusterConfigCommand::encode(std::vector<uint8_t>& buf) const {
1585 if (revision < 0) {
1586 // Use the old-style message
1587 writeHeader(buf, config.size(), 0);
1588 } else {
1589 writeHeader(buf, config.size(), sizeof(revision));
1590 append(buf, uint32_t(revision));
1591 }
1592 buf.insert(buf.end(), config.begin(), config.end());
1593 }
1594
BinprotSetClusterConfigCommand( uint64_t token_, std::string config, int revision, const std::string& bucket)1595 BinprotSetClusterConfigCommand::BinprotSetClusterConfigCommand(
1596 uint64_t token_,
1597 std::string config,
1598 int revision,
1599 const std::string& bucket)
1600 : BinprotGenericCommand(cb::mcbp::ClientOpcode::SetClusterConfig, bucket),
1601 config(std::move(config)),
1602 revision(revision) {
1603 setCas(htonll(token_));
1604 }
1605
BinprotObserveSeqnoCommand(Vbid vbid, uint64_t uuid)1606 BinprotObserveSeqnoCommand::BinprotObserveSeqnoCommand(Vbid vbid, uint64_t uuid)
1607 : BinprotGenericCommand(cb::mcbp::ClientOpcode::ObserveSeqno), uuid(uuid) {
1608 setVBucket(vbid);
1609 }
1610
encode(std::vector<uint8_t>& buf) const1611 void BinprotObserveSeqnoCommand::encode(std::vector<uint8_t>& buf) const {
1612 writeHeader(buf, sizeof(uuid), 0);
1613 append(buf, uuid);
1614 }
1615
BinprotObserveSeqnoResponse( BinprotResponse&& other)1616 BinprotObserveSeqnoResponse::BinprotObserveSeqnoResponse(
1617 BinprotResponse&& other)
1618 : BinprotResponse(other) {
1619 decode();
1620 }
1621
decode()1622 void BinprotObserveSeqnoResponse::decode() {
1623 if (!isSuccess()) {
1624 return;
1625 }
1626
1627 if ((getBodylen() != 43) && (getBodylen() != 27)) {
1628 throw std::runtime_error(
1629 "BinprotObserveSeqnoResponse::decode: Invalid payload size - "
1630 "expected:43 or 27, actual:" +
1631 std::to_string(getBodylen()));
1632 }
1633
1634 auto it = payload.begin() + getHeaderLen();
1635 it = extract(it, info.formatType);
1636 it = extract(it, info.vbId);
1637 it = extract(it, info.uuid);
1638 it = extract(it, info.lastPersistedSeqno);
1639 it = extract(it, info.currentSeqno);
1640
1641 switch (info.formatType) {
1642 case 0:
1643 // No more fields for format 0.
1644 break;
1645
1646 case 1:
1647 // Add in hard failover information
1648 it = extract(it, info.failoverUUID);
1649 it = extract(it, info.failoverSeqno);
1650 break;
1651
1652 default:
1653 throw std::runtime_error(
1654 "BinprotObserveSeqnoResponse::decode: Unexpected formatType:" +
1655 std::to_string(info.formatType));
1656 }
1657 }
1658
assign(std::vector<uint8_t>&& buf)1659 void BinprotObserveSeqnoResponse::assign(std::vector<uint8_t>&& buf) {
1660 BinprotResponse::assign(std::move(buf));
1661 decode();
1662 }
1663
BinprotUpdateUserPermissionsCommand( std::string payload)1664 BinprotUpdateUserPermissionsCommand::BinprotUpdateUserPermissionsCommand(
1665 std::string payload)
1666 : BinprotGenericCommand(
1667 cb::mcbp::ClientOpcode::UpdateExternalUserPermissions),
1668 payload(std::move(payload)) {
1669 }
1670
encode( std::vector<uint8_t>& buf) const1671 void BinprotUpdateUserPermissionsCommand::encode(
1672 std::vector<uint8_t>& buf) const {
1673 writeHeader(buf, payload.size(), 0);
1674 buf.insert(buf.end(), key.begin(), key.end());
1675 buf.insert(buf.end(), payload.begin(), payload.end());
1676 }
1677
BinprotAuditPutCommand(uint32_t id, std::string payload)1678 BinprotAuditPutCommand::BinprotAuditPutCommand(uint32_t id, std::string payload)
1679 : BinprotGenericCommand(cb::mcbp::ClientOpcode::AuditPut),
1680 id(id),
1681 payload(std::move(payload)) {
1682 }
1683
encode(std::vector<uint8_t>& buf) const1684 void BinprotAuditPutCommand::encode(std::vector<uint8_t>& buf) const {
1685 writeHeader(buf, payload.size(), sizeof(id));
1686 append(buf, id);
1687 buf.insert(buf.end(), payload.begin(), payload.end());
1688 }
1689
BinprotSetVbucketCommand(Vbid vbid, vbucket_state_t state, nlohmann::json payload)1690 BinprotSetVbucketCommand::BinprotSetVbucketCommand(Vbid vbid,
1691 vbucket_state_t state,
1692 nlohmann::json payload)
1693 : BinprotGenericCommand(cb::mcbp::ClientOpcode::SetVbucket),
1694 state(state),
1695 payload(std::move(payload)) {
1696 setVBucket(vbid);
1697 }
1698
encode(std::vector<uint8_t>& buf) const1699 void BinprotSetVbucketCommand::encode(std::vector<uint8_t>& buf) const {
1700 if (payload.empty()) {
1701 writeHeader(buf, 0, 1);
1702 buf.push_back(uint8_t(state));
1703 } else {
1704 std::string json = payload.dump();
1705 writeHeader(buf, json.size(), 1);
1706 buf.push_back(uint8_t(state));
1707 buf.insert(buf.end(), json.begin(), json.end());
1708 auto& hdr = *reinterpret_cast<cb::mcbp::Request*>(buf.data());
1709 hdr.setDatatype(cb::mcbp::Datatype::JSON);
1710 }
1711 }
1712
BinprotDcpAddStreamCommand(uint32_t flags)1713 BinprotDcpAddStreamCommand::BinprotDcpAddStreamCommand(uint32_t flags)
1714 : BinprotGenericCommand(cb::mcbp::ClientOpcode::DcpAddStream),
1715 flags(flags) {
1716 }
1717
encode(std::vector<uint8_t>& buf) const1718 void BinprotDcpAddStreamCommand::encode(std::vector<uint8_t>& buf) const {
1719 writeHeader(buf, 0, sizeof(flags));
1720 append(buf, flags);
1721 }
1722
BinprotDelWithMetaCommand(Document doc, Vbid vbucket, uint32_t flags, uint32_t delete_time, uint64_t seqno, uint64_t operationCas, bool quiet)1723 BinprotDelWithMetaCommand::BinprotDelWithMetaCommand(Document doc,
1724 Vbid vbucket,
1725 uint32_t flags,
1726 uint32_t delete_time,
1727 uint64_t seqno,
1728 uint64_t operationCas,
1729 bool quiet)
1730 : BinprotGenericCommand(quiet ? cb::mcbp::ClientOpcode::DelqWithMeta
1731 : cb::mcbp::ClientOpcode::DelWithMeta,
1732 doc.info.id),
1733 doc(std::move(doc)),
1734 flags(flags),
1735 delete_time(delete_time),
1736 seqno(seqno),
1737 operationCas(operationCas) {
1738 setVBucket(vbucket);
1739 setCas(operationCas);
1740 }
1741
encode(std::vector<uint8_t>& buf) const1742 void BinprotDelWithMetaCommand::encode(std::vector<uint8_t>& buf) const {
1743 cb::mcbp::request::DelWithMetaPayload extras(
1744 flags, delete_time, seqno, doc.info.cas);
1745 writeHeader(buf, doc.value.size(), sizeof(extras));
1746 auto& request = *reinterpret_cast<cb::mcbp::Request*>(buf.data());
1747 request.setDatatype(doc.info.datatype);
1748 auto extraBuf = extras.getBuffer();
1749 buf.insert(buf.end(), extraBuf.begin(), extraBuf.end());
1750 buf.insert(buf.end(), key.begin(), key.end());
1751 buf.insert(buf.end(), doc.value.begin(), doc.value.end());
1752 }
1753