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