Status Execute()

in src/commands/cmd_server.cc [723:811]


  Status Execute([[maybe_unused]] engine::Context &ctx, Server *srv, Connection *conn, std::string *output) override {
    size_t next_arg = 1;
    int protocol = 2;  // default protocol version is 2
    if (args_.size() >= 2) {
      auto parse_result = ParseInt<int>(args_[next_arg], 10);
      ++next_arg;
      if (!parse_result) {
        return {Status::NotOK, "Protocol version is not an integer or out of range"};
      }

      protocol = *parse_result;

      // In redis, it will check protocol < 2 or protocol > 3,
      // kvrocks only supports REPL2 by now, but for supporting some
      // `hello 3`, it will not report error when using 3.
      if (protocol < 2 || protocol > 3) {
        return {Status::RedisNoProto, "unsupported protocol version"};
      }
    }

    // Handling AUTH and SETNAME
    for (; next_arg < args_.size(); ++next_arg) {
      size_t more_args = args_.size() - next_arg - 1;
      const std::string &opt = args_[next_arg];
      if (util::ToLower(opt) == "auth" && more_args != 0) {
        if (more_args == 2 || more_args == 4) {
          if (args_[next_arg + 1] != "default") {
            return {Status::NotOK, "Invalid password"};
          }
          next_arg++;
        }
        const auto &user_password = args_[next_arg + 1];
        std::string ns;
        AuthResult auth_result = srv->AuthenticateUser(user_password, &ns);
        switch (auth_result) {
          case AuthResult::NO_REQUIRE_PASS:
            return {Status::NotOK, "Client sent AUTH, but no password is set"};
          case AuthResult::INVALID_PASSWORD:
            return {Status::NotOK, "Invalid password"};
          case AuthResult::IS_USER:
            conn->BecomeUser();
            break;
          case AuthResult::IS_ADMIN:
            conn->BecomeAdmin();
            break;
        }
        conn->SetNamespace(ns);
        next_arg += 1;
      } else if (util::ToLower(opt) == "setname" && more_args != 0) {
        const std::string &name = args_[next_arg + 1];
        conn->SetName(name);
        next_arg += 1;
      } else {
        return {Status::RedisExecErr, "Syntax error in HELLO option " + opt};
      }
    }

    std::vector<std::string> output_list;
    output_list.push_back(redis::BulkString("server"));
    output_list.push_back(redis::BulkString("redis"));
    output_list.push_back(redis::BulkString("version"));
    // What the client want is the Redis compatible version instead of the Kvrocks version.
    output_list.push_back(redis::BulkString(REDIS_VERSION));
    output_list.push_back(redis::BulkString("proto"));
    if (srv->GetConfig()->resp3_enabled) {
      output_list.push_back(redis::Integer(protocol));
      conn->SetProtocolVersion(protocol == 3 ? RESP::v3 : RESP::v2);
    } else {
      output_list.push_back(redis::Integer(2));
    }

    output_list.push_back(redis::BulkString("mode"));
    // Note: sentinel is not supported in kvrocks.
    if (srv->GetConfig()->cluster_enabled) {
      output_list.push_back(redis::BulkString("cluster"));
    } else {
      output_list.push_back(redis::BulkString("standalone"));
    }
    output_list.push_back(redis::BulkString("role"));
    output_list.push_back(redis::BulkString(srv->IsSlave() ? "slave" : "master"));
    // For Kvrocks, the modules is not supported.
    output_list.push_back(redis::BulkString("modules"));
    output_list.push_back(conn->NilArray());
    *output = conn->HeaderOfMap(output_list.size() / 2);
    for (const auto &item : output_list) {
      *output += item;
    }
    return Status::OK();
  }