Ejemplo n.º 1
0
SessionStore::Session* SessionStore::JsonSerializerDeserializer::
deserialize_session(const std::string& data)
{
    TRC_DEBUG("Deserialize JSON document: %s", data.c_str());

    rapidjson::Document doc;
    doc.Parse<0>(data.c_str());

    if (doc.HasParseError())
    {
        TRC_DEBUG("Failed to parse document");
        return NULL;
    }

    Session* session = new Session();

    try
    {
        JSON_GET_STRING_MEMBER(doc, JSON_SESSION_ID, session->session_id);

        JSON_ASSERT_CONTAINS(doc, JSON_CCFS);
        JSON_ASSERT_ARRAY(doc[JSON_CCFS]);

        for (rapidjson::Value::ConstValueIterator ccfs_it = doc[JSON_CCFS].Begin();
                ccfs_it != doc[JSON_CCFS].End();
                ++ccfs_it)
        {
            JSON_ASSERT_STRING(*ccfs_it);
            session->ccf.push_back(ccfs_it->GetString());
        }

        JSON_GET_INT_MEMBER(doc, JSON_ACCT_RECORD_NUM, session->acct_record_number);
        JSON_GET_STRING_MEMBER(doc, JSON_TIMER_ID, session->timer_id);
        JSON_GET_INT_MEMBER(doc, JSON_REFRESH_TIME, session->session_refresh_time);
        JSON_GET_INT_MEMBER(doc, JSON_INTERIM_INTERVAL, session->interim_interval);
    }
    catch(JsonFormatError err)
    {
        TRC_INFO("Failed to deserialize JSON document (hit error at %s:%d)",
                 err._file, err._line);
        delete session;
        session = NULL;
    }

    return session;
}
Ejemplo n.º 2
0
AuthStore::Digest* AuthStore::JsonSerializerDeserializer::
  deserialize_digest(const std::string& digest_s)
{
  TRC_DEBUG("Deserialize JSON document: %s", digest_s.c_str());

  rapidjson::Document doc;
  doc.Parse<0>(digest_s.c_str());

  if (doc.HasParseError())
  {
    TRC_DEBUG("Failed to parse document");
    return NULL;
  }

  Digest* digest = new Digest();

  try
  {
    JSON_ASSERT_OBJECT(doc);
    JSON_ASSERT_CONTAINS(doc, JSON_DIGEST);
    JSON_ASSERT_OBJECT(doc[JSON_DIGEST]);
    const rapidjson::Value& digest_block = doc[JSON_DIGEST];
    {
      JSON_GET_STRING_MEMBER(digest_block, JSON_HA1, digest->_ha1);
      // The QoP is assumed to always be 'auth'.
      JSON_GET_STRING_MEMBER(digest_block, JSON_REALM, digest->_realm);
    }

    JSON_GET_STRING_MEMBER(doc, JSON_OPAQUE, digest->_opaque);
    JSON_GET_STRING_MEMBER(doc, JSON_IMPU, digest->_impu);
    JSON_GET_INT_MEMBER(doc, JSON_NC, digest->_nonce_count);
  }
  catch(JsonFormatError err)
  {
    TRC_INFO("Failed to deserialize JSON document (hit error at %s:%d)",
             err._file, err._line);
    delete digest; digest = NULL;
  }

  return digest;
}
Ejemplo n.º 3
0
void ControllerTask::handle_delete()
{
  // Check the request has a valid JSON body
  std::string body = _req.get_rx_body();
  rapidjson::Document doc;
  doc.Parse<0>(body.c_str());

  if (doc.HasParseError())
  {
    TRC_INFO("Failed to parse document as JSON");
    send_http_reply(HTTP_BAD_REQUEST);
    return;
  }

  // Now loop through the body, pulling out the IDs/replica numbers
  // The JSON body should have the format:
  //  {"IDs": [{"ID": 123, "ReplicaIndex": 0},
  //           {"ID": 456, "ReplicaIndex": 2},
  //          ...]
  // The replica_index is zero-indexed (so the primary replica has an
  // index of 0).
  try
  {
    JSON_ASSERT_CONTAINS(doc, JSON_IDS);
    JSON_ASSERT_ARRAY(doc[JSON_IDS]);
    const rapidjson::Value& ids_arr = doc[JSON_IDS];

    // The request is valid, so respond with a 202. Now loop through the
    // the body and update the replica trackers.
    send_http_reply(HTTP_ACCEPTED);

    for (rapidjson::Value::ConstValueIterator ids_it = ids_arr.Begin();
         ids_it != ids_arr.End();
         ++ids_it)
    {
      try
      {
        TimerID timer_id;
        int replica_index;
        JSON_GET_INT_64_MEMBER(*ids_it, JSON_ID, timer_id);
        JSON_GET_INT_MEMBER(*ids_it, JSON_REPLICA_INDEX, replica_index);

        // Update the timer's replica_tracker to show that the replicas
        // at level 'replica_index' and higher have been informed
        // about the timer. This will tombstone the timer if all
        // replicas have been informed.
        _cfg->_handler->update_replica_tracker_for_timer(timer_id,
                                                         replica_index);
      }
      catch (JsonFormatError err)
      {
        TRC_INFO("JSON entry was invalid (hit error at %s:%d)",
                  err._file, err._line);
      }
    }
  }
  catch (JsonFormatError err)
  {
    TRC_INFO("JSON body didn't contain the IDs array");
    send_http_reply(HTTP_BAD_REQUEST);
  }
}
Ejemplo n.º 4
0
Timer* Timer::from_json_obj(TimerID id,
                            uint64_t replica_hash,
                            std::string& error,
                            bool& replicated,
                            rapidjson::Value& doc)
{
  Timer* timer = NULL;

  try
  {
    JSON_ASSERT_CONTAINS(doc, "timing");
    JSON_ASSERT_CONTAINS(doc, "callback");

    // Parse out the timing block
    rapidjson::Value& timing = doc["timing"];
    JSON_ASSERT_OBJECT(timing);

    JSON_ASSERT_CONTAINS(timing, "interval");
    rapidjson::Value& interval = timing["interval"];
    JSON_ASSERT_INT(interval);

    // Extract the repeat-for parameter, if it's absent, set it to the interval
    // instead.
    int repeat_for_int;
    if (timing.HasMember("repeat-for"))
    {
      JSON_GET_INT_MEMBER(timing, "repeat-for", repeat_for_int);
    }
    else
    {
      repeat_for_int = interval.GetInt();
    }

    if ((interval.GetInt() == 0) && (repeat_for_int != 0))
    {
      // If the interval time is 0 and the repeat_for_int isn't then reject the timer.
      error = "Can't have a zero interval time with a non-zero (%s) repeat-for time",
              std::to_string(repeat_for_int);
      return NULL;
    }

    timer = new Timer(id, (interval.GetInt() * 1000), (repeat_for_int * 1000));

    if (timing.HasMember("start-time-delta"))
    {
      // Timer JSON specified a time offset, use that to determine the true
      // start time.
      uint64_t start_time_delta;
      JSON_GET_INT_64_MEMBER(timing, "start-time-delta", start_time_delta);

      // This cast is safe as this sum is deliberately designed to wrap over UINT_MAX.
      timer->start_time_mono_ms = (uint32_t)(clock_gettime_ms(CLOCK_MONOTONIC) + start_time_delta);
    }
    else if (timing.HasMember("start-time"))
    {
      // Timer JSON specifies a start-time, use that instead of now.
      uint64_t real_start_time;
      JSON_GET_INT_64_MEMBER(timing, "start-time", real_start_time);
      uint64_t real_time = clock_gettime_ms(CLOCK_REALTIME);
      uint64_t mono_time = clock_gettime_ms(CLOCK_MONOTONIC);

      // This cast is safe as this sum is deliberately designed to wrap over UINT_MAX.
      timer->start_time_mono_ms = (uint32_t)(mono_time + real_start_time - real_time);
    }

    if (timing.HasMember("sequence-number"))
    {
      JSON_GET_INT_MEMBER(timing, "sequence-number", timer->sequence_number);
    }

    // Parse out the 'callback' block
    rapidjson::Value& callback = doc["callback"];
    JSON_ASSERT_OBJECT(callback);

    JSON_ASSERT_CONTAINS(callback, "http");
    rapidjson::Value& http = callback["http"];
    JSON_ASSERT_OBJECT(http);

    JSON_GET_STRING_MEMBER(http, "uri", timer->callback_url);
    JSON_GET_STRING_MEMBER(http, "opaque", timer->callback_body);

    if (doc.HasMember("reliability"))
    {
      // Parse out the 'reliability' block
      rapidjson::Value& reliability = doc["reliability"];
      JSON_ASSERT_OBJECT(reliability);

      if (reliability.HasMember("cluster-view-id"))
      {
        JSON_GET_STRING_MEMBER(reliability,
                               "cluster-view-id",
                               timer->cluster_view_id);
      }

      if (reliability.HasMember("replicas"))
      {
        rapidjson::Value& replicas = reliability["replicas"];
        JSON_ASSERT_ARRAY(replicas);

        if (replicas.Size() == 0)
        {
          error = "If replicas is specified it must be non-empty";
          delete timer; timer = NULL;
          return NULL;
        }

        timer->_replication_factor = replicas.Size();

        for (rapidjson::Value::ConstValueIterator it = replicas.Begin();
                                                  it != replicas.End();
                                                  ++it)
        {
          JSON_ASSERT_STRING(*it);
          timer->replicas.push_back(std::string(it->GetString(), it->GetStringLength()));
        }
      }
      else
      {
        if (reliability.HasMember("replication-factor"))
        {
          JSON_GET_INT_MEMBER(reliability,
                              "replication-factor",
                              timer->_replication_factor);
        }
        else
        {
          // Default replication factor is 2.
          timer->_replication_factor = 2;
        }
      }
    }
    else
    {
      // Default to 2 replicas
      timer->_replication_factor = 2;
    }

    timer->_replica_tracker = pow(2, timer->_replication_factor) - 1;

    if (timer->replicas.empty())
    {
      // Replicas not determined above, determine them now.  Note that this implies
      // the request is from a client, not another replica.
      replicated = false;
      timer->calculate_replicas(replica_hash);
    }
    else
    {
      // Replicas were specified in the request, must be a replication message
      // from another cluster node.
      replicated = true;
    }
  }
  catch (JsonFormatError err)
  {
    error = "Badly formed Timer entry - hit error on line " + std::to_string(err._line);
    delete timer; timer = NULL;
    return NULL;
  }

  return timer;
}
Ejemplo n.º 5
0
void SCSCFSelector::update_scscf()
{
  // Check whether the file exists.
  struct stat s;
  if ((stat(_configuration.c_str(), &s) != 0) &&
      (errno == ENOENT))
  {
    TRC_STATUS("No S-CSCF configuration data (file %s does not exist)",
               _configuration.c_str());
    CL_SPROUT_SCSCF_FILE_MISSING.log();
    return;
  }

  TRC_STATUS("Loading S-CSCF configuration from %s", _configuration.c_str());

  // Read from the file
  std::ifstream fs(_configuration.c_str());
  std::string scscf_str((std::istreambuf_iterator<char>(fs)),
                         std::istreambuf_iterator<char>());

  if (scscf_str == "")
  {
    // LCOV_EXCL_START
    TRC_ERROR("Failed to read S-CSCF configuration data from %s",
              _configuration.c_str());
    CL_SPROUT_SCSCF_FILE_EMPTY.log();
    return;
    // LCOV_EXCL_STOP
  }

  // Now parse the document
  rapidjson::Document doc;
  doc.Parse<0>(scscf_str.c_str());

  if (doc.HasParseError())
  {
    TRC_ERROR("Failed to read S-CSCF configuration data: %s\nError: %s",
              scscf_str.c_str(),
              rapidjson::GetParseError_En(doc.GetParseError()));
    CL_SPROUT_SCSCF_FILE_INVALID.log();
    return;
  }

  try
  {
    std::vector<scscf_t> new_scscfs;

    JSON_ASSERT_CONTAINS(doc, "s-cscfs");
    JSON_ASSERT_ARRAY(doc["s-cscfs"]);
    const rapidjson::Value& scscfs_arr = doc["s-cscfs"];

    for (rapidjson::Value::ConstValueIterator scscfs_it = scscfs_arr.Begin();
         scscfs_it != scscfs_arr.End();
         ++scscfs_it)
    {
      try
      {
        scscf_t new_scscf;
        JSON_GET_STRING_MEMBER(*scscfs_it, "server", new_scscf.server);
        JSON_GET_INT_MEMBER(*scscfs_it, "priority", new_scscf.priority);
        JSON_GET_INT_MEMBER(*scscfs_it, "weight", new_scscf.weight);

        JSON_ASSERT_CONTAINS(*scscfs_it, "capabilities");
        JSON_ASSERT_ARRAY((*scscfs_it)["capabilities"]);
        const rapidjson::Value& cap_arr = (*scscfs_it)["capabilities"];
        std::vector<int> capabilities_vec;

        for (rapidjson::Value::ConstValueIterator cap_it = cap_arr.Begin();
             cap_it != cap_arr.End();
             ++cap_it)
        {
          capabilities_vec.push_back((*cap_it).GetInt());
        }

        // Sort the capabilities and remove duplicates
        std::sort(capabilities_vec.begin(), capabilities_vec.end());
        capabilities_vec.erase(unique(capabilities_vec.begin(),
                                      capabilities_vec.end()),
                               capabilities_vec.end() );
        new_scscf.capabilities = capabilities_vec;
        new_scscfs.push_back(new_scscf);
        capabilities_vec.clear();
      }
      catch (JsonFormatError err)
      {
        // Badly formed number block.
        TRC_WARNING("Badly formed S-CSCF entry (hit error at %s:%d)",
                    err._file, err._line);
        CL_SPROUT_SCSCF_FILE_INVALID.log();
      }
    }

    // Take a write lock on the mutex in RAII style
    boost::lock_guard<boost::shared_mutex> write_lock(_scscfs_rw_lock);
    _scscfs = new_scscfs;
  }
  catch (JsonFormatError err)
  {
    TRC_ERROR("Badly formed S-CSCF configuration file - missing s-cscfs object");
    CL_SPROUT_SCSCF_FILE_INVALID.log();
  }
}
HTTPCode ChronosInternalConnection::resynchronise_with_single_node(
                             const std::string server_to_sync,
                             std::vector<std::string> cluster_nodes,
                             std::string localhost)
{
  TRC_DEBUG("Querying %s for timers", server_to_sync.c_str());

  // Get the cluster view ID from the global configuration
  std::string cluster_view_id;
  __globals->get_cluster_view_id(cluster_view_id);

  std::string response;
  HTTPCode rc;

  // Loop sending GETs to the server while the response is a 206
  do
  {
    std::map<TimerID, int> delete_map;

    rc = send_get(server_to_sync, 
                  localhost, 
                  PARAM_SYNC_MODE_VALUE_SCALE, 
                  cluster_view_id,
                  MAX_TIMERS_IN_RESPONSE, 
                  response);

    if ((rc == HTTP_PARTIAL_CONTENT) ||
        (rc == HTTP_OK))
    {
      // Parse the GET response
      rapidjson::Document doc;
      doc.Parse<0>(response.c_str());

      if (doc.HasParseError())
      {
        // We've failed to parse the document as JSON. This suggests that
        // there's something seriously wrong with the node we're trying
        // to query so don't retry
        TRC_WARNING("Failed to parse document as JSON");
        rc = HTTP_BAD_REQUEST;
        break;
      }

      try
      {
        JSON_ASSERT_CONTAINS(doc, JSON_TIMERS);
        JSON_ASSERT_ARRAY(doc[JSON_TIMERS]);
        const rapidjson::Value& ids_arr = doc[JSON_TIMERS];
        int total_timers = ids_arr.Size();
        int count_invalid_timers = 0;

        for (rapidjson::Value::ConstValueIterator ids_it = ids_arr.Begin();
             ids_it != ids_arr.End();
             ++ids_it)
        {
          try
          {
            const rapidjson::Value& id_arr = *ids_it;
            JSON_ASSERT_OBJECT(id_arr);

            // Get the timer ID
            TimerID timer_id;
            JSON_GET_INT_MEMBER(id_arr, JSON_TIMER_ID, timer_id);

            // Get the old replicas
            std::vector<std::string> old_replicas;
            JSON_ASSERT_CONTAINS(id_arr, JSON_OLD_REPLICAS);
            JSON_ASSERT_ARRAY(id_arr[JSON_OLD_REPLICAS]);
            const rapidjson::Value& old_repl_arr = id_arr[JSON_OLD_REPLICAS];
            for (rapidjson::Value::ConstValueIterator repl_it = old_repl_arr.Begin();
                                                      repl_it != old_repl_arr.End();
                                                      ++repl_it)
            {
              JSON_ASSERT_STRING(*repl_it);
              old_replicas.push_back(repl_it->GetString());
            }

            // Get the timer.
            JSON_ASSERT_CONTAINS(id_arr, JSON_TIMER);
            JSON_ASSERT_OBJECT(id_arr[JSON_TIMER]);
            const rapidjson::Value& timer_obj = id_arr[JSON_TIMER];

            bool store_timer = false;
            std::string error_str;
            bool replicated_timer; 
            Timer* timer = Timer::from_json_obj(timer_id,
                                                0,
                                                error_str,
                                                replicated_timer,
                                                (rapidjson::Value&)timer_obj);

            if (!timer)
            {
              count_invalid_timers++;
              TRC_INFO("Unable to create timer - error: %s", error_str.c_str());
              continue;
            }
            else if (!replicated_timer)
            {
              count_invalid_timers++;
              TRC_INFO("Unreplicated timer in response - ignoring");
              delete timer; timer = NULL;
              continue;
            }

            // Decide what we're going to do with this timer.
            int old_level = 0;
            bool in_old_replica_list = get_replica_level(old_level, 
                                                         localhost,
                                                         old_replicas);
            int new_level = 0;
            bool in_new_replica_list = get_replica_level(new_level,
                                                         localhost,
                                                         timer->replicas);

            // Add the timer to the delete map we're building up
            delete_map.insert(std::pair<TimerID, int>(timer_id, new_level));

            if (in_new_replica_list)
            {
              // Add the timer to my store if I can. 
              if (in_old_replica_list)
              {
                if (old_level >= new_level)
                {
                  // Add/update timer
                  // LCOV_EXCL_START - Adding timer paths are tested elsewhere
                  store_timer = true;
                  // LCOV_EXCL_STOP
                }
              }
              else
              {
                // Add/update timer
                store_timer = true;
              }

              // Now loop through the new replicas.
              int index = 0;
              for (std::vector<std::string>::iterator it = timer->replicas.begin();
                                                      it != timer->replicas.end();
                                                      ++it, ++index)
              {
                if (index <= new_level)
                {
                  // Do nothing. We've covered adding the timer to the store above
                }
                else
                {
                  // We can potentially replicate the timer to one of these nodes. 
                  // Check whether the new replica was involved previously
                  int old_rep_level = 0;
                  bool is_new_rep_in_old_rep = get_replica_level(old_rep_level,
                                                                 *it,
                                                                 old_replicas);
                  if (is_new_rep_in_old_rep)
                  {
                    if (old_rep_level >= new_level)
                    {
                      _replicator->replicate_timer_to_node(timer, *it);
                    }
                  }
                  else
                  {
                    _replicator->replicate_timer_to_node(timer, *it);
                  }
                }
              }

              // Now loop through the old replicas. We can send a tombstone 
              // replication to any node that used to be a replica and was 
              // higher in the replica list than the new replica.
              index = 0;
              for (std::vector<std::string>::iterator it = old_replicas.begin();
                                                      it != old_replicas.end();
                                                      ++it, ++index)
              {
                if (index >= new_level)
                {
                  // We can potentially tombstone the timer to one of these nodes.
                  bool old_rep_in_new_rep = get_replica_presence(*it,
                                                                 timer->replicas);

                  if (!old_rep_in_new_rep)
                  {
                    Timer* timer_copy = new Timer(*timer);
                    timer_copy->become_tombstone();
                    _replicator->replicate_timer_to_node(timer_copy, *it);
                    delete timer_copy; timer_copy = NULL;
                  }
                }
              }
            }

            // Add the timer to the store if we can. This is done 
            // last so we don't invalidate the pointer to the timer.
            if (store_timer)
            {
              _handler->add_timer(timer);
            }
            else
            {
              delete timer; timer = NULL;
            }

            // Finally, note that we processed the timer
            _timers_processed_stat->increment();
          }
          catch (JsonFormatError err)
          {
            // A single entry is badly formatted. This is unexpected but we'll try 
            // to keep going and process the rest of the timers. 
            count_invalid_timers++;
            _invalid_timers_processed_stat->increment();
            TRC_INFO("JSON entry was invalid (hit error at %s:%d)",
                     err._file, err._line);
          }
        }

        // Check if we were able to successfully process any timers - if not
        // then bail out as there's something wrong with the node we're
        // querying
        if ((total_timers != 0) && 
           (count_invalid_timers == total_timers))
        {
          TRC_WARNING("Unable to process any timer entries in GET response");
          rc = HTTP_BAD_REQUEST;
        }
      }
      catch (JsonFormatError err)
      {
        // We've failed to find the Timers array. This suggests that
        // there's something seriously wrong with the node we're trying
        // to query so don't retry
        TRC_WARNING("JSON body didn't contain the Timers array");
        rc = HTTP_BAD_REQUEST;
      }

      // Send a DELETE to all the nodes to update their timer references
      if (delete_map.size() > 0)
      {
        std::string delete_body = create_delete_body(delete_map);
        for (std::vector<std::string>::iterator it = cluster_nodes.begin();
                                                it != cluster_nodes.end();
                                                ++it)
        {
          HTTPCode delete_rc = send_delete(*it, delete_body);
          if (delete_rc != HTTP_ACCEPTED)
          {
            // We've received an error response to the DELETE request. There's
            // not much more we can do here (a timeout will have already 
            // been retried). A failed DELETE won't prevent the scaling operation
            // from finishing, it just means that we'll tell other nodes
            // about timers inefficiently. 
            TRC_INFO("Error response (%d) to DELETE request to %s", 
                     delete_rc,
                    (*it).c_str());
          }
        }
      }
    }
    else
    {
      // We've received an error response to the GET request. A timeout
      // will already have been retried by the underlying HTTPConnection, 
      // so don't retry again
      TRC_WARNING("Error response (%d) to GET request to %s", 
                  rc, 
                  server_to_sync.c_str());
    }
  }
  while (rc == HTTP_PARTIAL_CONTENT);

  return rc;
}