Skip to content

Commit

Permalink
fixup! Implement Entity Tag (ETag) for resources
Browse files Browse the repository at this point in the history
  • Loading branch information
Danielius1922 committed Jul 31, 2023
1 parent c3020b8 commit 86a4e55
Show file tree
Hide file tree
Showing 4 changed files with 155 additions and 38 deletions.
56 changes: 35 additions & 21 deletions api/oc_etag.c
Original file line number Diff line number Diff line change
Expand Up @@ -56,18 +56,29 @@ oc_etag_global(void)
return g_etag;
}

uint64_t
oc_etag_set_global(uint64_t etag)
{
if (etag == OC_ETAG_UNINITALIZED) {
etag = 1;
}
if (etag < g_etag) {
// TODO: handle wrap around = all resource etags must be reinitialized
OC_DBG("etag wrap around detected: %" PRIu64 " -> %" PRIu64, g_etag, etag);
}
return (g_etag = etag);
}

uint64_t
oc_etag_get(void)
{
uint64_t now = oc_clock_time();
if (now > g_etag) {
g_etag = now;
}
g_etag += etag_random();
if (g_etag == OC_ETAG_UNINITALIZED) {
g_etag = 1;
uint64_t etag = g_etag;
if (now > etag) {
etag = now;
}
return g_etag;
etag += etag_random();
return oc_etag_set_global(etag);
}

#ifdef OC_STORAGE
Expand Down Expand Up @@ -106,7 +117,10 @@ etag_iterate_resources(size_t device, etag_process_resource_fn_t process_fn,
// core resources
for (int type = OCF_CON; type <= OCF_D; ++type) {
oc_resource_t *core_res = oc_core_get_resource_by_index(type, device);
process_fn(core_res, data);
if (process_fn(core_res, data) != 0) {
OC_ERR("failed to process core resource(%s)", oc_string(core_res->uri));
return false;
}
}

// app resources
Expand Down Expand Up @@ -232,7 +246,7 @@ static int
etag_iterate_clear_etag(oc_resource_t *resource, void *data)
{
(void)data;
oc_resource_set_etag(resource, 0);
oc_resource_set_etag(resource, OC_ETAG_UNINITALIZED);
return 0;
}

Expand All @@ -259,7 +273,7 @@ etag_iterate_update_empty_etag(oc_resource_t *resource, void *data)
}

bool
oc_etag_load_from_storage(void)
oc_etag_load_from_storage(bool set_empty_etags)
{
bool success = true;
// load g_etag and resource etags from storage
Expand All @@ -272,27 +286,27 @@ oc_etag_load_from_storage(void)
long ret = oc_storage_data_load(OC_ETAG_STORE_NAME, i,
etag_store_decode_etags, &etag);
if (ret <= 0) {
OC_ERR("failed to load etag for device %zu", i);
OC_ERR("failed to load etags for device %zu", i);
success = false;
}
}
g_etag = etag + etag_random();
if (g_etag == OC_ETAG_UNINITALIZED) {
g_etag = 1;
}
OC_DBG("g_tag: %" PRIu64, g_etag);

// update empty etags
for (size_t i = 0; i < oc_core_get_num_devices(); ++i) {
etag_iterate_resources(i, etag_iterate_update_empty_etag, NULL);
etag += etag_random();
oc_etag_set_global(etag);
OC_DBG("g_tag: %" PRIu64, oc_etag_global());

if (set_empty_etags) {
// update empty etags
for (size_t i = 0; i < oc_core_get_num_devices(); ++i) {
etag_iterate_resources(i, etag_iterate_update_empty_etag, NULL);
}
}
return success;
}

bool
oc_etag_load_and_clear(void)
{
bool success = oc_etag_load_from_storage();
bool success = oc_etag_load_from_storage(true);
success = oc_etag_clear_storage() && success;
return success;
}
Expand Down
8 changes: 7 additions & 1 deletion api/oc_etag_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ extern "C" {
/** Get the global ETag value without modifying it. */
uint64_t oc_etag_global(void);

/** Set global ETag value */
uint64_t oc_etag_set_global(uint64_t etag);

/** Get the next global ETag value. */
uint64_t oc_etag_get(void);

Expand Down Expand Up @@ -71,10 +74,13 @@ bool oc_etag_clear_storage(void);
* load other stores and the resources of device 0 will be updated by calling
* oc_etag_get(). The function will return false in this case.
*
* @param set_empty_etags if true, the ETag values of resources thay aren't
* present in the persistent storage are updated by calling oc_etag_get()
*
* @return true everything was succesfully loaded from persistent storage
* @return false otherwise
*/
bool oc_etag_load_from_storage(void);
bool oc_etag_load_from_storage(bool set_empty_etags);

#endif /* OC_STORAGE */

Expand Down
2 changes: 1 addition & 1 deletion api/oc_storage.c
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ oc_storage_data_load(const char *name, size_t device,
OC_ERR("cannot load from %s from store: cannot parse representation", name);
goto error;
}
if (decode(rep, device, decode_data) != 0) {
if (rep != NULL && decode(rep, device, decode_data) != 0) {
OC_ERR("cannot load from %s from store: cannot decode data", name);
oc_free_rep(rep);
goto error;
Expand Down
127 changes: 112 additions & 15 deletions api/unittest/etagtest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,13 @@ TEST_F(TestETagWithServer, ETagsInitialized)
});
}

TEST_F(TestETagWithServer, ETagWrapAround)
{
oc_etag_set_global(0);
// TODO: check that all resources have reinitialized etags with the wrapped
// value
}

#ifdef OC_DYNAMIC_ALLOCATION

// check that newly created resources have etags
Expand Down Expand Up @@ -221,6 +228,21 @@ setAllETags(uint64_t etag)
[etag](oc_resource_t *resource) { oc_resource_set_etag(resource, etag); });
}

