/* * Copyright (C) 2018-present ScyllaDB */ /* * SPDX-License-Identifier: LicenseRef-ScyllaDB-Source-Available-1.1 */ #pragma once #include "server.hh" namespace cql_transport { enum class cql_binary_opcode : uint8_t { ERROR = 0, STARTUP = 1, READY = 2, AUTHENTICATE = 3, CREDENTIALS = 4, OPTIONS = 5, SUPPORTED = 6, QUERY = 7, RESULT = 8, PREPARE = 9, EXECUTE = 10, REGISTER = 11, EVENT = 12, BATCH = 13, AUTH_CHALLENGE = 14, AUTH_RESPONSE = 15, AUTH_SUCCESS = 16, OPCODES_COUNT }; class response { int16_t _stream; cql_binary_opcode _opcode; uint8_t _flags = 0; // a bitwise OR mask of zero or more cql_frame_flags values bytes_ostream _body; public: template class placeholder; response(int16_t stream, cql_binary_opcode opcode, const tracing::trace_state_ptr& tr_state_ptr) : _stream{stream} , _opcode{opcode} { if (tracing::should_return_id_in_response(tr_state_ptr)) { auto i = _body.write_place_holder(utils::UUID::serialized_size()); tr_state_ptr->session_id().serialize(i); set_frame_flag(cql_frame_flags::tracing); } } // Construct a response with pre-serialized body response(int16_t stream, cql_binary_opcode opcode, uint8_t flags, bytes_ostream body) : _stream{stream} , _opcode{opcode} , _flags{flags} , _body{std::move(body)} { } void set_frame_flag(cql_frame_flags flag) noexcept { _flags |= flag; } void serialize(const event::schema_change& event, uint8_t version); void serialize(const event::client_routes_change& event, uint8_t version); void write_byte(uint8_t b); void write_int(int32_t n); placeholder write_int_placeholder(); void write_long(int64_t n); void write_short(uint16_t n); void write_string(std::string_view s); void write_bytes_as_string(bytes_view s); void write_long_string(const sstring& s); void write_string_list(std::vector string_list); void write_bytes(bytes b); void write_short_bytes(bytes b); void write_inet(socket_address inet); void write_consistency(db::consistency_level c); void write_string_map(std::map string_map); void write_string_multimap(std::multimap string_map); void write_string_bytes_map(const std::unordered_map& map); void write_value(bytes_opt value); void write_value(std::optional value); void write(const cql3::metadata& m, const cql_metadata_id_wrapper& request_metadata_id, bool no_metadata = false); void write(const cql3::prepared_metadata& m, uint8_t version); future<> write_message(output_stream& out, uint8_t version, cql_compression compression, seastar::deleter); cql_binary_opcode opcode() const { return _opcode; } size_t size() const { return _body.size(); } uint8_t flags() const { return _flags; } bytes_ostream extract_body() && { return std::move(_body); } private: void compress(cql_compression compression); void compress_lz4(); void compress_snappy(); template temporary_buffer make_frame_one(uint8_t version, size_t length) { temporary_buffer frame_buf(sizeof(CqlFrameHeaderType)); auto* frame = reinterpret_cast(frame_buf.get_write()); frame->version = version | 0x80; frame->flags = _flags; frame->opcode = static_cast(_opcode); frame->length = htonl(length); frame->stream = net::hton((decltype(frame->stream))_stream); return frame_buf; } utils::result_with_exception_ptr> make_frame(uint8_t version, size_t length) { if (version > 0x04) { return bo::failure(std::make_exception_ptr(exceptions::protocol_exception(format("Invalid or unsupported protocol version: {:d}", version)))); } return make_frame_one(version, length); } }; template<> class response::placeholder { int8_t* _pointer; public: explicit placeholder(int8_t* ptr) : _pointer(ptr) { } void write(int32_t n) { auto u = htonl(n); auto* s = reinterpret_cast(&u); std::copy_n(s, sizeof(u), _pointer); } }; }