void testGet(dmlite_context* context) { SECTION("GET"); /* Mock returns always a location with two chunks */ dmlite_location* loc; TEST_CONTEXT_CALL_PTR(loc, context, dmlite_get, "/file"); TEST_ASSERT_EQUAL(2, loc->nchunks); TEST_ASSERT_STR_EQUAL("host1.cern.ch", loc->chunks[0].host); TEST_ASSERT_STR_EQUAL("/storage/chunk01", loc->chunks[0].path); TEST_ASSERT_EQUAL(0, loc->chunks[0].offset); TEST_ASSERT_EQUAL(100, loc->chunks[0].size); TEST_ASSERT_STR_EQUAL("host2.cern.ch", loc->chunks[1].host); TEST_ASSERT_STR_EQUAL("/storage/chunk02", loc->chunks[1].path); TEST_ASSERT_EQUAL(101, loc->chunks[1].offset); TEST_ASSERT_EQUAL(50, loc->chunks[1].size); /* Second has an extra */ char buffer[64]; dmlite_any* extra = dmlite_any_dict_get(loc->chunks[1].extra, "token"); dmlite_any_to_string(extra, buffer, sizeof(buffer)); TEST_ASSERT_STR_EQUAL("123456789", buffer); dmlite_any_free(extra); dmlite_location_free(context, loc); }
void testPut(dmlite_context* context) { char buffer[64]; SECTION("PUT"); /* Mock returns always one single location with one token */ dmlite_location* loc; TEST_CONTEXT_CALL_PTR(loc, context, dmlite_put, "/file"); TEST_ASSERT_EQUAL(1, loc->nchunks); TEST_ASSERT_STR_EQUAL("host1.cern.ch", loc->chunks[0].host); TEST_ASSERT_STR_EQUAL("/storage/chunk01", loc->chunks[0].path); TEST_ASSERT_EQUAL(0, loc->chunks[0].offset); TEST_ASSERT_EQUAL(0, loc->chunks[0].size); dmlite_any* extra = dmlite_any_dict_get(loc->chunks[0].extra, "token"); dmlite_any_to_string(extra, buffer, sizeof(buffer)); TEST_ASSERT_STR_EQUAL("987654321", buffer); dmlite_any_free(extra); dmlite_location_free(context, loc); /* A donewriting without token will fail */ dmlite_any_dict* dict = dmlite_any_dict_new(); TEST_ASSERT_EQUAL(DM_FORBIDDEN, dmlite_donewriting(context, "/storage/chunk01", dict)); /* With token */ dmlite_any* token = dmlite_any_new_string("987654321"); dmlite_any_dict_insert(dict, "token", token); TEST_CONTEXT_CALL(context, dmlite_donewriting, "/storage/chunk01", dict); dmlite_any_free(token); dmlite_any_dict_free(dict); }
/** * Open a stream. Used for PUT * @param resource The resource to open * @param mode The mode (basically, trucate or random, which we don't support) * @param stream Where to put the created structure * @return NULL on success */ static dav_error *dav_ns_open_stream(const dav_resource *resource, dav_stream_mode mode, dav_stream **stream) { dav_resource_private *info; unsigned has_range; const char *range, *length; int e; info = resource->info; /* Must be writable (if we are here, they want to write) */ if (!(info->d_conf->flags & DAV_NS_WRITE)) return dav_shared_new_error(info->request, NULL, HTTP_FORBIDDEN, "Configured as read-only endpoint (%s)", resource->uri); /* If content-length is 0, call create instead of put! */ length = apr_table_get(info->request->headers_in, "content-length"); if (length != NULL) { size_t clen = atol(length); if (clen == 0) { ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, info->request, "PUT with content-length 0. Creating ns entry."); if (dmlite_create(info->ctx, info->sfn, 0644) != 0) return dav_shared_new_error(info->request, info->ctx, 0, "Could not create empty file %s", info->sfn); *stream = (dav_stream*)calloc(1, sizeof(dav_stream)); return NULL; } } /* NS alone doesn't support PUTs with content! */ if (info->s_conf->type == DAV_NS_NODE_LFC) return dav_shared_new_error(info->request, NULL, HTTP_NOT_IMPLEMENTED, "LFC does not support PUTs"); /* Range header? */ range = apr_table_get(info->request->headers_in, "content-range"); has_range = (range != NULL); if (has_range) { ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, info->request, "Range: %s", range); } /* If token not set, call dm_put and see where do we have to go */ if (info->space_token != NULL) { dmlite_any* any = dmlite_any_new_string(info->space_token); e = dmlite_set(info->ctx, "SpaceToken", any); dmlite_any_free(any); if (e) return dav_shared_new_error(info->request, info->ctx, 0, "Could not set the space token %s", info->space_token); } dmlite_location *location; location = dmlite_put(info->ctx, info->sfn); switch (dmlite_errno(info->ctx)) { case 0: break; case DM_INVALID_VALUE: return dav_shared_new_error(info->request, info->ctx, HTTP_BAD_REQUEST, "Can not get the space token %s", info->space_token); default: return dav_shared_new_error(info->request, info->ctx, 0, "Can not put %s", info->sfn); } /* Redirect */ info->redirect = apr_psprintf(resource->pool, "%s://%s%s%s", info->d_conf->redir_scheme, location->chunks[0].host, location->chunks[0].path, dav_shared_build_extra(resource->pool, location->chunks[0].extra)); ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, info->request, "PUT request to be done in %s", info->redirect); apr_table_set(info->request->headers_out, "Location", info->redirect); dmlite_location_free(info->ctx, location); /* Standard says 301/302, but some clients will retry in that case with a GET. * 307 is unambiguous */ return dav_new_error(resource->pool, HTTP_TEMPORARY_REDIRECT, 0, info->redirect); }
/** * Get the location of the file * @param info Input/output parameter. sfn must be set. location will be set. * @param pool Pool for memory allocation * @return NULL on success */ dav_error *dav_ns_get_location(dav_resource_private *info, apr_pool_t *pool) { dmlite_replica *replicas; unsigned n_replicas, i; char *referrer, *p; dmlite_url url; /* If there is a rejected list, this is a fallback */ if ((info->s_conf->type == DAV_NS_NODE_LFC) && (info->forbidden_str != NULL || info->notfound_str != NULL)) { int64_t *rejected_ids, id; int n_forbidden = 0, n_notfound = 0, n_total = 0; /* Count first to allocate */ if (info->forbidden_str != NULL) { n_forbidden = 1; for (i = 0; info->forbidden_str[i] != '\0'; ++i) { if (info->forbidden_str[i] == ',') ++n_forbidden; } } if (info->notfound_str != NULL) { n_notfound = 1; for (i = 0; info->notfound_str[i] != '\0'; ++i) { if (info->notfound_str[i] == ',') ++n_notfound; } } /* Build rejected list */ n_total = n_forbidden + n_notfound; rejected_ids = apr_pcalloc(pool, sizeof(int64_t) * n_total); i = 0; p = (char*)info->forbidden_str; while (p != NULL && *p != '\0') { errno = 0; id = strtol(p, &p, 0); if (errno == 0) rejected_ids[i++] = id; if (*p != '\0') ++p; } p = (char*)info->notfound_str; while (p != NULL && *p != '\0') { errno = 0; id = strtol(p, &p, 0); if (errno == 0) rejected_ids[i++] = id; if (*p != '\0') ++p; } dmlite_any* any = dmlite_any_new_long_array(n_total, rejected_ids); i = dmlite_set(info->ctx, "ExcludeReplicas", any); dmlite_any_free(any); if (i != 0) { return dav_shared_new_error(info->request, info->ctx, 0, "Error on fall-back method"); } } /* Logic here depends on the node type */ dmlite_location *location; switch (info->s_conf->type) { case DAV_NS_NODE_HEAD: /* Ask for the best one */ location = dmlite_get(info->ctx, info->sfn); switch(dmlite_errno(info->ctx)) { case 0: break; case DM_FORBIDDEN: info->redirect = dav_shared_build_aggregation_url(info->request->pool, info->n_replicas, (const char**)info->replicas, info->replicas_ids, info->forbidden_str, info->notfound_str, info->r_id, -1); if (info->redirect != NULL) { ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, info->request, "Access forbidden for %s, forwarded to %s", info->sfn, info->redirect); return NULL; } default: return dav_shared_new_error(info->request, info->ctx, 0, NULL); } /* Destination URL */ info->redirect = apr_psprintf(pool, "%s://%s%s%s", info->d_conf->redir_scheme, location->chunks[0].host, location->chunks[0].path, dav_shared_build_extra(pool, location->chunks[0].extra)); dmlite_location_free(info->ctx, location); break; /* LFC wants the whole list */ case DAV_NS_NODE_LFC: if (dmlite_getreplicas(info->ctx, info->sfn, &n_replicas, &replicas) != 0) return dav_shared_new_error(info->request, info->ctx, 0, NULL); /* Initialize */ info->n_replicas = n_replicas + 1; info->forbidden_str = NULL; info->notfound_str = NULL; /* Build the referrer */ referrer = apr_psprintf(pool, "%s://%s:%u%s", info->d_conf->redir_scheme, info->request->hostname, info->request->server->port, info->request->uri); /* Build the replica list, skipping the first */ info->replicas = apr_pcalloc(pool, sizeof(char*) * info->n_replicas); info->replicas_ids = apr_pcalloc(pool, sizeof(int64_t) * info->n_replicas); for (i = 0; i < n_replicas && i < info->d_conf->max_replicas; ++i) { dmlite_parse_url(replicas[i].rfn, &url); info->replicas[i] = dav_shared_build_url(pool, &url, info->d_conf->redir_scheme); info->replicas_ids[i] = replicas[i].replicaid; } /* Append ourselves */ info->replicas[i] = referrer; info->replicas_ids[i] = -1; /* Build the definitive URL */ info->redirect = dav_shared_build_aggregation_url(info->request->pool, info->n_replicas, (const char**)info->replicas, info->replicas_ids, info->forbidden_str, info->notfound_str, -1, -1); /* Clean up */ dmlite_replicas_free(info->ctx, n_replicas, replicas); break; default: return dav_shared_new_error(info->request, NULL, HTTP_INTERNAL_SERVER_ERROR, "Invalid node type configured"); } return NULL; }