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); } }
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; }