diff --git a/src/plugins/apache-httpd/src/mod_lcgdm_disk/checksum.c b/src/plugins/apache-httpd/src/mod_lcgdm_disk/checksum.c
index 6189da03..61454e81 100644
--- a/src/plugins/apache-httpd/src/mod_lcgdm_disk/checksum.c
+++ b/src/plugins/apache-httpd/src/mod_lcgdm_disk/checksum.c
@@ -32,16 +32,17 @@
dav_error* dav_disk_digest_header(request_rec *r, const dav_resource *resource,
char* output, size_t outsize)
{
- const char *want_digest;
- char digest_name[32], digest[64], full_digest_name[64];
+ char sorted_digests[1024], *digest_name, *first_digest_name = NULL;
+ char digest[1024], full_digest_name[64];
+ int chksum_timeout = -1; // negative time can be interpreted as a request for already calculated checksum value
- want_digest = apr_table_get(r->headers_in, "Want-Digest");
-
- if (!resource->info->fd || !want_digest) {
+ dav_shared_sorted_digests(resource->info->request, sorted_digests, 1024);
+ if (strlen(sorted_digests) == 0) {
return NULL;
- }
+ };
- while (dav_shared_next_digest(&want_digest, digest_name, sizeof(digest_name))) {
+ digest_name = strtok(sorted_digests, ",");
+ while (digest_name) {
snprintf(full_digest_name, sizeof(full_digest_name), "checksum.%s", digest_name);
int ret = dmlite_getchecksum(resource->info->ctx,
@@ -49,7 +50,7 @@ dav_error* dav_disk_digest_header(request_rec *r, const dav_resource *resource,
full_digest_name, digest, sizeof(digest),
resource->info->loc.chunks[0].url.path, // replica
0, // 0 = do not recalculate
- 0 // 1800 seconds wait, otherwise return EAGAIN
+ chksum_timeout // negative time can be interpreted as a request for already calculated checksum value
);
if (ret == 0 && digest[0] != '\0') {
@@ -79,8 +80,26 @@ dav_error* dav_disk_digest_header(request_rec *r, const dav_resource *resource,
ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, resource->info->request,
"Failed to get the checksum %s: %s", digest_name, dmlite_error(resource->info->ctx));
} else {
- ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, resource->info->request,
- "Failed to get the checksum %s: empty value", digest_name);
+ if (!first_digest_name) {
+ // first checksum with empty value signalize it may be necessary
+ // to calculate missing checksum in case we don't find any other
+ // that was already calculated and it is now stored in database
+ first_digest_name = digest_name;
+ } else if (!(chksum_timeout < 0)) {
+ // only report problems in case of an attempt to calculate missing checksum
+ ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, resource->info->request,
+ "Failed to get the checksum %s: empty value", digest_name);
+ }
+ }
+
+ // forced checksum calculation is the last step after we already processed all digest names
+ if (!(chksum_timeout < 0))
+ break;
+
+ digest_name = strtok(NULL, ",");
+ if (!digest_name && first_digest_name) {
+ digest_name = first_digest_name;
+ chksum_timeout = 0; // calculate missing checksum with default timeout
}
}
diff --git a/src/plugins/apache-httpd/src/mod_lcgdm_disk/repository.c b/src/plugins/apache-httpd/src/mod_lcgdm_disk/repository.c
index d92b5cf5..343209cd 100644
--- a/src/plugins/apache-httpd/src/mod_lcgdm_disk/repository.c
+++ b/src/plugins/apache-httpd/src/mod_lcgdm_disk/repository.c
@@ -122,7 +122,7 @@ static int dav_finish_writing(dav_resource_private* info)
dmlite_fclose(info->fd);
info->fd = NULL;
}
- apr_table_unset(info->request->connection->notes, "dav_disk_info");
+ apr_table_unset(info->request->connection->notes, info->d_conf->info_key);
return e;
}
@@ -199,7 +199,7 @@ static dav_error *dav_disk_internal_get_resource(request_rec *r,
/* (Re?)Initialize info */
info->request = r;
info->method = r->method_number;
- info->query_str = apr_pstrdup(r->pool, r->parsed_uri.query);
+ info->query_str = apr_pstrdup(r->connection->pool, r->parsed_uri.query);
info->loc.nchunks = 1;
strncpy(info->loc.chunks[0].url.path, pfn, PATH_MAX);
info->loc.chunks[0].url.path[PATH_MAX - 1] = '\0';
@@ -267,7 +267,7 @@ static dav_error *dav_disk_internal_get_resource(request_rec *r,
/* Namespace URL */
sfn = apr_table_get(query, "dav_sfn");
if (sfn != NULL ) {
- info->namespace_path = apr_pstrdup(r->pool, sfn);
+ info->namespace_path = apr_pstrdup(r->connection->pool, sfn);
ap_unescape_url(info->namespace_path);
// this should be passed to dmlite according to LCGDM-1508
//apr_table_unset(query, "dav_sfn");
diff --git a/src/plugins/apache-httpd/src/mod_lcgdm_ns/checksum.c b/src/plugins/apache-httpd/src/mod_lcgdm_ns/checksum.c
index 98f80604..c9645aa1 100644
--- a/src/plugins/apache-httpd/src/mod_lcgdm_ns/checksum.c
+++ b/src/plugins/apache-httpd/src/mod_lcgdm_ns/checksum.c
@@ -31,16 +31,17 @@
dav_error* dav_ns_digest_header(request_rec *r, const dav_resource *resource,
char* output, size_t outsize)
{
- const char *want_digest;
- char digest_name[32], digest[1024], full_digest_name[64];
+ char sorted_digests[1024], *digest_name, *first_digest_name = NULL;
+ char digest[1024], full_digest_name[64];
+ int chksum_timeout = -1; // negative time can be interpreted as a request for already calculated checksum value
- want_digest = apr_table_get(r->headers_in, "Want-Digest");
-
- if (!want_digest) {
+ dav_shared_sorted_digests(resource->info->request, sorted_digests, 1024);
+ if (strlen(sorted_digests) == 0) {
return NULL;
- }
+ };
- while (dav_shared_next_digest(&want_digest, digest_name, sizeof(digest_name))) {
+ digest_name = strtok(sorted_digests, ",");
+ while (digest_name) {
snprintf(full_digest_name, sizeof(full_digest_name), "checksum.%s", digest_name);
int ret = dmlite_getchecksum(resource->info->ctx,
@@ -48,7 +49,7 @@ dav_error* dav_ns_digest_header(request_rec *r, const dav_resource *resource,
full_digest_name, digest, sizeof(digest),
NULL, // replica
0, // 0 = do not recalculate
- 0 // 1800 seconds wait, otherwise return EAGAIN
+ chksum_timeout // negative time can be interpreted as a request for already calculated checksum value
);
if (ret == DMLITE_RDR_ON_CHECKSUM) {
@@ -89,8 +90,26 @@ dav_error* dav_ns_digest_header(request_rec *r, const dav_resource *resource,
ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, resource->info->request,
"Failed to get the checksum %s: %s", digest_name, dmlite_error(resource->info->ctx));
} else {
- ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, resource->info->request,
- "Failed to get the checksum %s: empty value", digest_name);
+ if (!first_digest_name) {
+ // first checksum with empty value signalize it may be necessary
+ // to calculate missing checksum in case we don't find any other
+ // that was already calculated and it is now stored in database
+ first_digest_name = digest_name;
+ } else if (!(chksum_timeout < 0)) {
+ // only report problems in case of an attempt to calculate missing checksum
+ ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, resource->info->request,
+ "Failed to get the checksum %s: empty value", digest_name);
+ }
+ }
+
+ // forced checksum calculation is the last step after we already processed all digest names
+ if (!(chksum_timeout < 0))
+ break;
+
+ digest_name = strtok(NULL, ",");
+ if (!digest_name && first_digest_name) {
+ digest_name = first_digest_name;
+ chksum_timeout = 0; // calculate missing checksum with default timeout
}
}
diff --git a/src/plugins/apache-httpd/src/shared/checksum.c b/src/plugins/apache-httpd/src/shared/checksum.c
index 23d98c43..02da1f53 100644
--- a/src/plugins/apache-httpd/src/shared/checksum.c
+++ b/src/plugins/apache-httpd/src/shared/checksum.c
@@ -32,7 +32,15 @@
#include <openssl/bio.h>
#include <openssl/buffer.h>
-#define RFC3230_REGEXP "^([[:alnum:]]+)(;q=[[:digit:]]*)?([,]?)"
+#define RFC3230_REGEXP "^([[:alnum:]-]+)(;q=[[:digit:]]+\\.?[[:digit:]]*)?([,]?)"
+#define RFC3230_REGEXP_SINGLE "^[[:space:]]*([[:alnum:]-]+)(;q=([[:digit:]]+\\.?[[:digit:]]*))?[[:space:]]*$"
+
+ struct digest_list {
+ char *digest;
+ double q;
+ struct digest_list *next;
+};
+
static char* dav_shared_strcpy_lower(char *out, const char *in, size_t max)
{
@@ -78,6 +86,107 @@ int dav_shared_next_digest(const char **want_digest, char *output, size_t outsiz
}
+void dav_shared_sorted_digests(request_rec *r, char *output, size_t max)
+{
+ char *supported_digests[] = {
+ "md5", "crc32", "adler32", NULL
+ };
+
+ const char *want_digest;
+ char *s, *token;
+ struct digest_list *dl = NULL;
+
+ int status;
+ regex_t regex;
+ regmatch_t matches[4];
+
+ output[0] = '\0';
+ want_digest = apr_table_get(r->headers_in, "Want-Digest");
+
+ if (!want_digest) {
+ return;
+ }
+
+ if (regcomp(®ex, RFC3230_REGEXP_SINGLE, REG_EXTENDED) != 0)
+ abort();
+
+ // split want-digest string by comma
+ s = strdup(want_digest);
+ token = strtok(s, ",");
+
+ while (token) {
+ // parse checksum type and preference
+ status = regexec(®ex, token, 4, matches, 0);
+
+ if (status == 0) {
+ size_t digest_len = matches[1].rm_eo - matches[1].rm_so;
+ char *digest;
+ double q = 1;
+ int supported = 0;
+ int i;
+
+ digest = malloc(digest_len+1);
+ dav_shared_strcpy_lower(digest, token+matches[1].rm_so, digest_len);
+ digest[digest_len] = '\0';
+
+ for (i = 0; supported_digests[i]; ++i) {
+ if (strcmp(supported_digests[i], digest) == 0) {
+ supported = 1;
+ break;
+ }
+ }
+
+ if (supported) {
+ if (matches[3].rm_so < matches[3].rm_eo) {
+ const char *q_start = token + matches[3].rm_so;
+ token[matches[3].rm_eo] = '\0';
+ q = atof(q_start);
+ }
+ if (q < 0) q = 0;
+ if (q > 1) q = 1;
+
+ // add new checksum time in a list sorted by preference
+ struct digest_list *d = (struct digest_list*) malloc(sizeof(struct digest_list));
+ d->digest = digest;
+ d->q = q;
+ d->next = NULL;
+
+ if (dl) {
+ struct digest_list *p = dl;
+ while (p->q >= q && p->next) p = p->next;
+ d->next = p->next;
+ p->next = d;
+ } else {
+ dl = d;
+ }
+
+ } else {
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "Skipping unsupported checksum type %s", token);
+ }
+
+ } else {
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "Skipping unparsable checksum algorithm %s", token);
+ }
+
+ token = strtok(NULL, ",");
+ }
+
+ regfree(®ex);
+ free(s);
+
+ while (dl) {
+ if (strlen(output) + strlen(dl->digest) + 1 < max) {
+ if (output[0] != '\0') strcat(output, ",");
+ strcat(output, dl->digest);
+ } else {
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "Skipping digest %s because it exceeds max digest list size %lu", dl->digest, (unsigned long) max);
+ }
+ free(dl->digest);
+ dl = dl->next;
+ }
+}
+
+
static int
char_to_int(char c)
{
diff --git a/src/plugins/apache-httpd/src/shared/utils.h b/src/plugins/apache-httpd/src/shared/utils.h
index f8ff0a9a..658f973f 100644
--- a/src/plugins/apache-httpd/src/shared/utils.h
+++ b/src/plugins/apache-httpd/src/shared/utils.h
@@ -140,6 +140,14 @@ apr_table_t* dav_lcgdm_parse_cookies(apr_pool_t* pool, const char* cookie_str);
*/
int dav_shared_next_digest(const char **want_digest, char *output, size_t outsize);
+/**
+ * Return comma separated list of supported checksum types sorted by priority.
+ * @param r The request thar originated the error.
+ * @param output Pre-allocated char array for output sorted list of comma separated chksum types.
+ * @param max Length of pre-allocated output buffer.
+ */
+void dav_shared_sorted_digests(request_rec *r, char *output, size_t max);
+
/**
* Decode a hex digest array to base64 if needed. Returns the
* length of the resulting string, 0 if error