static bool
isETagStorageEmpty()
{
for (size_t i = 0; i < oc_core_get_num_devices(); ++i) {
long ret = oc_storage_data_load(
OC_ETAG_STORE_NAME, i, [](const oc_rep_t *, size_t, void *) { return 0; },
nullptr);
if (ret > 0) {
OC_ERR("storage for device %zu is not empty", i);
return false;
}
}
return true;
}

TEST_F(TestETagWithServer, DumpAndLoad)
{
#ifdef OC_COLLECTIONS
Expand Down Expand Up @@ -265,19 +287,7 @@ TEST_F(TestETagWithServer, DumpAndLoad)
});

// storage should be empty
auto is_empty_storage = []() {
for (size_t i = 0; i < oc_core_get_num_devices(); ++i) {
long ret = oc_storage_data_load(
OC_ETAG_STORE_NAME, i,
[](const oc_rep_t *, size_t, void *) { return 0; }, nullptr);
if (ret > 0) {
OC_ERR("storage for device %zu is not empty", i);
return false;
}
}
return true;
};
EXPECT_TRUE(is_empty_storage());
EXPECT_TRUE(isETagStorageEmpty());

// clean-up
#ifdef OC_DYNAMIC_ALLOCATION
Expand All @@ -287,6 +297,93 @@ TEST_F(TestETagWithServer, DumpAndLoad)
#endif // OC_DYNAMIC_ALLOCATION
}

TEST_F(TestETagWithServer, SkipDumpOfEmptyETags)
{
// set all etags to 0
setAllETags(OC_ETAG_UNINITALIZED);
// no etags should be stored
ASSERT_TRUE(oc_etag_dump());

// all etags should be reinitialized by oc_etag_load_from_storage
uint64_t max_etag = oc_etag_global();
EXPECT_TRUE(oc_etag_load_from_storage(true));
iterateAllResources([&max_etag](const oc_resource_t *resource) {
EXPECT_LT(max_etag, oc_resource_get_etag(resource));
});
}

