Skip to content

Commit eee2ef7

Browse files
committed
First attempt at an async refactoring of the SciTokens library.
This splits operations that may block on network access into a "start" and "continue" portion. The start portion returns a status object which may be queried for information about whether the operation is finished and information about file descriptors that are useful for select. Completely untested but it compiles and is roughly the top-to-bottom outline of what the async code will look like.
1 parent 910a9bb commit eee2ef7

File tree

4 files changed

+651
-134
lines changed

4 files changed

+651
-134
lines changed

src/scitokens.cpp

Lines changed: 195 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -338,7 +338,7 @@ int validator_validate(Validator validator, SciToken scitoken, char **err_msg) {
338338
auto real_scitoken = reinterpret_cast<scitokens::SciToken*>(scitoken);
339339

340340
try {
341-
real_validator->verify(*real_scitoken);
341+
real_validator->verify(*real_scitoken, time(NULL) + 20);
342342
} catch (std::exception &exc) {
343343
if (err_msg) {*err_msg = strdup(exc.what());}
344344
return -1;
@@ -388,6 +388,35 @@ void enforcer_set_validate_profile(Enforcer enf, SciTokenProfile profile) {
388388
}
389389

390390

391+
namespace {
392+
393+
Acl *convert_acls(scitokens::Enforcer::AclsList &acls_list, char **err_msg)
394+
{
395+
Acl *acl_result = static_cast<Acl*>(malloc((acls_list.size() + 1)*sizeof(Acl)));
396+
size_t idx = 0;
397+
for (const auto &acl : acls_list) {
398+
acl_result[idx].authz = strdup(acl.first.c_str());
399+
acl_result[idx].resource = strdup(acl.second.c_str());
400+
if (acl_result[idx].authz == nullptr) {
401+
enforcer_acl_free(acl_result);
402+
if (err_msg) {*err_msg = strdup("ACL was generated without an authorization set.");}
403+
return nullptr;
404+
}
405+
if (acl_result[idx].resource == nullptr) {
406+
enforcer_acl_free(acl_result);
407+
if (err_msg) {*err_msg = strdup("ACL was generated without a resource set.");}
408+
return nullptr;
409+
}
410+
idx++;
411+
}
412+
acl_result[idx].authz = nullptr;
413+
acl_result[idx].resource = nullptr;
414+
return acl_result;
415+
}
416+
417+
}
418+
419+
391420
int enforcer_generate_acls(const Enforcer enf, const SciToken scitoken, Acl **acls, char **err_msg) {
392421
if (enf == nullptr) {
393422
if (err_msg) {*err_msg = strdup("Enforcer may not be a null pointer");}
@@ -407,26 +436,78 @@ int enforcer_generate_acls(const Enforcer enf, const SciToken scitoken, Acl **ac
407436
if (err_msg) {*err_msg = strdup(exc.what());}
408437
return -1;
409438
}
410-
Acl *acl_result = static_cast<Acl*>(malloc((acls_list.size() + 1)*sizeof(Acl)));
411-
size_t idx = 0;
412-
for (const auto &acl : acls_list) {
413-
acl_result[idx].authz = strdup(acl.first.c_str());
414-
acl_result[idx].resource = strdup(acl.second.c_str());
415-
if (acl_result[idx].authz == nullptr) {
416-
enforcer_acl_free(acl_result);
417-
if (err_msg) {*err_msg = strdup("ACL was generated without an authorization set.");}
418-
return -1;
419-
}
420-
if (acl_result[idx].resource == nullptr) {
421-
enforcer_acl_free(acl_result);
422-
if (err_msg) {*err_msg = strdup("ACL was generated without a resource set.");}
423-
return -1;
424-
}
425-
idx++;
439+
auto result_acls = convert_acls(acls_list, err_msg);
440+
if (!result_acls) {return -1;}
441+
*acls = result_acls;
442+
return 0;
443+
}
444+
445+
446+
int enforcer_generate_acls_start(const Enforcer enf, const SciToken scitoken,
447+
SciTokenStatus *status_out, Acl **acls, char **err_msg)
448+
{
449+
if (enf == nullptr) {
450+
if (err_msg) {*err_msg = strdup("Enforcer may not be a null pointer");}
451+
return -1;
426452
}
427-
acl_result[idx].authz = nullptr;
428-
acl_result[idx].resource = nullptr;
429-
*acls = acl_result;
453+
auto real_enf = reinterpret_cast<scitokens::Enforcer*>(enf);
454+
if (scitoken == nullptr) {
455+
if (err_msg) {*err_msg = strdup("SciToken may not be a null pointer");}
456+
return -1;
457+
}
458+
auto real_scitoken = reinterpret_cast<scitokens::SciToken*>(scitoken);
459+
460+
scitokens::Enforcer::AclsList acls_list;
461+
std::unique_ptr<scitokens::Validator::AsyncStatus> status;
462+
try {
463+
status = real_enf->generate_acls_start(*real_scitoken, acls_list);
464+
} catch (std::exception &exc) {
465+
if (err_msg) {*err_msg = strdup(exc.what());}
466+
return -1;
467+
}
468+
if (status->m_done) {
469+
auto result_acls = convert_acls(acls_list, err_msg);
470+
if (!result_acls) {return -1;}
471+
*acls = result_acls;
472+
*status_out = nullptr;
473+
return 0;
474+
}
475+
*status_out = status.release();
476+
return 0;
477+
}
478+
479+
480+
int enforcer_generate_acls_continue(const Enforcer enf, SciTokenStatus *status,
481+
Acl **acls, char **err_msg)
482+
{
483+
if (enf == nullptr) {
484+
if (err_msg) {*err_msg = strdup("Enforcer may not be a null pointer");}
485+
return -1;
486+
}
487+
auto real_enf = reinterpret_cast<scitokens::Enforcer*>(enf);
488+
if (status == nullptr) {
489+
if (err_msg) {*err_msg = strdup("Status may not be a null pointer");}
490+
return -1;
491+
}
492+
493+
scitokens::Enforcer::AclsList acls_list;
494+
std::unique_ptr<scitokens::Validator::AsyncStatus> status_internal(
495+
reinterpret_cast<scitokens::Validator::AsyncStatus*>(*status));
496+
try {
497+
status_internal = real_enf->generate_acls_continue(std::move(status_internal), acls_list);
498+
} catch (std::exception &exc) {
499+
*status = nullptr;
500+
if (err_msg) {*err_msg = strdup(exc.what());}
501+
return -1;
502+
}
503+
if (status_internal->m_done) {
504+
auto result_acls = convert_acls(acls_list, err_msg);
505+
if (!result_acls) {return -1;}
506+
*acls = result_acls;
507+
*status = nullptr;
508+
return 0;
509+
}
510+
*status = status_internal.release();
430511
return 0;
431512
}
432513

@@ -455,3 +536,97 @@ int enforcer_test(const Enforcer enf, const SciToken scitoken, const Acl *acl, c
455536
}
456537
return 0;
457538
}
539+
540+
541+
void scitoken_status_free(SciTokenStatus status) {
542+
std::unique_ptr<scitokens::Validator::AsyncStatus> status_real(
543+
reinterpret_cast<scitokens::Validator::AsyncStatus*>(status));
544+
}
545+
546+
547+
int scitoken_status_get_timeout_val(const SciTokenStatus *status, time_t expiry_time, struct timeval *timeout, char **err_msg)
548+
{
549+
if (status == nullptr) {
550+
if (err_msg) {*err_msg = strdup("Status object may not be a null pointer");}
551+
return -1;
552+
}
553+
if (timeout == nullptr) {
554+
if (err_msg) {*err_msg = strdup("Timeout object may not be a null pointer");}
555+
return -1;
556+
}
557+
558+
auto real_status = reinterpret_cast<const scitokens::Validator::AsyncStatus*>(status);
559+
struct timeval timeout_internal = real_status->get_timeout_val(expiry_time);
560+
timeout->tv_sec = timeout_internal.tv_sec;
561+
timeout->tv_usec = timeout_internal.tv_usec;
562+
563+
return 0;
564+
}
565+
566+
567+
int scitoken_status_get_read_fd_set(SciTokenStatus *status, fd_set **read_fd_set, char **err_msg)
568+
{
569+
if (status == nullptr) {
570+
if (err_msg) {*err_msg = strdup("Status object may not be a null pointer");}
571+
return -1;
572+
}
573+
if (read_fd_set == nullptr) {
574+
if (err_msg) {*err_msg = strdup("Read fd_set object may not be a null pointer");}
575+
return -1;
576+
}
577+
578+
auto real_status = reinterpret_cast<scitokens::Validator::AsyncStatus*>(status);
579+
*read_fd_set = real_status->get_read_fd_set();
580+
return 0;
581+
}
582+
583+
584+
int scitoken_status_get_write_fd_set(SciTokenStatus *status, fd_set **write_fd_set, char **err_msg)
585+
{
586+
if (status == nullptr) {
587+
if (err_msg) {*err_msg = strdup("Status object may not be a null pointer");}
588+
return -1;
589+
}
590+
if (write_fd_set == nullptr) {
591+
if (err_msg) {*err_msg = strdup("Write fd_set object may not be a null pointer");}
592+
return -1;
593+
}
594+
595+
auto real_status = reinterpret_cast<scitokens::Validator::AsyncStatus*>(status);
596+
*write_fd_set = real_status->get_write_fd_set();
597+
return 0;
598+
}
599+
600+
601+
int scitoken_status_get_exc_fd_set(SciTokenStatus *status, fd_set **exc_fd_set, char **err_msg)
602+
{
603+
if (status == nullptr) {
604+
if (err_msg) {*err_msg = strdup("Status object may not be a null pointer");}
605+
return -1;
606+
}
607+
if (exc_fd_set == nullptr) {
608+
if (err_msg) {*err_msg = strdup("Read fd_set object may not be a null pointer");}
609+
return -1;
610+
}
611+
612+
auto real_status = reinterpret_cast<scitokens::Validator::AsyncStatus*>(status);
613+
*exc_fd_set = real_status->get_exc_fd_set();
614+
return 0;
615+
}
616+
617+
618+
int scitoken_status_get_max_fd(const SciTokenStatus *status, int *max_fd, char **err_msg)
619+
{
620+
if (status == nullptr) {
621+
if (err_msg) {*err_msg = strdup("Status object may not be a null pointer");}
622+
return -1;
623+
}
624+
if (max_fd == nullptr) {
625+
if (err_msg) {*err_msg = strdup("Max FD may not be a null pointer");}
626+
return -1;
627+
}
628+
629+
auto real_status = reinterpret_cast<const scitokens::Validator::AsyncStatus*>(status);
630+
*max_fd = real_status->get_max_fd();
631+
return 0;
632+
}

src/scitokens.h

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@
44
*
55
*/
66

7+
#include <time.h>
8+
#include <sys/select.h>
9+
710
#ifdef __cplusplus
811
extern "C" {
912
#endif
@@ -12,6 +15,7 @@ typedef void * SciTokenKey;
1215
typedef void * SciToken;
1316
typedef void * Validator;
1417
typedef void * Enforcer;
18+
typedef void * SciTokenStatus;
1519

1620
typedef int (*StringValidatorFunction)(const char *value, char **err_msg);
1721
typedef struct Acl_s {
@@ -123,10 +127,58 @@ void enforcer_set_validate_profile(Enforcer, SciTokenProfile profile);
123127

124128
int enforcer_generate_acls(const Enforcer enf, const SciToken scitokens, Acl **acls, char **err_msg);
125129

130+
/**
131+
* The asynchronous versions of enforcer_generate_acls.
132+
*/
133+
int enforcer_generate_acls_start(const Enforcer enf, const SciToken scitokens,
134+
SciTokenStatus *status, Acl **acls, char **err_msg);
135+
int enforcer_generate_acls_continue(const Enforcer enf, SciTokenStatus *status,
136+
Acl **acls, char **err_msg);
137+
126138
void enforcer_acl_free(Acl *acls);
127139

128140
int enforcer_test(const Enforcer enf, const SciToken sci, const Acl *acl, char **err_msg);
129141

142+
void scitoken_status_free(SciTokenStatus *status);
143+
144+
/**
145+
* Get the suggested timeout val. After the timeout value has passed, the asynchronous operation should continue.
146+
*
147+
* - `expiry_time`: the expiration time (in Unix epoch seconds) for the operation in total.
148+
* The returned timeout value will never take the operation past the expiration time.
149+
*/
150+
int scitoken_status_get_timeout_val(const SciTokenStatus *status, time_t expiry_time, struct timeval *timeout, char **err_msg);
151+
152+
/**
153+
* Get the set of read file descriptors. This will return a borrowed pointer (whose lifetime matches the
154+
* status object) pointing at a fd_set array of size FD_SETSIZE. Any file descriptors owned by the status
155+
* operation will be set and the returned fd_set can be used for select() operations.
156+
*
157+
* IMPLEMENTATION NOTE: If the file descriptor monitored by libcurl are too high to be stored in this set,
158+
* libcurl should give a corresponding low timeout val (100ms) and effectively switch to polling. See:
159+
* <https://curl.se/libcurl/c/curl_multi_fdset.html> for more information.
160+
*/
161+
int scitoken_status_get_read_fd_set(SciTokenStatus *status, fd_set **read_fd_set, char **err_msg);
162+
163+
/**
164+
* Get the set of write FDs; see documentation for scitoken_status_get_read_fd_set.
165+
*/
166+
int scitoken_status_get_write_fd_set(SciTokenStatus *status, fd_set **write_fd_set, char **err_msg);
167+
168+
/**
169+
* Get the set of exception FDs; see documentation for scitoken_status_get_exc_fd_set.
170+
*/
171+
int scitoken_status_get_exc_fd_set(SciTokenStatus *status, fd_set **exc_fd_set, char **err_msg);
172+
173+
/**
174+
* Get the maximum FD in the status set.
175+
*
176+
* IMPLEMENTATION NOTE: If the max FD is -1 then it implies libcurl is something that cannot be modelled
177+
* by a socket. In such a case, the libcurl docs suggest using a 100ms timeout for select operations.
178+
* See <https://curl.se/libcurl/c/curl_multi_fdset.html>.
179+
*/
180+
int scitoken_status_get_max_fd(const SciTokenStatus *status, int *max_fd, char **err_msg);
181+
130182
#ifdef __cplusplus
131183
}
132184
#endif

0 commit comments

Comments
 (0)