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 */
30static 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 */
42static 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
48void BinprotCommand::encode(std::vector<uint8_t>& buf) const {
49    writeHeader(buf);
50}
51
52BinprotCommand::Encoded BinprotCommand::encode() const {
53    Encoded bufs;
54    encode(bufs.header);
55    return bufs;
56}
57
58void 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
79void 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
95BinprotCommand& BinprotCommand::setKey(std::string key_) {
96    key = std::move(key_);
97    return *this;
98}
99
100BinprotCommand& BinprotCommand::setCas(uint64_t cas_) {
101    cas = cas_;
102    return *this;
103}
104
105BinprotCommand& BinprotCommand::setOp(cb::mcbp::ClientOpcode cmd_) {
106    opcode = cmd_;
107    return *this;
108}
109
110void BinprotCommand::clear() {
111    opcode = cb::mcbp::ClientOpcode::Invalid;
112    key.clear();
113    cas = 0;
114    vbucket = Vbid(0);
115}
116
117uint64_t BinprotCommand::getCas() const {
118    return cas;
119}
120
121const std::string& BinprotCommand::getKey() const {
122    return key;
123}
124
125cb::mcbp::ClientOpcode BinprotCommand::getOp() const {
126    return opcode;
127}
128
129BinprotCommand& BinprotCommand::setVBucket(Vbid vbid) {
130    vbucket = vbid;
131    return *this;
132}
133
134BinprotCommand& BinprotCommand::setOpaque(uint32_t opaq) {
135    opaque = opaq;
136    return *this;
137}
138
139BinprotCommand& BinprotCommand::addFrameInfo(const FrameInfo& fi) {
140    auto encoded = fi.encode();
141    return addFrameInfo({encoded.data(), encoded.size()});
142}
143
144BinprotCommand& BinprotCommand::addFrameInfo(cb::const_byte_buffer section) {
145    std::copy(section.cbegin(), section.cend(), std::back_inserter(frame_info));
146    return *this;
147}
148
149void BinprotCommand::ExpiryValue::assign(uint32_t value_) {
150    value = value_;
151    set = true;
152}
153
154void BinprotCommand::ExpiryValue::clear() {
155    set = false;
156}
157
158bool BinprotCommand::ExpiryValue::isSet() const {
159    return set;
160}
161
162uint32_t BinprotCommand::ExpiryValue::getValue() const {
163    return value;
164}
165
166void 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
173BinprotGenericCommand::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
182BinprotGenericCommand::BinprotGenericCommand(cb::mcbp::ClientOpcode opcode,
183                                             const std::string& key_)
184    : BinprotCommandT() {
185    setOp(opcode);
186    setKey(key_);
187}
188
189BinprotGenericCommand::BinprotGenericCommand(cb::mcbp::ClientOpcode opcode)
190    : BinprotCommandT() {
191    setOp(opcode);
192}
193
194BinprotGenericCommand::BinprotGenericCommand() : BinprotCommandT() {
195}
196
197BinprotGenericCommand& BinprotGenericCommand::setValue(std::string value_) {
198    value = std::move(value_);
199    return *this;
200}
201
202BinprotGenericCommand& BinprotGenericCommand::setExtras(
203        const std::vector<uint8_t>& buf) {
204    extras.assign(buf.begin(), buf.end());
205    return *this;
206}
207
208void BinprotGenericCommand::clear() {
209    BinprotCommand::clear();
210    value.clear();
211    extras.clear();
212}
213
214BinprotSubdocCommand::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
232BinprotSubdocCommand& 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
240void 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}
279BinprotSubdocCommand::BinprotSubdocCommand() : BinprotCommandT() {
280}
281BinprotSubdocCommand::BinprotSubdocCommand(cb::mcbp::ClientOpcode cmd_) {
282    setOp(cmd_);
283}
284BinprotSubdocCommand::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}
295BinprotSubdocCommand& BinprotSubdocCommand::setValue(std::string value_) {
296    value = std::move(value_);
297    return *this;
298}
299BinprotSubdocCommand& 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}
313BinprotSubdocCommand& 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}
326BinprotSubdocCommand& BinprotSubdocCommand::setExpiry(uint32_t value_) {
327    expiry.assign(value_);
328    return *this;
329}
330const std::string& BinprotSubdocCommand::getPath() const {
331    return path;
332}
333const std::string& BinprotSubdocCommand::getValue() const {
334    return value;
335}
336protocol_binary_subdoc_flag BinprotSubdocCommand::getFlags() const {
337    return flags;
338}
339
340bool BinprotResponse::isSuccess() const {
341    return cb::mcbp::isStatusSuccess(getStatus());
342}
343
344void BinprotResponse::assign(std::vector<uint8_t>&& srcbuf) {
345    payload = std::move(srcbuf);
346}
347
348boost::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
373cb::mcbp::ClientOpcode BinprotResponse::getOp() const {
374    return cb::mcbp::ClientOpcode(getResponse().getClientOpcode());
375}
376
377cb::mcbp::Status BinprotResponse::getStatus() const {
378    return getResponse().getStatus();
379}
380
381size_t BinprotResponse::getExtlen() const {
382    return getResponse().getExtlen();
383}
384
385size_t BinprotResponse::getBodylen() const {
386    return getResponse().getBodylen();
387}
388
389size_t BinprotResponse::getFramingExtraslen() const {
390    return getResponse().getFramingExtraslen();
391}
392
393size_t BinprotResponse::getHeaderLen() {
394    return sizeof(cb::mcbp::Response);
395}
396
397uint64_t BinprotResponse::getCas() const {
398    return getResponse().cas;
399}
400
401protocol_binary_datatype_t BinprotResponse::getDatatype() const {
402    return protocol_binary_datatype_t(getResponse().getDatatype());
403}
404
405cb::const_char_buffer BinprotResponse::getKey() const {
406    const auto buf = getResponse().getKey();
407    return {reinterpret_cast<const char*>(buf.data()), buf.size()};
408}
409
410std::string BinprotResponse::getKeyString() const {
411    const auto buf = getKey();
412    return {buf.data(), buf.size()};
413}
414
415cb::const_byte_buffer BinprotResponse::getData() const {
416    return getResponse().getValue();
417}
418
419std::string BinprotResponse::getDataString() const {
420    const auto buf = getData();
421    return {reinterpret_cast<const char*>(buf.data()), buf.size()};
422}
423
424const cb::mcbp::Response& BinprotResponse::getResponse() const {
425    return getHeader().getResponse();
426}
427
428void BinprotResponse::clear() {
429    payload.clear();
430}
431
432const cb::mcbp::Header& BinprotResponse::getHeader() const {
433    return *reinterpret_cast<const cb::mcbp::Header*>(payload.data());
434}
435
436void 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}
444const std::string& BinprotSubdocResponse::getValue() const {
445    return value;
446}
447void BinprotSubdocResponse::clear() {
448    BinprotResponse::clear();
449    value.clear();
450}
451bool 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
461void 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}
470void BinprotSaslAuthCommand::setMechanism(const std::string& mech_) {
471    setKey(mech_);
472}
473void BinprotSaslAuthCommand::setChallenge(cb::const_char_buffer data) {
474    challenge.assign(data.begin(), data.size());
475}
476
477void 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}
489void BinprotSaslStepCommand::setMechanism(const std::string& mech) {
490    setKey(mech);
491}
492void BinprotSaslStepCommand::setChallenge(cb::const_char_buffer data) {
493    challenge.assign(data.begin(), data.size());
494}
495
496void 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
503void 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}
511BinprotCreateBucketCommand::BinprotCreateBucketCommand(const char* name) {
512    setKey(name);
513}
514
515void BinprotGetCommand::encode(std::vector<uint8_t>& buf) const {
516    writeHeader(buf, 0, 0);
517    buf.insert(buf.end(), key.begin(), key.end());
518}
519
520void 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}
528BinprotGetAndLockCommand::BinprotGetAndLockCommand()
529    : BinprotCommandT(), lock_timeout(0) {
530}
531BinprotGetAndLockCommand& BinprotGetAndLockCommand::setLockTimeout(
532        uint32_t timeout) {
533    lock_timeout = timeout;
534    return *this;
535}
536
537void 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}
545BinprotGetAndTouchCommand::BinprotGetAndTouchCommand(std::string key,
546                                                     uint32_t exp)
547    : BinprotCommandT(), expirytime(exp) {
548    setKey(std::move(key));
549}
550bool BinprotGetAndTouchCommand::isQuiet() const {
551    return getOp() == cb::mcbp::ClientOpcode::Gatq;
552}
553BinprotGetAndTouchCommand& BinprotGetAndTouchCommand::setQuiet(bool quiet) {
554    setOp(quiet ? cb::mcbp::ClientOpcode::Gatq : cb::mcbp::ClientOpcode::Gat);
555    return *this;
556}
557BinprotGetAndTouchCommand& BinprotGetAndTouchCommand::setExpirytime(
558        uint32_t timeout) {
559    expirytime = timeout;
560    return *this;
561}
562
563BinprotTouchCommand::BinprotTouchCommand(std::string key, uint32_t exp)
564    : BinprotCommandT(), expirytime(exp) {
565    setKey(std::move(key));
566}
567
568void 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}
576BinprotTouchCommand& BinprotTouchCommand::setExpirytime(uint32_t timeout) {
577    expirytime = timeout;
578    return *this;
579}
580
581void BinprotUnlockCommand::encode(std::vector<uint8_t>& buf) const {
582    writeHeader(buf, 0, 0);
583    buf.insert(buf.end(), key.begin(), key.end());
584}
585
586uint32_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
601BinprotMutationCommand& 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
626std::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
639BinprotMutationCommand& 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
653void 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
689void 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
698BinprotCommand::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}
708BinprotMutationCommand& BinprotMutationCommand::setValue(
709        std::vector<uint8_t>&& value_) {
710    value = std::move(value_);
711    return *this;
712}
713template <typename T>
714BinprotMutationCommand& BinprotMutationCommand::setValue(const T& value_) {
715    value.assign(value_.begin(), value_.end());
716    return *this;
717}
718BinprotMutationCommand& BinprotMutationCommand::addValueBuffer(
719        cb::const_byte_buffer buf) {
720    value_refs.emplace_back(buf);
721    return *this;
722}
723BinprotMutationCommand& BinprotMutationCommand::setDatatype(uint8_t datatype_) {
724    datatype = datatype_;
725    return *this;
726}
727BinprotMutationCommand& BinprotMutationCommand::setDatatype(
728        cb::mcbp::Datatype datatype_) {
729    return setDatatype(uint8_t(datatype_));
730}
731BinprotMutationCommand& BinprotMutationCommand::setDocumentFlags(
732        uint32_t flags_) {
733    flags = flags_;
734    return *this;
735}
736BinprotMutationCommand& BinprotMutationCommand::setExpiry(uint32_t expiry_) {
737    expiry.assign(expiry_);
738    return *this;
739}
740
741BinprotMutationResponse::BinprotMutationResponse(BinprotResponse&& other)
742    : BinprotResponse(other) {
743    decode();
744}
745
746void BinprotMutationResponse::assign(std::vector<uint8_t>&& buf) {
747    BinprotResponse::assign(std::move(buf));
748    decode();
749}
750
751const MutationInfo& BinprotMutationResponse::getMutationInfo() const {
752    return mutation_info;
753}
754
755void 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
779void 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}
789BinprotHelloCommand::BinprotHelloCommand(const std::string& client_id)
790    : BinprotCommandT() {
791    setKey(client_id);
792}
793BinprotHelloCommand& 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
803BinprotHelloResponse::BinprotHelloResponse(BinprotResponse&& other)
804    : BinprotResponse(other) {
805    decode();
806}
807
808void BinprotHelloResponse::assign(std::vector<uint8_t>&& buf) {
809    BinprotResponse::assign(std::move(buf));
810    decode();
811}
812const std::vector<cb::mcbp::Feature>& BinprotHelloResponse::getFeatures()
813        const {
814    return features;
815}
816
817void 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
839void 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}
859BinprotIncrDecrCommand& BinprotIncrDecrCommand::setDelta(uint64_t delta_) {
860    delta = delta_;
861    return *this;
862}
863BinprotIncrDecrCommand& BinprotIncrDecrCommand::setInitialValue(
864        uint64_t initial_) {
865    initial = initial_;
866    return *this;
867}
868BinprotIncrDecrCommand& BinprotIncrDecrCommand::setExpiry(uint32_t expiry_) {
869    expiry.assign(expiry_);
870    return *this;
871}
872
873BinprotIncrDecrResponse::BinprotIncrDecrResponse(BinprotResponse&& other)
874    : BinprotMutationResponse(std::move(other)) {
875    decode();
876}
877
878void BinprotIncrDecrResponse::assign(std::vector<uint8_t>&& buf) {
879    BinprotMutationResponse::assign(std::move(buf));
880    decode();
881}
882
883uint64_t BinprotIncrDecrResponse::getValue() const {
884    return value;
885}
886
887void BinprotIncrDecrResponse::decode() {
888    if (isSuccess()) {
889        value = htonll(*reinterpret_cast<const uint64_t*>(getData().data()));
890    } else {
891        value = 0;
892    }
893}
894
895void BinprotRemoveCommand::encode(std::vector<uint8_t>& buf) const {
896    writeHeader(buf, 0, 0);
897    buf.insert(buf.end(), key.begin(), key.end());
898}
899
900void 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}
906void BinprotGetErrorMapCommand::setVersion(uint16_t version_) {
907    version = version_;
908}
909
910void 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}
954BinprotSubdocMultiMutationCommand::BinprotSubdocMultiMutationCommand()
955    : BinprotCommandT<BinprotSubdocMultiMutationCommand,
956                      cb::mcbp::ClientOpcode::SubdocMultiMutation>(),
957      docFlags(mcbp::subdoc::doc_flag::None) {
958}
959
960BinprotSubdocMultiMutationCommand::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
972BinprotSubdocMultiMutationCommand&
973BinprotSubdocMultiMutationCommand::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}
985BinprotSubdocMultiMutationCommand&
986BinprotSubdocMultiMutationCommand::addMutation(
987        const BinprotSubdocMultiMutationCommand::MutationSpecifier& spec) {
988    specs.push_back(spec);
989    return *this;
990}
991BinprotSubdocMultiMutationCommand&
992BinprotSubdocMultiMutationCommand::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}
1000BinprotSubdocMultiMutationCommand& BinprotSubdocMultiMutationCommand::setExpiry(
1001        uint32_t expiry_) {
1002    expiry.assign(expiry_);
1003    return *this;
1004}
1005
1006BinprotSubdocMultiMutationCommand&
1007BinprotSubdocMultiMutationCommand::setDurabilityReqs(
1008        const cb::durability::Requirements& durReqs) {
1009    addFrameInfo(DurabilityFrameInfo(durReqs.getLevel(), durReqs.getTimeout()));
1010    return *this;
1011}
1012
1013BinprotSubdocMultiMutationCommand::MutationSpecifier&
1014BinprotSubdocMultiMutationCommand::at(size_t index) {
1015    return specs.at(index);
1016}
1017BinprotSubdocMultiMutationCommand::MutationSpecifier&
1018        BinprotSubdocMultiMutationCommand::operator[](size_t index) {
1019    return specs[index];
1020}
1021bool BinprotSubdocMultiMutationCommand::empty() const {
1022    return specs.empty();
1023}
1024size_t BinprotSubdocMultiMutationCommand::size() const {
1025    return specs.size();
1026}
1027void BinprotSubdocMultiMutationCommand::clearMutations() {
1028    specs.clear();
1029}
1030void BinprotSubdocMultiMutationCommand::clearDocFlags() {
1031    docFlags = mcbp::subdoc::doc_flag::None;
1032}
1033
1034void 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}
1080void BinprotSubdocMultiMutationResponse::clear() {
1081    BinprotResponse::clear();
1082    results.clear();
1083}
1084const std::vector<BinprotSubdocMultiMutationResponse::MutationResult>&
1085BinprotSubdocMultiMutationResponse::getResults() const {
1086    return results;
1087}
1088
1089void 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}
1130BinprotSubdocMultiLookupCommand::BinprotSubdocMultiLookupCommand()
1131    : BinprotCommandT<BinprotSubdocMultiLookupCommand,
1132                      cb::mcbp::ClientOpcode::SubdocMultiLookup>(),
1133      docFlags(mcbp::subdoc::doc_flag::None) {
1134}
1135
1136BinprotSubdocMultiLookupCommand::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
1144BinprotSubdocMultiLookupCommand& BinprotSubdocMultiLookupCommand::addLookup(
1145        const BinprotSubdocMultiLookupCommand::LookupSpecifier& spec) {
1146    specs.push_back(spec);
1147    return *this;
1148}
1149BinprotSubdocMultiLookupCommand& 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}
1155BinprotSubdocMultiLookupCommand& BinprotSubdocMultiLookupCommand::addGet(
1156        const std::string& path, protocol_binary_subdoc_flag flags) {
1157    return addLookup(path, cb::mcbp::ClientOpcode::SubdocGet, flags);
1158}
1159BinprotSubdocMultiLookupCommand& BinprotSubdocMultiLookupCommand::addExists(
1160        const std::string& path, protocol_binary_subdoc_flag flags) {
1161    return addLookup(path, cb::mcbp::ClientOpcode::SubdocExists, flags);
1162}
1163BinprotSubdocMultiLookupCommand& BinprotSubdocMultiLookupCommand::addGetcount(
1164        const std::string& path, protocol_binary_subdoc_flag flags) {
1165    return addLookup(path, cb::mcbp::ClientOpcode::SubdocGetCount, flags);
1166}
1167BinprotSubdocMultiLookupCommand& 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}
1180void BinprotSubdocMultiLookupCommand::clearLookups() {
1181    specs.clear();
1182}
1183BinprotSubdocMultiLookupCommand::LookupSpecifier&
1184BinprotSubdocMultiLookupCommand::at(size_t index) {
1185    return specs.at(index);
1186}
1187BinprotSubdocMultiLookupCommand::LookupSpecifier&
1188        BinprotSubdocMultiLookupCommand::operator[](size_t index) {
1189    return specs[index];
1190}
1191bool BinprotSubdocMultiLookupCommand::empty() const {
1192    return specs.empty();
1193}
1194size_t BinprotSubdocMultiLookupCommand::size() const {
1195    return specs.size();
1196}
1197void BinprotSubdocMultiLookupCommand::clearDocFlags() {
1198    docFlags = mcbp::subdoc::doc_flag::None;
1199}
1200BinprotSubdocMultiLookupCommand&
1201BinprotSubdocMultiLookupCommand::setExpiry_Unsupported(uint32_t expiry_) {
1202    expiry.assign(expiry_);
1203    return *this;
1204}
1205
1206void BinprotSubdocMultiLookupResponse::assign(std::vector<uint8_t>&& buf) {
1207    BinprotResponse::assign(std::move(buf));
1208    decode();
1209}
1210const std::vector<BinprotSubdocMultiLookupResponse::LookupResult>&
1211BinprotSubdocMultiLookupResponse::getResults() const {
1212    return results;
1213}
1214void BinprotSubdocMultiLookupResponse::clear() {
1215    BinprotResponse::clear();
1216    results.clear();
1217}
1218
1219void 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
1253BinprotSubdocMultiLookupResponse::BinprotSubdocMultiLookupResponse(
1254        BinprotResponse&& other)
1255    : BinprotResponse(other) {
1256    decode();
1257}
1258
1259void 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}
1264BinprotGetCmdTimerCommand::BinprotGetCmdTimerCommand(
1265        cb::mcbp::ClientOpcode opcode)
1266    : BinprotCommandT(), opcode(opcode) {
1267}
1268BinprotGetCmdTimerCommand::BinprotGetCmdTimerCommand(
1269        const std::string& bucket, cb::mcbp::ClientOpcode opcode)
1270    : BinprotCommandT(), opcode(opcode) {
1271    setKey(bucket);
1272}
1273void BinprotGetCmdTimerCommand::setOpcode(cb::mcbp::ClientOpcode opcode) {
1274    BinprotGetCmdTimerCommand::opcode = opcode;
1275}
1276void BinprotGetCmdTimerCommand::setBucket(const std::string& bucket) {
1277    setKey(bucket);
1278}
1279
1280void 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}
1292nlohmann::json BinprotGetCmdTimerResponse::getTimings() const {
1293    return timings;
1294}
1295
1296void 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}
1302void BinprotVerbosityCommand::setLevel(int level) {
1303    BinprotVerbosityCommand::level = level;
1304}
1305
1306static uint8_t netToHost(uint8_t x) {
1307    return x;
1308}
1309
1310static uint64_t netToHost(uint64_t x) {
1311    return ntohll(x);
1312}
1313
1314static 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 */
1322template <typename T>
1323static 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 */
1336void 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
1342void 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
1360void BinprotDcpOpenCommand::setConsumerName(std::string name) {
1361    payload["consumer_name"] = name;
1362}
1363
1364BinprotDcpOpenCommand::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}
1371BinprotDcpOpenCommand& 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}
1380BinprotDcpOpenCommand& 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}
1389BinprotDcpOpenCommand& BinprotDcpOpenCommand::makeIncludeXattr() {
1390    flags |= cb::mcbp::request::DcpOpenPayload::IncludeXattrs;
1391    return *this;
1392}
1393BinprotDcpOpenCommand& BinprotDcpOpenCommand::makeNoValue() {
1394    flags |= cb::mcbp::request::DcpOpenPayload::NoValue;
1395    return *this;
1396}
1397BinprotDcpOpenCommand& BinprotDcpOpenCommand::setFlags(uint32_t flags) {
1398    BinprotDcpOpenCommand::flags = flags;
1399    return *this;
1400}
1401
1402void 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}
1412BinprotDcpStreamRequestCommand::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}
1422BinprotDcpStreamRequestCommand& BinprotDcpStreamRequestCommand::setDcpFlags(
1423        uint32_t value) {
1424    BinprotDcpStreamRequestCommand::dcp_flags = value;
1425    return *this;
1426}
1427BinprotDcpStreamRequestCommand& BinprotDcpStreamRequestCommand::setDcpReserved(
1428        uint32_t value) {
1429    BinprotDcpStreamRequestCommand::dcp_reserved = value;
1430    return *this;
1431}
1432BinprotDcpStreamRequestCommand&
1433BinprotDcpStreamRequestCommand::setDcpStartSeqno(uint64_t value) {
1434    BinprotDcpStreamRequestCommand::dcp_start_seqno = value;
1435    return *this;
1436}
1437BinprotDcpStreamRequestCommand& BinprotDcpStreamRequestCommand::setDcpEndSeqno(
1438        uint64_t value) {
1439    BinprotDcpStreamRequestCommand::dcp_end_seqno = value;
1440    return *this;
1441}
1442BinprotDcpStreamRequestCommand&
1443BinprotDcpStreamRequestCommand::setDcpVbucketUuid(uint64_t value) {
1444    BinprotDcpStreamRequestCommand::dcp_vbucket_uuid = value;
1445    return *this;
1446}
1447BinprotDcpStreamRequestCommand&
1448BinprotDcpStreamRequestCommand::setDcpSnapStartSeqno(uint64_t value) {
1449    BinprotDcpStreamRequestCommand::dcp_snap_start_seqno = value;
1450    return *this;
1451}
1452BinprotDcpStreamRequestCommand&
1453BinprotDcpStreamRequestCommand::setDcpSnapEndSeqno(uint64_t value) {
1454    BinprotDcpStreamRequestCommand::dcp_snap_end_seqno = value;
1455    return *this;
1456}
1457
1458void 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}
1467BinprotSetParamCommand::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
1477void 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
1508BinprotSetWithMetaCommand::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}
1525BinprotSetWithMetaCommand& 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}
1533uint32_t BinprotSetWithMetaCommand::getFlags() const {
1534    return doc.info.flags;
1535}
1536BinprotSetWithMetaCommand& BinprotSetWithMetaCommand::setFlags(uint32_t flags) {
1537    doc.info.flags = flags;
1538    return *this;
1539}
1540uint32_t BinprotSetWithMetaCommand::getExptime() const {
1541    return doc.info.expiration;
1542}
1543BinprotSetWithMetaCommand& BinprotSetWithMetaCommand::setExptime(
1544        uint32_t exptime) {
1545    doc.info.expiration = exptime;
1546    return *this;
1547}
1548uint64_t BinprotSetWithMetaCommand::getSeqno() const {
1549    return seqno;
1550}
1551BinprotSetWithMetaCommand& BinprotSetWithMetaCommand::setSeqno(uint64_t seqno) {
1552    BinprotSetWithMetaCommand::seqno = seqno;
1553    return *this;
1554}
1555uint64_t BinprotSetWithMetaCommand::getMetaCas() const {
1556    return doc.info.cas;
1557}
1558BinprotSetWithMetaCommand& BinprotSetWithMetaCommand::setMetaCas(uint64_t cas) {
1559    doc.info.cas = cas;
1560    return *this;
1561}
1562const std::vector<uint8_t>& BinprotSetWithMetaCommand::getMeta() {
1563    return meta;
1564}
1565BinprotSetWithMetaCommand& 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
1573void BinprotSetControlTokenCommand::encode(std::vector<uint8_t>& buf) const {
1574    writeHeader(buf, 0, sizeof(token));
1575    append(buf, token);
1576}
1577BinprotSetControlTokenCommand::BinprotSetControlTokenCommand(uint64_t token_,
1578                                                             uint64_t oldtoken)
1579    : BinprotGenericCommand(cb::mcbp::ClientOpcode::SetCtrlToken),
1580      token(token_) {
1581    setCas(htonll(oldtoken));
1582}
1583
1584void 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
1595BinprotSetClusterConfigCommand::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
1606BinprotObserveSeqnoCommand::BinprotObserveSeqnoCommand(Vbid vbid, uint64_t uuid)
1607    : BinprotGenericCommand(cb::mcbp::ClientOpcode::ObserveSeqno), uuid(uuid) {
1608    setVBucket(vbid);
1609}
1610
1611void BinprotObserveSeqnoCommand::encode(std::vector<uint8_t>& buf) const {
1612    writeHeader(buf, sizeof(uuid), 0);
1613    append(buf, uuid);
1614}
1615
1616BinprotObserveSeqnoResponse::BinprotObserveSeqnoResponse(
1617        BinprotResponse&& other)
1618    : BinprotResponse(other) {
1619    decode();
1620}
1621
1622void 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
1659void BinprotObserveSeqnoResponse::assign(std::vector<uint8_t>&& buf) {
1660    BinprotResponse::assign(std::move(buf));
1661    decode();
1662}
1663
1664BinprotUpdateUserPermissionsCommand::BinprotUpdateUserPermissionsCommand(
1665        std::string payload)
1666    : BinprotGenericCommand(
1667              cb::mcbp::ClientOpcode::UpdateExternalUserPermissions),
1668      payload(std::move(payload)) {
1669}
1670
1671void 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
1678BinprotAuditPutCommand::BinprotAuditPutCommand(uint32_t id, std::string payload)
1679    : BinprotGenericCommand(cb::mcbp::ClientOpcode::AuditPut),
1680      id(id),
1681      payload(std::move(payload)) {
1682}
1683
1684void 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
1690BinprotSetVbucketCommand::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
1699void 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
1713BinprotDcpAddStreamCommand::BinprotDcpAddStreamCommand(uint32_t flags)
1714    : BinprotGenericCommand(cb::mcbp::ClientOpcode::DcpAddStream),
1715      flags(flags) {
1716}
1717
1718void BinprotDcpAddStreamCommand::encode(std::vector<uint8_t>& buf) const {
1719    writeHeader(buf, 0, sizeof(flags));
1720    append(buf, flags);
1721}
1722
1723BinprotDelWithMetaCommand::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
1742void 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