TEST_F(TestETagWithServer, IgnoreInvalidStorageData)
{
// expected storage data:
// {
// "<uri>": {
// "etag": <etag in uint64_t format>,
// },
// ...
// }

auto store_encode_single_string = [](size_t, void *) {
oc_rep_start_root_object();
oc_rep_set_text_string(root, uri, "/oic/d");
oc_rep_end_root_object();
return 0;
};
ASSERT_LT(0, oc_storage_data_save(OC_ETAG_STORE_NAME, kDeviceID1,
store_encode_single_string, nullptr));
EXPECT_FALSE(oc_etag_load_from_storage(false));

auto store_encode_invalid_type = [](size_t, void *) {
oc_rep_start_root_object();
std::string uri = "/oic/d";
int err =
oc_rep_encode_text_string(oc_rep_object(root), uri.c_str(), uri.length());
CborEncoder etag_map;
memset(&etag_map, 0, sizeof(etag_map));
err |= oc_rep_encoder_create_map(oc_rep_object(root), &etag_map,
CborIndefiniteLength);
std::string key = "etag";
err |= oc_rep_encode_text_string(&etag_map, key.c_str(), key.length());
std::string value = "invalid";
err |= oc_rep_encode_text_string(&etag_map, value.c_str(), value.length());
err |= oc_rep_encoder_close_container(oc_rep_object(root), &etag_map);
oc_rep_end_root_object();
return err;
};
ASSERT_LT(0, oc_storage_data_save(OC_ETAG_STORE_NAME, kDeviceID1,
store_encode_invalid_type, nullptr));
EXPECT_FALSE(oc_etag_load_from_storage(false));

auto store_encode_invalid_value = [](size_t, void *) {
oc_rep_start_root_object();
std::string uri = "/oic/p";
int err =
oc_rep_encode_text_string(oc_rep_object(root), uri.c_str(), uri.length());
CborEncoder etag_map;
memset(&etag_map, 0, sizeof(etag_map));
err |= oc_rep_encoder_create_map(oc_rep_object(root), &etag_map,
CborIndefiniteLength);
std::string key = "etag";
err |= oc_rep_encode_text_string(&etag_map, key.c_str(), key.length());
err |= oc_rep_encode_uint(&etag_map, 0);
err |= oc_rep_encoder_close_container(oc_rep_object(root), &etag_map);

uri = "/oic/d";
err |=
oc_rep_encode_text_string(oc_rep_object(root), uri.c_str(), uri.length());
memset(&etag_map, 0, sizeof(etag_map));
err |= oc_rep_encoder_create_map(oc_rep_object(root), &etag_map,
CborIndefiniteLength);
err |= oc_rep_encode_text_string(&etag_map, key.c_str(), key.length());
err |= oc_rep_encode_int(&etag_map, -1);
err |= oc_rep_encoder_close_container(oc_rep_object(root), &etag_map);
oc_rep_end_root_object();
return err;
};
ASSERT_LT(0, oc_storage_data_save(OC_ETAG_STORE_NAME, kDeviceID1,
store_encode_invalid_value, nullptr));
EXPECT_FALSE(oc_etag_load_from_storage(false));
}

TEST_F(TestETagWithServer, LoadGlobalETagFromStorage)
{
uint64_t max_etag = oc_etag_global();
Expand All @@ -301,7 +398,7 @@ TEST_F(TestETagWithServer, LoadGlobalETagFromStorage)
oc_resource_set_etag(platform, max_etag);

ASSERT_TRUE(oc_etag_dump());
EXPECT_TRUE(oc_etag_load_from_storage());
EXPECT_TRUE(oc_etag_load_from_storage(false));

// the global etag should be > than the maximal etag of all resources
EXPECT_GT(oc_etag_global(), max_etag);
Expand All @@ -326,7 +423,7 @@ TEST_F(TestETagWithServer, ClearStorage)

// clear the storage
ASSERT_TRUE(oc_etag_clear_storage());
EXPECT_FALSE(oc_etag_load_from_storage());
EXPECT_FALSE(oc_etag_load_from_storage(true));

iterateAllResources([](const oc_resource_t *resource) {
// nor 0 nor 1337
Expand Down

0 comments on commit 86a4e55

Please sign in to comment.